backport 3.20 changes
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Tue, 21 Jun 2016 07:42:30 +0200
changeset 11371 2cc16363d6a3
parent 11224 a4fcee1e9789 (diff)
parent 11236 241b319ac59c (current diff)
child 11372 4482e94daabe
child 11404 98eebbe3de23
backport 3.20 changes
.hgtags
__pkginfo__.py
cubicweb.spec
debian/changelog
server/session.py
web/views/basetemplates.py
--- a/.hgignore	Mon May 09 17:24:03 2016 +0200
+++ b/.hgignore	Tue Jun 21 07:42:30 2016 +0200
@@ -1,6 +1,8 @@
 \.svn
 ^build$
 ^dist$
+\.egg-info$
+^.tox$
 \.pyc$
 \.pyo$
 \.bak$
@@ -11,11 +13,16 @@
 ^doc/book/en/apidoc$
 \.old$
 syntax: regexp
-.*/data/database/.*\.sqlite
-.*/data/database/.*\.config
+.*/data.*/database/.*\.sqlite
+.*/data.*/database/.*\.config
 .*/data/database/tmpdb.*
 .*/data/ldapdb/.*
+.*/data/uicache/
+.*/data/cubes/.*/i18n/.*\.po
 ^doc/html/
 ^doc/doctrees/
 ^doc/book/en/devweb/js_api/
+^doc/_build
+^doc/js_api/
 data/pgdb/
+data.*/pgdb.*
--- a/.hgtags	Mon May 09 17:24:03 2016 +0200
+++ b/.hgtags	Tue Jun 21 07:42:30 2016 +0200
@@ -520,6 +520,28 @@
 f66a4895759e0913b1203943fc2cd7be1a821e05 3.20.14
 f66a4895759e0913b1203943fc2cd7be1a821e05 debian/3.20.14-1
 f66a4895759e0913b1203943fc2cd7be1a821e05 centos/3.20.14-1
+887c6eef807781560adcd4ecd2dea9011f5a6681 3.21.0
+887c6eef807781560adcd4ecd2dea9011f5a6681 debian/3.21.0-1
+887c6eef807781560adcd4ecd2dea9011f5a6681 centos/3.21.0-1
+a8a0de0298a58306d63dbc998ad60c48bf18c80a 3.21.1
+a8a0de0298a58306d63dbc998ad60c48bf18c80a debian/3.21.1-1
+a8a0de0298a58306d63dbc998ad60c48bf18c80a centos/3.21.1-1
+a5428e1ab36491a8e6d66ce09d23b708b97e1337 3.21.2
+a5428e1ab36491a8e6d66ce09d23b708b97e1337 debian/3.21.2-1
+a5428e1ab36491a8e6d66ce09d23b708b97e1337 centos/3.21.2-1
+9edfe9429209848e31d1998df48da7a84db0c819 3.21.3
+9edfe9429209848e31d1998df48da7a84db0c819 debian/3.21.3-1
+9edfe9429209848e31d1998df48da7a84db0c819 centos/3.21.3-1
+d3b92d3a7db098b25168beef9b3ee7b36263a652 3.21.4
+d3b92d3a7db098b25168beef9b3ee7b36263a652 debian/3.21.4-1
+d3b92d3a7db098b25168beef9b3ee7b36263a652 centos/3.21.4-1
+e0572a786e6b4b0965d405dd95cf5bce754005a2 3.21.5
+e0572a786e6b4b0965d405dd95cf5bce754005a2 debian/3.21.5-1
+e0572a786e6b4b0965d405dd95cf5bce754005a2 centos/3.21.5-1
+228b6d2777e44d7bc158d0b4579d09960acea926 debian/3.21.5-2
+b3cbbb7690b6e193570ffe4846615d372868a923 3.21.6
+b3cbbb7690b6e193570ffe4846615d372868a923 debian/3.21.6-1
+b3cbbb7690b6e193570ffe4846615d372868a923 centos/3.21.6-1
 636a83e65870433c2560f3c49d55ca628bc96e11 3.20.15
 636a83e65870433c2560f3c49d55ca628bc96e11 debian/3.20.15-1
 636a83e65870433c2560f3c49d55ca628bc96e11 centos/3.20.15-1
--- a/MANIFEST.in	Mon May 09 17:24:03 2016 +0200
+++ b/MANIFEST.in	Tue Jun 21 07:42:30 2016 +0200
@@ -6,9 +6,18 @@
 include man/cubicweb-ctl.1
 
 include doc/*.rst
+include doc/Makefile
 recursive-include doc/book *
 recursive-include doc/tools *.py
 recursive-include doc/tutorials *.rst *.py
+include doc/api/*.rst
+recursive-include doc/_themes *
+recursive-include doc/_static *
+include doc/_templates/*.html
+include doc/changes/*.rst
+recursive-include doc/dev .txt *.rst
+recursive-include doc/images *.png *.svg
+include doc/conf.py
 
 recursive-include misc *.py *.png *.display
 
@@ -25,18 +34,18 @@
 recursive-include sobjects/test/data bootstrap_cubes *.py
 recursive-include hooks/test/data bootstrap_cubes *.py
 recursive-include server/test/data bootstrap_cubes *.py source* *.conf.in *.ldif
-recursive-include devtools/test/data bootstrap_cubes *.py *.txt *.js
+recursive-include devtools/test/data bootstrap_cubes *.py *.txt *.js *.po.ref
 recursive-include web/test/data bootstrap_cubes pouet.css *.py
+recursive-include etwist/test/data *.py
 
 recursive-include web/test/jstests *.js *.html *.css *.json
 recursive-include web/test/windmill *.py
 
-recursive-include skeleton *.py *.css *.js *.po compat *.in *.tmpl
+recursive-include skeleton *.py *.css *.js *.po compat *.in *.tmpl rules
 
 prune doc/book/en/.static
 prune doc/book/fr/.static
 prune doc/html/_sources/
 prune misc/cwfs
-prune goa
-prune doc/book/en/devweb/js_api
+prune doc/js_api
 global-exclude *.pyc
--- a/README	Mon May 09 17:24:03 2016 +0200
+++ b/README	Tue Jun 21 07:42:30 2016 +0200
@@ -14,7 +14,7 @@
 Install
 -------
 
-More details at http://docs.cubicweb.org/admin/setup
+More details at http://docs.cubicweb.org/book/admin/setup
 
 Getting started
 ---------------
--- a/__pkginfo__.py	Mon May 09 17:24:03 2016 +0200
+++ b/__pkginfo__.py	Tue Jun 21 07:42:30 2016 +0200
@@ -22,7 +22,7 @@
 
 modname = distname = "cubicweb"
 
-numversion = (3, 20, 15)
+numversion = (3, 21, 6)
 version = '.'.join(str(num) for num in numversion)
 
 description = "a repository of entities / relations for knowledge management"
@@ -42,7 +42,7 @@
     'logilab-common': '>= 0.63.1',
     'logilab-mtconverter': '>= 0.8.0',
     'rql': '>= 0.31.2, < 0.34',
-    'yams': '>= 0.40.0, < 0.42',
+    'yams': '>= 0.40.0',
     #gettext                    # for xgettext, msgcat, etc...
     # web dependencies
     'lxml': '',
@@ -55,7 +55,6 @@
 
 __recommends__ = {
     'docutils': '>= 0.6',
-    'Pyro': '>= 3.9.1, < 4.0.0',
     'Pillow': '',               # for captcha
     'pycrypto': '',             # for crypto extensions
     'fyzz': '>= 0.1.0',         # for sparql
@@ -115,8 +114,6 @@
         [join('share', 'cubicweb', 'cubes', 'shared', 'data'),
          [join(_data_dir, fname) for fname in listdir(_data_dir)
           if not isdir(join(_data_dir, fname))]],
-        [join('share', 'cubicweb', 'cubes', 'shared', 'data', 'timeline'),
-         [join(_data_dir, 'timeline', fname) for fname in listdir(join(_data_dir, 'timeline'))]],
         [join('share', 'cubicweb', 'cubes', 'shared', 'data', 'images'),
          [join(_data_dir, 'images', fname) for fname in listdir(join(_data_dir, 'images'))]],
         [join('share', 'cubicweb', 'cubes', 'shared', 'data', 'jquery-treeview'),
--- a/_exceptions.py	Mon May 09 17:24:03 2016 +0200
+++ b/_exceptions.py	Tue Jun 21 07:42:30 2016 +0200
@@ -82,6 +82,8 @@
         self.session = session
         assert 'rtypes' in kwargs or 'cstrname' in kwargs
         self.kwargs = kwargs
+        # fill cache while the session is open
+        self.rtypes
 
     @cachedproperty
     def rtypes(self):
@@ -100,6 +102,12 @@
         return None, self.rtypes
 
 
+class ViolatedConstraint(RepositoryError):
+    def __init__(self, cnx, cstrname):
+        self.cnx = cnx
+        self.cstrname = cstrname
+
+
 # security exceptions #########################################################
 
 class Unauthorized(SecurityError):
--- a/_gcdebug.py	Mon May 09 17:24:03 2016 +0200
+++ b/_gcdebug.py	Tue Jun 21 07:42:30 2016 +0200
@@ -19,6 +19,10 @@
 import gc, types, weakref
 
 from cubicweb.schema import CubicWebRelationSchema, CubicWebEntitySchema
+try:
+    from cubicweb.web.request import _NeedAuthAccessMock
+except ImportError:
+    _NeedAuthAccessMock = None
 
 listiterator = type(iter([]))
 
@@ -30,6 +34,8 @@
     types.ModuleType, types.FunctionType, types.MethodType,
     types.MemberDescriptorType, types.GetSetDescriptorType,
     )
+if _NeedAuthAccessMock is not None:
+    IGNORE_CLASSES = IGNORE_CLASSES + (_NeedAuthAccessMock,)
 
 def _get_counted_class(obj, classes):
     for cls in classes:
@@ -63,7 +69,8 @@
                 ocounters[key] = 1
         if isinstance(obj, viewreferrersclasses):
             print '   ', obj, referrers(obj, showobjs, maxlevel)
-    return counters, ocounters, gc.garbage
+    garbage = [repr(obj) for obj in gc.garbage]
+    return counters, ocounters, garbage
 
 
 def referrers(obj, showobj=False, maxlevel=1):
--- a/appobject.py	Mon May 09 17:24:03 2016 +0200
+++ b/appobject.py	Tue Jun 21 07:42:30 2016 +0200
@@ -16,7 +16,6 @@
 # You should have received a copy of the GNU Lesser General Public License along
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
 """
-.. _appobject:
 
 The `AppObject` class
 ---------------------
@@ -27,7 +26,6 @@
 We can find a certain number of attributes and methods defined in this class and
 common to all the application objects.
 
-.. autoclass:: AppObject
 """
 __docformat__ = "restructuredtext en"
 
--- a/bin/clone_deps.py	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,117 +0,0 @@
-#!/usr/bin/python
-import sys
-
-from subprocess import call as sbp_call, Popen, PIPE
-from urllib import urlopen
-import os
-from os import path as osp, pardir, chdir
-
-
-def find_mercurial():
-    print "trying to find mercurial from the command line ..."
-    print '-' * 20
-    tryhg = sbp_call(['hg', '--version'])
-    if tryhg:
-        print 'mercurial seems to be unavailable, please install it'
-        raise
-    print '-' * 20
-    def hg_call(args):
-        return sbp_call(['hg'] + args)
-
-    return hg_call
-
-
-BASE_URL = 'http://www.logilab.org/hg/'
-
-to_clone = ['fyzz', 'yams', 'rql',
-            'logilab/common', 'logilab/constraint', 'logilab/database',
-            'logilab/devtools', 'logilab/mtconverter',
-            'cubes/blog', 'cubes/calendar', 'cubes/card', 'cubes/comment',
-            'cubes/datafeed', 'cubes/email', 'cubes/file', 'cubes/folder',
-            'cubes/forgotpwd', 'cubes/keyword', 'cubes/link', 'cubes/localperms',
-            'cubes/mailinglist', 'cubes/nosylist', 'cubes/person',
-            'cubes/preview', 'cubes/registration', 'cubes/rememberme',
-            'cubes/tag', 'cubes/vcsfile', 'cubes/zone']
-
-# a couple of functions to be used to explore available
-# repositories and cubes
-def list_repos(repos_root):
-    assert repos_root.startswith('http://')
-    hgwebdir_repos = (repo.strip()
-                      for repo in urlopen(repos_root + '?style=raw').readlines()
-                      if repo.strip())
-    prefix = osp.commonprefix(hgwebdir_repos)
-    return (repo[len(prefix):].strip('/')
-            for repo in hgwebdir_repos)
-
-def list_all_cubes(base_url=BASE_URL):
-    all_repos = list_repos(base_url)
-    #search for cubes
-    for repo in all_repos:
-        if repo.startswith('cubes'):
-            to_clone.append(repo)
-
-def get_latest_debian_tag(path):
-    proc = Popen(['hg', '-R', path, 'tags'], stdout=PIPE)
-    out, _err = proc.communicate()
-    for line in out.splitlines():
-        if 'debian-version' in line:
-            return line.split()[0]
-
-def main():
-    if len(sys.argv) == 1:
-        base_url = BASE_URL
-    elif len(sys.argv) == 2:
-        base_url = sys.argv[1]
-    else:
-        sys.stderr.write('usage %s [base_url]\n' %  sys.argv[0])
-        sys.exit(1)
-    hg_call = find_mercurial()
-    print len(to_clone), 'repositories will be cloned'
-    base_dir = osp.normpath(osp.join(osp.dirname(__file__), pardir, pardir))
-    chdir(base_dir)
-    not_updated = []
-    for repo in to_clone:
-        url = base_url + repo
-        if '/' not in repo:
-            target_path = repo
-        else:
-            assert repo.count('/') == 1, repo
-            directory, repo = repo.split('/')
-            if not osp.isdir(directory):
-                os.mkdir(directory)
-                open(osp.join(directory, '__init__.py'), 'w').close()
-            target_path = osp.join(directory, repo)
-        if osp.exists(target_path):
-            print target_path, 'seems already cloned. Skipping it.'
-        else:
-            hg_call(['clone', '-U', url, target_path])
-            tag = get_latest_debian_tag(target_path)
-            if tag:
-                print 'updating to', tag
-                hg_call(['update', '-R', target_path, tag])
-            else:
-                not_updated.append(target_path)
-    print """
-CubicWeb dependencies and standard set of cubes have been fetched and
-update to the latest stable version.
-
-You should ensure your PYTHONPATH contains `%(basedir)s`.
-You might want to read the environment configuration section of the documentation
-at http://docs.cubicweb.org/admin/setup.html#environment-configuration
-
-You can find more cubes at http://www.cubicweb.org.
-Clone them from `%(baseurl)scubes/` into the `%(basedir)s%(sep)scubes%(sep)s` directory.
-
-To get started you may read http://docs.cubicweb.org/tutorials/base/index.html.
-""" % {'basedir': os.getcwd(), 'baseurl': base_url, 'sep': os.sep}
-    if not_updated:
-        sys.stderr.write('WARNING: The following repositories were not updated (no debian tag found):\n')
-        for path in not_updated:
-            sys.stderr.write('\t-%s\n' % path)
-
-if __name__ == '__main__':
-    main()
-
-
-
--- a/cubicweb.spec	Mon May 09 17:24:03 2016 +0200
+++ b/cubicweb.spec	Tue Jun 21 07:42:30 2016 +0200
@@ -7,7 +7,7 @@
 %endif
 
 Name:           cubicweb
-Version:        3.20.15
+Version:        3.21.6
 Release:        logilab.1%{?dist}
 Summary:        CubicWeb is a semantic web application framework
 Source0:        http://download.logilab.org/pub/cubicweb/cubicweb-%{version}.tar.gz
--- a/cwconfig.py	Mon May 09 17:24:03 2016 +0200
+++ b/cwconfig.py	Tue Jun 21 07:42:30 2016 +0200
@@ -279,7 +279,7 @@
     ('default-text-format',
      {'type' : 'choice',
       'choices': ('text/plain', 'text/rest', 'text/html', 'text/markdown'),
-      'default': 'text/html', # use fckeditor in the web ui
+      'default': 'text/plain',
       'help': _('default text format for rich text fields.'),
       'group': 'ui',
       }),
@@ -835,7 +835,7 @@
 
     # set by upgrade command
     verbosity = 0
-
+    cmdline_options = None
     options = CubicWebNoAppConfiguration.options + (
         ('log-file',
          {'type' : 'string',
@@ -843,6 +843,13 @@
           'help': 'file where output logs should be written',
           'group': 'main', 'level': 2,
           }),
+        ('statsd-endpoint',
+         {'type' : 'string',
+          'default': '',
+          'help': 'UDP address of the statsd endpoint; it must be formatted'
+                  'like <ip>:<port>; disabled is unset.',
+          'group': 'main', 'level': 2,
+          }),
         # email configuration
         ('smtp-host',
          {'type' : 'string',
@@ -870,6 +877,18 @@
 the repository',
           'group': 'email', 'level': 1,
           }),
+        ('logstat-interval',
+         {'type' : 'int',
+          'default': 0,
+          'help': 'interval (in seconds) at which stats are dumped in the logstat file; set 0 to disable',
+          'group': 'main', 'level': 2,
+          }),
+        ('logstat-file',
+         {'type' : 'string',
+          'default': Method('default_stats_file'),
+          'help': 'file where stats for the instance should be written',
+          'group': 'main', 'level': 2,
+          }),
         )
 
     @classmethod
@@ -953,6 +972,13 @@
             log_path = os.path.join(_INSTALL_PREFIX, 'var', 'log', 'cubicweb', '%s-%s.log')
             return log_path % (self.appid, self.name)
 
+    def default_stats_file(self):
+        """return default path to the stats file of the instance'server"""
+        logfile = self.default_log_file()
+        if logfile.endswith('.log'):
+            logfile = logfile[:-4]
+        return logfile + '.stats'
+
     def default_pid_file(self):
         """return default path to the pid file of the instance'server"""
         if self.mode == 'system':
@@ -1010,7 +1036,7 @@
         # or site_cubicweb files
         self.load_file_configuration(self.main_config_file())
         # configuration initialization hook
-        self.load_configuration()
+        self.load_configuration(**(self.cmdline_options or {}))
 
     def add_cubes(self, cubes):
         """add given cubes to the list of used cubes"""
@@ -1077,9 +1103,9 @@
         infos.append('cubicweb-%s' % str(self.cubicweb_version()))
         return md5(';'.join(infos)).hexdigest()
 
-    def load_configuration(self):
+    def load_configuration(self, **kw):
         """load instance's configuration files"""
-        super(CubicWebConfiguration, self).load_configuration()
+        super(CubicWebConfiguration, self).load_configuration(**kw)
         if self.apphome and not self.creating:
             # init gettext
             self._gettext_init()
@@ -1102,6 +1128,17 @@
         logconfig = join(self.apphome, 'logging.conf')
         if exists(logconfig):
             logging.config.fileConfig(logconfig)
+        # set the statsd address, if any
+        if self.get('statsd-endpoint'):
+            try:
+                address, port = self.get('statsd-endpoint').split(':')
+                port = int(port)
+            except:
+                self.error('statsd-endpoint: invalid address format ({}); '
+                           'it should be "ip:port"'.format(self.get('statsd-endpoint')))
+            else:
+                import statsd_logger
+                statsd_logger.setup('cubicweb.%s' % self.appid, (address, port))
 
     def available_languages(self, *args):
         """return available translation for an instance, by looking for
--- a/cwctl.py	Mon May 09 17:24:03 2016 +0200
+++ b/cwctl.py	Tue Jun 21 07:42:30 2016 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -25,7 +25,7 @@
 # possible (for cubicweb-ctl reactivity, necessary for instance for usable bash
 # completion). So import locally in command helpers.
 import sys
-from warnings import warn
+from warnings import warn, filterwarnings
 from os import remove, listdir, system, pathsep
 from os.path import exists, join, isfile, isdir, dirname, abspath
 from urlparse import urlparse
@@ -401,7 +401,7 @@
                            if 'type' in odict
                            and odict.get('level') <= self.config.config_level)
             for section in sections:
-                if section not in ('main', 'email', 'pyro', 'web'):
+                if section not in ('main', 'email', 'web'):
                     print '\n' + underline_title('%s options' % section)
                     config.input_config(section, self.config.config_level)
         # write down configuration
@@ -520,7 +520,12 @@
           'default': None, 'choices': ('debug', 'info', 'warning', 'error'),
           'help': 'debug if -D is set, error otherwise',
           }),
-        )
+        ('param',
+         {'short': 'p', 'type' : 'named', 'metavar' : 'key1:value1,key2:value2',
+          'default': {},
+          'help': 'override <key> configuration file option with <value>.',
+         }),
+       )
 
     def start_instance(self, appid):
         """start the instance's server"""
@@ -534,6 +539,8 @@
                 "- '{ctl} pyramid {appid}' (requires the pyramid cube)\n")
             raise ExecutionError(msg.format(ctl='cubicweb-ctl', appid=appid))
         config = cwcfg.config_for(appid, debugmode=self['debug'])
+        # override config file values with cmdline options
+        config.cmdline_options = self.config.param
         init_cmdline_log_threshold(config, self['loglevel'])
         if self['profile']:
             config.global_set_option('profile', self.config.profile)
@@ -900,9 +907,7 @@
         ('repo-uri',
          {'short': 'H', 'type' : 'string', 'metavar': '<protocol>://<[host][:port]>',
           'help': 'URI of the CubicWeb repository to connect to. URI can be \
-pyro://[host:port] the Pyro name server host; if the pyro nameserver is not set, \
-it will be detected by using a broadcast query, a ZMQ URL or \
-inmemory:// (default) use an in-memory repository. THIS OPTION IS DEPRECATED, \
+a ZMQ URL or inmemory:// (default) use an in-memory repository. THIS OPTION IS DEPRECATED, \
 directly give URI as instance id instead',
           'group': 'remote'
           }),
@@ -953,7 +958,7 @@
         if self.config.repo_uri:
             warn('[3.16] --repo-uri option is deprecated, directly give the URI as instance id',
                  DeprecationWarning)
-            if urlparse(self.config.repo_uri).scheme in ('pyro', 'inmemory'):
+            if urlparse(self.config.repo_uri).scheme == 'inmemory':
                 appuri = '%s/%s' % (self.config.repo_uri.rstrip('/'), appuri)
 
         from cubicweb.utils import parse_repo_uri
@@ -1135,6 +1140,7 @@
     import os
     sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
     sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', 0)
+    filterwarnings('default', category=DeprecationWarning)
     cwcfg.load_cwctl_plugins()
     try:
         CWCTL.run(args)
--- a/cwvreg.py	Mon May 09 17:24:03 2016 +0200
+++ b/cwvreg.py	Tue Jun 21 07:42:30 2016 +0200
@@ -15,179 +15,8 @@
 #
 # You should have received a copy of the GNU Lesser General Public License along
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-""".. RegistryStore:
-
-The `RegistryStore`
--------------------
-
-The `RegistryStore` can be seen as a two-level dictionary. It contains
-all dynamically loaded objects (subclasses of :ref:`appobject`) to
-build a |cubicweb| application. Basically:
-
-* the first level key returns a *registry*. This key corresponds to the
-  `__registry__` attribute of application object classes
-
-* the second level key returns a list of application objects which
-  share the same identifier. This key corresponds to the `__regid__`
-  attribute of application object classes.
-
-A *registry* holds a specific kind of application objects. There is
-for instance a registry for entity classes, another for views, etc...
-
-The `RegistryStore` has two main responsibilities:
-
-- being the access point to all registries
-
-- handling the registration process at startup time, and during automatic
-  reloading in debug mode.
-
-.. _AppObjectRecording:
-
-Details of the recording process
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. index::
-   vregistry: registration_callback
-
-On startup, |cubicweb| loads application objects defined in its library
-and in cubes used by the instance. Application objects from the
-library are loaded first, then those provided by cubes are loaded in
-dependency order (e.g. if your cube depends on an other, objects from
-the dependency will be loaded first). The layout of the modules or packages
-in a cube  is explained in :ref:`cubelayout`.
-
-For each module:
-
-* by default all objects are registered automatically
-
-* if some objects have to replace other objects, or have to be
-  included only if some condition is met, you'll have to define a
-  `registration_callback(vreg)` function in your module and explicitly
-  register **all objects** in this module, using the api defined
-  below.
-
-.. Note::
-    Once the function `registration_callback(vreg)` is implemented in a module,
-    all the objects from this module have to be explicitly registered as it
-    disables the automatic objects registration.
-
-
-API for objects registration
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Here are the registration methods that you can use in the `registration_callback`
-to register your objects to the `RegistryStore` instance given as argument (usually
-named `vreg`):
-
-.. automethod:: cubicweb.cwvreg.CWRegistryStore.register_all
-.. automethod:: cubicweb.cwvreg.CWRegistryStore.register_and_replace
-.. automethod:: cubicweb.cwvreg.CWRegistryStore.register
-.. automethod:: cubicweb.cwvreg.CWRegistryStore.unregister
-
-Examples:
-
-.. sourcecode:: python
-
-   # web/views/basecomponents.py
-   def registration_callback(vreg):
-      # register everything in the module except SeeAlsoComponent
-      vreg.register_all(globals().itervalues(), __name__, (SeeAlsoVComponent,))
-      # conditionally register SeeAlsoVComponent
-      if 'see_also' in vreg.schema:
-          vreg.register(SeeAlsoVComponent)
-
-In this example, we register all application object classes defined in the module
-except `SeeAlsoVComponent`. This class is then registered only if the 'see_also'
-relation type is defined in the instance'schema.
-
-.. sourcecode:: python
-
-   # goa/appobjects/sessions.py
-   def registration_callback(vreg):
-      vreg.register(SessionsCleaner)
-      # replace AuthenticationManager by GAEAuthenticationManager
-      vreg.register_and_replace(GAEAuthenticationManager, AuthenticationManager)
-      # replace PersistentSessionManager by GAEPersistentSessionManager
-      vreg.register_and_replace(GAEPersistentSessionManager, PersistentSessionManager)
-
-In this example, we explicitly register classes one by one:
-
-* the `SessionCleaner` class
-* the `GAEAuthenticationManager` to replace the `AuthenticationManager`
-* the `GAEPersistentSessionManager` to replace the `PersistentSessionManager`
-
-If at some point we register a new appobject class in this module, it won't be
-registered at all without modification to the `registration_callback`
-implementation. The previous example will register it though, thanks to the call
-to the `register_all` method.
-
-
-.. _Selection:
-
-Runtime objects selection
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Now that we have all application objects loaded, the question is : when
-I want some specific object, for instance the primary view for a given
-entity, how do I get the proper object ? This is what we call the
-**selection mechanism**.
-
-As explained in the :ref:`Concepts` section:
-
-* each application object has a **selector**, defined by its
-  `__select__` class attribute
-
-* this selector is responsible to return a **score** for a given context
-
-  - 0 score means the object doesn't apply to this context
-
-  - else, the higher the score, the better the object suits the context
-
-* the object with the highest score is selected.
-
-.. Note::
-
-  When no single object has the highest score, an exception is raised in development
-  mode to let you know that the engine was not able to identify the view to
-  apply. This error is silenced in production mode and one of the objects with
-  the highest score is picked.
-
-  In such cases you would need to review your design and make sure
-  your selectors or appobjects are properly defined. Such an error is
-  typically caused by either forgetting to change the __regid__ in a
-  derived class, or by having copy-pasted some code.
-
-For instance, if you are selecting the primary (`__regid__ =
-'primary'`) view (`__registry__ = 'views'`) for a result set
-containing a `Card` entity, two objects will probably be selectable:
-
-* the default primary view (`__select__ = is_instance('Any')`), meaning
-  that the object is selectable for any kind of entity type
-
-* the specific `Card` primary view (`__select__ = is_instance('Card')`,
-  meaning that the object is selectable for Card entities
-
-Other primary views specific to other entity types won't be selectable in this
-case. Among selectable objects, the `is_instance('Card')` selector will return a higher
-score since it's more specific, so the correct view will be selected as expected.
-
-.. _SelectionAPI:
-
-API for objects selections
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Here is the selection API you'll get on every registry. Some of them, as the
-'etypes' registry, containing entity classes, extend it. In those methods,
-`*args, **kwargs` is what we call the **context**. Those arguments are given to
-selectors that will inspect their content and return a score accordingly.
-
-.. automethod:: cubicweb.vregistry.Registry.select
-
-.. automethod:: cubicweb.vregistry.Registry.select_or_none
-
-.. automethod:: cubicweb.vregistry.Registry.possible_objects
-
-.. automethod:: cubicweb.vregistry.Registry.object_by_id
+"""
+Cubicweb registries
 """
 
 __docformat__ = "restructuredtext en"
@@ -229,6 +58,7 @@
     sys.modules.pop('cubicweb.web.uicfg', None)
     sys.modules.pop('cubicweb.web.uihelper', None)
 
+
 def require_appobject(obj):
     """return appobjects required by the given object by searching for
     `appobject_selectable` predicate
@@ -241,11 +71,16 @@
 
 class CWRegistry(Registry):
     def __init__(self, vreg):
+        """
+        :param vreg: the :py:class:`CWRegistryStore` managing this registry.
+        """
         super(CWRegistry, self).__init__(True)
         self.vreg = vreg
 
     @property
     def schema(self):
+        """The :py:class:`cubicweb.schema.CubicWebSchema`
+        """
         return self.vreg.schema
 
     def poss_visible_objects(self, *args, **kwargs):
@@ -269,7 +104,7 @@
 
     def selected(self, winner, args, kwargs):
         """overriden to avoid the default 'instanciation' behaviour, ie
-        winner(*args, **kwargs)
+        `winner(*args, **kwargs)`
         """
         return winner
 
--- a/dataimport.py	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1174 +0,0 @@
-# -*- coding: utf-8 -*-
-# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of CubicWeb.
-#
-# CubicWeb is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""This module provides tools to import tabular data.
-
-
-Example of use (run this with `cubicweb-ctl shell instance import-script.py`):
-
-.. sourcecode:: python
-
-  from cubicweb.dataimport import *
-  # define data generators
-  GENERATORS = []
-
-  USERS = [('Prenom', 'firstname', ()),
-           ('Nom', 'surname', ()),
-           ('Identifiant', 'login', ()),
-           ]
-
-  def gen_users(ctl):
-      for row in ctl.iter_and_commit('utilisateurs'):
-          entity = mk_entity(row, USERS)
-          entity['upassword'] = 'motdepasse'
-          ctl.check('login', entity['login'], None)
-          entity = ctl.store.create_entity('CWUser', **entity)
-          email = ctl.store.create_entity('EmailAddress', address=row['email'])
-          ctl.store.relate(entity.eid, 'use_email', email.eid)
-          ctl.store.rql('SET U in_group G WHERE G name "users", U eid %(x)s', {'x':entity['eid']})
-
-  CHK = [('login', check_doubles, 'Utilisateurs Login',
-          'Deux utilisateurs ne devraient pas avoir le même login.'),
-         ]
-
-  GENERATORS.append( (gen_users, CHK) )
-
-  # create controller
-  ctl = CWImportController(RQLObjectStore(cnx))
-  ctl.askerror = 1
-  ctl.generators = GENERATORS
-  ctl.data['utilisateurs'] = lazytable(ucsvreader(open('users.csv')))
-  # run
-  ctl.run()
-
-.. BUG file with one column are not parsable
-.. TODO rollback() invocation is not possible yet
-"""
-__docformat__ = "restructuredtext en"
-
-import csv
-import sys
-import threading
-import traceback
-import warnings
-import cPickle
-import os.path as osp
-import inspect
-from base64 import b64encode
-from collections import defaultdict
-from copy import copy
-from datetime import date, datetime, time
-from time import asctime
-from StringIO import StringIO
-
-from logilab.common import shellutils, attrdict
-from logilab.common.date import strptime
-from logilab.common.decorators import cached
-from logilab.common.deprecation import deprecated
-
-from cubicweb import QueryError
-from cubicweb.utils import make_uid
-from cubicweb.schema import META_RTYPES, VIRTUAL_RTYPES
-from cubicweb.server.edition import EditedEntity
-from cubicweb.server.sqlutils import SQL_PREFIX
-from cubicweb.server.utils import eschema_eid
-
-
-def count_lines(stream_or_filename):
-    if isinstance(stream_or_filename, basestring):
-        f = open(stream_or_filename)
-    else:
-        f = stream_or_filename
-        f.seek(0)
-    for i, line in enumerate(f):
-        pass
-    f.seek(0)
-    return i+1
-
-def ucsvreader_pb(stream_or_path, encoding='utf-8', delimiter=',', quotechar='"',
-                  skipfirst=False, withpb=True, skip_empty=True, separator=None,
-                  quote=None):
-    """same as :func:`ucsvreader` but a progress bar is displayed as we iter on rows"""
-    if separator is not None:
-        delimiter = separator
-        warnings.warn("[3.20] 'separator' kwarg is deprecated, use 'delimiter' instead")
-    if quote is not None:
-        quotechar = quote
-        warnings.warn("[3.20] 'quote' kwarg is deprecated, use 'quotechar' instead")
-    if isinstance(stream_or_path, basestring):
-        if not osp.exists(stream_or_path):
-            raise Exception("file doesn't exists: %s" % stream_or_path)
-        stream = open(stream_or_path)
-    else:
-        stream = stream_or_path
-    rowcount = count_lines(stream)
-    if skipfirst:
-        rowcount -= 1
-    if withpb:
-        pb = shellutils.ProgressBar(rowcount, 50)
-    for urow in ucsvreader(stream, encoding, delimiter, quotechar,
-                           skipfirst=skipfirst, skip_empty=skip_empty):
-        yield urow
-        if withpb:
-            pb.update()
-    print ' %s rows imported' % rowcount
-
-def ucsvreader(stream, encoding='utf-8', delimiter=',', quotechar='"',
-               skipfirst=False, ignore_errors=False, skip_empty=True,
-               separator=None, quote=None):
-    """A csv reader that accepts files with any encoding and outputs unicode
-    strings
-
-    if skip_empty (the default), lines without any values specified (only
-    separators) will be skipped. This is useful for Excel exports which may be
-    full of such lines.
-    """
-    if separator is not None:
-        delimiter = separator
-        warnings.warn("[3.20] 'separator' kwarg is deprecated, use 'delimiter' instead")
-    if quote is not None:
-        quotechar = quote
-        warnings.warn("[3.20] 'quote' kwarg is deprecated, use 'quotechar' instead")
-    it = iter(csv.reader(stream, delimiter=delimiter, quotechar=quotechar))
-    if not ignore_errors:
-        if skipfirst:
-            it.next()
-        for row in it:
-            decoded = [item.decode(encoding) for item in row]
-            if not skip_empty or any(decoded):
-                yield decoded
-    else:
-        if skipfirst:
-            try:
-                row = it.next()
-            except csv.Error:
-                pass
-        # Safe version, that can cope with error in CSV file
-        while True:
-            try:
-                row = it.next()
-            # End of CSV, break
-            except StopIteration:
-                break
-            # Error in CSV, ignore line and continue
-            except csv.Error:
-                continue
-            decoded = [item.decode(encoding) for item in row]
-            if not skip_empty or any(decoded):
-                yield decoded
-
-
-def callfunc_every(func, number, iterable):
-    """yield items of `iterable` one by one and call function `func`
-    every `number` iterations. Always call function `func` at the end.
-    """
-    for idx, item in enumerate(iterable):
-        yield item
-        if not idx % number:
-            func()
-    func()
-
-def lazytable(reader):
-    """The first row is taken to be the header of the table and
-    used to output a dict for each row of data.
-
-    >>> data = lazytable(ucsvreader(open(filename)))
-    """
-    header = reader.next()
-    for row in reader:
-        yield dict(zip(header, row))
-
-def lazydbtable(cu, table, headers, orderby=None):
-    """return an iterator on rows of a sql table. On each row, fetch columns
-    defined in headers and return values as a dictionary.
-
-    >>> data = lazydbtable(cu, 'experimentation', ('id', 'nickname', 'gps'))
-    """
-    sql = 'SELECT %s FROM %s' % (','.join(headers), table,)
-    if orderby:
-        sql += ' ORDER BY %s' % ','.join(orderby)
-    cu.execute(sql)
-    while True:
-        row = cu.fetchone()
-        if row is None:
-            break
-        yield dict(zip(headers, row))
-
-def mk_entity(row, map):
-    """Return a dict made from sanitized mapped values.
-
-    ValueError can be raised on unexpected values found in checkers
-
-    >>> row = {'myname': u'dupont'}
-    >>> map = [('myname', u'name', (call_transform_method('title'),))]
-    >>> mk_entity(row, map)
-    {'name': u'Dupont'}
-    >>> row = {'myname': u'dupont', 'optname': u''}
-    >>> map = [('myname', u'name', (call_transform_method('title'),)),
-    ...        ('optname', u'MARKER', (optional,))]
-    >>> mk_entity(row, map)
-    {'name': u'Dupont', 'optname': None}
-    """
-    res = {}
-    assert isinstance(row, dict)
-    assert isinstance(map, list)
-    for src, dest, funcs in map:
-        try:
-            res[dest] = row[src]
-        except KeyError:
-            continue
-        try:
-            for func in funcs:
-                res[dest] = func(res[dest])
-                if res[dest] is None:
-                    break
-        except ValueError as err:
-            raise ValueError('error with %r field: %s' % (src, err)), None, sys.exc_info()[-1]
-    return res
-
-# user interactions ############################################################
-
-def tell(msg):
-    print msg
-
-def confirm(question):
-    """A confirm function that asks for yes/no/abort and exits on abort."""
-    answer = shellutils.ASK.ask(question, ('Y', 'n', 'abort'), 'Y')
-    if answer == 'abort':
-        sys.exit(1)
-    return answer == 'Y'
-
-
-class catch_error(object):
-    """Helper for @contextmanager decorator."""
-
-    def __init__(self, ctl, key='unexpected error', msg=None):
-        self.ctl = ctl
-        self.key = key
-        self.msg = msg
-
-    def __enter__(self):
-        return self
-
-    def __exit__(self, type, value, traceback):
-        if type is not None:
-            if issubclass(type, (KeyboardInterrupt, SystemExit)):
-                return # re-raise
-            if self.ctl.catcherrors:
-                self.ctl.record_error(self.key, None, type, value, traceback)
-                return True # silent
-
-
-# base sanitizing/coercing functions ###########################################
-
-def optional(value):
-    """checker to filter optional field
-
-    If value is undefined (ex: empty string), return None that will
-    break the checkers validation chain
-
-    General use is to add 'optional' check in first condition to avoid
-    ValueError by further checkers
-
-    >>> MAPPER = [(u'value', 'value', (optional, int))]
-    >>> row = {'value': u'XXX'}
-    >>> mk_entity(row, MAPPER)
-    {'value': None}
-    >>> row = {'value': u'100'}
-    >>> mk_entity(row, MAPPER)
-    {'value': 100}
-    """
-    if value:
-        return value
-    return None
-
-def required(value):
-    """raise ValueError if value is empty
-
-    This check should be often found in last position in the chain.
-    """
-    if value:
-        return value
-    raise ValueError("required")
-
-def todatetime(format='%d/%m/%Y'):
-    """return a transformation function to turn string input value into a
-    `datetime.datetime` instance, using given format.
-
-    Follow it by `todate` or `totime` functions from `logilab.common.date` if
-    you want a `date`/`time` instance instead of `datetime`.
-    """
-    def coerce(value):
-        return strptime(value, format)
-    return coerce
-
-def call_transform_method(methodname, *args, **kwargs):
-    """return value returned by calling the given method on input"""
-    def coerce(value):
-        return getattr(value, methodname)(*args, **kwargs)
-    return coerce
-
-def call_check_method(methodname, *args, **kwargs):
-    """check value returned by calling the given method on input is true,
-    else raise ValueError
-    """
-    def check(value):
-        if getattr(value, methodname)(*args, **kwargs):
-            return value
-        raise ValueError('%s not verified on %r' % (methodname, value))
-    return check
-
-# base integrity checking functions ############################################
-
-def check_doubles(buckets):
-    """Extract the keys that have more than one item in their bucket."""
-    return [(k, len(v)) for k, v in buckets.items() if len(v) > 1]
-
-def check_doubles_not_none(buckets):
-    """Extract the keys that have more than one item in their bucket."""
-    return [(k, len(v)) for k, v in buckets.items()
-            if k is not None and len(v) > 1]
-
-# sql generator utility functions #############################################
-
-
-def _import_statements(sql_connect, statements, nb_threads=3,
-                       dump_output_dir=None,
-                       support_copy_from=True, encoding='utf-8'):
-    """
-    Import a bunch of sql statements, using different threads.
-    """
-    try:
-        chunksize = (len(statements) / nb_threads) + 1
-        threads = []
-        for i in xrange(nb_threads):
-            chunks = statements[i*chunksize:(i+1)*chunksize]
-            thread = threading.Thread(target=_execmany_thread,
-                                      args=(sql_connect, chunks,
-                                            dump_output_dir,
-                                            support_copy_from,
-                                            encoding))
-            thread.start()
-            threads.append(thread)
-        for t in threads:
-            t.join()
-    except Exception:
-        print 'Error in import statements'
-
-def _execmany_thread_not_copy_from(cu, statement, data, table=None,
-                                   columns=None, encoding='utf-8'):
-    """ Execute thread without copy from
-    """
-    cu.executemany(statement, data)
-
-def _execmany_thread_copy_from(cu, statement, data, table,
-                               columns, encoding='utf-8'):
-    """ Execute thread with copy from
-    """
-    try:
-        buf = _create_copyfrom_buffer(data, columns, encoding=encoding)
-    except ValueError:
-        _execmany_thread_not_copy_from(cu, statement, data)
-    else:
-        if columns is None:
-            cu.copy_from(buf, table, null='NULL')
-        else:
-            cu.copy_from(buf, table, null='NULL', columns=columns)
-
-def _execmany_thread(sql_connect, statements, dump_output_dir=None,
-                     support_copy_from=True, encoding='utf-8'):
-    """
-    Execute sql statement. If 'INSERT INTO', try to use 'COPY FROM' command,
-    or fallback to execute_many.
-    """
-    if support_copy_from:
-        execmany_func = _execmany_thread_copy_from
-    else:
-        execmany_func = _execmany_thread_not_copy_from
-    cnx = sql_connect()
-    cu = cnx.cursor()
-    try:
-        for statement, data in statements:
-            table = None
-            columns = None
-            try:
-                if not statement.startswith('INSERT INTO'):
-                    cu.executemany(statement, data)
-                    continue
-                table = statement.split()[2]
-                if isinstance(data[0], (tuple, list)):
-                    columns = None
-                else:
-                    columns = list(data[0])
-                execmany_func(cu, statement, data, table, columns, encoding)
-            except Exception:
-                print 'unable to copy data into table %s' % table
-                # Error in import statement, save data in dump_output_dir
-                if dump_output_dir is not None:
-                    pdata = {'data': data, 'statement': statement,
-                             'time': asctime(), 'columns': columns}
-                    filename = make_uid()
-                    try:
-                        with open(osp.join(dump_output_dir,
-                                           '%s.pickle' % filename), 'w') as fobj:
-                            fobj.write(cPickle.dumps(pdata))
-                    except IOError:
-                        print 'ERROR while pickling in', dump_output_dir, filename+'.pickle'
-                        pass
-                cnx.rollback()
-                raise
-    finally:
-        cnx.commit()
-        cu.close()
-
-
-def _copyfrom_buffer_convert_None(value, **opts):
-    '''Convert None value to "NULL"'''
-    return 'NULL'
-
-def _copyfrom_buffer_convert_number(value, **opts):
-    '''Convert a number into its string representation'''
-    return str(value)
-
-def _copyfrom_buffer_convert_string(value, **opts):
-    '''Convert string value.
-
-    Recognized keywords:
-    :encoding: resulting string encoding (default: utf-8)
-    :replace_sep: character used when input contains characters
-                  that conflict with the column separator.
-    '''
-    encoding = opts.get('encoding','utf-8')
-    replace_sep = opts.get('replace_sep', None)
-    # Remove separators used in string formatting
-    for _char in (u'\t', u'\r', u'\n'):
-        if _char in value:
-            # If a replace_sep is given, replace
-            # the separator
-            # (and thus avoid empty buffer)
-            if replace_sep is None:
-                raise ValueError('conflicting separator: '
-                                 'you must provide the replace_sep option')
-            value = value.replace(_char, replace_sep)
-        value = value.replace('\\', r'\\')
-    if isinstance(value, unicode):
-        value = value.encode(encoding)
-    return value
-
-def _copyfrom_buffer_convert_date(value, **opts):
-    '''Convert date into "YYYY-MM-DD"'''
-    # Do not use strftime, as it yields issue with date < 1900
-    # (http://bugs.python.org/issue1777412)
-    return '%04d-%02d-%02d' % (value.year, value.month, value.day)
-
-def _copyfrom_buffer_convert_datetime(value, **opts):
-    '''Convert date into "YYYY-MM-DD HH:MM:SS.UUUUUU"'''
-    # Do not use strftime, as it yields issue with date < 1900
-    # (http://bugs.python.org/issue1777412)
-    return '%s %s' % (_copyfrom_buffer_convert_date(value, **opts),
-                      _copyfrom_buffer_convert_time(value, **opts))
-
-def _copyfrom_buffer_convert_time(value, **opts):
-    '''Convert time into "HH:MM:SS.UUUUUU"'''
-    return '%02d:%02d:%02d.%06d' % (value.hour, value.minute,
-                                    value.second, value.microsecond)
-
-# (types, converter) list.
-_COPYFROM_BUFFER_CONVERTERS = [
-    (type(None), _copyfrom_buffer_convert_None),
-    ((long, int, float), _copyfrom_buffer_convert_number),
-    (basestring, _copyfrom_buffer_convert_string),
-    (datetime, _copyfrom_buffer_convert_datetime),
-    (date, _copyfrom_buffer_convert_date),
-    (time, _copyfrom_buffer_convert_time),
-]
-
-def _create_copyfrom_buffer(data, columns=None, **convert_opts):
-    """
-    Create a StringIO buffer for 'COPY FROM' command.
-    Deals with Unicode, Int, Float, Date... (see ``converters``)
-
-    :data: a sequence/dict of tuples
-    :columns: list of columns to consider (default to all columns)
-    :converter_opts: keyword arguements given to converters
-    """
-    # Create a list rather than directly create a StringIO
-    # to correctly write lines separated by '\n' in a single step
-    rows = []
-    if columns is None:
-        if isinstance(data[0], (tuple, list)):
-            columns = range(len(data[0]))
-        elif isinstance(data[0], dict):
-            columns = data[0].keys()
-        else:
-            raise ValueError('Could not get columns: you must provide columns.')
-    for row in data:
-        # Iterate over the different columns and the different values
-        # and try to convert them to a correct datatype.
-        # If an error is raised, do not continue.
-        formatted_row = []
-        for col in columns:
-            try:
-                value = row[col]
-            except KeyError:
-                warnings.warn(u"Column %s is not accessible in row %s"
-                              % (col, row), RuntimeWarning)
-                # XXX 'value' set to None so that the import does not end in
-                # error.
-                # Instead, the extra keys are set to NULL from the
-                # database point of view.
-                value = None
-            for types, converter in _COPYFROM_BUFFER_CONVERTERS:
-                if isinstance(value, types):
-                    value = converter(value, **convert_opts)
-                    break
-            else:
-                raise ValueError("Unsupported value type %s" % type(value))
-            # We push the value to the new formatted row
-            # if the value is not None and could be converted to a string.
-            formatted_row.append(value)
-        rows.append('\t'.join(formatted_row))
-    return StringIO('\n'.join(rows))
-
-
-# object stores #################################################################
-
-class ObjectStore(object):
-    """Store objects in memory for *faster* validation (development mode)
-
-    But it will not enforce the constraints of the schema and hence will miss some problems
-
-    >>> store = ObjectStore()
-    >>> user = store.create_entity('CWUser', login=u'johndoe')
-    >>> group = store.create_entity('CWUser', name=u'unknown')
-    >>> store.relate(user.eid, 'in_group', group.eid)
-    """
-    def __init__(self):
-        self.items = []
-        self.eids = {}
-        self.types = {}
-        self.relations = set()
-        self.indexes = {}
-
-    def create_entity(self, etype, **data):
-        data = attrdict(data)
-        data['eid'] = eid = len(self.items)
-        self.items.append(data)
-        self.eids[eid] = data
-        self.types.setdefault(etype, []).append(eid)
-        return data
-
-    def relate(self, eid_from, rtype, eid_to, **kwargs):
-        """Add new relation"""
-        relation = eid_from, rtype, eid_to
-        self.relations.add(relation)
-        return relation
-
-    def commit(self):
-        """this commit method does nothing by default"""
-        return
-
-    def flush(self):
-        """The method is provided so that all stores share a common API"""
-        pass
-
-    @property
-    def nb_inserted_entities(self):
-        return len(self.eids)
-    @property
-    def nb_inserted_types(self):
-        return len(self.types)
-    @property
-    def nb_inserted_relations(self):
-        return len(self.relations)
-
-class RQLObjectStore(ObjectStore):
-    """ObjectStore that works with an actual RQL repository (production mode)"""
-
-    def __init__(self, cnx, commit=None):
-        if commit is not None:
-            warnings.warn('[3.19] commit argument should not be specified '
-                          'as the cnx object already provides it.',
-                          DeprecationWarning, stacklevel=2)
-        super(RQLObjectStore, self).__init__()
-        self._cnx = cnx
-        self._commit = commit or cnx.commit
-
-    def commit(self):
-        return self._commit()
-
-    def rql(self, *args):
-        return self._cnx.execute(*args)
-
-    @property
-    def session(self):
-        warnings.warn('[3.19] deprecated property.', DeprecationWarning,
-                      stacklevel=2)
-        return self._cnx.repo._get_session(self._cnx.sessionid)
-
-    def create_entity(self, *args, **kwargs):
-        entity = self._cnx.create_entity(*args, **kwargs)
-        self.eids[entity.eid] = entity
-        self.types.setdefault(args[0], []).append(entity.eid)
-        return entity
-
-    def relate(self, eid_from, rtype, eid_to, **kwargs):
-        eid_from, rtype, eid_to = super(RQLObjectStore, self).relate(
-            eid_from, rtype, eid_to, **kwargs)
-        self.rql('SET X %s Y WHERE X eid %%(x)s, Y eid %%(y)s' % rtype,
-                 {'x': int(eid_from), 'y': int(eid_to)})
-
-    @deprecated("[3.19] use cnx.find(*args, **kwargs).entities() instead")
-    def find_entities(self, *args, **kwargs):
-        return self._cnx.find(*args, **kwargs).entities()
-
-    @deprecated("[3.19] use cnx.find(*args, **kwargs).one() instead")
-    def find_one_entity(self, *args, **kwargs):
-        return self._cnx.find(*args, **kwargs).one()
-
-# the import controller ########################################################
-
-class CWImportController(object):
-    """Controller of the data import process.
-
-    >>> ctl = CWImportController(store)
-    >>> ctl.generators = list_of_data_generators
-    >>> ctl.data = dict_of_data_tables
-    >>> ctl.run()
-    """
-
-    def __init__(self, store, askerror=0, catcherrors=None, tell=tell,
-                 commitevery=50):
-        self.store = store
-        self.generators = None
-        self.data = {}
-        self.errors = None
-        self.askerror = askerror
-        if  catcherrors is None:
-            catcherrors = askerror
-        self.catcherrors = catcherrors
-        self.commitevery = commitevery # set to None to do a single commit
-        self._tell = tell
-
-    def check(self, type, key, value):
-        self._checks.setdefault(type, {}).setdefault(key, []).append(value)
-
-    def check_map(self, entity, key, map, default):
-        try:
-            entity[key] = map[entity[key]]
-        except KeyError:
-            self.check(key, entity[key], None)
-            entity[key] = default
-
-    def record_error(self, key, msg=None, type=None, value=None, tb=None):
-        tmp = StringIO()
-        if type is None:
-            traceback.print_exc(file=tmp)
-        else:
-            traceback.print_exception(type, value, tb, file=tmp)
-        # use a list to avoid counting a <nb lines> errors instead of one
-        errorlog = self.errors.setdefault(key, [])
-        if msg is None:
-            errorlog.append(tmp.getvalue().splitlines())
-        else:
-            errorlog.append( (msg, tmp.getvalue().splitlines()) )
-
-    def run(self):
-        self.errors = {}
-        if self.commitevery is None:
-            self.tell('Will commit all or nothing.')
-        else:
-            self.tell('Will commit every %s iterations' % self.commitevery)
-        for func, checks in self.generators:
-            self._checks = {}
-            func_name = func.__name__
-            self.tell("Run import function '%s'..." % func_name)
-            try:
-                func(self)
-            except Exception:
-                if self.catcherrors:
-                    self.record_error(func_name, 'While calling %s' % func.__name__)
-                else:
-                    self._print_stats()
-                    raise
-            for key, func, title, help in checks:
-                buckets = self._checks.get(key)
-                if buckets:
-                    err = func(buckets)
-                    if err:
-                        self.errors[title] = (help, err)
-        try:
-            txuuid = self.store.commit()
-            if txuuid is not None:
-                self.tell('Transaction commited (txuuid: %s)' % txuuid)
-        except QueryError as ex:
-            self.tell('Transaction aborted: %s' % ex)
-        self._print_stats()
-        if self.errors:
-            if self.askerror == 2 or (self.askerror and confirm('Display errors ?')):
-                from pprint import pformat
-                for errkey, error in self.errors.items():
-                    self.tell("\n%s (%s): %d\n" % (error[0], errkey, len(error[1])))
-                    self.tell(pformat(sorted(error[1])))
-
-    def _print_stats(self):
-        nberrors = sum(len(err) for err in self.errors.itervalues())
-        self.tell('\nImport statistics: %i entities, %i types, %i relations and %i errors'
-                  % (self.store.nb_inserted_entities,
-                     self.store.nb_inserted_types,
-                     self.store.nb_inserted_relations,
-                     nberrors))
-
-    def get_data(self, key):
-        return self.data.get(key)
-
-    def index(self, name, key, value, unique=False):
-        """create a new index
-
-        If unique is set to True, only first occurence will be kept not the following ones
-        """
-        if unique:
-            try:
-                if value in self.store.indexes[name][key]:
-                    return
-            except KeyError:
-                # we're sure that one is the first occurence; so continue...
-                pass
-        self.store.indexes.setdefault(name, {}).setdefault(key, []).append(value)
-
-    def tell(self, msg):
-        self._tell(msg)
-
-    def iter_and_commit(self, datakey):
-        """iter rows, triggering commit every self.commitevery iterations"""
-        if self.commitevery is None:
-            return self.get_data(datakey)
-        else:
-            return callfunc_every(self.store.commit,
-                                  self.commitevery,
-                                  self.get_data(datakey))
-
-
-class NoHookRQLObjectStore(RQLObjectStore):
-    """ObjectStore that works with an actual RQL repository (production mode)"""
-
-    def __init__(self, cnx, metagen=None, baseurl=None):
-        super(NoHookRQLObjectStore, self).__init__(cnx)
-        self.source = cnx.repo.system_source
-        self.rschema = cnx.repo.schema.rschema
-        self.add_relation = self.source.add_relation
-        if metagen is None:
-            metagen = MetaGenerator(cnx, baseurl)
-        self.metagen = metagen
-        self._nb_inserted_entities = 0
-        self._nb_inserted_types = 0
-        self._nb_inserted_relations = 0
-        # deactivate security
-        cnx.read_security = False
-        cnx.write_security = False
-
-    def create_entity(self, etype, **kwargs):
-        for k, v in kwargs.iteritems():
-            kwargs[k] = getattr(v, 'eid', v)
-        entity, rels = self.metagen.base_etype_dicts(etype)
-        # make a copy to keep cached entity pristine
-        entity = copy(entity)
-        entity.cw_edited = copy(entity.cw_edited)
-        entity.cw_clear_relation_cache()
-        entity.cw_edited.update(kwargs, skipsec=False)
-        entity_source, extid = self.metagen.init_entity(entity)
-        cnx = self._cnx
-        self.source.add_entity(cnx, entity)
-        self.source.add_info(cnx, entity, entity_source, extid)
-        kwargs = dict()
-        if inspect.getargspec(self.add_relation).keywords:
-            kwargs['subjtype'] = entity.cw_etype
-        for rtype, targeteids in rels.iteritems():
-            # targeteids may be a single eid or a list of eids
-            inlined = self.rschema(rtype).inlined
-            try:
-                for targeteid in targeteids:
-                    self.add_relation(cnx, entity.eid, rtype, targeteid,
-                                      inlined, **kwargs)
-            except TypeError:
-                self.add_relation(cnx, entity.eid, rtype, targeteids,
-                                  inlined, **kwargs)
-        self._nb_inserted_entities += 1
-        return entity
-
-    def relate(self, eid_from, rtype, eid_to, **kwargs):
-        assert not rtype.startswith('reverse_')
-        self.add_relation(self._cnx, eid_from, rtype, eid_to,
-                          self.rschema(rtype).inlined)
-        if self.rschema(rtype).symmetric:
-            self.add_relation(self._cnx, eid_to, rtype, eid_from,
-                              self.rschema(rtype).inlined)
-        self._nb_inserted_relations += 1
-
-    @property
-    def nb_inserted_entities(self):
-        return self._nb_inserted_entities
-    @property
-    def nb_inserted_types(self):
-        return self._nb_inserted_types
-    @property
-    def nb_inserted_relations(self):
-        return self._nb_inserted_relations
-
-
-class MetaGenerator(object):
-    META_RELATIONS = (META_RTYPES
-                      - VIRTUAL_RTYPES
-                      - set(('eid', 'cwuri',
-                             'is', 'is_instance_of', 'cw_source')))
-
-    def __init__(self, cnx, baseurl=None, source=None):
-        self._cnx = cnx
-        if baseurl is None:
-            config = cnx.vreg.config
-            baseurl = config['base-url'] or config.default_base_url()
-        if not baseurl[-1] == '/':
-            baseurl += '/'
-        self.baseurl = baseurl
-        if source is None:
-            source = cnx.repo.system_source
-        self.source = source
-        self.create_eid = cnx.repo.system_source.create_eid
-        self.time = datetime.now()
-        # attributes/relations shared by all entities of the same type
-        self.etype_attrs = []
-        self.etype_rels = []
-        # attributes/relations specific to each entity
-        self.entity_attrs = ['cwuri']
-        #self.entity_rels = [] XXX not handled (YAGNI?)
-        schema = cnx.vreg.schema
-        rschema = schema.rschema
-        for rtype in self.META_RELATIONS:
-            # skip owned_by / created_by if user is the internal manager
-            if cnx.user.eid == -1 and rtype in ('owned_by', 'created_by'):
-                continue
-            if rschema(rtype).final:
-                self.etype_attrs.append(rtype)
-            else:
-                self.etype_rels.append(rtype)
-
-    @cached
-    def base_etype_dicts(self, etype):
-        entity = self._cnx.vreg['etypes'].etype_class(etype)(self._cnx)
-        # entity are "surface" copied, avoid shared dict between copies
-        del entity.cw_extra_kwargs
-        entity.cw_edited = EditedEntity(entity)
-        for attr in self.etype_attrs:
-            genfunc = self.generate(attr)
-            if genfunc:
-                entity.cw_edited.edited_attribute(attr, genfunc(entity))
-        rels = {}
-        for rel in self.etype_rels:
-            genfunc = self.generate(rel)
-            if genfunc:
-                rels[rel] = genfunc(entity)
-        return entity, rels
-
-    def init_entity(self, entity):
-        entity.eid = self.create_eid(self._cnx)
-        extid = entity.cw_edited.get('cwuri')
-        for attr in self.entity_attrs:
-            if attr in entity.cw_edited:
-                # already set, skip this attribute
-                continue
-            genfunc = self.generate(attr)
-            if genfunc:
-                entity.cw_edited.edited_attribute(attr, genfunc(entity))
-        if isinstance(extid, unicode):
-            extid = extid.encode('utf-8')
-        return self.source, extid
-
-    def generate(self, rtype):
-        return getattr(self, 'gen_%s' % rtype, None)
-
-    def gen_cwuri(self, entity):
-        assert self.baseurl, 'baseurl is None while generating cwuri'
-        return u'%s%s' % (self.baseurl, entity.eid)
-
-    def gen_creation_date(self, entity):
-        return self.time
-
-    def gen_modification_date(self, entity):
-        return self.time
-
-    def gen_created_by(self, entity):
-        return self._cnx.user.eid
-
-    def gen_owned_by(self, entity):
-        return self._cnx.user.eid
-
-
-###########################################################################
-## SQL object store #######################################################
-###########################################################################
-class SQLGenObjectStore(NoHookRQLObjectStore):
-    """Controller of the data import process. This version is based
-    on direct insertions throught SQL command (COPY FROM or execute many).
-
-    >>> store = SQLGenObjectStore(cnx)
-    >>> store.create_entity('Person', ...)
-    >>> store.flush()
-    """
-
-    def __init__(self, cnx, dump_output_dir=None, nb_threads_statement=3):
-        """
-        Initialize a SQLGenObjectStore.
-
-        Parameters:
-
-          - cnx: connection on the cubicweb instance
-          - dump_output_dir: a directory to dump failed statements
-            for easier recovery. Default is None (no dump).
-          - nb_threads_statement: number of threads used
-            for SQL insertion (default is 3).
-        """
-        super(SQLGenObjectStore, self).__init__(cnx)
-        ### hijack default source
-        self.source = SQLGenSourceWrapper(
-            self.source, cnx.vreg.schema,
-            dump_output_dir=dump_output_dir,
-            nb_threads_statement=nb_threads_statement)
-        ### XXX This is done in super().__init__(), but should be
-        ### redone here to link to the correct source
-        self.add_relation = self.source.add_relation
-        self.indexes_etypes = {}
-
-    def flush(self):
-        """Flush data to the database"""
-        self.source.flush()
-
-    def relate(self, subj_eid, rtype, obj_eid, **kwargs):
-        if subj_eid is None or obj_eid is None:
-            return
-        # XXX Could subjtype be inferred ?
-        self.source.add_relation(self._cnx, subj_eid, rtype, obj_eid,
-                                 self.rschema(rtype).inlined, **kwargs)
-        if self.rschema(rtype).symmetric:
-            self.source.add_relation(self._cnx, obj_eid, rtype, subj_eid,
-                                     self.rschema(rtype).inlined, **kwargs)
-
-    def drop_indexes(self, etype):
-        """Drop indexes for a given entity type"""
-        if etype not in self.indexes_etypes:
-            cu = self._cnx.cnxset.cu
-            def index_to_attr(index):
-                """turn an index name to (database) attribute name"""
-                return index.replace(etype.lower(), '').replace('idx', '').strip('_')
-            indices = [(index, index_to_attr(index))
-                       for index in self.source.dbhelper.list_indices(cu, etype)
-                       # Do not consider 'cw_etype_pkey' index
-                       if not index.endswith('key')]
-            self.indexes_etypes[etype] = indices
-        for index, attr in self.indexes_etypes[etype]:
-            self._cnx.system_sql('DROP INDEX %s' % index)
-
-    def create_indexes(self, etype):
-        """Recreate indexes for a given entity type"""
-        for index, attr in self.indexes_etypes.get(etype, []):
-            sql = 'CREATE INDEX %s ON cw_%s(%s)' % (index, etype, attr)
-            self._cnx.system_sql(sql)
-
-
-###########################################################################
-## SQL Source #############################################################
-###########################################################################
-
-class SQLGenSourceWrapper(object):
-
-    def __init__(self, system_source, schema,
-                 dump_output_dir=None, nb_threads_statement=3):
-        self.system_source = system_source
-        self._sql = threading.local()
-        # Explicitely backport attributes from system source
-        self._storage_handler = self.system_source._storage_handler
-        self.preprocess_entity = self.system_source.preprocess_entity
-        self.sqlgen = self.system_source.sqlgen
-        self.uri = self.system_source.uri
-        self.eid = self.system_source.eid
-        # Directory to write temporary files
-        self.dump_output_dir = dump_output_dir
-        # Allow to execute code with SQLite backend that does
-        # not support (yet...) copy_from
-        # XXX Should be dealt with in logilab.database
-        spcfrom = system_source.dbhelper.dbapi_module.support_copy_from
-        self.support_copy_from = spcfrom
-        self.dbencoding = system_source.dbhelper.dbencoding
-        self.nb_threads_statement = nb_threads_statement
-        # initialize thread-local data for main thread
-        self.init_thread_locals()
-        self._inlined_rtypes_cache = {}
-        self._fill_inlined_rtypes_cache(schema)
-        self.schema = schema
-        self.do_fti = False
-
-    def _fill_inlined_rtypes_cache(self, schema):
-        cache = self._inlined_rtypes_cache
-        for eschema in schema.entities():
-            for rschema in eschema.ordered_relations():
-                if rschema.inlined:
-                    cache[eschema.type] = SQL_PREFIX + rschema.type
-
-    def init_thread_locals(self):
-        """initializes thread-local data"""
-        self._sql.entities = defaultdict(list)
-        self._sql.relations = {}
-        self._sql.inlined_relations = {}
-        # keep track, for each eid of the corresponding data dict
-        self._sql.eid_insertdicts = {}
-
-    def flush(self):
-        print 'starting flush'
-        _entities_sql = self._sql.entities
-        _relations_sql = self._sql.relations
-        _inlined_relations_sql = self._sql.inlined_relations
-        _insertdicts = self._sql.eid_insertdicts
-        try:
-            # try, for each inlined_relation, to find if we're also creating
-            # the host entity (i.e. the subject of the relation).
-            # In that case, simply update the insert dict and remove
-            # the need to make the
-            # UPDATE statement
-            for statement, datalist in _inlined_relations_sql.iteritems():
-                new_datalist = []
-                # for a given inlined relation,
-                # browse each couple to be inserted
-                for data in datalist:
-                    keys = list(data)
-                    # For inlined relations, it exists only two case:
-                    # (rtype, cw_eid) or (cw_eid, rtype)
-                    if keys[0] == 'cw_eid':
-                        rtype = keys[1]
-                    else:
-                        rtype = keys[0]
-                    updated_eid = data['cw_eid']
-                    if updated_eid in _insertdicts:
-                        _insertdicts[updated_eid][rtype] = data[rtype]
-                    else:
-                        # could not find corresponding insert dict, keep the
-                        # UPDATE query
-                        new_datalist.append(data)
-                _inlined_relations_sql[statement] = new_datalist
-            _import_statements(self.system_source.get_connection,
-                               _entities_sql.items()
-                               + _relations_sql.items()
-                               + _inlined_relations_sql.items(),
-                               dump_output_dir=self.dump_output_dir,
-                               nb_threads=self.nb_threads_statement,
-                               support_copy_from=self.support_copy_from,
-                               encoding=self.dbencoding)
-        finally:
-            _entities_sql.clear()
-            _relations_sql.clear()
-            _insertdicts.clear()
-            _inlined_relations_sql.clear()
-
-    def add_relation(self, cnx, subject, rtype, object,
-                     inlined=False, **kwargs):
-        if inlined:
-            _sql = self._sql.inlined_relations
-            data = {'cw_eid': subject, SQL_PREFIX + rtype: object}
-            subjtype = kwargs.get('subjtype')
-            if subjtype is None:
-                # Try to infer it
-                targets = [t.type for t in
-                           self.schema.rschema(rtype).subjects()]
-                if len(targets) == 1:
-                    subjtype = targets[0]
-                else:
-                    raise ValueError('You should give the subject etype for '
-                                     'inlined relation %s'
-                                     ', as it cannot be inferred: '
-                                     'this type is given as keyword argument '
-                                     '``subjtype``'% rtype)
-            statement = self.sqlgen.update(SQL_PREFIX + subjtype,
-                                           data, ['cw_eid'])
-        else:
-            _sql = self._sql.relations
-            data = {'eid_from': subject, 'eid_to': object}
-            statement = self.sqlgen.insert('%s_relation' % rtype, data)
-        if statement in _sql:
-            _sql[statement].append(data)
-        else:
-            _sql[statement] = [data]
-
-    def add_entity(self, cnx, entity):
-        with self._storage_handler(entity, 'added'):
-            attrs = self.preprocess_entity(entity)
-            rtypes = self._inlined_rtypes_cache.get(entity.cw_etype, ())
-            if isinstance(rtypes, str):
-                rtypes = (rtypes,)
-            for rtype in rtypes:
-                if rtype not in attrs:
-                    attrs[rtype] = None
-            sql = self.sqlgen.insert(SQL_PREFIX + entity.cw_etype, attrs)
-            self._sql.eid_insertdicts[entity.eid] = attrs
-            self._append_to_entities(sql, attrs)
-
-    def _append_to_entities(self, sql, attrs):
-        self._sql.entities[sql].append(attrs)
-
-    def _handle_insert_entity_sql(self, cnx, sql, attrs):
-        # We have to overwrite the source given in parameters
-        # as here, we directly use the system source
-        attrs['asource'] = self.system_source.uri
-        self._append_to_entities(sql, attrs)
-
-    def _handle_is_relation_sql(self, cnx, sql, attrs):
-        self._append_to_entities(sql, attrs)
-
-    def _handle_is_instance_of_sql(self, cnx, sql, attrs):
-        self._append_to_entities(sql, attrs)
-
-    def _handle_source_relation_sql(self, cnx, sql, attrs):
-        self._append_to_entities(sql, attrs)
-
-    # add_info is _copypasted_ from the one in NativeSQLSource. We want it
-    # there because it will use the _handlers of the SQLGenSourceWrapper, which
-    # are not like the ones in the native source.
-    def add_info(self, cnx, entity, source, extid):
-        """add type and source info for an eid into the system table"""
-        # begin by inserting eid/type/source/extid into the entities table
-        if extid is not None:
-            assert isinstance(extid, str)
-            extid = b64encode(extid)
-        attrs = {'type': entity.cw_etype, 'eid': entity.eid, 'extid': extid,
-                 'asource': source.uri}
-        self._handle_insert_entity_sql(cnx, self.sqlgen.insert('entities', attrs), attrs)
-        # insert core relations: is, is_instance_of and cw_source
-        try:
-            self._handle_is_relation_sql(cnx, 'INSERT INTO is_relation(eid_from,eid_to) VALUES (%s,%s)',
-                                         (entity.eid, eschema_eid(cnx, entity.e_schema)))
-        except IndexError:
-            # during schema serialization, skip
-            pass
-        else:
-            for eschema in entity.e_schema.ancestors() + [entity.e_schema]:
-                self._handle_is_relation_sql(cnx,
-                                             'INSERT INTO is_instance_of_relation(eid_from,eid_to) VALUES (%s,%s)',
-                                             (entity.eid, eschema_eid(cnx, eschema)))
-        if 'CWSource' in self.schema and source.eid is not None: # else, cw < 3.10
-            self._handle_is_relation_sql(cnx, 'INSERT INTO cw_source_relation(eid_from,eid_to) VALUES (%s,%s)',
-                                         (entity.eid, source.eid))
-        # now we can update the full text index
-        if self.do_fti and self.need_fti_indexation(entity.cw_etype):
-            self.index_entity(cnx, entity=entity)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dataimport/__init__.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,35 @@
+# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+"""Package containing various utilities to import data into cubicweb."""
+
+
+def callfunc_every(func, number, iterable):
+    """yield items of `iterable` one by one and call function `func`
+    every `number` iterations. Always call function `func` at the end.
+    """
+    for idx, item in enumerate(iterable):
+        yield item
+        if not idx % number:
+            func()
+    func()
+
+# import for backward compat
+from cubicweb.dataimport.stores import *
+from cubicweb.dataimport.pgstore import *
+from cubicweb.dataimport.csv import *
+from cubicweb.dataimport.deprecated import *
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dataimport/csv.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,113 @@
+# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+"""Functions to help importing CSV data"""
+
+from __future__ import absolute_import
+
+import csv as csvmod
+import warnings
+import os.path as osp
+
+from logilab.common import shellutils
+
+
+def count_lines(stream_or_filename):
+    if isinstance(stream_or_filename, basestring):
+        f = open(stream_or_filename)
+    else:
+        f = stream_or_filename
+        f.seek(0)
+    for i, line in enumerate(f):
+        pass
+    f.seek(0)
+    return i+1
+
+
+def ucsvreader_pb(stream_or_path, encoding='utf-8', delimiter=',', quotechar='"',
+                  skipfirst=False, withpb=True, skip_empty=True, separator=None,
+                  quote=None):
+    """same as :func:`ucsvreader` but a progress bar is displayed as we iter on rows"""
+    if separator is not None:
+        delimiter = separator
+        warnings.warn("[3.20] 'separator' kwarg is deprecated, use 'delimiter' instead")
+    if quote is not None:
+        quotechar = quote
+        warnings.warn("[3.20] 'quote' kwarg is deprecated, use 'quotechar' instead")
+    if isinstance(stream_or_path, basestring):
+        if not osp.exists(stream_or_path):
+            raise Exception("file doesn't exists: %s" % stream_or_path)
+        stream = open(stream_or_path)
+    else:
+        stream = stream_or_path
+    rowcount = count_lines(stream)
+    if skipfirst:
+        rowcount -= 1
+    if withpb:
+        pb = shellutils.ProgressBar(rowcount, 50)
+    for urow in ucsvreader(stream, encoding, delimiter, quotechar,
+                           skipfirst=skipfirst, skip_empty=skip_empty):
+        yield urow
+        if withpb:
+            pb.update()
+    print ' %s rows imported' % rowcount
+
+
+def ucsvreader(stream, encoding='utf-8', delimiter=',', quotechar='"',
+               skipfirst=False, ignore_errors=False, skip_empty=True,
+               separator=None, quote=None):
+    """A csv reader that accepts files with any encoding and outputs unicode
+    strings
+
+    if skip_empty (the default), lines without any values specified (only
+    separators) will be skipped. This is useful for Excel exports which may be
+    full of such lines.
+    """
+    if separator is not None:
+        delimiter = separator
+        warnings.warn("[3.20] 'separator' kwarg is deprecated, use 'delimiter' instead")
+    if quote is not None:
+        quotechar = quote
+        warnings.warn("[3.20] 'quote' kwarg is deprecated, use 'quotechar' instead")
+    it = iter(csvmod.reader(stream, delimiter=delimiter, quotechar=quotechar))
+    if not ignore_errors:
+        if skipfirst:
+            it.next()
+        for row in it:
+            decoded = [item.decode(encoding) for item in row]
+            if not skip_empty or any(decoded):
+                yield decoded
+    else:
+        if skipfirst:
+            try:
+                row = it.next()
+            except csvmod.Error:
+                pass
+        # Safe version, that can cope with error in CSV file
+        while True:
+            try:
+                row = it.next()
+            # End of CSV, break
+            except StopIteration:
+                break
+            # Error in CSV, ignore line and continue
+            except csvmod.Error:
+                continue
+            decoded = [item.decode(encoding) for item in row]
+            if not skip_empty or any(decoded):
+                yield decoded
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dataimport/deprecated.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,460 @@
+# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+"""Old and deprecated dataimport API that provides tools to import tabular data.
+
+
+Example of use (run this with `cubicweb-ctl shell instance import-script.py`):
+
+.. sourcecode:: python
+
+  from cubicweb.dataimport import *
+  # define data generators
+  GENERATORS = []
+
+  USERS = [('Prenom', 'firstname', ()),
+           ('Nom', 'surname', ()),
+           ('Identifiant', 'login', ()),
+           ]
+
+  def gen_users(ctl):
+      for row in ctl.iter_and_commit('utilisateurs'):
+          entity = mk_entity(row, USERS)
+          entity['upassword'] = 'motdepasse'
+          ctl.check('login', entity['login'], None)
+          entity = ctl.store.prepare_insert_entity('CWUser', **entity)
+          email = ctl.store.prepare_insert_entity('EmailAddress', address=row['email'])
+          ctl.store.prepare_insert_relation(entity, 'use_email', email)
+          ctl.store.rql('SET U in_group G WHERE G name "users", U eid %(x)s', {'x': entity})
+
+  CHK = [('login', check_doubles, 'Utilisateurs Login',
+          'Deux utilisateurs ne devraient pas avoir le meme login.'),
+         ]
+
+  GENERATORS.append( (gen_users, CHK) )
+
+  # create controller
+  ctl = CWImportController(RQLObjectStore(cnx))
+  ctl.askerror = 1
+  ctl.generators = GENERATORS
+  ctl.data['utilisateurs'] = lazytable(ucsvreader(open('users.csv')))
+  # run
+  ctl.run()
+
+.. BUG file with one column are not parsable
+.. TODO rollback() invocation is not possible yet
+"""
+
+import sys
+import traceback
+from StringIO import StringIO
+
+from logilab.common import attrdict, shellutils
+from logilab.common.date import strptime
+from logilab.common.deprecation import deprecated, class_deprecated
+
+from cubicweb import QueryError
+from cubicweb.dataimport import callfunc_every
+
+
+@deprecated('[3.21] deprecated')
+def lazytable(reader):
+    """The first row is taken to be the header of the table and
+    used to output a dict for each row of data.
+
+    >>> data = lazytable(ucsvreader(open(filename)))
+    """
+    header = reader.next()
+    for row in reader:
+        yield dict(zip(header, row))
+
+
+@deprecated('[3.21] deprecated')
+def lazydbtable(cu, table, headers, orderby=None):
+    """return an iterator on rows of a sql table. On each row, fetch columns
+    defined in headers and return values as a dictionary.
+
+    >>> data = lazydbtable(cu, 'experimentation', ('id', 'nickname', 'gps'))
+    """
+    sql = 'SELECT %s FROM %s' % (','.join(headers), table,)
+    if orderby:
+        sql += ' ORDER BY %s' % ','.join(orderby)
+    cu.execute(sql)
+    while True:
+        row = cu.fetchone()
+        if row is None:
+            break
+        yield dict(zip(headers, row))
+
+
+@deprecated('[3.21] deprecated')
+def tell(msg):
+    print msg
+
+
+@deprecated('[3.21] deprecated')
+def confirm(question):
+    """A confirm function that asks for yes/no/abort and exits on abort."""
+    answer = shellutils.ASK.ask(question, ('Y', 'n', 'abort'), 'Y')
+    if answer == 'abort':
+        sys.exit(1)
+    return answer == 'Y'
+
+
+class catch_error(object):
+    """Helper for @contextmanager decorator."""
+    __metaclass__ = class_deprecated
+    __deprecation_warning__ = '[3.21] deprecated'
+
+    def __init__(self, ctl, key='unexpected error', msg=None):
+        self.ctl = ctl
+        self.key = key
+        self.msg = msg
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, type, value, traceback):
+        if type is not None:
+            if issubclass(type, (KeyboardInterrupt, SystemExit)):
+                return # re-raise
+            if self.ctl.catcherrors:
+                self.ctl.record_error(self.key, None, type, value, traceback)
+                return True # silent
+
+@deprecated('[3.21] deprecated')
+def mk_entity(row, map):
+    """Return a dict made from sanitized mapped values.
+
+    ValueError can be raised on unexpected values found in checkers
+
+    >>> row = {'myname': u'dupont'}
+    >>> map = [('myname', u'name', (call_transform_method('title'),))]
+    >>> mk_entity(row, map)
+    {'name': u'Dupont'}
+    >>> row = {'myname': u'dupont', 'optname': u''}
+    >>> map = [('myname', u'name', (call_transform_method('title'),)),
+    ...        ('optname', u'MARKER', (optional,))]
+    >>> mk_entity(row, map)
+    {'name': u'Dupont', 'optname': None}
+    """
+    res = {}
+    assert isinstance(row, dict)
+    assert isinstance(map, list)
+    for src, dest, funcs in map:
+        try:
+            res[dest] = row[src]
+        except KeyError:
+            continue
+        try:
+            for func in funcs:
+                res[dest] = func(res[dest])
+                if res[dest] is None:
+                    break
+        except ValueError as err:
+            raise ValueError('error with %r field: %s' % (src, err)), None, sys.exc_info()[-1]
+    return res
+
+
+# base sanitizing/coercing functions ###########################################
+
+@deprecated('[3.21] deprecated')
+def optional(value):
+    """checker to filter optional field
+
+    If value is undefined (ex: empty string), return None that will
+    break the checkers validation chain
+
+    General use is to add 'optional' check in first condition to avoid
+    ValueError by further checkers
+
+    >>> MAPPER = [(u'value', 'value', (optional, int))]
+    >>> row = {'value': u'XXX'}
+    >>> mk_entity(row, MAPPER)
+    {'value': None}
+    >>> row = {'value': u'100'}
+    >>> mk_entity(row, MAPPER)
+    {'value': 100}
+    """
+    if value:
+        return value
+    return None
+
+
+@deprecated('[3.21] deprecated')
+def required(value):
+    """raise ValueError if value is empty
+
+    This check should be often found in last position in the chain.
+    """
+    if value:
+        return value
+    raise ValueError("required")
+
+
+@deprecated('[3.21] deprecated')
+def todatetime(format='%d/%m/%Y'):
+    """return a transformation function to turn string input value into a
+    `datetime.datetime` instance, using given format.
+
+    Follow it by `todate` or `totime` functions from `logilab.common.date` if
+    you want a `date`/`time` instance instead of `datetime`.
+    """
+    def coerce(value):
+        return strptime(value, format)
+    return coerce
+
+
+@deprecated('[3.21] deprecated')
+def call_transform_method(methodname, *args, **kwargs):
+    """return value returned by calling the given method on input"""
+    def coerce(value):
+        return getattr(value, methodname)(*args, **kwargs)
+    return coerce
+
+
+@deprecated('[3.21] deprecated')
+def call_check_method(methodname, *args, **kwargs):
+    """check value returned by calling the given method on input is true,
+    else raise ValueError
+    """
+    def check(value):
+        if getattr(value, methodname)(*args, **kwargs):
+            return value
+        raise ValueError('%s not verified on %r' % (methodname, value))
+    return check
+
+
+# base integrity checking functions ############################################
+
+@deprecated('[3.21] deprecated')
+def check_doubles(buckets):
+    """Extract the keys that have more than one item in their bucket."""
+    return [(k, len(v)) for k, v in buckets.items() if len(v) > 1]
+
+
+@deprecated('[3.21] deprecated')
+def check_doubles_not_none(buckets):
+    """Extract the keys that have more than one item in their bucket."""
+    return [(k, len(v)) for k, v in buckets.items()
+            if k is not None and len(v) > 1]
+
+
+class ObjectStore(object):
+    """Store objects in memory for *faster* validation (development mode)
+
+    But it will not enforce the constraints of the schema and hence will miss some problems
+
+    >>> store = ObjectStore()
+    >>> user = store.prepare_insert_entity('CWUser', login=u'johndoe')
+    >>> group = store.prepare_insert_entity('CWUser', name=u'unknown')
+    >>> store.prepare_insert_relation(user, 'in_group', group)
+    """
+    __metaclass__ = class_deprecated
+    __deprecation_warning__ = '[3.21] use the new importer API'
+
+    def __init__(self):
+        self.items = []
+        self.eids = {}
+        self.types = {}
+        self.relations = set()
+        self.indexes = {}
+
+    def prepare_insert_entity(self, etype, **data):
+        """Given an entity type, attributes and inlined relations, return an eid for the entity that
+        would be inserted with a real store.
+        """
+        data = attrdict(data)
+        data['eid'] = eid = len(self.items)
+        self.items.append(data)
+        self.eids[eid] = data
+        self.types.setdefault(etype, []).append(eid)
+        return eid
+
+    def prepare_update_entity(self, etype, eid, **kwargs):
+        """Given an entity type and eid, updates the corresponding fake entity with specified
+        attributes and inlined relations.
+        """
+        assert eid in self.types[etype], 'Trying to update with wrong type {}'.format(etype)
+        data = self.eids[eid]
+        data.update(kwargs)
+
+    def prepare_insert_relation(self, eid_from, rtype, eid_to, **kwargs):
+        """Store into the `relations` attribute that a relation ``rtype`` exists between entities
+        with eids ``eid_from`` and ``eid_to``.
+        """
+        relation = eid_from, rtype, eid_to
+        self.relations.add(relation)
+        return relation
+
+    def flush(self):
+        """Nothing to flush for this store."""
+        pass
+
+    def commit(self):
+        """Nothing to commit for this store."""
+        return
+
+    def finish(self):
+        """Nothing to do once import is terminated for this store."""
+        pass
+
+    @property
+    def nb_inserted_entities(self):
+        return len(self.eids)
+
+    @property
+    def nb_inserted_types(self):
+        return len(self.types)
+
+    @property
+    def nb_inserted_relations(self):
+        return len(self.relations)
+
+    @deprecated('[3.21] use prepare_insert_entity instead')
+    def create_entity(self, etype, **data):
+        self.prepare_insert_entity(etype, **data)
+        return attrdict(data)
+
+    @deprecated('[3.21] use prepare_insert_relation instead')
+    def relate(self, eid_from, rtype, eid_to, **kwargs):
+        self.prepare_insert_relation(eid_from, rtype, eid_to, **kwargs)
+
+
+class CWImportController(object):
+    """Controller of the data import process.
+
+    >>> ctl = CWImportController(store)
+    >>> ctl.generators = list_of_data_generators
+    >>> ctl.data = dict_of_data_tables
+    >>> ctl.run()
+    """
+    __metaclass__ = class_deprecated
+    __deprecation_warning__ = '[3.21] use the new importer API'
+
+    def __init__(self, store, askerror=0, catcherrors=None, tell=tell,
+                 commitevery=50):
+        self.store = store
+        self.generators = None
+        self.data = {}
+        self.errors = None
+        self.askerror = askerror
+        if  catcherrors is None:
+            catcherrors = askerror
+        self.catcherrors = catcherrors
+        self.commitevery = commitevery # set to None to do a single commit
+        self._tell = tell
+
+    def check(self, type, key, value):
+        self._checks.setdefault(type, {}).setdefault(key, []).append(value)
+
+    def check_map(self, entity, key, map, default):
+        try:
+            entity[key] = map[entity[key]]
+        except KeyError:
+            self.check(key, entity[key], None)
+            entity[key] = default
+
+    def record_error(self, key, msg=None, type=None, value=None, tb=None):
+        tmp = StringIO()
+        if type is None:
+            traceback.print_exc(file=tmp)
+        else:
+            traceback.print_exception(type, value, tb, file=tmp)
+        # use a list to avoid counting a <nb lines> errors instead of one
+        errorlog = self.errors.setdefault(key, [])
+        if msg is None:
+            errorlog.append(tmp.getvalue().splitlines())
+        else:
+            errorlog.append( (msg, tmp.getvalue().splitlines()) )
+
+    def run(self):
+        self.errors = {}
+        if self.commitevery is None:
+            self.tell('Will commit all or nothing.')
+        else:
+            self.tell('Will commit every %s iterations' % self.commitevery)
+        for func, checks in self.generators:
+            self._checks = {}
+            func_name = func.__name__
+            self.tell("Run import function '%s'..." % func_name)
+            try:
+                func(self)
+            except Exception:
+                if self.catcherrors:
+                    self.record_error(func_name, 'While calling %s' % func.__name__)
+                else:
+                    self._print_stats()
+                    raise
+            for key, func, title, help in checks:
+                buckets = self._checks.get(key)
+                if buckets:
+                    err = func(buckets)
+                    if err:
+                        self.errors[title] = (help, err)
+        try:
+            txuuid = self.store.commit()
+            if txuuid is not None:
+                self.tell('Transaction commited (txuuid: %s)' % txuuid)
+        except QueryError as ex:
+            self.tell('Transaction aborted: %s' % ex)
+        self._print_stats()
+        if self.errors:
+            if self.askerror == 2 or (self.askerror and confirm('Display errors ?')):
+                from pprint import pformat
+                for errkey, error in self.errors.items():
+                    self.tell("\n%s (%s): %d\n" % (error[0], errkey, len(error[1])))
+                    self.tell(pformat(sorted(error[1])))
+
+    def _print_stats(self):
+        nberrors = sum(len(err) for err in self.errors.itervalues())
+        self.tell('\nImport statistics: %i entities, %i types, %i relations and %i errors'
+                  % (self.store.nb_inserted_entities,
+                     self.store.nb_inserted_types,
+                     self.store.nb_inserted_relations,
+                     nberrors))
+
+    def get_data(self, key):
+        return self.data.get(key)
+
+    def index(self, name, key, value, unique=False):
+        """create a new index
+
+        If unique is set to True, only first occurence will be kept not the following ones
+        """
+        if unique:
+            try:
+                if value in self.store.indexes[name][key]:
+                    return
+            except KeyError:
+                # we're sure that one is the first occurence; so continue...
+                pass
+        self.store.indexes.setdefault(name, {}).setdefault(key, []).append(value)
+
+    def tell(self, msg):
+        self._tell(msg)
+
+    def iter_and_commit(self, datakey):
+        """iter rows, triggering commit every self.commitevery iterations"""
+        if self.commitevery is None:
+            return self.get_data(datakey)
+        else:
+            return callfunc_every(self.store.commit,
+                                  self.commitevery,
+                                  self.get_data(datakey))
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dataimport/importer.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,417 @@
+# copyright 2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr -- mailto:contact@logilab.fr
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+"""Data import of external entities.
+
+Main entry points:
+
+.. autoclass:: ExtEntitiesImporter
+.. autoclass:: ExtEntity
+
+Utilities:
+
+.. autofunction:: cwuri2eid
+.. autoclass:: RelationMapping
+.. autofunction:: cubicweb.dataimport.importer.use_extid_as_cwuri
+"""
+
+from collections import defaultdict
+import logging
+
+from logilab.mtconverter import xml_escape
+
+
+def cwuri2eid(cnx, etypes, source_eid=None):
+    """Return a dictionary mapping cwuri to eid for entities of the given entity types and / or
+    source.
+    """
+    assert source_eid or etypes, 'no entity types nor source specified'
+    rql = 'Any U, X WHERE X cwuri U'
+    args = {}
+    if len(etypes) == 1:
+        rql += ', X is %s' % etypes[0]
+    elif etypes:
+        rql += ', X is IN (%s)' % ','.join(etypes)
+    if source_eid is not None:
+        rql += ', X cw_source S, S eid %(s)s'
+        args['s'] = source_eid
+    return dict(cnx.execute(rql, args))
+
+
+def use_extid_as_cwuri(extid2eid):
+    """Return a generator of :class:`ExtEntity` objects that will set `cwuri`
+    using entity's extid if the entity does not exist yet and has no `cwuri`
+    defined.
+
+    `extid2eid` is an extid to eid dictionary coming from an
+    :class:`ExtEntitiesImporter` instance.
+
+    Example usage:
+
+    .. code-block:: python
+
+        importer = SKOSExtEntitiesImporter(cnx, store, import_log)
+        set_cwuri = use_extid_as_cwuri(importer.extid2eid)
+        importer.import_entities(set_cwuri(extentities))
+    """
+    def use_extid_as_cwuri_filter(extentities):
+        for extentity in extentities:
+            if extentity.extid not in extid2eid:
+                extentity.values.setdefault('cwuri', set([unicode(extentity.extid)]))
+            yield extentity
+    return use_extid_as_cwuri_filter
+
+
+class RelationMapping(object):
+    """Read-only mapping from relation type to set of related (subject, object) eids.
+
+    If `source` is specified, only returns relations implying entities from
+    this source.
+    """
+
+    def __init__(self, cnx, source=None):
+        self.cnx = cnx
+        self._rql_template = 'Any S,O WHERE S {} O'
+        self._kwargs = {}
+        if source is not None:
+            self._rql_template += ', S cw_source SO, O cw_source SO, SO eid %(s)s'
+            self._kwargs['s'] = source.eid
+
+    def __getitem__(self, rtype):
+        """Return a set of (subject, object) eids already related by `rtype`"""
+        rql = self._rql_template.format(rtype)
+        return set(tuple(x) for x in self.cnx.execute(rql, self._kwargs))
+
+
+class ExtEntity(object):
+    """Transitional representation of an entity for use in data importer.
+
+    An external entity has the following properties:
+
+    * ``extid`` (external id), an identifier for the ext entity,
+
+    * ``etype`` (entity type), a string which must be the name of one entity type in the schema
+      (eg. ``'Person'``, ``'Animal'``, ...),
+
+    * ``values``, a dictionary whose keys are attribute or relation names from the schema (eg.
+      ``'first_name'``, ``'friend'``), and whose values are *sets*
+
+    For instance:
+
+    .. code-block:: python
+
+        ext_entity.extid = 'http://example.org/person/debby'
+        ext_entity.etype = 'Person'
+        ext_entity.values = {'first_name': set([u"Deborah", u"Debby"]),
+                            'friend': set(['http://example.org/person/john'])}
+
+    """
+
+    def __init__(self, etype, extid, values=None):
+        self.etype = etype
+        self.extid = extid
+        if values is None:
+            values = {}
+        self.values = values
+        self._schema = None
+
+    def __repr__(self):
+        return '<%s %s %s>' % (self.etype, self.extid, self.values)
+
+    def iter_rdefs(self):
+        """Yield (key, rtype, role) defined in `.values` dict, with:
+
+        * `key` is the original key in `.values` (i.e. the relation type or a 2-uple (relation type,
+          role))
+
+        * `rtype` is a yams relation type, expected to be found in the schema (attribute or
+          relation)
+
+        * `role` is the role of the entity in the relation, 'subject' or 'object'
+
+        Iteration is done on a copy of the keys so values may be inserted/deleted during it.
+        """
+        for key in list(self.values):
+            if isinstance(key, tuple):
+                rtype, role = key
+                assert role in ('subject', 'object'), key
+                yield key, rtype, role
+            else:
+                yield key, key, 'subject'
+
+    def prepare(self, schema):
+        """Prepare an external entity for later insertion:
+
+        * ensure attributes and inlined relations have a single value
+        * turn set([value]) into value and remove key associated to empty set
+        * remove non inlined relations and return them as a [(e1key, relation, e2key)] list
+
+        Return a list of non inlined relations that may be inserted later, each relations defined by
+        a 3-tuple (subject extid, relation type, object extid).
+
+        Take care the importer may call this method several times.
+        """
+        assert self._schema is None, 'prepare() has already been called for %s' % self
+        self._schema = schema
+        eschema = schema.eschema(self.etype)
+        deferred = []
+        entity_dict = self.values
+        for key, rtype, role in self.iter_rdefs():
+            rschema = schema.rschema(rtype)
+            if rschema.final or (rschema.inlined and role == 'subject'):
+                assert len(entity_dict[key]) <= 1, \
+                    "more than one value for %s: %s (%s)" % (rtype, entity_dict[key], self.extid)
+                if entity_dict[key]:
+                    entity_dict[rtype] = entity_dict[key].pop()
+                    if key != rtype:
+                        del entity_dict[key]
+                    if (rschema.final and eschema.has_metadata(rtype, 'format')
+                            and not rtype + '_format' in entity_dict):
+                        entity_dict[rtype + '_format'] = u'text/plain'
+                else:
+                    del entity_dict[key]
+            else:
+                for target_extid in entity_dict.pop(key):
+                    if role == 'subject':
+                        deferred.append((self.extid, rtype, target_extid))
+                    else:
+                        deferred.append((target_extid, rtype, self.extid))
+        return deferred
+
+    def is_ready(self, extid2eid):
+        """Return True if the ext entity is ready, i.e. has all the URIs used in inlined relations
+        currently existing.
+        """
+        assert self._schema, 'prepare() method should be called first on %s' % self
+        # as .prepare has been called, we know that .values only contains subject relation *type* as
+        # key (no more (rtype, role) tuple)
+        schema = self._schema
+        entity_dict = self.values
+        for rtype in entity_dict:
+            rschema = schema.rschema(rtype)
+            if not rschema.final:
+                # .prepare() should drop other cases from the entity dict
+                assert rschema.inlined
+                if not entity_dict[rtype] in extid2eid:
+                    return False
+        # entity is ready, replace all relation's extid by eids
+        for rtype in entity_dict:
+            rschema = schema.rschema(rtype)
+            if rschema.inlined:
+                entity_dict[rtype] = extid2eid[entity_dict[rtype]]
+        return True
+
+
+class ExtEntitiesImporter(object):
+    """This class is responsible for importing externals entities, that is instances of
+    :class:`ExtEntity`, into CubicWeb entities.
+
+    :param schema: the CubicWeb's instance schema
+    :param store: a CubicWeb `Store`
+    :param extid2eid: optional {extid: eid} dictionary giving information on existing entities. It
+        will be completed during import. You may want to use :func:`cwuri2eid` to build it.
+    :param existing_relation: optional {rtype: set((subj eid, obj eid))} mapping giving information on
+        existing relations of a given type. You may want to use :class:`RelationMapping` to build it.
+    :param  etypes_order_hint: optional ordered iterable on entity types, giving an hint on the order in
+        which they should be attempted to be imported
+    :param  import_log: optional object implementing the :class:`SimpleImportLog` interface to record
+        events occuring during the import
+    :param  raise_on_error: optional boolean flag - default to false, indicating whether errors should
+        be raised or logged. You usually want them to be raised during test but to be logged in
+        production.
+
+    Instances of this class are meant to import external entities through :meth:`import_entities`
+    which handles a stream of :class:`ExtEntity`. One may then plug arbitrary filters into the
+    external entities stream.
+
+    .. automethod:: import_entities
+
+    """
+
+    def __init__(self, schema, store, extid2eid=None, existing_relations=None,
+                 etypes_order_hint=(), import_log=None, raise_on_error=False):
+        self.schema = schema
+        self.store = store
+        self.extid2eid = extid2eid if extid2eid is not None else {}
+        self.existing_relations = (existing_relations if existing_relations is not None
+                                   else defaultdict(set))
+        self.etypes_order_hint = etypes_order_hint
+        if import_log is None:
+            import_log = SimpleImportLog('<unspecified>')
+        self.import_log = import_log
+        self.raise_on_error = raise_on_error
+        # set of created/updated eids
+        self.created = set()
+        self.updated = set()
+
+    def import_entities(self, ext_entities):
+        """Import given external entities (:class:`ExtEntity`) stream (usually a generator)."""
+        # {etype: [etype dict]} of entities that are in the import queue
+        queue = {}
+        # order entity dictionaries then create/update them
+        deferred = self._import_entities(ext_entities, queue)
+        # create deferred relations that don't exist already
+        missing_relations = self.prepare_insert_deferred_relations(deferred)
+        self._warn_about_missing_work(queue, missing_relations)
+
+    def _import_entities(self, ext_entities, queue):
+        extid2eid = self.extid2eid
+        deferred = {}  # non inlined relations that may be deferred
+        self.import_log.record_debug('importing entities')
+        for ext_entity in self.iter_ext_entities(ext_entities, deferred, queue):
+            try:
+                eid = extid2eid[ext_entity.extid]
+            except KeyError:
+                self.prepare_insert_entity(ext_entity)
+            else:
+                if ext_entity.values:
+                    self.prepare_update_entity(ext_entity, eid)
+        return deferred
+
+    def iter_ext_entities(self, ext_entities, deferred, queue):
+        """Yield external entities in an order which attempts to satisfy
+        schema constraints (inlined / cardinality) and to optimize the import.
+        """
+        schema = self.schema
+        extid2eid = self.extid2eid
+        for ext_entity in ext_entities:
+            # check data in the transitional representation and prepare it for
+            # later insertion in the database
+            for subject_uri, rtype, object_uri in ext_entity.prepare(schema):
+                deferred.setdefault(rtype, set()).add((subject_uri, object_uri))
+            if not ext_entity.is_ready(extid2eid):
+                queue.setdefault(ext_entity.etype, []).append(ext_entity)
+                continue
+            yield ext_entity
+            # check for some entities in the queue that may now be ready. We'll have to restart
+            # search for ready entities until no one is generated
+            new = True
+            while new:
+                new = False
+                for etype in self.etypes_order_hint:
+                    if etype in queue:
+                        new_queue = []
+                        for ext_entity in queue[etype]:
+                            if ext_entity.is_ready(extid2eid):
+                                yield ext_entity
+                                # may unlock entity previously handled within this loop
+                                new = True
+                            else:
+                                new_queue.append(ext_entity)
+                        if new_queue:
+                            queue[etype][:] = new_queue
+                        else:
+                            del queue[etype]
+
+    def prepare_insert_entity(self, ext_entity):
+        """Call the store to prepare insertion of the given external entity"""
+        eid = self.store.prepare_insert_entity(ext_entity.etype, **ext_entity.values)
+        self.extid2eid[ext_entity.extid] = eid
+        self.created.add(eid)
+        return eid
+
+    def prepare_update_entity(self, ext_entity, eid):
+        """Call the store to prepare update of the given external entity"""
+        self.store.prepare_update_entity(ext_entity.etype, eid, **ext_entity.values)
+        self.updated.add(eid)
+
+    def prepare_insert_deferred_relations(self, deferred):
+        """Call the store to insert deferred relations (not handled during insertion/update for
+        entities). Return a list of relations `[(subj ext id, obj ext id)]` that may not be inserted
+        because the target entities don't exists yet.
+        """
+        prepare_insert_relation = self.store.prepare_insert_relation
+        rschema = self.schema.rschema
+        extid2eid = self.extid2eid
+        missing_relations = []
+        for rtype, relations in deferred.items():
+            self.import_log.record_debug('importing %s %s relations' % (len(relations), rtype))
+            symmetric = rschema(rtype).symmetric
+            existing = self.existing_relations[rtype]
+            for subject_uri, object_uri in relations:
+                try:
+                    subject_eid = extid2eid[subject_uri]
+                    object_eid = extid2eid[object_uri]
+                except KeyError:
+                    missing_relations.append((subject_uri, rtype, object_uri))
+                    continue
+                if (subject_eid, object_eid) not in existing:
+                    prepare_insert_relation(subject_eid, rtype, object_eid)
+                    existing.add((subject_eid, object_eid))
+                    if symmetric:
+                        existing.add((object_eid, subject_eid))
+        return missing_relations
+
+    def _warn_about_missing_work(self, queue, missing_relations):
+        error = self.import_log.record_error
+        if queue:
+            msgs = ["can't create some entities, is there some cycle or "
+                    "missing data?"]
+            for ext_entities in queue.values():
+                for ext_entity in ext_entities:
+                    msgs.append(str(ext_entity))
+            map(error, msgs)
+            if self.raise_on_error:
+                raise Exception('\n'.join(msgs))
+        if missing_relations:
+            msgs = ["can't create some relations, is there missing data?"]
+            for subject_uri, rtype, object_uri in missing_relations:
+                msgs.append("%s %s %s" % (subject_uri, rtype, object_uri))
+            map(error, msgs)
+            if self.raise_on_error:
+                raise Exception('\n'.join(msgs))
+
+
+class SimpleImportLog(object):
+    """Fake CWDataImport log using a simple text format.
+
+    Useful to display logs in the UI instead of storing them to the
+    database.
+    """
+
+    def __init__(self, filename):
+        self.logs = []
+        self.filename = filename
+
+    def record_debug(self, msg, path=None, line=None):
+        self._log(logging.DEBUG, msg, path, line)
+
+    def record_info(self, msg, path=None, line=None):
+        self._log(logging.INFO, msg, path, line)
+
+    def record_warning(self, msg, path=None, line=None):
+        self._log(logging.WARNING, msg, path, line)
+
+    def record_error(self, msg, path=None, line=None):
+        self._log(logging.ERROR, msg, path, line)
+
+    def record_fatal(self, msg, path=None, line=None):
+        self._log(logging.FATAL, msg, path, line)
+
+    def _log(self, severity, msg, path, line):
+        encodedmsg = u'%s\t%s\t%s\t%s' % (severity, self.filename,
+                                          line or u'', msg)
+        self.logs.append(encodedmsg)
+
+
+class HTMLImportLog(SimpleImportLog):
+    """Fake CWDataImport log using a simple HTML format."""
+    def __init__(self, filename):
+        super(HTMLImportLog, self).__init__(xml_escape(filename))
+
+    def _log(self, severity, msg, path, line):
+        encodedmsg = u'%s\t%s\t%s\t%s<br/>' % (severity, self.filename,
+                                               line or u'', xml_escape(msg))
+        self.logs.append(encodedmsg)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dataimport/pgstore.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,447 @@
+# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+"""Postgres specific store"""
+
+import warnings
+import cPickle
+import os.path as osp
+from StringIO import StringIO
+from time import asctime
+from datetime import date, datetime, time
+from collections import defaultdict
+from base64 import b64encode
+
+from cubicweb.utils import make_uid
+from cubicweb.server.utils import eschema_eid
+from cubicweb.server.sqlutils import SQL_PREFIX
+from cubicweb.dataimport.stores import NoHookRQLObjectStore
+
+
+def _execmany_thread_not_copy_from(cu, statement, data, table=None,
+                                   columns=None, encoding='utf-8'):
+    """ Execute thread without copy from
+    """
+    cu.executemany(statement, data)
+
+def _execmany_thread_copy_from(cu, statement, data, table,
+                               columns, encoding='utf-8'):
+    """ Execute thread with copy from
+    """
+    try:
+        buf = _create_copyfrom_buffer(data, columns, encoding=encoding)
+    except ValueError:
+        _execmany_thread_not_copy_from(cu, statement, data)
+    else:
+        if columns is None:
+            cu.copy_from(buf, table, null='NULL')
+        else:
+            cu.copy_from(buf, table, null='NULL', columns=columns)
+
+def _execmany_thread(sql_connect, statements, dump_output_dir=None,
+                     support_copy_from=True, encoding='utf-8'):
+    """
+    Execute sql statement. If 'INSERT INTO', try to use 'COPY FROM' command,
+    or fallback to execute_many.
+    """
+    if support_copy_from:
+        execmany_func = _execmany_thread_copy_from
+    else:
+        execmany_func = _execmany_thread_not_copy_from
+    cnx = sql_connect()
+    cu = cnx.cursor()
+    try:
+        for statement, data in statements:
+            table = None
+            columns = None
+            try:
+                if not statement.startswith('INSERT INTO'):
+                    cu.executemany(statement, data)
+                    continue
+                table = statement.split()[2]
+                if isinstance(data[0], (tuple, list)):
+                    columns = None
+                else:
+                    columns = list(data[0])
+                execmany_func(cu, statement, data, table, columns, encoding)
+            except Exception:
+                print 'unable to copy data into table %s' % table
+                # Error in import statement, save data in dump_output_dir
+                if dump_output_dir is not None:
+                    pdata = {'data': data, 'statement': statement,
+                             'time': asctime(), 'columns': columns}
+                    filename = make_uid()
+                    try:
+                        with open(osp.join(dump_output_dir,
+                                           '%s.pickle' % filename), 'w') as fobj:
+                            fobj.write(cPickle.dumps(pdata))
+                    except IOError:
+                        print 'ERROR while pickling in', dump_output_dir, filename+'.pickle'
+                        pass
+                cnx.rollback()
+                raise
+    finally:
+        cnx.commit()
+        cu.close()
+
+
+def _copyfrom_buffer_convert_None(value, **opts):
+    '''Convert None value to "NULL"'''
+    return 'NULL'
+
+def _copyfrom_buffer_convert_number(value, **opts):
+    '''Convert a number into its string representation'''
+    return str(value)
+
+def _copyfrom_buffer_convert_string(value, **opts):
+    '''Convert string value.
+
+    Recognized keywords:
+    :encoding: resulting string encoding (default: utf-8)
+    '''
+    encoding = opts.get('encoding','utf-8')
+    escape_chars = ((u'\\', ur'\\'), (u'\t', u'\\t'), (u'\r', u'\\r'),
+                    (u'\n', u'\\n'))
+    for char, replace in escape_chars:
+        value = value.replace(char, replace)
+    if isinstance(value, unicode):
+        value = value.encode(encoding)
+    return value
+
+def _copyfrom_buffer_convert_date(value, **opts):
+    '''Convert date into "YYYY-MM-DD"'''
+    # Do not use strftime, as it yields issue with date < 1900
+    # (http://bugs.python.org/issue1777412)
+    return '%04d-%02d-%02d' % (value.year, value.month, value.day)
+
+def _copyfrom_buffer_convert_datetime(value, **opts):
+    '''Convert date into "YYYY-MM-DD HH:MM:SS.UUUUUU"'''
+    # Do not use strftime, as it yields issue with date < 1900
+    # (http://bugs.python.org/issue1777412)
+    return '%s %s' % (_copyfrom_buffer_convert_date(value, **opts),
+                      _copyfrom_buffer_convert_time(value, **opts))
+
+def _copyfrom_buffer_convert_time(value, **opts):
+    '''Convert time into "HH:MM:SS.UUUUUU"'''
+    return '%02d:%02d:%02d.%06d' % (value.hour, value.minute,
+                                    value.second, value.microsecond)
+
+# (types, converter) list.
+_COPYFROM_BUFFER_CONVERTERS = [
+    (type(None), _copyfrom_buffer_convert_None),
+    ((long, int, float), _copyfrom_buffer_convert_number),
+    (basestring, _copyfrom_buffer_convert_string),
+    (datetime, _copyfrom_buffer_convert_datetime),
+    (date, _copyfrom_buffer_convert_date),
+    (time, _copyfrom_buffer_convert_time),
+]
+
+def _create_copyfrom_buffer(data, columns=None, **convert_opts):
+    """
+    Create a StringIO buffer for 'COPY FROM' command.
+    Deals with Unicode, Int, Float, Date... (see ``converters``)
+
+    :data: a sequence/dict of tuples
+    :columns: list of columns to consider (default to all columns)
+    :converter_opts: keyword arguements given to converters
+    """
+    # Create a list rather than directly create a StringIO
+    # to correctly write lines separated by '\n' in a single step
+    rows = []
+    if columns is None:
+        if isinstance(data[0], (tuple, list)):
+            columns = range(len(data[0]))
+        elif isinstance(data[0], dict):
+            columns = data[0].keys()
+        else:
+            raise ValueError('Could not get columns: you must provide columns.')
+    for row in data:
+        # Iterate over the different columns and the different values
+        # and try to convert them to a correct datatype.
+        # If an error is raised, do not continue.
+        formatted_row = []
+        for col in columns:
+            try:
+                value = row[col]
+            except KeyError:
+                warnings.warn(u"Column %s is not accessible in row %s"
+                              % (col, row), RuntimeWarning)
+                # XXX 'value' set to None so that the import does not end in
+                # error.
+                # Instead, the extra keys are set to NULL from the
+                # database point of view.
+                value = None
+            for types, converter in _COPYFROM_BUFFER_CONVERTERS:
+                if isinstance(value, types):
+                    value = converter(value, **convert_opts)
+                    break
+            else:
+                raise ValueError("Unsupported value type %s" % type(value))
+            # We push the value to the new formatted row
+            # if the value is not None and could be converted to a string.
+            formatted_row.append(value)
+        rows.append('\t'.join(formatted_row))
+    return StringIO('\n'.join(rows))
+
+
+class SQLGenObjectStore(NoHookRQLObjectStore):
+    """Controller of the data import process. This version is based
+    on direct insertions throught SQL command (COPY FROM or execute many).
+
+    >>> store = SQLGenObjectStore(cnx)
+    >>> store.create_entity('Person', ...)
+    >>> store.flush()
+    """
+
+    def __init__(self, cnx, dump_output_dir=None, nb_threads_statement=1):
+        """
+        Initialize a SQLGenObjectStore.
+
+        Parameters:
+
+          - cnx: connection on the cubicweb instance
+          - dump_output_dir: a directory to dump failed statements
+            for easier recovery. Default is None (no dump).
+        """
+        super(SQLGenObjectStore, self).__init__(cnx)
+        ### hijack default source
+        self.source = SQLGenSourceWrapper(
+            self.source, cnx.vreg.schema,
+            dump_output_dir=dump_output_dir)
+        ### XXX This is done in super().__init__(), but should be
+        ### redone here to link to the correct source
+        self.add_relation = self.source.add_relation
+        self.indexes_etypes = {}
+        if nb_threads_statement != 1:
+            warn('[3.21] SQLGenObjectStore is no longer threaded', DeprecationWarning)
+
+    def flush(self):
+        """Flush data to the database"""
+        self.source.flush()
+
+    def relate(self, subj_eid, rtype, obj_eid, **kwargs):
+        if subj_eid is None or obj_eid is None:
+            return
+        # XXX Could subjtype be inferred ?
+        self.source.add_relation(self._cnx, subj_eid, rtype, obj_eid,
+                                 self.rschema(rtype).inlined, **kwargs)
+        if self.rschema(rtype).symmetric:
+            self.source.add_relation(self._cnx, obj_eid, rtype, subj_eid,
+                                     self.rschema(rtype).inlined, **kwargs)
+
+    def drop_indexes(self, etype):
+        """Drop indexes for a given entity type"""
+        if etype not in self.indexes_etypes:
+            cu = self._cnx.cnxset.cu
+            def index_to_attr(index):
+                """turn an index name to (database) attribute name"""
+                return index.replace(etype.lower(), '').replace('idx', '').strip('_')
+            indices = [(index, index_to_attr(index))
+                       for index in self.source.dbhelper.list_indices(cu, etype)
+                       # Do not consider 'cw_etype_pkey' index
+                       if not index.endswith('key')]
+            self.indexes_etypes[etype] = indices
+        for index, attr in self.indexes_etypes[etype]:
+            self._cnx.system_sql('DROP INDEX %s' % index)
+
+    def create_indexes(self, etype):
+        """Recreate indexes for a given entity type"""
+        for index, attr in self.indexes_etypes.get(etype, []):
+            sql = 'CREATE INDEX %s ON cw_%s(%s)' % (index, etype, attr)
+            self._cnx.system_sql(sql)
+
+
+###########################################################################
+## SQL Source #############################################################
+###########################################################################
+
+class SQLGenSourceWrapper(object):
+
+    def __init__(self, system_source, schema,
+                 dump_output_dir=None):
+        self.system_source = system_source
+        # Explicitely backport attributes from system source
+        self._storage_handler = self.system_source._storage_handler
+        self.preprocess_entity = self.system_source.preprocess_entity
+        self.sqlgen = self.system_source.sqlgen
+        self.uri = self.system_source.uri
+        self.eid = self.system_source.eid
+        # Directory to write temporary files
+        self.dump_output_dir = dump_output_dir
+        # Allow to execute code with SQLite backend that does
+        # not support (yet...) copy_from
+        # XXX Should be dealt with in logilab.database
+        spcfrom = system_source.dbhelper.dbapi_module.support_copy_from
+        self.support_copy_from = spcfrom
+        self.dbencoding = system_source.dbhelper.dbencoding
+        self.init_statement_lists()
+        self._inlined_rtypes_cache = {}
+        self._fill_inlined_rtypes_cache(schema)
+        self.schema = schema
+        self.do_fti = False
+
+    def _fill_inlined_rtypes_cache(self, schema):
+        cache = self._inlined_rtypes_cache
+        for eschema in schema.entities():
+            for rschema in eschema.ordered_relations():
+                if rschema.inlined:
+                    cache[eschema.type] = SQL_PREFIX + rschema.type
+
+    def init_statement_lists(self):
+        self._sql_entities = defaultdict(list)
+        self._sql_relations = {}
+        self._sql_inlined_relations = {}
+        self._sql_eids = defaultdict(list)
+        # keep track, for each eid of the corresponding data dict
+        self._sql_eid_insertdicts = {}
+
+    def flush(self):
+        print 'starting flush'
+        _entities_sql = self._sql_entities
+        _relations_sql = self._sql_relations
+        _inlined_relations_sql = self._sql_inlined_relations
+        _insertdicts = self._sql_eid_insertdicts
+        try:
+            # try, for each inlined_relation, to find if we're also creating
+            # the host entity (i.e. the subject of the relation).
+            # In that case, simply update the insert dict and remove
+            # the need to make the
+            # UPDATE statement
+            for statement, datalist in _inlined_relations_sql.iteritems():
+                new_datalist = []
+                # for a given inlined relation,
+                # browse each couple to be inserted
+                for data in datalist:
+                    keys = list(data)
+                    # For inlined relations, it exists only two case:
+                    # (rtype, cw_eid) or (cw_eid, rtype)
+                    if keys[0] == 'cw_eid':
+                        rtype = keys[1]
+                    else:
+                        rtype = keys[0]
+                    updated_eid = data['cw_eid']
+                    if updated_eid in _insertdicts:
+                        _insertdicts[updated_eid][rtype] = data[rtype]
+                    else:
+                        # could not find corresponding insert dict, keep the
+                        # UPDATE query
+                        new_datalist.append(data)
+                _inlined_relations_sql[statement] = new_datalist
+            _execmany_thread(self.system_source.get_connection,
+                             self._sql_eids.items()
+                             + _entities_sql.items()
+                             + _relations_sql.items()
+                             + _inlined_relations_sql.items(),
+                             dump_output_dir=self.dump_output_dir,
+                             support_copy_from=self.support_copy_from,
+                             encoding=self.dbencoding)
+        finally:
+            _entities_sql.clear()
+            _relations_sql.clear()
+            _insertdicts.clear()
+            _inlined_relations_sql.clear()
+
+    def add_relation(self, cnx, subject, rtype, object,
+                     inlined=False, **kwargs):
+        if inlined:
+            _sql = self._sql_inlined_relations
+            data = {'cw_eid': subject, SQL_PREFIX + rtype: object}
+            subjtype = kwargs.get('subjtype')
+            if subjtype is None:
+                # Try to infer it
+                targets = [t.type for t in
+                           self.schema.rschema(rtype).subjects()]
+                if len(targets) == 1:
+                    subjtype = targets[0]
+                else:
+                    raise ValueError('You should give the subject etype for '
+                                     'inlined relation %s'
+                                     ', as it cannot be inferred: '
+                                     'this type is given as keyword argument '
+                                     '``subjtype``'% rtype)
+            statement = self.sqlgen.update(SQL_PREFIX + subjtype,
+                                           data, ['cw_eid'])
+        else:
+            _sql = self._sql_relations
+            data = {'eid_from': subject, 'eid_to': object}
+            statement = self.sqlgen.insert('%s_relation' % rtype, data)
+        if statement in _sql:
+            _sql[statement].append(data)
+        else:
+            _sql[statement] = [data]
+
+    def add_entity(self, cnx, entity):
+        with self._storage_handler(cnx, entity, 'added'):
+            attrs = self.preprocess_entity(entity)
+            rtypes = self._inlined_rtypes_cache.get(entity.cw_etype, ())
+            if isinstance(rtypes, str):
+                rtypes = (rtypes,)
+            for rtype in rtypes:
+                if rtype not in attrs:
+                    attrs[rtype] = None
+            sql = self.sqlgen.insert(SQL_PREFIX + entity.cw_etype, attrs)
+            self._sql_eid_insertdicts[entity.eid] = attrs
+            self._append_to_entities(sql, attrs)
+
+    def _append_to_entities(self, sql, attrs):
+        self._sql_entities[sql].append(attrs)
+
+    def _handle_insert_entity_sql(self, cnx, sql, attrs):
+        # We have to overwrite the source given in parameters
+        # as here, we directly use the system source
+        attrs['asource'] = self.system_source.uri
+        self._sql_eids[sql].append(attrs)
+
+    def _handle_is_relation_sql(self, cnx, sql, attrs):
+        self._append_to_entities(sql, attrs)
+
+    def _handle_is_instance_of_sql(self, cnx, sql, attrs):
+        self._append_to_entities(sql, attrs)
+
+    def _handle_source_relation_sql(self, cnx, sql, attrs):
+        self._append_to_entities(sql, attrs)
+
+    # add_info is _copypasted_ from the one in NativeSQLSource. We want it
+    # there because it will use the _handlers of the SQLGenSourceWrapper, which
+    # are not like the ones in the native source.
+    def add_info(self, cnx, entity, source, extid):
+        """add type and source info for an eid into the system table"""
+        # begin by inserting eid/type/source/extid into the entities table
+        if extid is not None:
+            assert isinstance(extid, str)
+            extid = b64encode(extid)
+        attrs = {'type': entity.cw_etype, 'eid': entity.eid, 'extid': extid,
+                 'asource': source.uri}
+        self._handle_insert_entity_sql(cnx, self.sqlgen.insert('entities', attrs), attrs)
+        # insert core relations: is, is_instance_of and cw_source
+        try:
+            self._handle_is_relation_sql(cnx, 'INSERT INTO is_relation(eid_from,eid_to) VALUES (%s,%s)',
+                                         (entity.eid, eschema_eid(cnx, entity.e_schema)))
+        except IndexError:
+            # during schema serialization, skip
+            pass
+        else:
+            for eschema in entity.e_schema.ancestors() + [entity.e_schema]:
+                self._handle_is_relation_sql(cnx,
+                                             'INSERT INTO is_instance_of_relation(eid_from,eid_to) VALUES (%s,%s)',
+                                             (entity.eid, eschema_eid(cnx, eschema)))
+        if 'CWSource' in self.schema and source.eid is not None: # else, cw < 3.10
+            self._handle_is_relation_sql(cnx, 'INSERT INTO cw_source_relation(eid_from,eid_to) VALUES (%s,%s)',
+                                         (entity.eid, source.eid))
+        # now we can update the full text index
+        if self.do_fti and self.need_fti_indexation(entity.cw_etype):
+            self.index_entity(cnx, entity=entity)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dataimport/stores.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,323 @@
+# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+"""
+Stores are responsible to insert properly formatted entities and relations into the database. They
+have the following API::
+
+    >>> user_eid = store.prepare_insert_entity('CWUser', login=u'johndoe')
+    >>> group_eid = store.prepare_insert_entity('CWUser', name=u'unknown')
+    >>> store.relate(user_eid, 'in_group', group_eid)
+    >>> store.flush()
+    >>> store.commit()
+    >>> store.finish()
+
+Some store **requires a flush** to copy data in the database, so if you want to have store
+independant code you should explicitly call it. (There may be multiple flushes during the
+process, or only one at the end if there is no memory issue). This is different from the
+commit which validates the database transaction. At last, the `finish()` method should be called in
+case the store requires additional work once everything is done.
+
+* ``prepare_insert_entity(<entity type>, **kwargs) -> eid``: given an entity
+  type, attributes and inlined relations, return the eid of the entity to be
+  inserted, *with no guarantee that anything has been inserted in database*,
+
+* ``prepare_update_entity(<entity type>, eid, **kwargs) -> None``: given an
+  entity type and eid, promise for update given attributes and inlined
+  relations *with no guarantee that anything has been inserted in database*,
+
+* ``prepare_insert_relation(eid_from, rtype, eid_to) -> None``: indicate that a
+  relation ``rtype`` should be added between entities with eids ``eid_from``
+  and ``eid_to``. Similar to ``prepare_insert_entity()``, *there is no
+  guarantee that the relation will be inserted in database*,
+
+* ``flush() -> None``: flush any temporary data to database. May be called
+  several times during an import,
+
+* ``commit() -> None``: commit the database transaction,
+
+* ``finish() -> None``: additional stuff to do after import is terminated.
+
+.. autoclass:: cubicweb.dataimport.stores.RQLObjectStore
+.. autoclass:: cubicweb.dataimport.stores.NoHookRQLObjectStore
+.. autoclass:: cubicweb.dataimport.stores.MetaGenerator
+"""
+import inspect
+import warnings
+from datetime import datetime
+from copy import copy
+
+from logilab.common.deprecation import deprecated
+from logilab.common.decorators import cached
+
+from cubicweb.schema import META_RTYPES, VIRTUAL_RTYPES
+from cubicweb.server.edition import EditedEntity
+
+
+class RQLObjectStore(object):
+    """Store that works by making RQL queries, hence with all the cubicweb's machinery activated.
+    """
+
+    def __init__(self, cnx, commit=None):
+        if commit is not None:
+            warnings.warn('[3.19] commit argument should not be specified '
+                          'as the cnx object already provides it.',
+                          DeprecationWarning, stacklevel=2)
+        self._cnx = cnx
+        self._commit = commit or cnx.commit
+        # XXX 3.21 deprecated attributes
+        self.eids = {}
+        self.types = {}
+
+    def rql(self, *args):
+        """Execute a RQL query. This is NOT part of the store API."""
+        return self._cnx.execute(*args)
+
+    def prepare_insert_entity(self, *args, **kwargs):
+        """Given an entity type, attributes and inlined relations, returns the inserted entity's
+        eid.
+        """
+        entity = self._cnx.create_entity(*args, **kwargs)
+        self.eids[entity.eid] = entity
+        self.types.setdefault(args[0], []).append(entity.eid)
+        return entity.eid
+
+    def prepare_update_entity(self, etype, eid, **kwargs):
+        """Given an entity type and eid, updates the corresponding entity with specified attributes
+        and inlined relations.
+        """
+        entity = self._cnx.entity_from_eid(eid)
+        assert entity.cw_etype == etype, 'Trying to update with wrong type {}'.format(etype)
+        # XXX some inlined relations may already exists
+        entity.cw_set(**kwargs)
+
+    def prepare_insert_relation(self, eid_from, rtype, eid_to, **kwargs):
+        """Insert into the database a  relation ``rtype`` between entities with eids ``eid_from``
+        and ``eid_to``.
+        """
+        self.rql('SET X %s Y WHERE X eid %%(x)s, Y eid %%(y)s' % rtype,
+                 {'x': int(eid_from), 'y': int(eid_to)})
+
+    def flush(self):
+        """Nothing to flush for this store."""
+        pass
+
+    def commit(self):
+        """Commit the database transaction."""
+        return self._commit()
+
+    @property
+    def session(self):
+        warnings.warn('[3.19] deprecated property.', DeprecationWarning, stacklevel=2)
+        return self._cnx.repo._get_session(self._cnx.sessionid)
+
+    @deprecated("[3.19] use cnx.find(*args, **kwargs).entities() instead")
+    def find_entities(self, *args, **kwargs):
+        return self._cnx.find(*args, **kwargs).entities()
+
+    @deprecated("[3.19] use cnx.find(*args, **kwargs).one() instead")
+    def find_one_entity(self, *args, **kwargs):
+        return self._cnx.find(*args, **kwargs).one()
+
+    @deprecated('[3.21] use prepare_insert_entity instead')
+    def create_entity(self, *args, **kwargs):
+        eid = self.prepare_insert_entity(*args, **kwargs)
+        return self._cnx.entity_from_eid(eid)
+
+    @deprecated('[3.21] use prepare_insert_relation instead')
+    def relate(self, eid_from, rtype, eid_to, **kwargs):
+        self.prepare_insert_relation(eid_from, rtype, eid_to, **kwargs)
+
+
+class NoHookRQLObjectStore(RQLObjectStore):
+    """Store that works by accessing low-level CubicWeb's source API, with all hooks deactivated. It
+    must be given a metadata generator object to handle metadata which are usually handled by hooks
+    (see :class:`MetaGenerator`).
+    """
+
+    def __init__(self, cnx, metagen=None):
+        super(NoHookRQLObjectStore, self).__init__(cnx)
+        self.source = cnx.repo.system_source
+        self.rschema = cnx.repo.schema.rschema
+        self.add_relation = self.source.add_relation
+        if metagen is None:
+            metagen = MetaGenerator(cnx)
+        self.metagen = metagen
+        self._nb_inserted_entities = 0
+        self._nb_inserted_types = 0
+        self._nb_inserted_relations = 0
+        # deactivate security
+        cnx.read_security = False
+        cnx.write_security = False
+
+    def prepare_insert_entity(self, etype, **kwargs):
+        """Given an entity type, attributes and inlined relations, returns the inserted entity's
+        eid.
+        """
+        for k, v in kwargs.iteritems():
+            kwargs[k] = getattr(v, 'eid', v)
+        entity, rels = self.metagen.base_etype_dicts(etype)
+        # make a copy to keep cached entity pristine
+        entity = copy(entity)
+        entity.cw_edited = copy(entity.cw_edited)
+        entity.cw_clear_relation_cache()
+        entity.cw_edited.update(kwargs, skipsec=False)
+        entity_source, extid = self.metagen.init_entity(entity)
+        cnx = self._cnx
+        self.source.add_info(cnx, entity, entity_source, extid)
+        self.source.add_entity(cnx, entity)
+        kwargs = dict()
+        if inspect.getargspec(self.add_relation).keywords:
+            kwargs['subjtype'] = entity.cw_etype
+        for rtype, targeteids in rels.iteritems():
+            # targeteids may be a single eid or a list of eids
+            inlined = self.rschema(rtype).inlined
+            try:
+                for targeteid in targeteids:
+                    self.add_relation(cnx, entity.eid, rtype, targeteid,
+                                      inlined, **kwargs)
+            except TypeError:
+                self.add_relation(cnx, entity.eid, rtype, targeteids,
+                                  inlined, **kwargs)
+        self._nb_inserted_entities += 1
+        return entity.eid
+
+    # XXX: prepare_update_entity is inherited from RQLObjectStore, it should be reimplemented to
+    # actually skip hooks as prepare_insert_entity
+
+    def prepare_insert_relation(self, eid_from, rtype, eid_to, **kwargs):
+        """Insert into the database a  relation ``rtype`` between entities with eids ``eid_from``
+        and ``eid_to``.
+        """
+        assert not rtype.startswith('reverse_')
+        self.add_relation(self._cnx, eid_from, rtype, eid_to,
+                          self.rschema(rtype).inlined)
+        if self.rschema(rtype).symmetric:
+            self.add_relation(self._cnx, eid_to, rtype, eid_from,
+                              self.rschema(rtype).inlined)
+        self._nb_inserted_relations += 1
+
+    @property
+    @deprecated('[3.21] deprecated')
+    def nb_inserted_entities(self):
+        return self._nb_inserted_entities
+
+    @property
+    @deprecated('[3.21] deprecated')
+    def nb_inserted_types(self):
+        return self._nb_inserted_types
+
+    @property
+    @deprecated('[3.21] deprecated')
+    def nb_inserted_relations(self):
+        return self._nb_inserted_relations
+
+
+class MetaGenerator(object):
+    """Class responsible for generating standard metadata for imported entities. You may want to
+    derive it to add application specific's metadata.
+
+    Parameters:
+    * `cnx`: connection to the repository
+    * `baseurl`: optional base URL to be used for `cwuri` generation - default to config['base-url']
+    * `source`: optional source to be used as `cw_source` for imported entities
+    """
+    META_RELATIONS = (META_RTYPES
+                      - VIRTUAL_RTYPES
+                      - set(('eid', 'cwuri',
+                             'is', 'is_instance_of', 'cw_source')))
+
+    def __init__(self, cnx, baseurl=None, source=None):
+        self._cnx = cnx
+        if baseurl is None:
+            config = cnx.vreg.config
+            baseurl = config['base-url'] or config.default_base_url()
+        if not baseurl[-1] == '/':
+            baseurl += '/'
+        self.baseurl = baseurl
+        if source is None:
+            source = cnx.repo.system_source
+        self.source = source
+        self.create_eid = cnx.repo.system_source.create_eid
+        self.time = datetime.now()
+        # attributes/relations shared by all entities of the same type
+        self.etype_attrs = []
+        self.etype_rels = []
+        # attributes/relations specific to each entity
+        self.entity_attrs = ['cwuri']
+        #self.entity_rels = [] XXX not handled (YAGNI?)
+        schema = cnx.vreg.schema
+        rschema = schema.rschema
+        for rtype in self.META_RELATIONS:
+            # skip owned_by / created_by if user is the internal manager
+            if cnx.user.eid == -1 and rtype in ('owned_by', 'created_by'):
+                continue
+            if rschema(rtype).final:
+                self.etype_attrs.append(rtype)
+            else:
+                self.etype_rels.append(rtype)
+
+    @cached
+    def base_etype_dicts(self, etype):
+        entity = self._cnx.vreg['etypes'].etype_class(etype)(self._cnx)
+        # entity are "surface" copied, avoid shared dict between copies
+        del entity.cw_extra_kwargs
+        entity.cw_edited = EditedEntity(entity)
+        for attr in self.etype_attrs:
+            genfunc = self.generate(attr)
+            if genfunc:
+                entity.cw_edited.edited_attribute(attr, genfunc(entity))
+        rels = {}
+        for rel in self.etype_rels:
+            genfunc = self.generate(rel)
+            if genfunc:
+                rels[rel] = genfunc(entity)
+        return entity, rels
+
+    def init_entity(self, entity):
+        entity.eid = self.create_eid(self._cnx)
+        extid = entity.cw_edited.get('cwuri')
+        for attr in self.entity_attrs:
+            if attr in entity.cw_edited:
+                # already set, skip this attribute
+                continue
+            genfunc = self.generate(attr)
+            if genfunc:
+                entity.cw_edited.edited_attribute(attr, genfunc(entity))
+        if isinstance(extid, unicode):
+            extid = extid.encode('utf-8')
+        return self.source, extid
+
+    def generate(self, rtype):
+        return getattr(self, 'gen_%s' % rtype, None)
+
+    def gen_cwuri(self, entity):
+        assert self.baseurl, 'baseurl is None while generating cwuri'
+        return u'%s%s' % (self.baseurl, entity.eid)
+
+    def gen_creation_date(self, entity):
+        return self.time
+
+    def gen_modification_date(self, entity):
+        return self.time
+
+    def gen_created_by(self, entity):
+        return self._cnx.user.eid
+
+    def gen_owned_by(self, entity):
+        return self._cnx.user.eid
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dataimport/test/data-massimport/schema.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,154 @@
+# copyright 2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr -- mailto:contact@logilab.fr
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""cubicweb-geonames schema"""
+
+from yams.buildobjs import (EntityType, RelationDefinition, SubjectRelation,
+			    String, Int, BigInt, Float, Date)
+from cubicweb.schemas.base import ExternalUri
+
+"""
+See geonames readme.txt for more details.
+"""
+
+class TestLocation(EntityType):
+    """
+    Entity type for location of Geonames.
+    See cities1000.zip, cities5000.zip, cities15000.zip and allCountries.txt
+    """
+    name = String(maxsize=1024, indexed=True, fulltextindexed=True)
+    geonameid = Int(required=True, unique=True, indexed=True)
+
+class Location(EntityType):
+    """
+    Entity type for location of Geonames.
+    See cities1000.zip, cities5000.zip, cities15000.zip and allCountries.txt
+    """
+    name = String(maxsize=1024, indexed=True, fulltextindexed=True)
+    geonameid = Int(indexed=True)
+    asciiname = String(maxsize=200, fulltextindexed=True)
+    alternatenames = String(fulltextindexed=True)
+    names = SubjectRelation('LocationName', cardinality='**')
+    latitude = Float(indexed=True)
+    longitude = Float(indexed=True)
+    feature_class = String(maxsize=1, indexed=True)
+    feature_code = SubjectRelation('FeatureCode', cardinality='?*', inlined=True)
+    country = SubjectRelation('Country', cardinality='?*', inlined=True)
+    alternate_country_code = String(maxsize=60)
+    main_administrative_region = SubjectRelation('AdministrativeRegion', cardinality='?*', inlined=True)
+    second_administrative_region = SubjectRelation('AdministrativeRegion', cardinality='?*', inlined=True)
+    admin_code_1 = String(maxsize=124)
+    admin_code_2 = String(maxsize=124)
+    admin_code_3 = String(maxsize=20)
+    admin_code_4 = String(maxsize=20)
+    population = BigInt(indexed=True)
+    elevation = Int(indexed=True)
+    gtopo30 = Int(indexed=True)
+    timezone = SubjectRelation('TimeZone', cardinality='?*', inlined=True)
+    geonames_date = Date()
+
+class LocationName(EntityType):
+    """
+    Name of a Location
+    """
+    name = String(maxsize=1024, indexed=True, fulltextindexed=True)
+    language = SubjectRelation('Language', cardinality='?*', inlined=True)
+    alternatenamesid = Int(indexed=True)
+
+class FeatureCode(EntityType):
+    """
+    Entity type for feature codes of Geonames.
+    See featureCodes_en.txt
+    """
+    name = String(maxsize=1024, indexed=True, fulltextindexed=True)
+    main_code = String(maxsize=1, indexed=True)
+    code = String(maxsize=12)
+    description = String(maxsize=1024, fulltextindexed=True)
+
+class AdministrativeRegion(EntityType):
+    """
+    Entity type for administrative regions of Geonames.
+    See admin1CodesASCII.txt and admin2Codes.txt
+    """
+    name = String(maxsize=1024, indexed=True, fulltextindexed=True)
+    code = String(maxsize=64, indexed=True)
+    country = SubjectRelation('Country', cardinality='?*', inlined=True)
+    geonameid = Int(indexed=True)
+    asciiname = String(maxsize=200, fulltextindexed=True)
+
+class Language(EntityType):
+    """
+    Entity type for languages of Geonames.
+    See admin1CodesASCII.txt and admin2Codes.txt
+    """
+    name = String(maxsize=1024, indexed=True, fulltextindexed=True)
+    iso_639_3 = String(maxsize=3, indexed=True)
+    iso_639_2 = String(maxsize=64, indexed=True)
+    iso_639_1 = String(maxsize=3, indexed=True)
+
+class Continent(EntityType):
+    """
+    Entity type for continents of geonames.
+    """
+    name = String(maxsize=1024, indexed=True, fulltextindexed=True)
+    code = String(maxsize=2, indexed=True)
+    geonameid = Int(indexed=True)
+
+class Country(EntityType):
+    """
+    Entity type for countries of geonames.
+    See countryInfo.txt
+    """
+    name = String(maxsize=1024, indexed=True, fulltextindexed=True)
+    code = String(maxsize=2, indexed=True)
+    code3 = String(maxsize=3, indexed=True)
+    codenum = Int(indexed=True)
+    fips = String(maxsize=2)
+    capital = String(maxsize=1024, fulltextindexed=True)
+    area = Float(indexed=True)
+    population = BigInt(indexed=True)
+    continent_code = String(maxsize=3)
+    continent = SubjectRelation('Continent', cardinality='?*', inlined=True)
+    tld = String(maxsize=64)
+    currency = String(maxsize=1024, fulltextindexed=True)
+    currency_code = String(maxsize=64)
+    geonameid = Int(indexed=True)
+    phone = String(maxsize=64)
+    postal_code = String(maxsize=200)
+    postal_code_regex = String(maxsize=200)
+    languages_code = String(maxsize=200)
+    neighbours_code = String(maxsize=200)
+    equivalent_fips = String(maxsize=2)
+
+class TimeZone(EntityType):
+    """
+    Entity type for timezone of geonames.
+    See timeZones.txt
+    """
+    code = String(maxsize=1024, indexed=True)
+    gmt = Float()
+    dst = Float()
+    raw_offset = Float()
+
+class used_language(RelationDefinition):
+    subject = 'Country'
+    object = 'Language'
+    cardinality = '**'
+
+class neighbour_of(RelationDefinition):
+    subject = 'Country'
+    object = 'Country'
+    cardinality = '**'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dataimport/test/data/geonames.csv	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,4000 @@
+3038814	Costa de Xurius	Costa de Xurius		42.5	1.48333	T	SLP	AD		00				0		1316	Europe/Andorra	1993-12-23
+3038815	Font de la Xona	Font de la Xona		42.55003	1.44986	H	SPNG	AD		04				0		2082	Europe/Andorra	2010-01-11
+3038816	Xixerella	Xixerella		42.55327	1.48736	P	PPL	AD		04				0		1520	Europe/Andorra	2009-04-24
+3038817	Xixerella	Xixerella	Xixerella	42.55	1.48333	A	ADMD	AD		00				0		1548	Europe/Andorra	2011-11-05
+3038818	Riu Xic	Riu Xic		42.56667	1.68333	H	STM	AD		00				0		2340	Europe/Andorra	1993-12-23
+3038819	Pas del Xic	Pas del Xic		42.5	1.58333	R	TRL	AD		00				0		1888	Europe/Andorra	1993-12-23
+3038820	Roc del Xeig	Roc del Xeig		42.56667	1.48333	T	RK	AD		00				0		1508	Europe/Andorra	1993-12-23
+3038821	Collada del Xeig	Collada del Xeig		42.56667	1.48333	T	PK	AD		00				0		1508	Europe/Andorra	1993-12-23
+3038822	Fonts Vives	Fonts Vives		42.5	1.56667	H	SPNG	AD		00				0		1776	Europe/Andorra	1993-12-23
+3038823	Roc de Vista	Roc de Vista		42.5	1.48333	T	RK	AD		00				0		1316	Europe/Andorra	1993-12-23
+3038824	Obaga de Vista	Obaga de Vista		42.48333	1.45	T	SLP	AD		00				0		1195	Europe/Andorra	1993-12-23
+3038825	Coll de Vista	Coll de Vista		42.46667	1.58333	T	SPUR	AD		00				0		2367	Europe/Andorra	1993-12-23
+3038826	Coll de Vista	Coll de Vista	Coll de Vista	42.48333	1.43333	T	PASS	AD		00				0		1938	Europe/Andorra	2011-11-05
+3038827	Visanseny	Visanseny	Visanceny,Visanseny	42.56667	1.61667	L	LCTY	AD	AD	00				0		1920	Europe/Andorra	2011-11-05
+3038828	Roc de la Vinya	Roc de la Vinya		42.53333	1.56667	T	RK	AD		00				0		1418	Europe/Andorra	1993-12-23
+3038829	Canal de la Vinya	Canal de la Vinya		42.51667	1.51667	H	STM	AD		00				0		1265	Europe/Andorra	1993-12-23
+3038830	Bosc de Villar	Bosc de Villar		42.6	1.55	V	FRST	AD		00				0		2298	Europe/Andorra	1993-12-23
+3038831	Torrent de Vila	Torrent de Vila		42.53333	1.56667	H	STM	AD		00				0		1418	Europe/Andorra	1993-12-23
+3038832	Vila	Vila	Casas Vila,Vila	42.53222	1.57392	P	PPL	AD		03				0		1418	Europe/Andorra	2011-11-05
+3038833	Basers de Vicenç	Basers de Vicenc		42.48333	1.48333	T	CLF	AD		00				0		981	Europe/Andorra	1993-12-23
+3038834	Pla de Viàs	Pla de Vias		42.58333	1.66667	T	UPLD	AD		00				0		2159	Europe/Andorra	1993-12-23
+3038835	Vial del Cardaire	Vial del Cardaire		42.56667	1.5	L	LCTY	AD		00				0		1636	Europe/Andorra	1993-12-23
+3038836	Font del Vi	Font del Vi		42.51667	1.48333	H	SPNG	AD		00				0		1839	Europe/Andorra	1993-12-23
+3038837	Font Verda	Font Verda		42.55	1.56667	H	SPNG	AD		00				0		1828	Europe/Andorra	1993-12-23
+3038838	Costa Verda	Costa Verda		42.48333	1.66667	T	SLP	AD		00				0		2524	Europe/Andorra	1993-12-23
+3038839	Costa Verda	Costa Verda		42.48333	1.56667	T	SLP	AD		00				0		2231	Europe/Andorra	1993-12-23
+3038840	Serrat de Ventader	Serrat de Ventader		42.48333	1.43333	T	MT	AD		00				0		1938	Europe/Andorra	1993-12-23
+3038841	Bony de Vellatocina	Bony de Vellatocina		42.61667	1.5	T	PK	AD		00				0		2390	Europe/Andorra	1993-12-23
+3038842	Pleta Vella	Pleta Vella		42.51667	1.61667	L	GRAZ	AD		00				0		2254	Europe/Andorra	1993-12-23
+3038843	Solà Vell	Sola Vell		42.46667	1.48333	T	SLP	AD		00				0		1134	Europe/Andorra	1993-12-23
+3038844	Port Vell	Port Vell		42.65	1.55	T	PASS	AD		00				0		2181	Europe/Andorra	1993-12-23
+3038845	Port Negre del Pallars	Port Negre del Pallars	Port Negre del Pallars,Port Vell	42.56667	1.45	T	PASS	AD		00				0		2137	Europe/Andorra	2011-11-05
+3038846	Bancal Vedeller	Bancal Vedeller		42.6	1.46667	R	TRL	AD		00				0		2421	Europe/Andorra	1993-12-23
+3038847	Vedat del Xeig	Vedat del Xeig		42.56667	1.48333	L	LCTY	AD		00				0		1508	Europe/Andorra	1993-12-23
+3038848	Vedat dels Plans	Vedat dels Plans		42.53333	1.5	A	ADMD	AD		00				0		1357	Europe/Andorra	1993-12-23
+3038849	Vedat de Coll de les Cases	Vedat de Coll de les Cases		42.56667	1.5	L	LCTY	AD		00				0		1636	Europe/Andorra	1993-12-23
+3038850	Serra del Vedat	Serra del Vedat		42.46667	1.48333	T	RDGE	AD		00				0		1134	Europe/Andorra	1993-12-23
+3038851	Planades del Vedat	Planades del Vedat		42.53333	1.5	T	UPLD	AD		00				0		1357	Europe/Andorra	1993-12-23
+3038852	Veda de Sorteny	Veda de Sorteny		42.61667	1.55	L	LCTY	AD		00				0		2007	Europe/Andorra	1993-12-23
+3038853	Veda del Castellar	Veda del Castellar		42.63333	1.51667	A	ADMD	AD		00				0		1894	Europe/Andorra	1993-12-23
+3038854	Canal de les Veces	Canal de les Veces		42.51667	1.51667	H	STM	AD		00				0		1265	Europe/Andorra	1993-12-23
+3038855	Coma de Varilles	Coma de Varilles		42.63333	1.53333	T	VAL	AD		00				0		2072	Europe/Andorra	1993-12-23
+3038856	Portella de les Vaques	Portella de les Vaques		42.56667	1.45	T	PASS	AD		00				0		2137	Europe/Andorra	1993-12-23
+3038857	Pas de les Vaques	Pas de les Vaques		42.58333	1.7	T	PASS	AD		00				0		2584	Europe/Andorra	1993-12-23
+3038858	Collada de les Vaques	Collada de les Vaques		42.56667	1.56667	T	PASS	AD		00				0		2089	Europe/Andorra	1993-12-23
+3038859	Roc del Vaquer	Roc del Vaquer		42.63333	1.48333	T	CLF	AD		00				0		2283	Europe/Andorra	1993-12-23
+3038860	Valls de la Coma	Valls de la Coma		42.6	1.63333	L	LCTY	AD		00				0		1893	Europe/Andorra	1993-12-23
+3038861	Vall d'Incles	Vall d'Incles		42.58386	1.66331	L	LCTY	AD		02				0		1867	Europe/Andorra	2010-01-11
+3038862	Vall de Soldeu	Vall de Soldeu		42.55	1.68333	L	LCTY	AD		00				0		2254	Europe/Andorra	1993-12-23
+3038863	Riu de la Vall del Riu	Riu de la Vall del Riu		42.56667	1.61667	H	STM	AD		00				0		1920	Europe/Andorra	1993-12-23
+3038864	Estany Gran de la Vall del Riu	Estany Gran de la Vall del Riu		42.601	1.59368	H	LK	AD		00				0		2467	Europe/Andorra	2011-04-19
+3038865	Cascada de la Vall del Riu	Cascada de la Vall del Riu		42.56667	1.61667	H	FLLS	AD		00				0		1920	Europe/Andorra	1993-12-23
+3038866	Canals de la Vall del Riu	Canals de la Vall del Riu		42.58333	1.6	H	RVN	AD		00				0		1828	Europe/Andorra	1993-12-23
+3038867	Camí de la Vall del Riu	Cami de la Vall del Riu		42.58333	1.61667	R	TRL	AD		00				0		1707	Europe/Andorra	1993-12-23
+3038868	Bosc de la Vall del Riu	Bosc de la Vall del Riu		42.58333	1.61667	V	FRST	AD		00				0		1707	Europe/Andorra	1993-12-23
+3038869	Vall del Riu	Vall del Riu		42.6	1.6	A	ADMD	AD		00				0		2143	Europe/Andorra	1993-12-23
+3038870	Coll de Vall Civera	Coll de Vall Civera	Coll de Valcibera,Coll de Vall Civera,Collado de Vall-Civera,Port de Vall Civera	42.48333	1.66667	T	PASS	AD		00				0		2524	Europe/Andorra	2011-11-05
+3038871	Obac de la Vall	Obac de la Vall		42.46667	1.5	T	SLP	AD		00				0		1383	Europe/Andorra	1993-12-23
+3038872	Riu Valira d’Orient	Riu Valira d'Orient	Rio Balira del Orien,Riu Valira d'Orient,Riu Valira d’Orient,Río Balira del Orien	42.51667	1.53333	H	STM	AD		00				0		1460	Europe/Andorra	2011-11-05
+3038873	Riu Valira del Nord	Riu Valira del Nord	Riu Valira d'Ordino,Riu Valira del Nord,Riu Valira del Nort,Riu Valira d’Ordino,Valira del Nord,Valira del Norte	42.34645	1.44271	H	STM	AD		00				0		682	Europe/Andorra	2011-11-05
+3038874	Estanys de la Val del Riu	Estanys de la Val del Riu		42.60138	1.59418	H	LKS	AD		00				0		2467	Europe/Andorra	2011-04-19
+3038875	Vaca Morta	Vaca Morta		42.56667	1.76667	L	LCTY	AD		00				0		1674	Europe/Andorra	1993-12-23
+3038876	Riu d’ Urina	Riu d' Urina		42.56667	1.58333	H	STM	AD		00				0		1919	Europe/Andorra	1993-12-23
+3038877	Serrat de la Uïna	Serrat de la Uina		42.56667	1.51667	T	RDGE	AD		00				0		1500	Europe/Andorra	1993-12-23
+3038878	Obaga de la Tuta	Obaga de la Tuta		42.48333	1.45	T	SLP	AD		00				0		1195	Europe/Andorra	1993-12-23
+3038879	Cova de la Tuta	Cova de la Tuta		42.48333	1.45	S	CAVE	AD		00				0		1195	Europe/Andorra	1993-12-23
+3038880	Roca del Tut	Roca del Tut		42.55	1.46667	T	RK	AD		00				0		1585	Europe/Andorra	1993-12-23
+3038881	Coll de Turer	Coll de Turer		42.56667	1.46667	T	PK	AD		00				0		1673	Europe/Andorra	1993-12-23
+3038882	Font del Tudó	Font del Tudo		42.43333	1.53333	H	SPNG	AD		00				0		2108	Europe/Andorra	1993-12-23
+3038883	Estany de les Truites	Estany de les Truites		42.58333	1.45	H	LK	AD		00				0		2156	Europe/Andorra	1993-12-23
+3038884	Serra de Tristaina	Serra de Tristaina	Serra de Tristaina	42.65	1.48333	T	RDGE	AD		00				0		2341	Europe/Andorra	2011-11-05
+3038885	Riu de Tristaina	Riu de Tristaina		42.61667	1.55	H	STM	AD		00				0		2007	Europe/Andorra	1993-12-23
+3038886	Pic de Tristaina	Pic de Tristaina	Pic de Triatagne,Pic de Tristagne,Pic de Tristaina,Pic des Tri-Stagnes,Tristaina	42.65265	1.49242	T	PK	AD	AD,FR	00				0		2384	Europe/Andorra	2007-04-29
+3038887	Clot de Tres Torrents	Clot de Tres Torrents		42.53333	1.53333	H	RVN	AD		00				0		1521	Europe/Andorra	1993-12-23
+3038888	Bony de Tres Corts	Bony de Tres Corts		42.53333	1.53333	T	SPUR	AD		00				0		1521	Europe/Andorra	1993-12-23
+3038889	Canal dels Trèmols	Canal dels Tremols		42.5	1.48333	H	STM	AD		00				0		1316	Europe/Andorra	1993-12-23
+3038890	Travenc	Travenc		42.6	1.68333	L	LCTY	AD		00				0		2089	Europe/Andorra	1993-12-23
+3038891	Pleta de la Trava	Pleta de la Trava		42.48333	1.61667	L	GRAZ	AD		00				0		2217	Europe/Andorra	1993-12-23
+3038892	Coll de la Trava	Coll de la Trava		42.48333	1.46667	T	SPUR	AD		00				0		1148	Europe/Andorra	1993-12-23
+3038893	Canal de la Trava	Canal de la Trava		42.56667	1.53333	H	RVN	AD		00				0		1669	Europe/Andorra	1993-12-23
+3038894	Bosc de la Trava	Bosc de la Trava		42.48333	1.63333	V	FRST	AD		00				0		2296	Europe/Andorra	1993-12-23
+3038895	Font dels Traginers	Font dels Traginers		42.43333	1.51667	H	SPNG	AD		00				0		2031	Europe/Andorra	1993-12-23
+3038896	Pla de les Toves	Pla de les Toves		42.46667	1.45	T	UPLD	AD		00				0		1562	Europe/Andorra	1993-12-23
+3038897	Torrent del Tossalroi	Torrent del Tossalroi		42.46667	1.51667	H	STM	AD		00				0		1985	Europe/Andorra	1993-12-23
+3038898	Tossal Gran	Tossal Gran		42.48333	1.48333	L	LCTY	AD		00				0		981	Europe/Andorra	1993-12-23
+3038899	Tossalet i Vinyals	Tossalet i Vinyals		42.48333	1.48333	L	LCTY	AD		00				0		981	Europe/Andorra	1993-12-23
+3038900	Bosc del Tossal de les Poselles	Bosc del Tossal de les Poselles		42.53333	1.5	V	FRST	AD		00				0		1357	Europe/Andorra	1993-12-23
+3038901	Tossal	Tossal		42.46667	1.48333	L	LCTY	AD		00				0		1134	Europe/Andorra	1993-12-23
+3038902	Tosquers	Tosquers		42.56667	1.48333	T	SLP	AD		00				0		1508	Europe/Andorra	1993-12-23
+3038903	Canal del Tosquer	Canal del Tosquer		42.56667	1.51667	H	STM	AD		00				0		1500	Europe/Andorra	1993-12-23
+3038904	Bosc del Tosquer	Bosc del Tosquer		42.56667	1.53333	V	FRST	AD		00				0		1669	Europe/Andorra	1993-12-23
+3038905	Bosc del Tosquer	Bosc del Tosquer		42.53333	1.6	V	FRST	AD		00				0		1888	Europe/Andorra	1993-12-23
+3038906	Bosc de la Tosca	Bosc de la Tosca		42.43333	1.45	V	FRST	AD		00				0		877	Europe/Andorra	1993-12-23
+3038907	Tosa d’Incles	Tosa d'Incles		42.58333	1.68333	A	ADMD	AD		00				0		2294	Europe/Andorra	1993-12-23
+3038908	Cap de Tosa d’Entor	Cap de Tosa d'Entor		42.6	1.65	T	PK	AD		00				0		2131	Europe/Andorra	1993-12-23
+3038909	Tosa de les Mussoles	Tosa de les Mussoles		42.61667	1.68333	L	LCTY	AD		00				0		2406	Europe/Andorra	1993-12-23
+3038910	Pic de la Tosa de Juclar	Pic de la Tosa de Juclar		42.6	1.71667	T	PK	AD		00				0		2516	Europe/Andorra	1993-12-23
+3038911	Collada de la Tosa de Caraup	Collada de la Tosa de Caraup		42.6	1.65	T	SPUR	AD		00				0		2131	Europe/Andorra	1993-12-23
+3038912	Planell de la Tosa	Planell de la Tosa		42.53333	1.45	T	UPLD	AD		00				0		2130	Europe/Andorra	1993-12-23
+3038913	Canals de la Tosa	Canals de la Tosa		42.58333	1.7	H	RVN	AD		00				0		2584	Europe/Andorra	1993-12-23
+3038914	Canal de la Tosa	Canal de la Tosa		42.5	1.56667	H	STM	AD		00				0		1776	Europe/Andorra	1993-12-23
+3038915	Bosc de la Tosa	Bosc de la Tosa		42.55	1.58333	V	FRST	AD		00				0		1499	Europe/Andorra	1993-12-23
+3038916	Canal Torta	Canal Torta		42.56667	1.51667	H	STM	AD		00				0		1500	Europe/Andorra	1993-12-23
+3038917	Canal Torta	Canal Torta		42.53333	1.53333	H	STM	AD		00				0		1521	Europe/Andorra	1993-12-23
+3038918	Torrent Tort	Torrent Tort		42.53333	1.58333	H	STM	AD		00				0		1571	Europe/Andorra	1993-12-23
+3038919	Torretinyà	Torretinya		42.45	1.51667	L	LCTY	AD		00				0		1790	Europe/Andorra	1993-12-23
+3038920	Prat d’en Torres	Prat d'en Torres		42.6	1.51667	L	GRAZ	AD		00				0		1445	Europe/Andorra	1993-12-23
+3038921	Canals de Torrent Pregó	Canals de Torrent Prego		42.48333	1.48333	H	STM	AD		00				0		981	Europe/Andorra	1993-12-23
+3038922	Canal de Torrentinyà	Canal de Torrentinya		42.45	1.5	H	STM	AD		00				0		1614	Europe/Andorra	1993-12-23
+3038923	Torrentill	Torrentill		42.46667	1.5	H	STM	AD		00				0		1383	Europe/Andorra	1993-12-23
+3038924	Camí de Torrent Forcat	Cami de Torrent Forcat		42.46667	1.51667	R	TRL	AD		00				0		1985	Europe/Andorra	1993-12-23
+3038925	Canal del Torrent Cauber	Canal del Torrent Cauber		42.6	1.51667	H	RVN	AD		00				0		1445	Europe/Andorra	1993-12-23
+3038926	Torre dels Soldats	Torre dels Soldats	Pic Monturull,Torre dels Soldats	42.45	1.58333	T	PK	AD		00				0		2537	Europe/Andorra	2011-11-05
+3038927	Torrapuis	Torrapuis		42.53333	1.53333	L	LCTY	AD		00				0		1521	Europe/Andorra	1993-12-23
+3038928	Pic de Torradella	Pic de Torradella		42.6	1.61667	T	PK	AD		00				0		2271	Europe/Andorra	1993-12-23
+3038929	Pala de la Torradella	Pala de la Torradella		42.58333	1.61667	T	SLP	AD		00				0		1707	Europe/Andorra	1993-12-23
+3038930	Obaga de Torradella	Obaga de Torradella		42.6	1.63333	T	SLP	AD		00				0		1893	Europe/Andorra	1993-12-23
+3038931	Canals de Torradella	Canals de Torradella		42.6	1.63333	H	STM	AD		00				0		1893	Europe/Andorra	1993-12-23
+3038932	Borda del Torner	Borda del Torner		42.58333	1.48333	S	FRM	AD		00				0		1809	Europe/Andorra	1993-12-23
+3038933	Planell de la Tora	Planell de la Tora		42.55	1.6	T	UPLD	AD		00				0		2210	Europe/Andorra	1993-12-23
+3038934	Clots de la Tora	Clots de la Tora		42.46667	1.58333	H	RVN	AD		00				0		2367	Europe/Andorra	1993-12-23
+3038935	Prat del Toni	Prat del Toni		42.55	1.61667	L	GRAZ	AD		00				0		2206	Europe/Andorra	1993-12-23
+3038936	Molí del Tomàs	Moli del Tomas		42.58333	1.65	S	ML	AD		00				0		1767	Europe/Andorra	1993-12-23
+3038937	Cortals de Tolse	Cortals de Tolse		42.45	1.48333	S	CRRL	AD		00				0		1111	Europe/Andorra	1993-12-23
+3038938	Bosc de Tolse	Bosc de Tolse		42.45	1.48333	V	FRST	AD		00				0		1111	Europe/Andorra	1993-12-23
+3038939	Tolse	Tolse		42.45	1.48333	S	HUTS	AD		00				0		1111	Europe/Andorra	1993-12-23
+3038940	Basera dels Tolls de l’Olla	Basera dels Tolls de l'Olla		42.48333	1.45	T	CLF	AD		00				0		1195	Europe/Andorra	1993-12-23
+3038941	Tolls de l’Olla	Tolls de l'Olla		42.46667	1.56667	L	LCTY	AD		00				0		2365	Europe/Andorra	1993-12-23
+3038942	Torrent del Tirader	Torrent del Tirader		42.53333	1.6	H	STM	AD		00				0		1888	Europe/Andorra	1993-12-23
+3038943	Canal del Timbarro	Canal del Timbarro		42.5	1.46667	H	STM	AD		00				0		1678	Europe/Andorra	1993-12-23
+3038944	Terres de Serra	Terres de Serra		42.48333	1.5	L	LCTY	AD		00				0		1631	Europe/Andorra	1993-12-23
+3038945	Terres de Sant Miguel	Terres de Sant Miguel		42.5	1.58333	L	LCTY	AD		00				0		1888	Europe/Andorra	1993-12-23
+3038946	Terres del Torrent Pregó	Terres del Torrent Prego		42.55	1.58333	L	LCTY	AD		00				0		1499	Europe/Andorra	1993-12-23
+3038947	Terres del Solà	Terres del Sola		42.55	1.55	L	LCTY	AD		00				0		2097	Europe/Andorra	1993-12-23
+3038948	Terres de Jacques	Terres de Jacques		42.56667	1.61667	L	LCTY	AD		00				0		1920	Europe/Andorra	1993-12-23
+3038949	Obaga de les Terres de Casamanya	Obaga de les Terres de Casamanya		42.56667	1.55	T	SLP	AD		00				0		1996	Europe/Andorra	1993-12-23
+3038950	Canal del Terrer Roig	Canal del Terrer Roig		42.58333	1.46667	H	STM	AD		00				0		1643	Europe/Andorra	1993-12-23
+3038951	Terrer Roig	Terrer Roig		42.58333	1.45	L	LCTY	AD		00				0		2156	Europe/Andorra	1993-12-23
+3038952	Clots dels Terregalls	Clots dels Terregalls		42.61667	1.61667	H	STMH	AD		00				0		2352	Europe/Andorra	1993-12-23
+3038953	Terregalls	Terregalls		42.5	1.51667	L	LCTY	AD		00				0		1410	Europe/Andorra	1993-12-23
+3038954	Terra del Pou	Terra del Pou		42.46667	1.48333	L	LCTY	AD		00				0		1134	Europe/Andorra	1993-12-23
+3038955	Terra Bogada	Terra Bogada		42.46667	1.5	L	LCTY	AD		00				0		1383	Europe/Andorra	1993-12-23
+3038956	Canal del Teixó	Canal del Teixo		42.55	1.5	H	STM	AD		00				0		1292	Europe/Andorra	1993-12-23
+3038957	Coma de Teix	Coma de Teix		42.46667	1.46667	T	SLP	AD		00				0		1340	Europe/Andorra	1993-12-23
+3038958	Font de les Taules	Font de les Taules		42.55	1.55	H	SPNG	AD		00				0		2097	Europe/Andorra	1993-12-23
+3038959	Coma Tarterosa	Coma Tarterosa		42.48333	1.45	H	RVN	AD		00				0		1195	Europe/Andorra	1993-12-23
+3038960	Bosc de la Tarterosa	Bosc de la Tarterosa		42.6	1.65	V	FRST	AD		00				0		2131	Europe/Andorra	1993-12-23
+3038961	Tarter Gran	Tarter Gran		42.58333	1.45	L	LCTY	AD		00				0		2156	Europe/Andorra	1993-12-23
+3038962	Tartera Gran	Tartera Gran		42.48333	1.46667	L	LCTY	AD		00				0		1148	Europe/Andorra	1993-12-23
+3038963	Pont del Tarter	Pont del Tarter		42.58333	1.65	S	BDG	AD		00				0		1767	Europe/Andorra	1993-12-23
+3038964	Coll de la Tàpia	Coll de la Tapia		42.46667	1.48333	T	PASS	AD		00				0		1134	Europe/Andorra	1993-12-23
+3038965	Canal Tancada	Canal Tancada		42.48333	1.55	H	STM	AD		00				0		2233	Europe/Andorra	1993-12-23
+3038966	Roc del Tampuent	Roc del Tampuent		42.58333	1.48333	T	RK	AD		00				0		1809	Europe/Andorra	1993-12-23
+3038967	Basers de les Tallades	Basers de les Tallades		42.5	1.46667	T	CLF	AD		00				0		1678	Europe/Andorra	1993-12-23
+3038968	Bosc de la Tallada Nova	Bosc de la Tallada Nova		42.58333	1.48333	V	FRST	AD		00				0		1809	Europe/Andorra	1993-12-23
+3038969	Camí de la Tallada	Cami de la Tallada		42.48333	1.56667	R	TRL	AD		00				0		2231	Europe/Andorra	1993-12-23
+3038970	Canal del Tabanell	Canal del Tabanell		42.56667	1.51667	H	STM	AD		00				0		1500	Europe/Andorra	1993-12-23
+3038971	Bosc del Tabanell	Bosc del Tabanell		42.58333	1.5	V	FRST	AD		00				0		1595	Europe/Andorra	1993-12-23
+3038972	Suriguera	Suriguera		42.58333	1.61667	T	SLP	AD		00				0		1707	Europe/Andorra	1993-12-23
+3038973	Sudornar	Sudornar		42.58333	1.45	T	SLP	AD		00				0		2156	Europe/Andorra	1993-12-23
+3038974	Font del Sucre	Font del Sucre		42.51667	1.46667	H	SPNG	AD		00				0		1840	Europe/Andorra	1993-12-23
+3038975	Costa de la Sucarana	Costa de la Sucarana		42.6	1.5	T	SLP	AD		00				0		1923	Europe/Andorra	1993-12-23
+3038976	Canya de la Sucarana	Canya de la Sucarana		42.6	1.5	T	GRGE	AD		00				0		1923	Europe/Andorra	1993-12-23
+3038977	Borda del Sucarana	Borda del Sucarana		42.55	1.58333	S	HUTS	AD		00				0		1499	Europe/Andorra	1993-12-23
+3038978	Riu de Sorteny	Riu de Sorteny		42.62341	1.54869	H	STM	AD		00				0		1950	Europe/Andorra	2011-04-19
+3038979	Pla de Sorteny	Pla de Sorteny		42.61952	1.58872	T	UPLD	AD		00				0		2524	Europe/Andorra	2011-04-19
+3038980	Marrades de Sorteny	Marrades de Sorteny		42.61667	1.55	R	TRL	AD		00				0		2007	Europe/Andorra	1993-12-23
+3038981	Sorteny	Sorteny		42.61667	1.58333	A	ADMD	AD		00				0		2374	Europe/Andorra	1993-12-23
+3038982	Riu de les Soronelles	Riu de les Soronelles		42.53333	1.65	H	STM	AD		00				0		2508	Europe/Andorra	1993-12-23
+3038983	Collada de les Soronelles	Collada de les Soronelles		42.53333	1.65	T	PASS	AD		00				0		2508	Europe/Andorra	1993-12-23
+3038984	Pic de les Sorobilles	Pic de les Sorobilles		42.56667	1.53333	T	PK	AD		00				0		1669	Europe/Andorra	1993-12-23
+3038985	Riu de Sornàs	Riu de Sornas		42.56667	1.53333	H	STM	AD		00				0		1669	Europe/Andorra	1993-12-23
+3038986	Clot de Sornàs	Clot de Sornas		42.56667	1.55	H	RVN	AD		00				0		1996	Europe/Andorra	1993-12-23
+3038987	Sornàs	Sornas	Sornas,Sornàs	42.5654	1.5287	P	PPL	AD		05				0		1514	Europe/Andorra	2011-11-05
+3038988	Riu de Soriguera	Riu de Soriguera	Riu de Soriguera,Riu de Suriguera	42.58333	1.61667	H	STM	AD	AD	00				0		1707	Europe/Andorra	2011-11-05
+3038989	Solana del Soriguer	Solana del Soriguer		42.56667	1.55	T	SLP	AD		00				0		1996	Europe/Andorra	1993-12-23
+3038990	Clot Sord	Clot Sord		42.6	1.65	T	CRQ	AD		00				0		2131	Europe/Andorra	1993-12-23
+3038991	Solà de Soquer	Sola de Soquer		42.45	1.48333	T	SLP	AD		00				0		1111	Europe/Andorra	1993-12-23
+3038992	Riu de Soplanàs	Riu de Soplanas	Riu de Soplanars,Riu de Soplanas,Riu de Soplanàs	42.58333	1.63333	H	STM	AD	AD	00				0		1722	Europe/Andorra	2011-11-05
+3038993	Borda de Som	Borda de Som		42.56667	1.58333	S	HUTS	AD		00				0		1919	Europe/Andorra	1993-12-23
+3038994	Roc de Solobre	Roc de Solobre		42.48333	1.51667	T	RKS	AD		00				0		2061	Europe/Andorra	1993-12-23
+3038995	Bosc de Solobre	Bosc de Solobre		42.48333	1.5	V	FRST	AD		00				0		1631	Europe/Andorra	1993-12-23
+3038996	Bosc del Soleador	Bosc del Soleador		42.6	1.51667	V	FRST	AD		00				0		1445	Europe/Andorra	1993-12-23
+3038997	Camí de Soldeu	Cami de Soldeu		42.58333	1.66667	R	TRL	AD		00				0		2159	Europe/Andorra	1993-12-23
+3038998	Bosc de Soldeu	Bosc de Soldeu	Bois de Soldeu,Bosc de Soldeu,Bosque de Soldeu	42.57107	1.65402	V	FRST	AD		00				0		1988	Europe/Andorra	2011-11-05
+3038999	Soldeu	Soldeu		42.57688	1.66769	P	PPL	AD		02				0		2159	Europe/Andorra	2007-04-16
+3039000	Solana del Solanyó	Solana del Solanyo		42.53333	1.55	T	SLP	AD		00				0		1344	Europe/Andorra	1993-12-23
+3039001	Riu del Solanyò	Riu del Solanyo		42.53333	1.55	H	STM	AD		00				0		1344	Europe/Andorra	1993-12-23
+3039002	Bosc del Solanyó	Bosc del Solanyo		42.55	1.45	V	FRST	AD		00				0		1788	Europe/Andorra	1993-12-23
+3039003	Barranc del Solanyó	Barranc del Solanyo		42.55	1.45	H	STM	AD		00				0		1788	Europe/Andorra	1993-12-23
+3039004	Solans	Solans		42.53333	1.6	L	LCTY	AD		00				0		1888	Europe/Andorra	1993-12-23
+3039005	Solanet de Ràmio	Solanet de Ramio		42.5	1.56667	L	LCTY	AD		00				0		1776	Europe/Andorra	1993-12-23
+3039006	Terregalls del Solanet	Terregalls del Solanet		42.63333	1.6	T	RDGE	AD		00				0		2635	Europe/Andorra	1993-12-23
+3039007	Bosc del Solanet	Bosc del Solanet		42.55	1.48333	V	FRST	AD		00				0		1548	Europe/Andorra	1993-12-23
+3039008	Solanes de la Peguera	Solanes de la Peguera		42.45	1.51667	A	ADMD	AD		00				0		1790	Europe/Andorra	1993-12-23
+3039009	Serra de les Solanelles	Serra de les Solanelles		42.55	1.66667	T	RDGE	AD		00				0		2224	Europe/Andorra	1993-12-23
+3039010	Riu de les Solanelles	Riu de les Solanelles		42.55	1.68333	H	STM	AD		00				0		2254	Europe/Andorra	1993-12-23
+3039011	Collet de les Solanelles	Collet de les Solanelles		42.55	1.61667	T	PASS	AD		00				0		2206	Europe/Andorra	1993-12-23
+3039012	Collada de les Solanelles	Collada de les Solanelles		42.55	1.66667	T	PASS	AD		00				0		2224	Europe/Andorra	1993-12-23
+3039013	Clot de les Solanelles	Clot de les Solanelles		42.55	1.63333	H	RVN	AD		00				0		2336	Europe/Andorra	1993-12-23
+3039014	Canal de les Solanelles	Canal de les Solanelles		42.55	1.63333	H	STM	AD		00				0		2336	Europe/Andorra	1993-12-23
+3039015	Cabana de les Solanelles	Cabana de les Solanelles		42.53333	1.66667	S	HUT	AD		00				0		2489	Europe/Andorra	1993-12-23
+3039016	Solanelles	Solanelles		42.55	1.66667	L	LCTY	AD		00				0		2224	Europe/Andorra	1993-12-23
+3039017	Barranc de la Solana del Pas de la Casa	Barranc de la Solana del Pas de la Casa		42.53333	1.73333	H	STM	AD		00				0		2300	Europe/Andorra	1993-12-23
+3039018	Solana del Lloser d’Ordino	Solana del Lloser d'Ordino		42.55	1.51667	L	LCTY	AD		00				0		1397	Europe/Andorra	1993-12-23
+3039019	Riu de la Solana del Forn	Riu de la Solana del Forn		42.56154	1.67948	H	STM	AD		00				0		2094	Europe/Andorra	2011-04-19
+3039020	Obagot de la Solana del Forn	Obagot de la Solana del Forn		42.55	1.66667	T	SLP	AD		00				0		2224	Europe/Andorra	1993-12-23
+3039021	Solana del Forn	Solana del Forn		42.55	1.66667	A	ADMD	AD		00				0		2224	Europe/Andorra	1993-12-23
+3039022	Solana de l’Estall Serrer	Solana de l'Estall Serrer		42.5	1.61667	A	ADMD	AD		00				0		2560	Europe/Andorra	1993-12-23
+3039023	Solana de les Aubes	Solana de les Aubes		42.56667	1.56667	L	LCTY	AD		00				0		2089	Europe/Andorra	1993-12-23
+3039024	Solana de la Baronia	Solana de la Baronia		42.53333	1.61667	A	ADMD	AD		00				0		2237	Europe/Andorra	1993-12-23
+3039025	Prats de la Solana	Prats de la Solana		42.56667	1.78333	L	GRAZ	AD		00				0		1680	Europe/Andorra	1993-12-23
+3039026	Camí de la Solana	Cami de la Solana		42.43333	1.45	R	TRL	AD		00				0		877	Europe/Andorra	1993-12-23
+3039027	Bosc de la Solana	Bosc de la Solana		42.51667	1.46667	V	FRST	AD		00				0		1840	Europe/Andorra	1993-12-23
+3039028	Solà de Soldeu	Sola de Soldeu		42.58333	1.66667	A	ADMD	AD		00				0		2159	Europe/Andorra	1993-12-23
+3039029	Serra del Solà de Sispony	Serra del Sola de Sispony		42.53333	1.48333	T	MT	AD		00				0		1677	Europe/Andorra	1993-12-23
+3039030	Pic del Solà d’Erts	Pic del Sola d'Erts		42.56667	1.5	T	PK	AD		00				0		1636	Europe/Andorra	1993-12-23
+3039031	Solà d’Erts	Sola d'Erts		42.56667	1.5	A	ADMD	AD		00				0		1636	Europe/Andorra	1993-12-23
+3039032	Solà de Riambert	Sola de Riambert		42.58333	1.53333	A	ADMD	AD		00				0		1924	Europe/Andorra	1993-12-23
+3039033	Serra del Solà de Ràmio	Serra del Sola de Ramio		42.5	1.58333	T	MT	AD		00				0		1888	Europe/Andorra	1993-12-23
+3039034	Solà de Ràmio	Sola de Ramio		42.5	1.58333	A	ADMD	AD		00				0		1888	Europe/Andorra	1993-12-23
+3039035	Bosc del Solà d’Envalira	Bosc del Sola d'Envalira		42.55	1.68333	V	FRST	AD		00				0		2254	Europe/Andorra	1993-12-23
+3039036	Carena del Solà d’Engordany	Carena del Sola d'Engordany		42.51667	1.55	T	RDGE	AD		00				0		1322	Europe/Andorra	1993-12-23
+3039037	Solá d’Engordany	Sola d'Engordany		42.51667	1.53333	A	ADMD	AD		00				0		1460	Europe/Andorra	1993-12-23
+3039038	Solà d’Enclar	Sola d'Enclar		42.51667	1.48333	A	ADMD	AD		00				0		1839	Europe/Andorra	1993-12-23
+3039039	Solà d’Encamp	Sola d'Encamp		42.53333	1.58333	A	ADMD	AD		00				0		1571	Europe/Andorra	1993-12-23
+3039040	Cresta del Solà de Nadal	Cresta del Sola de Nadal		42.51667	1.51667	T	SPUR	AD		00				0		1265	Europe/Andorra	1993-12-23
+3039041	Aspres del Solà de Nadal	Aspres del Sola de Nadal		42.51667	1.51667	V	VINS	AD		00				0		1265	Europe/Andorra	1993-12-23
+3039042	Solà de Mossers	Sola de Mossers		42.45	1.46667	A	ADMD	AD		00				0		935	Europe/Andorra	1993-12-23
+3039043	Solà de Mereig	Sola de Mereig		42.56667	1.58333	L	LCTY	AD		00				0		1919	Europe/Andorra	1993-12-23
+3039044	Solà del Tarter	Sola del Tarter		42.58333	1.65	A	ADMD	AD		00				0		1767	Europe/Andorra	1993-12-23
+3039045	Solà dels Sulls	Sola dels Sulls		42.48333	1.56667	A	ADMD	AD		00				0		2231	Europe/Andorra	1993-12-23
+3039046	Solà dels Plans	Sola dels Plans		42.58333	1.63333	A	ADMD	AD		00				0		1722	Europe/Andorra	1993-12-23
+3039047	Serra del Sola del Quart Mitger	Serra del Sola del Quart Mitger		42.55	1.55	T	RDGE	AD		00				0		2097	Europe/Andorra	1993-12-23
+3039048	Solà del Quart Mitger	Sola del Quart Mitger		42.55	1.53333	A	ADMD	AD		00				0		1593	Europe/Andorra	1993-12-23
+3039049	Solà del Pui	Sola del Pui		42.55	1.51667	A	ADMD	AD		00				0		1397	Europe/Andorra	1993-12-23
+3039050	Cap del Solà de les Comes	Cap del Sola de les Comes		42.58333	1.5	T	PK	AD		00				0		1595	Europe/Andorra	1993-12-23
+3039051	Canal del Solà de les Comes	Canal del Sola de les Comes		42.56667	1.48333	H	STM	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039052	Solà de la Moixella	Sola de la Moixella		42.45	1.48333	A	ADMD	AD		00				0		1111	Europe/Andorra	1993-12-23
+3039053	Balmes del Solà de l’Allau	Balmes del Sola de l'Allau		42.6	1.53333	T	CLF	AD		00				0		1695	Europe/Andorra	1993-12-23
+3039054	Solà de l’Aldosa	Sola de l'Aldosa		42.58333	1.61667	A	ADMD	AD		00				0		1707	Europe/Andorra	1993-12-23
+3039055	Solà de la Farga	Sola de la Farga		42.48333	1.6	A	ADMD	AD		00				0		2250	Europe/Andorra	1993-12-23
+3039056	Canals del Solà de Juclar	Canals del Sola de Juclar		42.6	1.7	H	RVN	AD		00				0		2354	Europe/Andorra	1993-12-23
+3039057	Solà d’Arcavell	Sola d'Arcavell	Sola d'Arcabell,Sola d'Arcavell,Sola d’Arcabell,Solà d’Arcavell	42.43333	1.48333	A	ADMD	AD		00				0		1228	Europe/Andorra	2011-11-05
+3039058	Solà d’Andorra	Sola d'Andorra		42.51667	1.5	A	ADMD	AD		00				0		1688	Europe/Andorra	1993-12-23
+3039059	Solà d’Aixovall	Sola d'Aixovall		42.48333	1.48333	A	ADMD	AD		00				0		981	Europe/Andorra	1993-12-23
+3039060	Riu del Solà	Riu del Sola		42.55	1.46667	H	STM	AD		00				0		1585	Europe/Andorra	1993-12-23
+3039061	Clot del Solà	Clot del Sola		42.46667	1.46667	H	RVN	AD		00				0		1340	Europe/Andorra	1993-12-23
+3039062	Carrera del Solà	Carrera del Sola		42.53333	1.58333	R	TRL	AD		00				0		1571	Europe/Andorra	1993-12-23
+3039063	Costa del Sodorn	Costa del Sodorn		42.5	1.46667	T	SLP	AD		00				0		1678	Europe/Andorra	1993-12-23
+3039064	Pla del Socarrat	Pla del Socarrat		42.55	1.6	T	UPLD	AD		00				0		2210	Europe/Andorra	1993-12-23
+3039065	Camí del Socarrat	Cami del Socarrat		42.58333	1.63333	R	TRL	AD		00				0		1722	Europe/Andorra	1993-12-23
+3039066	Bosc del Socarrat	Bosc del Socarrat		42.58333	1.65	V	FRST	AD		00				0		1767	Europe/Andorra	1993-12-23
+3039067	Vial de la Socarrada de Coll Carnisser	Vial de la Socarrada de Coll Carnisser		42.58333	1.46667	R	RD	AD		00				0		1643	Europe/Andorra	1993-12-23
+3039068	Pala de Sobre l’Estany	Pala de Sobre l'Estany		42.61667	1.71667	T	CLF	AD		00				0		2352	Europe/Andorra	1993-12-23
+3039069	Pala de Sobre les Basses	Pala de Sobre les Basses		42.58333	1.7	T	SLP	AD		00				0		2584	Europe/Andorra	1993-12-23
+3039070	Basers de Sobre la Pleta	Basers de Sobre la Pleta		42.6	1.46667	T	CLF	AD		00				0		2421	Europe/Andorra	1993-12-23
+3039071	Sobre dels Camps de la Cortinada	Sobre dels Camps de la Cortinada		42.58333	1.5	A	ADMD	AD		00				0		1595	Europe/Andorra	1993-12-23
+3039072	Planells Sobirans	Planells Sobirans		42.58333	1.46667	T	UPLD	AD		00				0		1643	Europe/Andorra	1993-12-23
+3039073	Riu del Sisqueró	Riu del Sisquero		42.6	1.7	H	STM	AD		00				0		2354	Europe/Andorra	1993-12-23
+3039074	Camí del Sisquero	Cami del Sisquero		42.6	1.68333	R	TRL	AD		00				0		2089	Europe/Andorra	1993-12-23
+3039075	Solà de Sispony	Sola de Sispony		42.53333	1.48333	T	SLP	AD		00				0		1677	Europe/Andorra	1993-12-23
+3039076	Camí de Sispony	Cami de Sispony		42.53333	1.46667	R	TRL	AD		00				0		1846	Europe/Andorra	1993-12-23
+3039077	Sispony	Sispony		42.53355	1.51481	P	PPL	AD		04				0		1252	Europe/Andorra	2011-04-19
+3039078	Pletes del Siscaró	Pletes del Siscaro		42.58333	1.7	L	GRAZ	AD		00				0		2584	Europe/Andorra	1993-12-23
+3039079	Estanys del Siscaro	Estanys del Siscaro		42.58333	1.7	H	LKS	AD		00				0		2584	Europe/Andorra	1993-12-23
+3039080	Canals del Siscaró	Canals del Siscaro		42.58333	1.7	H	RVN	AD		00				0		2584	Europe/Andorra	1993-12-23
+3039081	Pic de Siscarou	Pic de Siscarou	Pic de Siscarou,Pico de Siscarou,Siscaro,Siscaró	42.6	1.73333	T	PK	AD		00				0		2383	Europe/Andorra	2011-11-05
+3039082	Siscaró	Siscaro		42.58333	1.71667	A	ADMD	AD		00				0		2553	Europe/Andorra	1993-12-23
+3039083	Marrades del Siscar	Marrades del Siscar		42.58333	1.71667	R	TRL	AD		00				0		2553	Europe/Andorra	1993-12-23
+3039084	Canals del Siscar	Canals del Siscar		42.6	1.71667	H	RVN	AD		00				0		2516	Europe/Andorra	1993-12-23
+3039085	Basses del Siscar	Basses del Siscar		42.58333	1.71667	H	LKS	AD		00				0		2553	Europe/Andorra	1993-12-23
+3039086	Sincloset	Sincloset		42.48333	1.5	T	MT	AD		00				0		1631	Europe/Andorra	1993-12-23
+3039087	Port de Siguer	Port de Siguer	Port de Siguer	42.65	1.56667	T	PASS	AD		00				0		2471	Europe/Andorra	2011-11-05
+3039088	Bosc del Sigarró	Bosc del Sigarro		42.51667	1.6	V	FRST	AD		00				0		2085	Europe/Andorra	1993-12-23
+3039089	Canal de la Sicalma	Canal de la Sicalma		42.5	1.48333	H	STM	AD		00				0		1316	Europe/Andorra	1993-12-23
+3039090	Portella de Satut	Portella de Satut	Port de Setut,Portella de Satut,Portella de Setut	42.46667	1.63333	T	PK	AD		00				0		2619	Europe/Andorra	2011-11-05
+3039091	Cabana de Setut	Cabana de Setut		42.48333	1.63333	S	HUT	AD		00				0		2296	Europe/Andorra	1993-12-23
+3039092	Basses de Setut	Basses de Setut		42.48333	1.65	H	LKS	AD		00				0		2658	Europe/Andorra	1993-12-23
+3039093	Setut	Setut		42.46667	1.65	A	ADMD	AD		00				0		2700	Europe/Andorra	1993-12-23
+3039094	Carretera de Setúria	Carretera de Seturia		42.55	1.48333	R	RD	AD		00				0		1548	Europe/Andorra	1993-12-23
+3039095	Camí de Setúria	Cami de Seturia		42.55	1.46667	R	TRL	AD		00				0		1585	Europe/Andorra	1993-12-23
+3039096	Bordes de Setúria	Bordes de Seturia		42.53288	1.43718	S	HUTS	AD		00				0		1972	Europe/Andorra	2011-04-19
+3039097	Setúria	Seturia		42.55	1.43333	A	ADMD	AD		00				0		1949	Europe/Andorra	1993-12-23
+3039098	Tarteres de la Serrera	Tarteres de la Serrera		42.61667	1.58333	T	TAL	AD		00				0		2374	Europe/Andorra	1993-12-23
+3039099	Riu de la Serrera	Riu de la Serrera		42.61667	1.56667	H	STM	AD		00				0		2228	Europe/Andorra	1993-12-23
+3039100	Pleta de la Serrera	Pleta de la Serrera		42.61667	1.58333	L	GRAZ	AD		00				0		2374	Europe/Andorra	1993-12-23
+3039101	Pic de la Serrera	Pic de la Serrera	Pic de Serrere,Pic de Serrère,Pic de la Serrera,Serrera	42.63333	1.6	T	PK	AD		00				0		2635	Europe/Andorra	2011-11-05
+3039102	Pas de la Serrera	Pas de la Serrera		42.61667	1.58333	T	PASS	AD		00				0		2374	Europe/Andorra	1993-12-23
+3039103	Clots de la Serrera	Clots de la Serrera		42.61667	1.6	H	STMH	AD		00				0		2528	Europe/Andorra	1993-12-23
+3039104	Aspres de la Serrera	Aspres de la Serrera		42.61667	1.6	V	VINS	AD		00				0		2528	Europe/Andorra	1993-12-23
+3039105	Canal de Serrats	Canal de Serrats		42.53333	1.5	H	STM	AD		00				0		1357	Europe/Andorra	1993-12-23
+3039106	Camí del Serrat Pla	Cami del Serrat Pla		42.43333	1.46667	R	TRL	AD		00				0		1113	Europe/Andorra	1993-12-23
+3039107	Bosc del Serrat Llong	Bosc del Serrat Llong		42.51667	1.48333	V	FRST	AD		00				0		1839	Europe/Andorra	1993-12-23
+3039108	Planell del Serrat del Tronc	Planell del Serrat del Tronc		42.58333	1.61667	T	UPLD	AD		00				0		1707	Europe/Andorra	1993-12-23
+3039109	Cabana del Serrat de la Barracota	Cabana del Serrat de la Barracota		42.48333	1.63333	S	HUT	AD		00				0		2296	Europe/Andorra	1993-12-23
+3039110	Pic de Serra Seca	Pic de Serra Seca		42.51667	1.7	T	PK	AD		00				0		2435	Europe/Andorra	1993-12-23
+3039111	Canal de Serra Plana	Canal de Serra Plana		42.48333	1.48333	H	STM	AD		00				0		981	Europe/Andorra	1993-12-23
+3039112	Riu de Serrana	Riu de Serrana		42.55	1.51667	H	STM	AD		00				0		1397	Europe/Andorra	1993-12-23
+3039113	Pic de Serra Mitjana	Pic de Serra Mitjana		42.47479	1.61523	T	PK	AD		00				0		2418	Europe/Andorra	2011-04-19
+3039114	Estany de Serra Mitjana	Estany de Serra Mitjana		42.46667	1.6	H	LK	AD		00				0		2449	Europe/Andorra	1993-12-23
+3039115	Canal Ampla de Serra Mitjana	Canal Ampla de Serra Mitjana		42.46667	1.61667	H	STM	AD		00				0		2448	Europe/Andorra	1993-12-23
+3039116	Canal de la Serradora	Canal de la Serradora		42.48333	1.46667	H	STM	AD		00				0		1148	Europe/Andorra	1993-12-23
+3039117	Canal Serradora	Canal Serradora		42.5	1.56667	H	STM	AD		00				0		1776	Europe/Andorra	1993-12-23
+3039118	Cap de la Serra dels Isards	Cap de la Serra dels Isards		42.58333	1.58333	T	PK	AD		00				0		1993	Europe/Andorra	1993-12-23
+3039119	Serrat dels Serradells	Serrat dels Serradells		42.61667	1.53333	T	SPUR	AD		00				0		1609	Europe/Andorra	1993-12-23
+3039120	Cortal de la Serra	Cortal de la Serra		42.53333	1.5	S	CRRL	AD		00				0		1357	Europe/Andorra	1993-12-23
+3039121	Cap de la Serra	Cap de la Serra		42.61667	1.6	T	RDGE	AD		00				0		2528	Europe/Andorra	1993-12-23
+3039122	Roc de la Senyoreta	Roc de la Senyoreta		42.45	1.48333	T	RK	AD		00				0		1111	Europe/Andorra	1993-12-23
+3039123	Bosc de la Senyoreta	Bosc de la Senyoreta		42.45	1.48333	V	FRST	AD		00				0		1111	Europe/Andorra	1993-12-23
+3039124	Senyal Negre	Senyal Negre		42.65	1.55	T	MT	AD		00				0		2181	Europe/Andorra	1993-12-23
+3039125	Senyal de Missa	Senyal de Missa		42.46667	1.48333	T	PK	AD		00				0		1134	Europe/Andorra	1993-12-23
+3039126	Roc de Senders	Roc de Senders		42.5	1.53333	T	RK	AD		00				0		1574	Europe/Andorra	1993-12-23
+3039127	Sella	Sella		42.56667	1.6	T	CLF	AD		00				0		1655	Europe/Andorra	1993-12-23
+3039128	Riu del Seig	Riu del Seig		42.56667	1.61667	H	STM	AD		00				0		1920	Europe/Andorra	1993-12-23
+3039129	Canal del Seig	Canal del Seig		42.61667	1.53333	H	STM	AD		00				0		1609	Europe/Andorra	1993-12-23
+3039130	Solà de Segudet	Sola de Segudet		42.56667	1.53333	T	SLP	AD		00				0		1669	Europe/Andorra	1993-12-23
+3039131	Riu de Segudet	Riu de Segudet		42.5583	1.54304	H	STM	AD		00				0		1722	Europe/Andorra	2011-04-19
+3039132	Segudet	Segudet	Segudet	42.55755	1.53858	P	PPL	AD		05				0		1556	Europe/Andorra	2011-11-05
+3039133	Estany Segon	Estany Segon		42.61667	1.73333	H	LK	AD		00				0		2508	Europe/Andorra	1993-12-23
+3039134	Torrent del Segalars	Torrent del Segalars		42.55	1.58333	H	STM	AD		00				0		1499	Europe/Andorra	1993-12-23
+3039135	Camí de Sedornet	Cami de Sedornet		42.58333	1.51667	R	TRL	AD		00				0		1722	Europe/Andorra	1993-12-23
+3039136	Bordes de Sedornet	Bordes de Sedornet		42.58333	1.51667	S	HUTS	AD		00				0		1722	Europe/Andorra	1993-12-23
+3039137	Costa Seda	Costa Seda		42.48333	1.5	T	SLP	AD		00				0		1631	Europe/Andorra	1993-12-23
+3039138	Planells Secants	Planells Secants		42.43333	1.53333	T	UPLD	AD		00				0		2108	Europe/Andorra	1993-12-23
+3039139	Riu Sec	Riu Sec		42.51667	1.46667	H	STM	AD		00				0		1840	Europe/Andorra	1993-12-23
+3039140	Estany Sec	Estany Sec		42.46667	1.61667	H	LK	AD		00				0		2448	Europe/Andorra	1993-12-23
+3039141	Borda del Savoiano	Borda del Savoiano		42.56667	1.51667	S	HUTS	AD		00				0		1500	Europe/Andorra	1993-12-23
+3039142	Serra de la Sauvata	Serra de la Sauvata		42.56667	1.58333	T	RDGE	AD		00				0		1919	Europe/Andorra	1993-12-23
+3039143	Costa de la Sauvata	Costa de la Sauvata		42.56667	1.58333	T	SLP	AD		00				0		1919	Europe/Andorra	1993-12-23
+3039144	Roc del Sastre	Roc del Sastre	Roc del Sastra,Roc del Sastre	42.58333	1.61667	T	SPUR	AD	AD	00				0		1707	Europe/Andorra	2011-11-05
+3039145	Pont Sassanat	Pont Sassanat		42.5	1.55	S	BDG	AD		00				0		1566	Europe/Andorra	1993-12-23
+3039146	Saquet	Saquet		42.55	1.6	L	LCTY	AD		00				0		2210	Europe/Andorra	1993-12-23
+3039147	Roc de Sant Vicenç	Roc de Sant Vicenc		42.5	1.5	T	RK	AD		00				0		1135	Europe/Andorra	1993-12-23
+3039148	Collet de Sant Vicenç	Collet de Sant Vicenc		42.5	1.48333	T	PASS	AD		00				0		1316	Europe/Andorra	1993-12-23
+3039149	Solà de Santserra	Sola de Santserra		42.48333	1.46667	T	SLP	AD		00				0		1148	Europe/Andorra	1993-12-23
+3039150	Sant Romà de Vila	Sant Roma de Vila		42.53333	1.56667	S	CH	AD		00				0		1418	Europe/Andorra	1993-12-23
+3039151	Sant Romà de les Bons	Sant Roma de les Bons		42.53333	1.58333	S	CH	AD		00				0		1571	Europe/Andorra	1993-12-23
+3039152	Bosc de Sant Romà	Bosc de Sant Roma		42.45	1.5	V	FRST	AD		00				0		1614	Europe/Andorra	1993-12-23
+3039153	Sant Romà	Sant Roma		42.45	1.5	S	CH	AD		00				0		1614	Europe/Andorra	1993-12-23
+3039154	Sant Pere	Sant Pere	Sant Pere	42.57952	1.65362	P	PPL	AD		02				0		1767	Europe/Andorra	2011-11-05
+3039155	Sant Miquel d'Engolasters	Sant Miquel d'Engolasters	Sant Miquel d'Engolasters	42.51094	1.56008	S	CH	AD	AD	07				0		1661	Europe/Andorra	2007-04-05
+3039156	Sant Miquel de Fontaneda	Sant Miquel de Fontaneda	Sant Miquel,Sant Miquel de Fontaneda	42.45	1.46667	S	CH	AD	AD	00				0		935	Europe/Andorra	2011-11-05
+3039157	Solà de Sant Miquel	Sola de Sant Miquel		42.58333	1.66667	T	SLP	AD		00				0		2159	Europe/Andorra	1993-12-23
+3039158	Roc de Sant Miquel	Roc de Sant Miquel		42.58333	1.66667	T	CLF	AD		00				0		2159	Europe/Andorra	1993-12-23
+3039159	Drecera de Sant Martí	Drecera de Sant Marti		42.48333	1.5	R	TRL	AD		00				0		1631	Europe/Andorra	1993-12-23
+3039160	Sant Martí	Sant Marti		42.48333	1.5	T	UPLD	AD		00				0		1631	Europe/Andorra	1993-12-23
+3039161	Sant Martí	Sant Marti		42.48333	1.48333	S	RUIN	AD		00				0		981	Europe/Andorra	1993-12-23
+3039162	Parròquia de Sant Julià de Lòria	Parroquia de Sant Julia de Loria	Parroquia de Sant Julia de Loria,Parroquia de Sant Julià de Lòria,Sant Julia,Sant Julia de Loria,Sant Julià,Sant Julià de Lòria	42.46247	1.48247	A	ADM1	AD		06				9448		966	Europe/Andorra	2010-08-13
+3039163	Sant Julià de Lòria	Sant Julia de Loria	San Julia,San Julià,San-Dzhulija-de-Lorija,San-Khulija-de-Lorija,Sant Julia de Loria,Sant Julià de Lòria,sheng hu li ya-de luo li ya,Сан-ДжулиÑ-де-ЛориÑ,Сан-ХулиÑ-де-ЛориÑ,サン・ジュリア・デ・ロリア教区,圣胡利娅-德洛里亚,圣胡利娅ï¼å¾·æ´›é‡Œäºš	42.46372	1.49129	P	PPLA	AD		06				8022		1045	Europe/Andorra	2008-10-15
+3039164	Riu de Sant Josep	Riu de Sant Josep		42.56419	1.75244	H	STM	AD		00				0		1836	Europe/Andorra	2011-04-19
+3039165	Clot de Sant Josep	Clot de Sant Josep		42.56667	1.7	T	CRQ	AD		00				0		2375	Europe/Andorra	1993-12-23
+3039166	Sant Joan de Caselles	Sant Joan de Caselles	San Joan de Casettas,Sant Joan de Casellas,Sant Joan de Caselles	42.56988	1.60922	P	PPL	AD		02				0		1724	Europe/Andorra	2011-11-05
+3039167	Serrat de Sant Jaume	Serrat de Sant Jaume		42.53333	1.6	T	RDGE	AD		00				0		1888	Europe/Andorra	1993-12-23
+3039168	Sant Jaume	Sant Jaume		42.58333	1.63333	S	CH	AD		00				0		1722	Europe/Andorra	1993-12-23
+3039169	Sant Jaume	Sant Jaume	Sant Jaume,Sant Joums	42.53333	1.6	S	CH	AD	AD	00				0		1888	Europe/Andorra	2011-11-05
+3039170	Sant Esteve	Sant Esteve		42.43333	1.48333	S	CH	AD		00				0		1228	Europe/Andorra	1993-12-23
+3039171	Sant Esteve	Sant Esteve		42.43333	1.45	S	CH	AD		00				0		877	Europe/Andorra	1993-12-23
+3039172	Sant Cristòfol	Sant Cristofol		42.45	1.5	S	SHRN	AD		00				0		1614	Europe/Andorra	1993-12-23
+3039173	Sant Cristòfol	Sant Cristofol		42.53333	1.53333	S	CH	AD		00				0		1521	Europe/Andorra	1993-12-23
+3039174	Solà de Sant Cerni	Sola de Sant Cerni		42.46667	1.5	T	SLP	AD		00				0		1383	Europe/Andorra	1993-12-23
+3039175	Eglèsia de Sant Cerni	Eglesia de Sant Cerni		42.56667	1.6	S	CH	AD		00				0		1655	Europe/Andorra	1993-12-23
+3039176	Sant Cerni	Sant Cerni		42.46981	1.50133	S	CH	AD		00				0		1383	Europe/Andorra	2011-04-19
+3039177	Sant Antoni de la Grella	Sant Antoni de la Grella		42.53333	1.53333	S	CH	AD		00				0		1521	Europe/Andorra	1993-12-23
+3039178	Túnels de Sant Antoni	Tunels de Sant Antoni		42.51667	1.51667	R	TNLS	AD		00				0		1265	Europe/Andorra	1993-12-23
+3039179	Pont de Sant Antoni	Pont de Sant Antoni		42.51667	1.51667	S	BDG	AD		00				0		1265	Europe/Andorra	1993-12-23
+3039180	Pont de Santa Creu	Pont de Santa Creu		42.56667	1.6	S	BDG	AD		00				0		1655	Europe/Andorra	1993-12-23
+3039181	Santa Coloma	Santa Coloma	Santa Coloma	42.5	1.5	P	PPL	AD		07				0		1135	Europe/Andorra	2011-11-05
+3039182	Santa Caterina	Santa Caterina		42.55	1.51667	L	LCTY	AD		00				0		1397	Europe/Andorra	1993-12-23
+3039183	Cortal del Sansa	Cortal del Sansa		42.5	1.51667	S	CRRL	AD		00				0		1410	Europe/Andorra	1993-12-23
+3039184	Portella de Sanfons	Portella de Sanfons	Portella de Sanfons	42.56667	1.43333	T	PASS	AD		00				0		2402	Europe/Andorra	2011-11-05
+3039185	Pic de Sanfons	Pic de Sanfons	Pic de Sanfons	42.58333	1.43333	T	PK	AD		00				0		2412	Europe/Andorra	2011-11-05
+3039186	Roc de la Salve	Roc de la Salve		42.53333	1.6	T	RK	AD		00				0		1888	Europe/Andorra	1993-12-23
+3039187	Bosc de la Salvata	Bosc de la Salvata		42.45	1.51667	V	FRST	AD		00				0		1790	Europe/Andorra	1993-12-23
+3039188	Solana del Saltader	Solana del Saltader		42.56667	1.53333	T	SLP	AD		00				0		1669	Europe/Andorra	1993-12-23
+3039189	Clot del Saltader	Clot del Saltader		42.56667	1.46667	H	RVN	AD		00				0		1673	Europe/Andorra	1993-12-23
+3039190	Roc del Salt	Roc del Salt		42.58333	1.58333	T	RK	AD		00				0		1993	Europe/Andorra	1993-12-23
+3039191	Clot del Salt	Clot del Salt		42.58333	1.6	H	RVN	AD		00				0		1828	Europe/Andorra	1993-12-23
+3039192	Rius de les Salses	Rius de les Salses		42.63333	1.5	H	STM	AD		00				0		1979	Europe/Andorra	1993-12-23
+3039193	Pont de les Salines	Pont de les Salines		42.61667	1.53333	S	BDG	AD		00				0		1609	Europe/Andorra	1993-12-23
+3039194	Costa de les Salines	Costa de les Salines		42.61667	1.53333	T	SLP	AD		00				0		1609	Europe/Andorra	1993-12-23
+3039195	Bosc de les Salines	Bosc de les Salines		42.61667	1.53333	V	FRST	AD		00				0		1609	Europe/Andorra	1993-12-23
+3039196	Basses de les Salamandres	Basses de les Salamandres		42.6	1.66667	H	LKS	AD		00				0		1858	Europe/Andorra	1993-12-23
+3039197	Costa de la Salamandra	Costa de la Salamandra		42.55	1.43333	T	SLP	AD		00				0		1949	Europe/Andorra	1993-12-23
+3039198	Costa Salamandra	Costa Salamandra		42.5	1.48333	T	SLP	AD		00				0		1316	Europe/Andorra	1993-12-23
+3039199	Roca de la Sabina	Roca de la Sabina		42.56667	1.46667	T	SLP	AD		00				0		1673	Europe/Andorra	1993-12-23
+3039200	Cortal del Sabater	Cortal del Sabater		42.45	1.48333	S	CRRL	AD		00				0		1111	Europe/Andorra	1993-12-23
+3039201	Bosc del Sabater	Bosc del Sabater		42.45	1.48333	V	FRST	AD		00				0		1111	Europe/Andorra	1993-12-23
+3039202	Borda del Sabater	Borda del Sabater		42.45	1.48333	S	FRM	AD		00				0		1111	Europe/Andorra	1993-12-23
+3039203	Canal dels Rulls	Canal dels Rulls		42.48333	1.46667	H	STM	AD		00				0		1148	Europe/Andorra	1993-12-23
+3039204	Canal de Ruixol	Canal de Ruixol		42.58333	1.51667	H	STM	AD		00				0		1722	Europe/Andorra	1993-12-23
+3039205	Torrent del Ruïder	Torrent del Ruider		42.58333	1.48333	H	STM	AD		00				0		1809	Europe/Andorra	1993-12-23
+3039206	Roc del Ruïder	Roc del Ruider		42.58333	1.48333	T	SPUR	AD		00				0		1809	Europe/Andorra	1993-12-23
+3039207	Pala del Ruf	Pala del Ruf		42.58333	1.45	T	SLP	AD		00				0		2156	Europe/Andorra	1993-12-23
+3039208	Basses del Ruf	Basses del Ruf		42.58333	1.45	H	LKS	AD		00				0		2156	Europe/Andorra	1993-12-23
+3039209	Serrat de Rudielles	Serrat de Rudielles		42.5	1.45	T	MT	AD		00				0		1840	Europe/Andorra	1993-12-23
+3039210	Canal de Rudielles	Canal de Rudielles		42.5	1.45	H	STM	AD		00				0		1840	Europe/Andorra	1993-12-23
+3039211	Vial de Rubials	Vial de Rubials		42.48333	1.45	R	RD	AD		00				0		1195	Europe/Andorra	1993-12-23
+3039212	Torrent de Rubials	Torrent de Rubials		42.48333	1.45	H	STM	AD		00				0		1195	Europe/Andorra	1993-12-23
+3039213	Solana de Rubials	Solana de Rubials		42.48333	1.45	T	SLP	AD		00				0		1195	Europe/Andorra	1993-12-23
+3039214	Rua del Terrer Roi	Rua del Terrer Roi		42.43333	1.5	L	LCTY	AD		00				0		1804	Europe/Andorra	1993-12-23
+3039215	Bosc de Roures	Bosc de Roures		42.6	1.51667	V	FRST	AD		00				0		1445	Europe/Andorra	1993-12-23
+3039216	Collet de Roques Negres	Collet de Roques Negres		42.45	1.45	T	PASS	AD		00				0		1482	Europe/Andorra	1993-12-23
+3039217	Roques Negres	Roques Negres		42.45	1.45	L	LCTY	AD		00				0		1482	Europe/Andorra	1993-12-23
+3039218	Serrat de Roques Grosses	Serrat de Roques Grosses		42.58333	1.6	T	RDGE	AD		00				0		1828	Europe/Andorra	1993-12-23
+3039219	Canal de Roques Blanques	Canal de Roques Blanques		42.5	1.48333	H	STM	AD		00				0		1316	Europe/Andorra	1993-12-23
+3039220	Fontanal de les Roques	Fontanal de les Roques		42.53333	1.46667	H	SPNG	AD		00				0		1846	Europe/Andorra	1993-12-23
+3039221	Pleta de les Romes	Pleta de les Romes		42.65	1.55	L	GRAZ	AD		00				0		2181	Europe/Andorra	1993-12-23
+3039222	Font Roja	Font Roja		42.55	1.45	H	SPNG	AD		00				0		1788	Europe/Andorra	1993-12-23
+3039223	Font Roja	Font Roja		42.48333	1.5	H	SPNG	AD		00				0		1631	Europe/Andorra	1993-12-23
+3039224	Bassa Roja	Bassa Roja		42.6	1.66667	H	LK	AD		00				0		1858	Europe/Andorra	1993-12-23
+3039225	Grau Roig	Grau Roig		42.58333	1.46667	T	SLP	AD		00				0		1643	Europe/Andorra	1993-12-23
+3039226	Borda de Roig	Borda de Roig		42.56667	1.58333	S	HUT	AD		00				0		1919	Europe/Andorra	1993-12-23
+3039227	Fonts Roges	Fonts Roges		42.56667	1.46667	H	SPNG	AD		00				0		1673	Europe/Andorra	1993-12-23
+3039228	Fonts Roges	Fonts Roges		42.55	1.65	H	SPNG	AD		00				0		2432	Europe/Andorra	1993-12-23
+3039229	Canals Roges	Canals Roges		42.58333	1.71667	H	RVN	AD		00				0		2553	Europe/Andorra	1993-12-23
+3039230	Basses Roges	Basses Roges		42.46667	1.55	H	LKS	AD		00				0		2341	Europe/Andorra	1993-12-23
+3039231	Font Rodona	Font Rodona		42.55	1.41667	H	SPNG	AD		00				0		2105	Europe/Andorra	1993-12-23
+3039232	Costa Rodona	Costa Rodona		42.65	1.48333	T	SLP	AD		00				0		2341	Europe/Andorra	1993-12-23
+3039233	Costa Rodona	Costa Rodona		42.58333	1.45	T	SLP	AD		00				0		2156	Europe/Andorra	1993-12-23
+3039234	Costa Rodona	Costa Rodona		42.55	1.71667	T	SLP	AD		00				0		2192	Europe/Andorra	1993-12-23
+3039235	Boïga Rodona	Boiga Rodona		42.43333	1.53333	V	CULT	AD		00				0		2108	Europe/Andorra	1993-12-23
+3039236	Bosc del Ródol	Bosc del Rodol		42.48882	1.57683	V	FRST	AD		00				0		2246	Europe/Andorra	2011-04-19
+3039237	Turó Rodó	Turo Rodo		42.56667	1.55	T	PK	AD		00				0		1996	Europe/Andorra	1993-12-23
+3039238	Roc Rodó	Roc Rodo		42.56667	1.48333	T	SPUR	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039239	Roc Rodó	Roc Rodo		42.56667	1.43333	T	SPUR	AD		00				0		2402	Europe/Andorra	1993-12-23
+3039240	Pla de Rodó	Pla de Rodo		42.55	1.43333	T	UPLD	AD		00				0		1949	Europe/Andorra	1993-12-23
+3039241	Estany Rodó	Estany Rodo		42.51667	1.68333	H	LK	AD		00				0		2352	Europe/Andorra	1993-12-23
+3039242	Estany Rodó	Estany Rodo		42.5	1.65	H	LK	AD		00				0		2542	Europe/Andorra	1993-12-23
+3039243	Bony Rodó	Bony Rodo		42.61667	1.55	T	SPUR	AD		00				0		2007	Europe/Andorra	1993-12-23
+3039244	Rocs Tous	Rocs Tous		42.55	1.58333	L	LCTY	AD		00				0		1499	Europe/Andorra	1993-12-23
+3039245	Rocs Negres	Rocs Negres		42.53333	1.61667	A	ADMD	AD		00				0		2237	Europe/Andorra	1993-12-23
+3039246	Planell del Roc Gros	Planell del Roc Gros		42.58333	1.48333	T	UPLD	AD		00				0		1809	Europe/Andorra	1993-12-23
+3039247	Bosc del Roc Gros	Bosc del Roc Gros		42.58333	1.48333	V	FRST	AD		00				0		1809	Europe/Andorra	1993-12-23
+3039248	Serra del Roc del Rellotge	Serra del Roc del Rellotge		42.61667	1.58333	T	MT	AD		00				0		2374	Europe/Andorra	1993-12-23
+3039249	Font del Roc del Porquer	Font del Roc del Porquer		42.6	1.45	H	SPNG	AD		00				0		2174	Europe/Andorra	1993-12-23
+3039250	Canal del Roc de la Grael	Canal del Roc de la Grael		42.51667	1.53333	H	STM	AD		00				0		1460	Europe/Andorra	1993-12-23
+3039251	Roca Podrida	Roca Podrida		42.55	1.51667	L	LCTY	AD		00				0		1397	Europe/Andorra	1993-12-23
+3039252	Costa de Roca Negra	Costa de Roca Negra		42.58333	1.58333	T	SLP	AD		00				0		1993	Europe/Andorra	1993-12-23
+3039253	Canal de Rocanegra	Canal de Rocanegra		42.5	1.46667	H	STM	AD		00				0		1678	Europe/Andorra	1993-12-23
+3039254	Canal de Roca Major	Canal de Roca Major		42.43333	1.5	H	RVN	AD		00				0		1804	Europe/Andorra	1993-12-23
+3039255	Planell de la Roca Grossa	Planell de la Roca Grossa		42.56667	1.7	T	UPLD	AD		00				0		2375	Europe/Andorra	1993-12-23
+3039256	Planell de Roca Grossa	Planell de Roca Grossa		42.55	1.66667	T	UPLD	AD		00				0		2224	Europe/Andorra	1993-12-23
+3039257	Canal de Rocafort	Canal de Rocafort		42.46667	1.48333	H	STM	AD		00				0		1134	Europe/Andorra	1993-12-23
+3039258	Rocafort	Rocafort		42.46667	1.48333	A	ADMD	AD		00				0		1134	Europe/Andorra	1993-12-23
+3039259	Solana de la Roca de la Sabina	Solana de la Roca de la Sabina		42.56667	1.46667	T	SLP	AD		00				0		1673	Europe/Andorra	1993-12-23
+3039260	Canal de la Roca Blanca	Canal de la Roca Blanca		42.51667	1.51667	H	STM	AD		00				0		1265	Europe/Andorra	1993-12-23
+3039261	Vial de la Roca	Vial de la Roca		42.55	1.6	R	RD	AD		00				0		2210	Europe/Andorra	1993-12-23
+3039262	Pala de la Roca	Pala de la Roca		42.55	1.61667	T	SLP	AD		00				0		2206	Europe/Andorra	1993-12-23
+3039263	Bosc de la Roca	Bosc de la Roca		42.55	1.46667	V	FRST	AD		00				0		1585	Europe/Andorra	1993-12-23
+3039264	Pont del Riu Montaner	Pont del Riu Montaner		42.53333	1.51667	S	BDG	AD		00				0		1361	Europe/Andorra	1993-12-23
+3039265	Camí del Riu del Seig	Cami del Riu del Seig		42.56667	1.6	R	TRL	AD		00				0		1655	Europe/Andorra	1993-12-23
+3039266	Riu del Seig	Riu del Seig		42.56667	1.61667	A	ADMD	AD		00				0		1920	Europe/Andorra	1993-12-23
+3039267	Forat del Riu dels Clots de Massat	Forat del Riu dels Clots de Massat		42.55	1.68333	H	RVN	AD		00				0		2254	Europe/Andorra	1993-12-23
+3039268	Fonts del Riu de les Cebes	Fonts del Riu de les Cebes		42.61667	1.58333	H	SPNG	AD		00				0		2374	Europe/Andorra	1993-12-23
+3039269	Costa del Riu de les Cebes	Costa del Riu de les Cebes		42.61667	1.58333	T	SLP	AD		00				0		2374	Europe/Andorra	1993-12-23
+3039270	Collada del Riu de les Cebes	Collada del Riu de les Cebes		42.61667	1.58333	T	SPUR	AD		00				0		2374	Europe/Andorra	1993-12-23
+3039271	Bony del Riu de les Cebes	Bony del Riu de les Cebes		42.61667	1.58333	T	RK	AD		00				0		2374	Europe/Andorra	1993-12-23
+3039272	Prats del Riu	Prats del Riu		42.46667	1.5	L	GRAZ	AD		00				0		1383	Europe/Andorra	1993-12-23
+3039273	Canal de Rita	Canal de Rita		42.53333	1.6	H	STM	AD		00				0		1888	Europe/Andorra	1993-12-23
+3039274	Solà del Riguer	Sola del Riguer		42.5	1.55	T	SLP	AD		00				0		1566	Europe/Andorra	1993-12-23
+3039275	Obaga del Riguer	Obaga del Riguer		42.48333	1.53333	T	SLP	AD		00				0		2255	Europe/Andorra	1993-12-23
+3039276	Bordes de Rigoder	Bordes de Rigoder		42.53333	1.61667	S	HUTS	AD		00				0		2237	Europe/Andorra	1993-12-23
+3039277	Bosc de les Ribes	Bosc de les Ribes		42.55	1.55	V	FRST	AD		00				0		2097	Europe/Andorra	1993-12-23
+3039278	Riberal d’Envalira	Riberal d'Envalira		42.53333	1.7	L	LCTY	AD		00				0		2357	Europe/Andorra	1993-12-23
+3039279	Canals de Ribera	Canals de Ribera		42.43333	1.48333	H	STM	AD		00				0		1228	Europe/Andorra	1993-12-23
+3039280	Ribassot	Ribassot		42.58333	1.48333	L	LCTY	AD		00				0		1809	Europe/Andorra	1993-12-23
+3039281	Torrent de Ribassols	Torrent de Ribassols		42.58333	1.48333	H	STM	AD		00				0		1809	Europe/Andorra	1993-12-23
+3039282	Torrent Ribal	Torrent Ribal		42.58333	1.48333	H	STM	AD		00				0		1809	Europe/Andorra	1993-12-23
+3039283	Tosa de Riba Escorjada	Tosa de Riba Escorjada		42.55	1.65	T	UPLD	AD		00				0		2432	Europe/Andorra	1993-12-23
+3039284	Solanelles de Riba Escorjada	Solanelles de Riba Escorjada		42.55	1.63333	T	SLP	AD		00				0		2336	Europe/Andorra	1993-12-23
+3039285	Planells de Riba Escorjada	Planells de Riba Escorjada		42.56667	1.63333	T	UPLD	AD		00				0		2016	Europe/Andorra	1993-12-23
+3039286	Camí de Riba Escorjada	Cami de Riba Escorjada		42.56667	1.63333	R	TRL	AD		00				0		2016	Europe/Andorra	1993-12-23
+3039287	Camí de Riba Escorjada	Cami de Riba Escorjada		42.55	1.6	R	TRL	AD		00				0		2210	Europe/Andorra	1993-12-23
+3039288	Riba Escorjada	Riba Escorjada		42.55	1.63333	A	ADMD	AD		00				0		2336	Europe/Andorra	1993-12-23
+3039289	Bosc de Riba	Bosc de Riba		42.55	1.58333	V	FRST	AD		00				0		1499	Europe/Andorra	1993-12-23
+3039290	Bosc del Riambert	Bosc del Riambert		42.56667	1.51667	V	FRST	AD		00				0		1500	Europe/Andorra	1993-12-23
+3039291	Riu de Rialb	Riu de Rialb	Riu de Rialb,Riu de Rialp	42.61667	1.53333	H	STM	AD	AD	00				0		1609	Europe/Andorra	2011-11-05
+3039292	Portella de Rialb	Portella de Rialb		42.63333	1.53333	T	PASS	AD		00				0		2072	Europe/Andorra	1993-12-23
+3039293	Basera de Rialb	Basera de Rialb		42.65	1.56667	T	CLF	AD		00				0		2471	Europe/Andorra	1993-12-23
+3039294	Rialb	Rialb		42.65	1.55	A	ADMD	AD		00				0		2181	Europe/Andorra	1993-12-23
+3039295	Pala de Rep	Pala de Rep		42.55	1.6	T	SLP	AD		00				0		2210	Europe/Andorra	1993-12-23
+3039296	Coll del Rep	Coll del Rep		42.45	1.46667	T	PASS	AD		00				0		935	Europe/Andorra	1993-12-23
+3039297	Cap de Rep	Cap de Rep	Cap de Rep,Cap de l' Ovella,Cap de l’ Ovella	42.54325	1.61092	T	PK	AD		00				0		2213	Europe/Andorra	2011-11-05
+3039298	Rep	Rep		42.53333	1.58333	A	ADMD	AD		00				0		1571	Europe/Andorra	1993-12-23
+3039299	Roc del Rellotge	Roc del Rellotge		42.61667	1.56667	T	SPUR	AD		00				0		2228	Europe/Andorra	1993-12-23
+3039300	Roc del Rellotge	Roc del Rellotge		42.56667	1.61667	T	RK	AD		00				0		1920	Europe/Andorra	1993-12-23
+3039301	Font de les Reïneres	Font de les Reineres		42.6	1.68333	H	SPNG	AD		00				0		2089	Europe/Andorra	1993-12-23
+3039302	Solana de la Regalíssia	Solana de la Regalissia		42.48333	1.41667	T	SLP	AD		00				0		1920	Europe/Andorra	1993-12-23
+3039303	Obaga de Redort	Obaga de Redort		42.55	1.56667	T	SLP	AD		00				0		1828	Europe/Andorra	1993-12-23
+3039304	Redort	Redort		42.56667	1.55	L	LCTY	AD		00				0		1996	Europe/Andorra	1993-12-23
+3039305	Clot de la Rectoria	Clot de la Rectoria		42.51667	1.5	H	RVN	AD		00				0		1688	Europe/Andorra	1993-12-23
+3039306	Serrat del Rec d’Areny	Serrat del Rec d'Areny		42.58333	1.46667	T	SPUR	AD		00				0		1643	Europe/Andorra	1993-12-23
+3039307	Bosc de Rèbols	Bosc de Rebols		42.46667	1.51667	V	FRST	AD		00				0		1985	Europe/Andorra	1993-12-23
+3039308	Pont de la Rebollissa	Pont de la Rebollissa		42.61667	1.53333	S	BDG	AD		00				0		1609	Europe/Andorra	1993-12-23
+3039309	Llanesques de les Rebes	Llanesques de les Rebes		42.63333	1.61667	T	CLF	AD		00				0		2541	Europe/Andorra	1993-12-23
+3039310	Clot de les Rebes	Clot de les Rebes		42.61667	1.61667	H	RVN	AD		00				0		2352	Europe/Andorra	1993-12-23
+3039311	Rebaixant del Maià	Rebaixant del Maia		42.56667	1.73333	L	LCTY	AD		00				0		2096	Europe/Andorra	1993-12-23
+3039312	Canal de la Rata	Canal de la Rata		42.58333	1.46667	H	RVN	AD		00				0		1643	Europe/Andorra	1993-12-23
+3039313	Port de Rat	Port de Rat	Port de Rat,Port de Rat du D'Auzat	42.62127	1.47371	T	PASS	AD	FR,AD	07				0		2442	Europe/Andorra	2007-03-04
+3039314	Bosc de Rasets	Bosc de Rasets		42.43333	1.53333	V	FRST	AD		00				0		2108	Europe/Andorra	1993-12-23
+3039315	Rasa de Perafita	Rasa de Perafita		42.48333	1.58333	T	UPLD	AD		00				0		2349	Europe/Andorra	1993-12-23
+3039316	Costa Rasa	Costa Rasa		42.46667	1.45	T	SLP	AD		00				0		1562	Europe/Andorra	1993-12-23
+3039317	Bosc de la Rasa	Bosc de la Rasa		42.46667	1.53333	V	FRST	AD		00				0		2332	Europe/Andorra	1993-12-23
+3039318	Presa de Ransol	Presa de Ransol		42.58333	1.63333	S	DAM	AD		00				0		1722	Europe/Andorra	1993-12-23
+3039319	Carretera de Ransol	Carretera de Ransol		42.58333	1.65	R	RD	AD		00				0		1767	Europe/Andorra	1993-12-23
+3039320	Ransol	Ransol		42.58137	1.63812	P	PPL	AD		02				0		1727	Europe/Andorra	2007-04-16
+3039321	Roca de Ràmio	Roca de Ramio		42.5	1.56667	T	RK	AD		00				0		1776	Europe/Andorra	1993-12-23
+3039322	Ràmio	Ramio		42.49702	1.57414	S	HUTS	AD		00				0		1776	Europe/Andorra	2011-04-19
+3039323	Canal del Ramer	Canal del Ramer		42.56667	1.48333	H	RVN	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039324	Canal de la Ramenada	Canal de la Ramenada		42.5	1.51667	H	STM	AD		00				0		1410	Europe/Andorra	1993-12-23
+3039325	Camí Ral	Cami Ral		42.55	1.58333	R	TRL	AD		00				0		1499	Europe/Andorra	1993-12-23
+3039326	Camí Ral	Cami Ral		42.53333	1.58333	R	TRL	AD		00				0		1571	Europe/Andorra	1993-12-23
+3039327	Radonella	Radonella		42.48333	1.45	L	LCTY	AD		00				0		1195	Europe/Andorra	1993-12-23
+3039328	Rádio Andorra	Radio Andorra		42.5282	1.57019	S	STNR	AD		00				0		1418	Europe/Andorra	2011-04-19
+3039329	Racons	Racons		42.56667	1.58333	A	ADMD	AD		00				0		1919	Europe/Andorra	1993-12-23
+3039330	Pic de la Raconada de la Maiana	Pic de la Raconada de la Maiana	Pic de la Raconada de la Maiana	42.46667	1.61667	T	PK	AD		00				0		2448	Europe/Andorra	2011-11-05
+3039331	Clots de la Raconada de la Maiana	Clots de la Raconada de la Maiana		42.46667	1.6	T	CRQS	AD		00				0		2449	Europe/Andorra	1993-12-23
+3039332	Pic de Racofred	Pic de Racofred	Pic de Racofred,Pic de Racofret	42.6	1.45	T	PK	AD		00				0		2174	Europe/Andorra	2011-11-05
+3039333	Racó de l’Estany de Cabana Sorda	Raco de l'Estany de Cabana Sorda		42.61667	1.66667	L	LCTY	AD		00				0		2536	Europe/Andorra	1993-12-23
+3039334	Pleta del Racó	Pleta del Raco		42.61667	1.56667	L	GRAZ	AD		00				0		2228	Europe/Andorra	1993-12-23
+3039335	Pleta del Racó	Pleta del Raco		42.58333	1.45	L	GRAZ	AD		00				0		2156	Europe/Andorra	1993-12-23
+3039336	Canal del Racó	Canal del Raco		42.56667	1.48333	H	RVN	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039337	Bassa del Racó	Bassa del Raco		42.61667	1.5	H	LK	AD		00				0		2390	Europe/Andorra	1993-12-23
+3039338	Pleta de la Rabassa	Pleta de la Rabassa		42.63333	1.55	L	GRAZ	AD		00				0		2053	Europe/Andorra	1993-12-23
+3039339	Carretera de la Rabassa	Carretera de la Rabassa		42.43333	1.51667	R	RD	AD		00				0		2031	Europe/Andorra	1993-12-23
+3039340	Canya de la Rabassa	Canya de la Rabassa		42.63333	1.55	S	CAVE	AD		00				0		2053	Europe/Andorra	1993-12-23
+3039341	Bosc de la Rabassa	Bosc de la Rabassa		42.4379	1.51425	V	FRST	AD		00				0		1990	Europe/Andorra	2011-04-19
+3039342	Solana del Querol	Solana del Querol		42.58333	1.53333	T	SLP	AD		00				0		1924	Europe/Andorra	1993-12-23
+3039343	Riu del Querol	Riu del Querol		42.58333	1.66667	H	STM	AD		00				0		2159	Europe/Andorra	1993-12-23
+3039344	Riu del Querol	Riu del Querol		42.58333	1.53333	H	STM	AD		00				0		1924	Europe/Andorra	1993-12-23
+3039345	Pleta del Querol	Pleta del Querol		42.6	1.66667	L	GRAZ	AD		00				0		1858	Europe/Andorra	1993-12-23
+3039346	Estanyó del Querol	Estanyo del Querol		42.61303	1.67019	H	LK	AD		00				0		2355	Europe/Andorra	2011-04-19
+3039347	Serrat de la Quera	Serrat de la Quera		42.51667	1.51667	T	RDGE	AD		00				0		1265	Europe/Andorra	1993-12-23
+3039348	Canal Gran de la Quera	Canal Gran de la Quera		42.45	1.46667	H	STM	AD		00				0		935	Europe/Andorra	1993-12-23
+3039349	Bosc de la Quera	Bosc de la Quera		42.51667	1.51667	V	FRST	AD		00				0		1265	Europe/Andorra	1993-12-23
+3039350	Roc del Quer	Roc del Quer		42.61667	1.55	T	SPUR	AD		00				0		2007	Europe/Andorra	1993-12-23
+3039351	Roc del Quer	Roc del Quer		42.55	1.51667	T	SPUR	AD		00				0		1397	Europe/Andorra	1993-12-23
+3039352	Roc del Quer	Roc del Quer		42.58333	1.48333	T	RK	AD		00				0		1809	Europe/Andorra	1993-12-23
+3039353	Roc del Quer	Roc del Quer		42.52798	1.60146	T	RK	AD		00				0		1888	Europe/Andorra	2011-04-19
+3039354	Roc del Quer	Roc del Quer		42.56667	1.6	T	CLF	AD		00				0		1655	Europe/Andorra	1993-12-23
+3039355	Roc de Quer	Roc de Quer		42.48333	1.46667	T	RK	AD		00				0		1148	Europe/Andorra	1993-12-23
+3039356	Planell del Quer	Planell del Quer		42.63333	1.56667	T	UPLD	AD		00				0		2394	Europe/Andorra	1993-12-23
+3039357	Canal del Quer	Canal del Quer		42.63333	1.55	H	RVN	AD		00				0		2053	Europe/Andorra	1993-12-23
+3039358	Canal del Quer	Canal del Quer		42.51667	1.6	H	RVN	AD		00				0		2085	Europe/Andorra	1993-12-23
+3039359	Bosc del Quer	Bosc del Quer		42.51667	1.6	V	FRST	AD		00				0		2085	Europe/Andorra	1993-12-23
+3039360	Solà del Quart de Nagol	Sola del Quart de Nagol		42.48333	1.51667	T	SLP	AD		00				0		2061	Europe/Andorra	1993-12-23
+3039361	Collet Purgat	Collet Purgat		42.46667	1.48333	T	PK	AD		00				0		1134	Europe/Andorra	1993-12-23
+3039363	Pont de Puntal	Pont de Puntal		42.61667	1.55	S	BDG	AD		00				0		2007	Europe/Andorra	1993-12-23
+3039364	Bosc de Puntal	Bosc de Puntal		42.63333	1.55	V	FRST	AD		00				0		2053	Europe/Andorra	1993-12-23
+3039365	Puntal	Puntal		42.63333	1.55	L	LCTY	AD		00				0		2053	Europe/Andorra	1993-12-23
+3039366	Vial dels Pujols	Vial dels Pujols		42.48333	1.48333	R	RD	AD		00				0		981	Europe/Andorra	1993-12-23
+3039367	Tarteres dels Pujols	Tarteres dels Pujols		42.48333	1.48333	T	TAL	AD		00				0		981	Europe/Andorra	1993-12-23
+3039368	Roc del Pujol	Roc del Pujol		42.46667	1.5	T	RK	AD		00				0		1383	Europe/Andorra	1993-12-23
+3039369	Pujant de Donges	Pujant de Donges		42.45	1.5	L	LCTY	AD		00				0		1614	Europe/Andorra	1993-12-23
+3039370	Puiol del Piu	Puiol del Piu		42.56667	1.5	P	PPL	AD		04				0		1636	Europe/Andorra	1993-12-23
+3039371	Borda del Puigcernal	Borda del Puigcernal		42.55	1.58333	S	HUT	AD		00				0		1499	Europe/Andorra	1993-12-23
+3039372	Pui d’Olivesa	Pui d'Olivesa		42.45	1.48333	L	LCTY	AD		00				0		1111	Europe/Andorra	1993-12-23
+3039373	Pui d’Encamp	Pui d'Encamp		42.53333	1.56667	L	LCTY	AD		00				0		1418	Europe/Andorra	1993-12-23
+3039374	Estany Primer	Estany Primer		42.63721	1.49019	H	LK	AD		05				0		2254	Europe/Andorra	2010-01-12
+3039375	Estany Primer	Estany Primer		42.61667	1.71667	H	LK	AD		00				0		2352	Europe/Andorra	1993-12-23
+3039376	Estany Primer	Estany Primer		42.51667	1.68333	H	LK	AD		00				0		2352	Europe/Andorra	1993-12-23
+3039377	Canal de la Presa	Canal de la Presa		42.58333	1.63333	H	CNL	AD		00				0		1722	Europe/Andorra	1993-12-23
+3039378	Canal de la Premsa	Canal de la Premsa		42.46667	1.48333	H	STM	AD		00				0		1134	Europe/Andorra	1993-12-23
+3039379	Canal Pregona	Canal Pregona		42.56667	1.48333	H	STM	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039380	Canal Pregona	Canal Pregona		42.55	1.5	H	STM	AD		00				0		1292	Europe/Andorra	1993-12-23
+3039381	Torrent Pregó	Torrent Prego		42.53333	1.58333	H	STM	AD		00				0		1571	Europe/Andorra	1993-12-23
+3039382	Bosc dels Prats Sobirans	Bosc dels Prats Sobirans		42.51667	1.46667	V	FRST	AD		00				0		1840	Europe/Andorra	1993-12-23
+3039383	Planells dels Prats Nous	Planells dels Prats Nous		42.58333	1.48333	T	UPLD	AD		00				0		1809	Europe/Andorra	1993-12-23
+3039384	Bordes dels Prats Nous	Bordes dels Prats Nous		42.58333	1.48333	S	HUTS	AD		00				0		1809	Europe/Andorra	1993-12-23
+3039385	Riu de Prats	Riu de Prats		42.55	1.58333	H	STM	AD		00				0		1499	Europe/Andorra	1993-12-23
+3039386	Prats	Prats		42.56003	1.59396	P	PPL	AD		02				0		1677	Europe/Andorra	2007-04-16
+3039387	Solana de Prat Primer	Solana de Prat Primer		42.48333	1.55	T	SLP	AD		00				0		2233	Europe/Andorra	1993-12-23
+3039388	Planell de Prat Primer	Planell de Prat Primer		42.48333	1.55	T	UPLD	AD		00				0		2233	Europe/Andorra	1993-12-23
+3039389	Obaga de Prat Primer	Obaga de Prat Primer		42.48333	1.55	T	SLP	AD		00				0		2233	Europe/Andorra	1993-12-23
+3039390	Font de Prat Primer	Font de Prat Primer		42.48333	1.55	H	SPNG	AD		00				0		2233	Europe/Andorra	1993-12-23
+3039391	Collada de Prat Primer	Collada de Prat Primer		42.46667	1.55	T	PASS	AD		00				0		2341	Europe/Andorra	1993-12-23
+3039392	Clots de Prat Primer	Clots de Prat Primer		42.48333	1.55	H	RVN	AD		00				0		2233	Europe/Andorra	1993-12-23
+3039393	Prat Primer	Prat Primer		42.48333	1.55	A	ADMD	AD		00				0		2233	Europe/Andorra	1993-12-23
+3039394	Collada de Prat Porceller	Collada de Prat Porceller	Collada de Prat Porceller	42.46667	1.45	T	PASS	AD		00				0		1562	Europe/Andorra	2011-11-05
+3039395	Font del Prat de Roca	Font del Prat de Roca		42.56667	1.58333	H	SPNG	AD		00				0		1919	Europe/Andorra	1993-12-23
+3039396	Font del Prat dels Pollins	Font del Prat dels Pollins		42.56667	1.58333	H	SPNG	AD		00				0		1919	Europe/Andorra	1993-12-23
+3039397	Basers del Prat del Quart	Basers del Prat del Quart		42.61667	1.63333	T	CLF	AD		00				0		2331	Europe/Andorra	1993-12-23
+3039398	Font del Prat del Jep	Font del Prat del Jep	Font del Prat del Gep,Font del Prat del Jep	42.56667	1.58333	H	SPNG	AD	AD	00				0		1919	Europe/Andorra	2011-11-05
+3039399	Torrent del Prat del Gaspar	Torrent del Prat del Gaspar		42.53333	1.56667	H	STM	AD		00				0		1418	Europe/Andorra	1993-12-23
+3039400	Bosc del Prat de l’Estel	Bosc del Prat de l'Estel		42.53333	1.46667	V	FRST	AD		00				0		1846	Europe/Andorra	1993-12-23
+3039401	Riu del Prat del Comellar	Riu del Prat del Comellar		42.58333	1.65	H	STM	AD		00				0		1767	Europe/Andorra	1993-12-23
+3039402	Solana del Prat del Bosc	Solana del Prat del Bosc		42.58333	1.46667	T	SLP	AD		00				0		1643	Europe/Andorra	1993-12-23
+3039403	Riu del Prat del Bosc	Riu del Prat del Bosc		42.55	1.48333	H	STM	AD		00				0		1548	Europe/Andorra	1993-12-23
+3039404	Pont del Prat del Bosc	Pont del Prat del Bosc		42.53333	1.46667	S	BDG	AD		00				0		1846	Europe/Andorra	1993-12-23
+3039405	Bosc del Prat del Bosc	Bosc del Prat del Bosc		42.53333	1.46667	V	FRST	AD		00				0		1846	Europe/Andorra	1993-12-23
+3039406	Obagues del Prat de la Posella	Obagues del Prat de la Posella		42.48333	1.45	T	SLP	AD		00				0		1195	Europe/Andorra	1993-12-23
+3039407	Basera del Prat de la Farga	Basera del Prat de la Farga		42.48333	1.45	T	CLF	AD		00				0		1195	Europe/Andorra	1993-12-23
+3039408	Basers del Prat de la Creu	Basers del Prat de la Creu		42.6	1.63333	T	CLF	AD		00				0		1893	Europe/Andorra	1993-12-23
+3039409	Obaga dels Pradets	Obaga dels Pradets		42.48333	1.43333	T	SLP	AD		00				0		1938	Europe/Andorra	1993-12-23
+3039410	Roc dels Pous	Roc dels Pous		42.55	1.61667	T	RK	AD		00				0		2206	Europe/Andorra	1993-12-23
+3039411	Bosc dels Pous	Bosc dels Pous		42.55	1.48333	V	FRST	AD		00				0		1548	Europe/Andorra	1993-12-23
+3039412	Serrat del Pouet	Serrat del Pouet		42.55	1.51667	T	RDGE	AD		00				0		1397	Europe/Andorra	1993-12-23
+3039413	Canal del Pouet	Canal del Pouet		42.56667	1.53333	H	STM	AD		00				0		1669	Europe/Andorra	1993-12-23
+3039414	Costa del Pou	Costa del Pou		42.58333	1.58333	T	SLP	AD		00				0		1993	Europe/Andorra	1993-12-23
+3039415	Bosc de les Poselletes	Bosc de les Poselletes		42.55	1.45	V	FRST	AD		00				0		1788	Europe/Andorra	1993-12-23
+3039416	Bony de la Posella	Bony de la Posella		42.58333	1.48333	T	SPUR	AD		00				0		1809	Europe/Andorra	1993-12-23
+3039417	Posada dels Pastors	Posada dels Pastors		42.58333	1.48333	L	LCTY	AD		00				0		1809	Europe/Andorra	1993-12-23
+3039418	Font de la Posada	Font de la Posada		42.46667	1.45	H	SPNG	AD		00				0		1562	Europe/Andorra	1993-12-23
+3039419	Serrat de la Posa	Serrat de la Posa		42.55	1.66667	T	RDGE	AD		00				0		2224	Europe/Andorra	1993-12-23
+3039420	Turó del Port Vell	Turo del Port Vell		42.65	1.56667	T	PK	AD		00				0		2471	Europe/Andorra	1993-12-23
+3039421	Pic del Port Vell	Pic del Port Vell	Pic del Port Vell	42.57205	1.44341	T	PK	AD		00				0	2655	2305	Europe/Andorra	2011-02-09
+3039422	Canal del Port Vell	Canal del Port Vell		42.56667	1.45	H	STM	AD		00				0		2137	Europe/Andorra	1993-12-23
+3039423	Clot de Port Negre	Clot de Port Negre		42.46667	1.56667	H	RVN	AD		00				0		2365	Europe/Andorra	1993-12-23
+3039424	Basers de les Portes	Basers de les Portes		42.48333	1.5	T	CLF	AD		00				0		1631	Europe/Andorra	1993-12-23
+3039425	Portella de la Portelleta	Portella de la Portelleta		42.46667	1.65	T	PASS	AD		00				0		2700	Europe/Andorra	1993-12-23
+3039426	Tossa Plana de Lles	Tossa Plana de Lles	Pic de la Portelleta,Tosal Plane,Tossa Plana de Lles	42.46667	1.66667	T	PK	AD		00				0		2559	Europe/Andorra	2011-11-05
+3039427	Font de la Portelleta	Font de la Portelleta		42.46667	1.66667	H	SPNG	AD		00				0		2559	Europe/Andorra	1993-12-23
+3039428	Collada de la Portelleta	Collada de la Portelleta	Collada de la Portelleta	42.46667	1.66667	T	PASS	AD		00				0		2559	Europe/Andorra	2011-11-05
+3039429	Riu de les Portelles	Riu de les Portelles		42.61667	1.63333	H	STM	AD		00				0		2331	Europe/Andorra	1993-12-23
+3039430	Clots de la Portella de Setut	Clots de la Portella de Setut		42.46667	1.63333	T	CRQS	AD		00				0		2619	Europe/Andorra	1993-12-23
+3039431	Pleta de la Portella	Pleta de la Portella		42.56667	1.71667	L	GRAZ	AD		00				0		2219	Europe/Andorra	1993-12-23
+3039432	Collada de la Portella	Collada de la Portella		42.46667	1.63333	T	PASS	AD		00				0		2619	Europe/Andorra	1993-12-23
+3039433	Riu del Port Dret	Riu del Port Dret		42.6	1.46667	H	STM	AD		00				0		2421	Europe/Andorra	1993-12-23
+3039434	Estany del Port Dret	Estany del Port Dret		42.6	1.46667	H	LK	AD		00				0		2421	Europe/Andorra	1993-12-23
+3039435	Costa del Port Dret	Costa del Port Dret		42.58333	1.7	T	SLP	AD		00				0		2584	Europe/Andorra	1993-12-23
+3039436	Clots del Port Dret	Clots del Port Dret		42.56667	1.68333	H	RVN	AD		00				0		2340	Europe/Andorra	1993-12-23
+3039437	Camí del Port Dret	Cami del Port Dret		42.56667	1.66667	R	TRL	AD		00				0		1938	Europe/Andorra	1993-12-23
+3039439	Camí del Port de Setut	Cami del Port de Setut		42.46667	1.63333	R	TRL	AD		00				0		2619	Europe/Andorra	1993-12-23
+3039440	Basses del Port de Rat	Basses del Port de Rat		42.61667	1.48333	H	LKS	AD		05				0		2470	Europe/Andorra	2010-01-12
+3039441	Font del Port de Cabús	Font del Port de Cabus		42.55	1.41667	H	SPNG	AD		00				0		2105	Europe/Andorra	1993-12-23
+3039442	Planades del Port	Planades del Port		42.56667	1.45	T	UPLD	AD		00				0		2137	Europe/Andorra	1993-12-23
+3039443	Costa del Port	Costa del Port		42.56667	1.45	T	SLP	AD		00				0		2137	Europe/Andorra	1993-12-23
+3039444	Costa del Port	Costa del Port		42.53333	1.71667	T	SLP	AD		00				0		2400	Europe/Andorra	1993-12-23
+3039445	Clots del Port	Clots del Port		42.46667	1.58333	T	CRQS	AD		00				0		2367	Europe/Andorra	1993-12-23
+3039446	Clot del Port	Clot del Port		42.48333	1.65	H	RVN	AD		00				0		2658	Europe/Andorra	1993-12-23
+3039447	Canal del Port	Canal del Port		42.56667	1.45	H	STM	AD		00				0		2137	Europe/Andorra	1993-12-23
+3039448	Camí del Port	Cami del Port		42.48333	1.58333	R	TRL	AD		00				0		2349	Europe/Andorra	1993-12-23
+3039449	Roc del Porquer	Roc del Porquer		42.58333	1.45	T	SPUR	AD		00				0		2156	Europe/Andorra	1993-12-23
+3039450	Oratori del Pont d’Aixovall	Oratori del Pont d'Aixovall		42.48333	1.5	S	AMTH	AD		00				0		1631	Europe/Andorra	1993-12-23
+3039451	Canal del Pont	Canal del Pont		42.5	1.55	H	STM	AD		00				0		1566	Europe/Andorra	1993-12-23
+3039452	Riu Pollós	Riu Pollos		42.58333	1.46667	H	STM	AD		00				0		1643	Europe/Andorra	1993-12-23
+3039453	Bosc de la Pollentia	Bosc de la Pollentia		42.53333	1.5	V	FRST	AD		00				0		1357	Europe/Andorra	1993-12-23
+3039454	Roc del Poll	Roc del Poll		42.61667	1.53333	T	RK	AD		00				0		1609	Europe/Andorra	1993-12-23
+3039455	Roc de Podoïna	Roc de Podoina		42.45	1.5	T	RK	AD		00				0		1614	Europe/Andorra	1993-12-23
+3039456	Canal del Pletiu	Canal del Pletiu		42.5	1.58333	H	STM	AD		00				0		1888	Europe/Andorra	1993-12-23
+3039457	Serrat de les Pletes	Serrat de les Pletes		42.48333	1.43333	T	SPUR	AD		00				0		1938	Europe/Andorra	1993-12-23
+3039458	Serrat de la Pleta Vella	Serrat de la Pleta Vella		42.6	1.55	T	RDGE	AD		00				0		2298	Europe/Andorra	1993-12-23
+3039459	Canal de Pleta Mosquera	Canal de Pleta Mosquera		42.61667	1.51667	H	STM	AD		00				0		1716	Europe/Andorra	1993-12-23
+3039460	Canal de la Pleta dels Llacs	Canal de la Pleta dels Llacs		42.6	1.63333	H	STM	AD		00				0		1893	Europe/Andorra	1993-12-23
+3039461	Solana de la Pleta del Perro	Solana de la Pleta del Perro		42.53333	1.61667	T	SLP	AD		00				0		2237	Europe/Andorra	1993-12-23
+3039462	Canals de la Pleta del Llomar	Canals de la Pleta del Llomar		42.61667	1.56667	H	RVN	AD		00				0		2228	Europe/Andorra	1993-12-23
+3039463	Bony de la Pleta de Jan	Bony de la Pleta de Jan		42.61667	1.63333	T	MT	AD		00				0		2331	Europe/Andorra	1993-12-23
+3039464	Roc de la Pleta	Roc de la Pleta		42.6	1.46667	T	RK	AD		00				0		2421	Europe/Andorra	1993-12-23
+3039465	Costa de la Pleta	Costa de la Pleta		42.58333	1.45	T	SLP	AD		00				0		2156	Europe/Andorra	1993-12-23
+3039466	Bosc de la Pleta	Bosc de la Pleta		42.55	1.45	V	FRST	AD		00				0		1788	Europe/Andorra	1993-12-23
+3039467	Barraca de la Pleta	Barraca de la Pleta		42.56667	1.71667	S	HUT	AD		00				0		2219	Europe/Andorra	1993-12-23
+3039468	Camí dels Plans	Cami dels Plans		42.58333	1.61667	R	TRL	AD		00				0		1707	Europe/Andorra	1993-12-23
+3039469	Camí dels Plans	Cami dels Plans		42.53333	1.51667	R	TRL	AD		00				0		1361	Europe/Andorra	1993-12-23
+3039470	Bosc dels Plans	Bosc dels Plans		42.58333	1.63333	V	FRST	AD		00				0		1722	Europe/Andorra	1993-12-23
+3039471	Bordes dels Plans	Bordes dels Plans		42.53333	1.51667	S	FRM	AD		00				0		1361	Europe/Andorra	1993-12-23
+3039472	Obaga dels Plannels de la Regalíssia	Obaga dels Plannels de la Regalissia		42.48333	1.41667	T	SLP	AD		00				0		1920	Europe/Andorra	1993-12-23
+3039473	Planes de Fels	Planes de Fels		42.58333	1.53333	L	LCTY	AD		00				0		1924	Europe/Andorra	1993-12-23
+3039474	Solana de les Planes	Solana de les Planes		42.56667	1.53333	T	SLP	AD		00				0		1669	Europe/Andorra	1993-12-23
+3039475	Riu de les Planes	Riu de les Planes		42.63333	1.5	H	STM	AD		00				0		1979	Europe/Andorra	1993-12-23
+3039476	Pleta de les Planes	Pleta de les Planes		42.65	1.5	L	GRAZ	AD		00				0		2455	Europe/Andorra	1993-12-23
+3039477	Pic de l' Albeille	Pic de l' Albeille	Pic de les Planes	42.6457	1.49929	T	RDGE	AD	FR,AD	07				0		2542	Europe/Andorra	2007-03-04
+3039478	Les Planes	Les Planes		42.56667	1.55	T	UPLD	AD		00				0		1996	Europe/Andorra	1993-12-23
+3039479	Collet de les Planes	Collet de les Planes		42.56667	1.53333	T	SPUR	AD		00				0		1669	Europe/Andorra	1993-12-23
+3039480	Collada de les Planes	Collada de les Planes		42.65	1.5	T	PASS	AD		00				0		2455	Europe/Andorra	1993-12-23
+3039481	Bosc de les Planes	Bosc de les Planes		42.51667	1.6	V	FRST	AD		00				0		2085	Europe/Andorra	1993-12-23
+3039482	Bony de les Planes	Bony de les Planes		42.55	1.51667	T	SPUR	AD		00				0		1397	Europe/Andorra	1993-12-23
+3039483	Serrat dels Planells Grans	Serrat dels Planells Grans		42.53333	1.53333	T	RDGE	AD		00				0		1521	Europe/Andorra	1993-12-23
+3039484	Cabana dels Planells de Rialb	Cabana dels Planells de Rialb		42.65	1.55	S	HUT	AD		00				0		2181	Europe/Andorra	1993-12-23
+3039485	Planells de Rialb	Planells de Rialb		42.65	1.55	L	LCTY	AD		00				0		2181	Europe/Andorra	1993-12-23
+3039486	Riu dels Planells de Caraup	Riu dels Planells de Caraup		42.6	1.63333	H	STM	AD		00				0		1893	Europe/Andorra	1993-12-23
+3039487	Planells d’Arcalís	Planells d'Arcalis		42.63333	1.5	L	LCTY	AD		00				0		1979	Europe/Andorra	1993-12-23
+3039488	Serrat del Planell Lluent	Serrat del Planell Lluent		42.56667	1.51667	T	RDGE	AD		00				0		1500	Europe/Andorra	1993-12-23
+3039489	Bony del Planell Gran	Bony del Planell Gran		42.63333	1.55	T	SPUR	AD		00				0		2053	Europe/Andorra	1993-12-23
+3039490	Bosc de Planavilla	Bosc de Planavilla		42.55	1.68333	V	FRST	AD		00				0		2254	Europe/Andorra	1993-12-23
+3039491	Planavilla	Planavilla		42.55	1.68333	L	CLG	AD		00				0		2254	Europe/Andorra	1993-12-23
+3039492	Bosc de la Planassa	Bosc de la Planassa		42.55	1.5	V	FRST	AD		00				0		1292	Europe/Andorra	1993-12-23
+3039493	Bosc de Plana en Blanca	Bosc de Plana en Blanca		42.55	1.56667	V	FRST	AD		00				0		1828	Europe/Andorra	1993-12-23
+3039494	Tarteres de Plana de Gral	Tarteres de Plana de Gral		42.58333	1.48333	T	TAL	AD		00				0		1809	Europe/Andorra	1993-12-23
+3039495	Tosa Plana	Tosa Plana		42.46667	1.6	T	UPLD	AD		00				0		2449	Europe/Andorra	1993-12-23
+3039496	Torrent de la Plana	Torrent de la Plana		42.53333	1.6	H	STM	AD		00				0		1888	Europe/Andorra	1993-12-23
+3039497	Serra Plana	Serra Plana		42.58333	1.6	T	SPUR	AD		00				0		1828	Europe/Andorra	1993-12-23
+3039498	Serra Plana	Serra Plana		42.56667	1.48333	T	MT	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039499	Sierra Plana	Sierra Plana	Serra Plana,Sierra Plana	42.48333	1.43333	T	MT	AD		00				0		1938	Europe/Andorra	2011-11-05
+3039500	Cortal de la Plana	Cortal de la Plana		42.48333	1.53333	S	HUT	AD		00				0		2255	Europe/Andorra	1993-12-23
+3039501	Coll de la Plana	Coll de la Plana		42.45	1.5	T	SPUR	AD		00				0		1614	Europe/Andorra	1993-12-23
+3039502	Camí de la Plana	Cami de la Plana		42.48333	1.55	R	TRL	AD		00				0		2233	Europe/Andorra	1993-12-23
+3039503	Bosquet de la Plana	Bosquet de la Plana		42.5	1.53333	V	FRST	AD		00				0		1574	Europe/Andorra	1993-12-23
+3039504	Bosc de la Plana	Bosc de la Plana		42.55	1.55	V	FRST	AD		00				0		2097	Europe/Andorra	1993-12-23
+3039505	Bordes de la Plana	Bordes de la Plana		42.53333	1.6	S	HUTS	AD		00				0		1888	Europe/Andorra	1993-12-23
+3039506	Borda de la Plana	Borda de la Plana		42.55	1.55	S	HUTS	AD		00				0		2097	Europe/Andorra	1993-12-23
+3039507	Barranc de la Plana	Barranc de la Plana		42.55	1.55	H	STM	AD		00				0		2097	Europe/Andorra	1993-12-23
+3039508	Serrat del Pla Morell	Serrat del Pla Morell		42.53333	1.53333	T	RDGE	AD		00				0		1521	Europe/Andorra	1993-12-23
+3039509	Torrent de Plamanera	Torrent de Plamanera		42.53333	1.56667	H	STM	AD		00				0		1418	Europe/Andorra	1993-12-23
+3039510	Bosc del Pla de Rodó	Bosc del Pla de Rodo		42.55	1.43333	V	FRST	AD		00				0		1949	Europe/Andorra	1993-12-23
+3039511	Bosc del Pla de Miretes	Bosc del Pla de Miretes		42.53333	1.48333	V	FRST	AD		00				0		1677	Europe/Andorra	1993-12-23
+3039512	Pic del Pla de l’Ingla	Pic del Pla de l'Ingla		42.48333	1.63333	T	PK	AD		00				0		2296	Europe/Andorra	1993-12-23
+3039513	Pla de l’Ingla	Pla de l'Ingla		42.48333	1.63333	A	ADMD	AD		00				0		2296	Europe/Andorra	1993-12-23
+3039514	Riu del Pla de l’Estany	Riu del Pla de l'Estany		42.58333	1.46667	H	STM	AD		00				0		1643	Europe/Andorra	1993-12-23
+3039515	Pleta del Pla de l’Estany	Pleta del Pla de l'Estany		42.58333	1.46667	L	GRAZ	AD		00				0		1643	Europe/Andorra	1993-12-23
+3039516	Pic des Bareytes	Pic des Bareytes	Pic del Pla de l'Estany,Pic del Pla de l’Estany,Pic des Bareytes	42.6	1.46667	T	PK	AD		00				0		2421	Europe/Andorra	2011-11-05
+3039517	Estret del Pla de l’Estany	Estret del Pla de l'Estany		42.6	1.45	T	PASS	AD		00				0		2174	Europe/Andorra	1993-12-23
+3039518	Pla de l’Estany	Pla de l'Estany		42.6	1.45	A	ADMD	AD		00				0		2174	Europe/Andorra	1993-12-23
+3039519	Camí del Pla de les Pedres	Cami del Pla de les Pedres		42.53333	1.68333	R	TRL	AD		00				0		2322	Europe/Andorra	1993-12-23
+3039520	Camí del Pla del Bosc	Cami del Pla del Bosc		42.53333	1.61667	R	TRL	AD		00				0		2237	Europe/Andorra	1993-12-23
+3039521	Bosc del Pla de la Cot	Bosc del Pla de la Cot		42.53333	1.46667	V	FRST	AD		00				0		1846	Europe/Andorra	1993-12-23
+3039522	Grau de les Places	Grau de les Places		42.55	1.46667	T	SLP	AD		00				0		1585	Europe/Andorra	1993-12-23
+3039523	Pleta de Pixolell	Pleta de Pixolell		42.58333	1.63333	L	GRAZ	AD		00				0		1722	Europe/Andorra	1993-12-23
+3039524	Costes de Pixolell	Costes de Pixolell		42.58333	1.63333	T	SLP	AD		00				0		1722	Europe/Andorra	1993-12-23
+3039525	Pont de la Pixistella	Pont de la Pixistella		42.56667	1.5	S	BDG	AD		00				0		1636	Europe/Andorra	1993-12-23
+3039526	Obac de la Pixistella	Obac de la Pixistella		42.55	1.5	T	SLP	AD		00				0		1292	Europe/Andorra	1993-12-23
+3039527	Canal de la Pixistella	Canal de la Pixistella		42.55	1.5	H	STM	AD		00				0		1292	Europe/Andorra	1993-12-23
+3039528	Bosc de la Pixistella	Bosc de la Pixistella		42.55	1.48333	V	FRST	AD		00				0		1548	Europe/Andorra	1993-12-23
+3039529	Font Pixadera	Font Pixadera		42.5	1.56667	H	SPNG	AD		00				0		1776	Europe/Andorra	1993-12-23
+3039530	Roca del Pisó	Roca del Piso		42.53333	1.63333	T	RK	AD		00				0		2360	Europe/Andorra	1993-12-23
+3039531	Borda del Pirot	Borda del Pirot		42.58333	1.65	S	HUT	AD		00				0		1767	Europe/Andorra	1993-12-23
+3039532	Pirineu	Pirineu		42.51667	1.55	L	LCTY	AD		00				0		1322	Europe/Andorra	1993-12-23
+3039533	Bosc de la Pinosa de Llumeneres	Bosc de la Pinosa de Llumeneres		42.46667	1.51667	V	FRST	AD		00				0		1985	Europe/Andorra	1993-12-23
+3039534	Bosc de la Pinosa	Bosc de la Pinosa		42.6	1.68333	V	FRST	AD		00				0		2089	Europe/Andorra	1993-12-23
+3039535	Bosc de la Pinosa	Bosc de la Pinosa		42.45	1.5	V	FRST	AD		00				0		1614	Europe/Andorra	1993-12-23
+3039536	Serrat Pinós	Serrat Pinos		42.55	1.68333	T	MT	AD		00				0		2254	Europe/Andorra	1993-12-23
+3039537	Collada de Pimes	Collada de Pimes		42.42951	1.54658	T	PASS	AD		00				0		2186	Europe/Andorra	2011-04-19
+3039538	Canal del Pi Gros	Canal del Pi Gros		42.5	1.46667	H	STM	AD		00				0		1678	Europe/Andorra	1993-12-23
+3039539	Borda del Piedro	Borda del Piedro		42.58333	1.63333	S	HUT	AD		00				0		1722	Europe/Andorra	1993-12-23
+3039540	Bosc del Pi de Montsalla	Bosc del Pi de Montsalla		42.53333	1.48333	V	FRST	AD		00				0		1677	Europe/Andorra	1993-12-23
+3039541	Camí del Pi de la Creu	Cami del Pi de la Creu		42.55	1.6	R	TRL	AD		00				0		2210	Europe/Andorra	1993-12-23
+3039542	Canal dels Picons	Canal dels Picons		42.56667	1.5	H	STM	AD		00				0		1636	Europe/Andorra	1993-12-23
+3039543	Bosc del Picó	Bosc del Pico		42.5	1.55	V	FRST	AD		00				0		1566	Europe/Andorra	1993-12-23
+3039544	Basers del Pic de la Cabaneta	Basers del Pic de la Cabaneta		42.61667	1.6	T	CLF	AD		00				0		2528	Europe/Andorra	1993-12-23
+3039545	Font Picadora	Font Picadora		42.56667	1.56667	H	SPNG	AD		00				0		2089	Europe/Andorra	1993-12-23
+3039546	Canal de la Pica	Canal de la Pica		42.5	1.5	H	STM	AD		00				0		1135	Europe/Andorra	1993-12-23
+3039547	Bony de la Pica	Bony de la Pica	Bony de la Pica,Pic d' Os,Pic d’ Ós,Pico de Ancla,Pico de Anclá	42.5	1.45	T	MT	AD		00				0		1840	Europe/Andorra	2011-11-05
+3039548	Basses del Pic	Basses del Pic		42.61667	1.61667	H	LKS	AD		00				0		2352	Europe/Andorra	1993-12-23
+3039549	Bosc del Peu dels Pessons	Bosc del Peu dels Pessons		42.51667	1.68333	V	FRST	AD		00				0		2352	Europe/Andorra	1993-12-23
+3039550	Font dels Pets	Font dels Pets		42.58333	1.48333	H	SPNG	AD		00				0		1809	Europe/Andorra	1993-12-23
+3039551	Coll Petit	Coll Petit	Coll Petit,Collado Pequeno,Collado Pequeño	42.56498	1.44248	T	PASS	AD		00				0		2408	Europe/Andorra	2011-11-05
+3039552	Riu dels Pessons	Riu dels Pessons		42.51667	1.7	H	STM	AD		00				0		2435	Europe/Andorra	1993-12-23
+3039553	Pic dels Pessons	Pic dels Pessons	Pic de Pessons,Pic dels Pessons,Pic des Pessons	42.50832	1.6585	T	PK	AD		00				0		2727	Europe/Andorra	2011-11-05
+3039554	Collada dels Pessons	Collada dels Pessons		42.5	1.65	T	PASS	AD		00				0		2542	Europe/Andorra	1993-12-23
+3039555	Pleta dels Pescadors	Pleta dels Pescadors		42.48333	1.65	L	GRAZ	AD		00				0		2658	Europe/Andorra	1993-12-23
+3039556	Pesada de Pal	Pesada de Pal		42.56667	1.48333	L	LCTY	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039557	Pont de Pesada	Pont de Pesada		42.56667	1.48333	S	BDG	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039558	Canal de Pesada	Canal de Pesada		42.56667	1.48333	H	STM	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039559	Basera de Pesada	Basera de Pesada		42.56667	1.48333	T	CLF	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039560	Pesada	Pesada		42.56667	1.48333	T	SLP	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039561	Roc de Persoma	Roc de Persoma		42.48333	1.48333	T	RK	AD		00				0		981	Europe/Andorra	1993-12-23
+3039562	Plana Perdiguera	Plana Perdiguera		42.55	1.45	T	UPLD	AD		00				0		1788	Europe/Andorra	1993-12-23
+3039563	Pic de Percanela	Pic de Percanela		42.58915	1.49666	T	PK	AD		00				0		2089	Europe/Andorra	2011-04-19
+3039564	Bordes de Percanela	Bordes de Percanela		42.5825	1.48809	S	FRM	AD		00				0		2019	Europe/Andorra	2011-04-19
+3039565	Percanela	Percanela	Coma de Percanela,Loma de Percanela,Percanela	42.58333	1.48333	A	ADMD	AD	AD	00				0		1809	Europe/Andorra	2011-11-05
+3039566	Camí de Per Baix	Cami de Per Baix		42.55	1.68333	R	TRL	AD		00				0		2254	Europe/Andorra	1993-12-23
+3039567	Riu de Perafita	Riu de Perafita		42.49733	1.55811	H	STM	AD		00				0		1566	Europe/Andorra	2011-04-19
+3039568	Pleta de Perafita	Pleta de Perafita		42.48333	1.58333	L	GRAZ	AD		00				0		2349	Europe/Andorra	1993-12-23
+3039569	Planells de Perafita	Planells de Perafita		42.48333	1.58333	T	UPLD	AD		00				0		2349	Europe/Andorra	1993-12-23
+3039570	Estanys de Perafita	Estanys de Perafita		42.47027	1.58958	H	LKS	AD		00				0		2518	Europe/Andorra	2011-04-19
+3039571	Costa de Perafita	Costa de Perafita		42.48333	1.58333	T	SLP	AD		00				0		2349	Europe/Andorra	1993-12-23
+3039572	Camí de Perafita	Cami de Perafita		42.5	1.56667	R	TRL	AD		00				0		1776	Europe/Andorra	1993-12-23
+3039573	Cabana de Perafita	Cabana de Perafita		42.48333	1.58333	S	HUT	AD		00				0		2349	Europe/Andorra	1993-12-23
+3039574	Perafita	Perafita		42.46667	1.58333	A	ADMD	AD		00				0		2367	Europe/Andorra	1993-12-23
+3039575	Canal de la Pera	Canal de la Pera		42.56667	1.48333	H	RVN	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039576	Roc de la Penya	Roc de la Penya		42.53333	1.55	T	RK	AD		00				0		1344	Europe/Andorra	1993-12-23
+3039577	Costa Pentinada	Costa Pentinada		42.48333	1.53333	T	SLP	AD		00				0		2255	Europe/Andorra	1993-12-23
+3039578	Riu de la Peguera	Riu de la Peguera		42.45	1.51667	H	STM	AD		00				0		1790	Europe/Andorra	1993-12-23
+3039579	Conreu de la Peguera	Conreu de la Peguera		42.45	1.53333	V	CULT	AD		00				0		1859	Europe/Andorra	1993-12-23
+3039580	Carretera de la Peguera	Carretera de la Peguera		42.46667	1.55	R	RD	AD		00				0		2341	Europe/Andorra	1993-12-23
+3039581	Bosc de la Peguera	Bosc de la Peguera		42.46321	1.52655	V	FRST	AD		00				0		2068	Europe/Andorra	2011-04-19
+3039582	Bordes de la Peguera	Bordes de la Peguera		42.45	1.53333	S	HUTS	AD		00				0		1859	Europe/Andorra	1993-12-23
+3039583	Serrat de les Pedrusques	Serrat de les Pedrusques		42.6	1.5	T	SPUR	AD		00				0		1923	Europe/Andorra	1993-12-23
+3039584	Torrent Pedrós	Torrent Pedros		42.45	1.48333	H	STM	AD		00				0		1111	Europe/Andorra	1993-12-23
+3039585	Bosc de Pedres Blanques	Bosc de Pedres Blanques		42.53333	1.48333	V	FRST	AD		00				0		1677	Europe/Andorra	1993-12-23
+3039586	Pla de les Pedres	Pla de les Pedres		42.55	1.66667	T	UPLD	AD		00				0		2224	Europe/Andorra	1993-12-23
+3039587	Pont Pedregat	Pont Pedregat		42.58333	1.48333	S	BDG	AD		00				0		1809	Europe/Andorra	1993-12-23
+3039588	Canal de Pedra Plana	Canal de Pedra Plana		42.51667	1.5	H	STM	AD		00				0		1688	Europe/Andorra	1993-12-23
+3039589	Corral de Pedra	Corral de Pedra		42.56667	1.7	L	GRAZ	AD		00				0		2375	Europe/Andorra	1993-12-23
+3039590	Corral de Pedra	Corral de Pedra		42.56667	1.65	L	GRAZ	AD		00				0		1988	Europe/Andorra	1993-12-23
+3039591	Canal de la Peca Rodona	Canal de la Peca Rodona		42.53333	1.48333	H	STM	AD		00				0		1677	Europe/Andorra	1993-12-23
+3039592	Bosc de Paulelles	Bosc de Paulelles		42.53333	1.53333	V	FRST	AD		00				0		1521	Europe/Andorra	1993-12-23
+3039593	Borda de Paulelles	Borda de Paulelles		42.53333	1.55	S	HUT	AD		00				0		1344	Europe/Andorra	1993-12-23
+3039594	Passos dels Estanys	Passos dels Estanys		42.6	1.6	L	LCTY	AD		00				0		2143	Europe/Andorra	1993-12-23
+3039595	Passos de la Tarterosa	Passos de la Tarterosa		42.6	1.65	L	LCTY	AD		00				0		2131	Europe/Andorra	1993-12-23
+3039596	Canal del Passatorrents	Canal del Passatorrents		42.43333	1.48333	H	STM	AD		00				0		1228	Europe/Andorra	1993-12-23
+3039597	Coll Passader	Coll Passader		42.55	1.5	T	PK	AD		00				0		1292	Europe/Andorra	1993-12-23
+3039598	Riu del Pas Mal	Riu del Pas Mal		42.53333	1.65	H	STM	AD		00				0		2508	Europe/Andorra	1993-12-23
+3039599	Costa del Pas del Monjo	Costa del Pas del Monjo		42.63333	1.56667	T	SLP	AD		00				0		2394	Europe/Andorra	1993-12-23
+3039600	Pas del Monjo	Pas del Monjo		42.63333	1.55	L	LCTY	AD		00				0		2053	Europe/Andorra	1993-12-23
+3039601	Bosc del Pas de la Clau	Bosc del Pas de la Clau		42.51667	1.61667	V	FRST	AD		00				0		2254	Europe/Andorra	1993-12-23
+3039602	Solana del Pas de la Casa	Solana del Pas de la Casa		42.53333	1.73333	T	SLP	AD		00				0		2300	Europe/Andorra	1993-12-23
+3039603	Riu del Pas de la Casa	Riu del Pas de la Casa	Riu del Pas de la Casa	42.56667	1.75	H	STMX	AD		00				0		1923	Europe/Andorra	2011-11-05
+3039604	Pas de la Casa	Pas de la Casa	Pas de la Kasa,ÐŸÐ°Ñ Ð´Ðµ ла КаÑа	42.54277	1.73361	P	PPL	AD		03				2363	2050	2230	Europe/Andorra	2008-06-09
+3039605	Pas de la Casa	Pas de la Casa		42.53333	1.71667	A	ADMD	AD		00				0		2400	Europe/Andorra	1993-12-23
+3039606	Partida d’Ensucaranes	Partida d'Ensucaranes		42.51667	1.55	L	LCTY	AD		00				0		1322	Europe/Andorra	1993-12-23
+3039607	Partida de l’Any de la Part	Partida de l'Any de la Part		42.55	1.53333	A	ADMD	AD		00				0		1593	Europe/Andorra	1993-12-23
+3039608	Partida de la Grella	Partida de la Grella		42.51667	1.51667	A	ADMD	AD		00				0		1265	Europe/Andorra	1993-12-23
+3039609	Borda de les Pardines	Borda de les Pardines		42.53333	1.6	S	FRM	AD		00				0		1888	Europe/Andorra	1993-12-23
+3039610	Tossal de la Pardina	Tossal de la Pardina		42.5	1.48333	T	SPUR	AD		00				0		1316	Europe/Andorra	1993-12-23
+3039611	Bosc del Pardal	Bosc del Pardal		42.55	1.5	V	FRST	AD		00				0		1292	Europe/Andorra	1993-12-23
+3039612	Riu de la Palomera	Riu de la Palomera		42.56667	1.78333	H	STM	AD		00				0		1680	Europe/Andorra	1993-12-23
+3039613	Cap de la Palomera	Cap de la Palomera	Cap de la Palomera	42.58333	1.78333	T	PK	AD		00				0		1694	Europe/Andorra	2011-11-05
+3039614	Pont de Palomer	Pont de Palomer		42.56667	1.48333	S	BDG	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039615	Pic de Palomer	Pic de Palomer		42.56667	1.48333	T	PK	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039616	Obaga de Palomer	Obaga de Palomer		42.56667	1.48333	T	SLP	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039617	Canal de Palomer	Canal de Palomer		42.56667	1.48333	H	STM	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039618	Callissa de Palomer	Callissa de Palomer		42.56667	1.48333	T	GRGE	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039619	Palomer	Palomer		42.56667	1.48333	A	ADMD	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039620	Feixa Pallola	Feixa Pallola		42.43333	1.46667	V	CULT	AD		00				0		1113	Europe/Andorra	1993-12-23
+3039621	Serrat Pallero	Serrat Pallero		42.45	1.45	T	SPUR	AD		00				0		1482	Europe/Andorra	1993-12-23
+3039622	Prat de Paleta	Prat de Paleta		42.48333	1.6	L	GRAZ	AD		00				0		2250	Europe/Andorra	1993-12-23
+3039623	Serrat de la Palanqueta	Serrat de la Palanqueta		42.55	1.6	T	RDGE	AD		00				0		2210	Europe/Andorra	1993-12-23
+3039624	Riu de la Palanqueta	Riu de la Palanqueta		42.55	1.6	H	STM	AD		00				0		2210	Europe/Andorra	1993-12-23
+3039625	Pont de les Palanques	Pont de les Palanques		42.55	1.51667	S	BDG	AD		00				0		1397	Europe/Andorra	1993-12-23
+3039626	Planell de la Palanca	Planell de la Palanca		42.55	1.68333	T	UPLD	AD		00				0		2254	Europe/Andorra	1993-12-23
+3039627	Pic de la Pala de Coll Carnisser	Pic de la Pala de Coll Carnisser		42.6	1.48333	T	PK	AD		00				0		2441	Europe/Andorra	1993-12-23
+3039628	Pic de la Pala Alta	Pic de la Pala Alta		42.6	1.63333	T	PK	AD		00				0		1893	Europe/Andorra	1993-12-23
+3039629	Solà de Pal	Sola de Pal		42.55	1.46667	T	SLP	AD		00				0		1585	Europe/Andorra	1993-12-23
+3039630	Riu de Pal	Riu de Pal		42.56159	1.49544	H	STM	AD		00				0		1430	Europe/Andorra	2011-04-19
+3039631	Pont de Pal	Pont de Pal		42.55	1.46667	S	BDG	AD		00				0		1585	Europe/Andorra	1993-12-23
+3039632	Carretera de Pal	Carretera de Pal		42.55	1.48333	R	RD	AD		00				0		1548	Europe/Andorra	1993-12-23
+3039633	Bosc de Pal	Bosc de Pal		42.53333	1.46667	V	FRST	AD		00				0		1846	Europe/Andorra	1993-12-23
+3039634	Pal	Pal	Pal	42.55	1.48333	P	PPL	AD		04				0		1548	Europe/Andorra	2011-11-05
+3039635	Serra de Padern	Serra de Padern		42.53333	1.55	T	RDGE	AD		00				0		1344	Europe/Andorra	1993-12-23
+3039636	Riu de Padern	Riu de Padern		42.52834	1.52213	H	STM	AD		00				0		1361	Europe/Andorra	2011-04-19
+3039637	Pic de Padern	Pic de Padern		42.52429	1.54697	T	PK	AD		00				0		1290	Europe/Andorra	2011-04-19
+3039638	Bosc de Padern	Bosc de Padern		42.53333	1.53333	V	FRST	AD		00				0		1521	Europe/Andorra	1993-12-23
+3039639	Coll Pa	Coll Pa		42.55	1.43333	T	PK	AD		00				0		1949	Europe/Andorra	1993-12-23
+3039640	Coll Pa	Coll Pa		42.48333	1.56667	T	PASS	AD		00				0		2231	Europe/Andorra	1993-12-23
+3039641	Canal de l’ Ovella Morta	Canal de l' Ovella Morta		42.48333	1.58333	H	STM	AD		00				0		2349	Europe/Andorra	1993-12-23
+3039642	Riu de l’ Ovella	Riu de l' Ovella		42.53333	1.6	H	STM	AD		00				0		1888	Europe/Andorra	1993-12-23
+3039643	Port de l’ Ovella	Port de l' Ovella	Port de L'Ovella,Port de L’Ovella,Port de l' Ovella,Port de l’ Ovella	42.55	1.43333	T	PASS	AD		00				0		1949	Europe/Andorra	2011-11-05
+3039644	Bosc de l’ Ovella	Bosc de l' Ovella		42.53333	1.6	V	FRST	AD		00				0		1888	Europe/Andorra	1993-12-23
+3039645	Font de l’ Óssa	Font de l' Ossa		42.51667	1.48333	H	SPNG	AD		00				0		1839	Europe/Andorra	1993-12-23
+3039646	Cova de l’ Óssa	Cova de l' Ossa		42.46667	1.48333	S	CAVE	AD		00				0		1134	Europe/Andorra	1993-12-23
+3039647	Cova de l’ Óssa	Cova de l' Ossa		42.45	1.48333	S	CAVE	AD		00				0		1111	Europe/Andorra	1993-12-23
+3039648	Canya de l’ Óssa	Canya de l' Ossa		42.56667	1.51667	S	CAVE	AD		00				0		1500	Europe/Andorra	1993-12-23
+3039649	Canal de l’ Óssa	Canal de l' Ossa		42.5	1.63333	H	STM	AD		00				0		2545	Europe/Andorra	1993-12-23
+3039650	Canal de l’ Óssa	Canal de l' Ossa		42.58333	1.46667	H	RVN	AD		00				0		1643	Europe/Andorra	1993-12-23
+3039651	Carretera d’ Ós de Civís	Carretera d' Os de Civis		42.48333	1.46667	R	RD	AD		00				0		1148	Europe/Andorra	1993-12-23
+3039652	Canal de l’ Osca de Migdia	Canal de l' Osca de Migdia		42.46667	1.46667	H	STM	AD		00				0		1340	Europe/Andorra	1993-12-23
+3039653	Clots de l’ Ós	Clots de l' Os		42.58333	1.68333	H	RVN	AD		00				0		2294	Europe/Andorra	1993-12-23
+3039654	Clot de l’ Ós	Clot de l' Os		42.58333	1.66667	H	RVN	AD		00				0		2159	Europe/Andorra	1993-12-23
+3039655	Tosa d’ Ortafà	Tosa d' Ortafa		42.56667	1.71667	T	UPLD	AD		00				0		2219	Europe/Andorra	1993-12-23
+3039656	Tarteres d’ Ortafà	Tarteres d' Ortafa		42.56667	1.71667	T	TAL	AD		00				0		2219	Europe/Andorra	1993-12-23
+3039657	Font d’ Ortafà	Font d' Ortafa		42.56667	1.71667	H	SPNG	AD		00				0		2219	Europe/Andorra	1993-12-23
+3039658	Collet d’ Ortafà	Collet d' Ortafa		42.56667	1.7	T	PASS	AD		00				0		2375	Europe/Andorra	1993-12-23
+3039659	Clots d’ Ortafà	Clots d' Ortafa		42.55	1.71667	H	RVN	AD		00				0		2192	Europe/Andorra	1993-12-23
+3039660	Canals d’ Ortafà	Canals d' Ortafa		42.56667	1.7	H	RVN	AD		00				0		2375	Europe/Andorra	1993-12-23
+3039661	Ortafà	Ortafa		42.56667	1.71667	A	ADMD	AD		00				0		2219	Europe/Andorra	1993-12-23
+3039662	Bony de l’ Orri Vell	Bony de l' Orri Vell		42.53333	1.63333	T	SPUR	AD		00				0		2360	Europe/Andorra	1993-12-23
+3039663	Riu dels Orris	Riu dels Orris		42.48333	1.63333	H	STM	AD		00				0		2296	Europe/Andorra	1993-12-23
+3039664	Prats dels Orris	Prats dels Orris		42.53333	1.63333	L	GRAZ	AD		00				0		2360	Europe/Andorra	1993-12-23
+3039665	Pleta dels Orris	Pleta dels Orris		42.53333	1.63333	L	GRAZ	AD		00				0		2360	Europe/Andorra	1993-12-23
+3039666	Barraca de l’ Orri de Rusca	Barraca de l' Orri de Rusca		42.55	1.68333	S	HUT	AD		00				0		2254	Europe/Andorra	1993-12-23
+3039667	Bosc de l’ Orri del Call	Bosc de l' Orri del Call		42.6	1.65	V	FRST	AD		00				0		2131	Europe/Andorra	1993-12-23
+3039668	Serrat de l’ Orri de Gastó	Serrat de l' Orri de Gasto		42.48333	1.46667	T	SPUR	AD		00				0		1148	Europe/Andorra	1993-12-23
+3039669	Serrat de l’ Orri	Serrat de l' Orri		42.58333	1.66667	T	SLP	AD		00				0		2159	Europe/Andorra	1993-12-23
+3039670	Pont de l’ Orri	Pont de l' Orri		42.58333	1.66667	S	BDG	AD		00				0		2159	Europe/Andorra	1993-12-23
+3039671	Obaga de l’ Orri	Obaga de l' Orri		42.46667	1.45	T	SLP	AD		00				0		1562	Europe/Andorra	1993-12-23
+3039672	Borda de l’ Orri	Borda de l' Orri		42.55	1.43333	S	HUT	AD		00				0		1949	Europe/Andorra	1993-12-23
+3039673	Solana dels Oriols	Solana dels Oriols		42.56667	1.48333	T	SLP	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039674	Cap dels Oriols	Cap dels Oriols		42.56667	1.46667	T	PK	AD		00				0		1673	Europe/Andorra	1993-12-23
+3039675	Pont d'Ordino	Pont d'Ordino		42.5492	1.52373	S	BDG	AD		07				0		1397	Europe/Andorra	2007-04-04
+3039676	Parròquia d'Ordino	Parroquia d'Ordino	Ordino,Parroquia d'Ordino,Parròquia d'Ordino	42.59758	1.52573	A	ADM1	AD		05				3467		1695	Europe/Andorra	2008-03-17
+3039677	Coll d'Ordino	Coll d'Ordino	Coll d'Ordino,Port d'Ordino	42.55615	1.57147	T	PASS	AD	AD	07				0	1883	1879	Europe/Andorra	2007-04-04
+3039678	Ordino	Ordino	Ordino,ao er di nuo,orudino jiao qu,Ордино,オルディノ教区,奥尔迪诺	42.55623	1.53319	P	PPLA	AD		05				3066		1340	Europe/Andorra	2009-12-11
+3039679	Font de les Ordigues	Font de les Ordigues		42.51667	1.56667	H	SPNG	AD		00				0		1759	Europe/Andorra	1993-12-23
+3039680	Font de l’ Ordigal	Font de l' Ordigal		42.46667	1.46667	H	SPNG	AD		00				0		1340	Europe/Andorra	1993-12-23
+3039681	Roc de l’ Oral	Roc de l' Oral		42.53333	1.56667	T	CLF	AD		00				0		1418	Europe/Andorra	1993-12-23
+3039682	Planells d’ Olio	Planells d' Olio		42.53333	1.6	T	UPLD	AD		00				0		1888	Europe/Andorra	1993-12-23
+3039683	Obagueta de Cantí	Obagueta de Canti		42.56667	1.51667	L	LCTY	AD		00				0		1500	Europe/Andorra	1993-12-23
+3039684	Riu de les Obagues	Riu de les Obagues		42.58333	1.63333	H	STM	AD		00				0		1722	Europe/Andorra	1993-12-23
+3039685	Canal de les Obagues	Canal de les Obagues		42.58333	1.48333	H	STM	AD		00				0		1809	Europe/Andorra	1993-12-23
+3039686	Bosc de les Obagues	Bosc de les Obagues		42.58333	1.63333	V	FRST	AD		00				0		1722	Europe/Andorra	1993-12-23
+3039687	Bosc de l’ Obaga Sobirana	Bosc de l' Obaga Sobirana		42.55	1.48333	V	FRST	AD		00				0		1548	Europe/Andorra	1993-12-23
+3039688	Canal de l’ Obaga Fosca	Canal de l' Obaga Fosca		42.48333	1.43333	H	STM	AD		00				0		1938	Europe/Andorra	1993-12-23
+3039689	Clots de l’ Obaga de Torradella	Clots de l' Obaga de Torradella		42.6	1.63333	H	RVN	AD		00				0		1893	Europe/Andorra	1993-12-23
+3039690	Serra de l’ Obaga d’Enclar	Serra de l' Obaga d'Enclar		42.51246	1.477	T	MT	AD		00				0		2069	Europe/Andorra	2011-04-19
+3039691	Obaga d’Enclar	Obaga d'Enclar		42.5	1.46667	A	ADMD	AD		00				0		1678	Europe/Andorra	1993-12-23
+3039692	Canal de l’ Obaga de l’Óssa	Canal de l' Obaga de l'Ossa		42.55	1.48333	H	STM	AD		00				0		1548	Europe/Andorra	1993-12-23
+3039693	Bosc de l’ Obaga de l’Óssa	Bosc de l' Obaga de l'Ossa		42.55	1.48333	V	FRST	AD		00				0		1548	Europe/Andorra	1993-12-23
+3039694	Obaga de l’Estall Serrer	Obaga de l'Estall Serrer		42.48333	1.6	A	ADMD	AD		00				0		2250	Europe/Andorra	1993-12-23
+3039695	Obaga de la Gonarda	Obaga de la Gonarda		42.55	1.53333	A	ADMD	AD		00				0		1593	Europe/Andorra	1993-12-23
+3039696	Obaga de Juclar	Obaga de Juclar		42.61667	1.7	L	LCTY	AD		00				0		2285	Europe/Andorra	1993-12-23
+3039697	Obaga de Juberri	Obaga de Juberri		42.43333	1.48333	A	ADMD	AD		00				0		1228	Europe/Andorra	1993-12-23
+3039698	Bosc de l’ Obaga de Gali	Bosc de l' Obaga de Gali		42.55	1.48333	V	FRST	AD		00				0		1548	Europe/Andorra	1993-12-23
+3039699	Obaga de Fontverd	Obaga de Fontverd		42.48333	1.58333	A	ADMD	AD		00				0		2349	Europe/Andorra	1993-12-23
+3039700	Obaga d’Ansalonga	Obaga d'Ansalonga		42.56667	1.51667	A	ADMD	AD		00				0		1500	Europe/Andorra	1993-12-23
+3039701	Obaga d’Andorra	Obaga d'Andorra		42.48333	1.51667	A	ADMD	AD		00				0		2061	Europe/Andorra	1993-12-23
+3039702	Camí de l’ Obaga	Cami de l' Obaga		42.55	1.55	R	TRL	AD		00				0		2097	Europe/Andorra	1993-12-23
+3039703	Serrat dels Obacs	Serrat dels Obacs		42.6	1.5	T	SLP	AD		00				0		1923	Europe/Andorra	1993-12-23
+3039704	Fonts dels Obacs	Fonts dels Obacs		42.6	1.5	H	SPNG	AD		00				0		1923	Europe/Andorra	1993-12-23
+3039705	Obac d’Incles	Obac d'Incles		42.58333	1.68333	A	ADMD	AD		00				0		2294	Europe/Andorra	1993-12-23
+3039706	Obac de Soldeu	Obac de Soldeu		42.56667	1.66667	A	ADMD	AD		00				0		1938	Europe/Andorra	1993-12-23
+3039707	Obac de Sispony	Obac de Sispony		42.51667	1.48333	A	ADMD	AD		00				0		1839	Europe/Andorra	1993-12-23
+3039708	Bosc de l’ Obac de Salla	Bosc de l' Obac de Salla		42.55	1.5	V	FRST	AD		00				0		1292	Europe/Andorra	1993-12-23
+3039709	Obac d’Envalira	Obac d'Envalira		42.55	1.68333	A	ADMD	AD		00				0		2254	Europe/Andorra	1993-12-23
+3039710	Obac d’Encamp	Obac d'Encamp		42.51667	1.6	A	ADMD	AD		00				0		2085	Europe/Andorra	1993-12-23
+3039711	Obac del Tarter	Obac del Tarter		42.56667	1.63333	A	ADMD	AD		00				0		2016	Europe/Andorra	1993-12-23
+3039712	Obac dels Cortals	Obac dels Cortals		42.51667	1.61667	A	ADMD	AD		00				0		2254	Europe/Andorra	1993-12-23
+3039713	Obac de les Escaldes i Engordany	Obac de les Escaldes i Engordany		42.5	1.53333	L	LCTY	AD		00				0		1574	Europe/Andorra	2007-04-05
+3039714	Obac de la Cebollera	Obac de la Cebollera		42.61667	1.58333	L	LCTY	AD		00				0		2374	Europe/Andorra	1993-12-23
+3039715	Pont de l’ Obac de Fontaneda	Pont de l' Obac de Fontaneda		42.45	1.46667	S	BDG	AD		00				0		935	Europe/Andorra	1993-12-23
+3039716	Obac de Canillo	Obac de Canillo		42.56667	1.63333	A	ADMD	AD		00				0		2016	Europe/Andorra	1993-12-23
+3039717	Obac d’Anyós	Obac d'Anyos		42.53333	1.53333	A	ADMD	AD		00				0		1521	Europe/Andorra	1993-12-23
+3039718	Serrat de l’ Obac	Serrat de l' Obac		42.48333	1.5	T	MT	AD		00				0		1631	Europe/Andorra	1993-12-23
+3039719	Rec de l’ Obac	Rec de l' Obac		42.5	1.53333	H	CNL	AD		00				0		1574	Europe/Andorra	1993-12-23
+3039720	Prats de l’ Obac	Prats de l' Obac		42.53333	1.61667	L	GRAZ	AD		00				0		2237	Europe/Andorra	1993-12-23
+3039721	Font de l’ Obac	Font de l' Obac		42.5	1.56667	H	SPNG	AD		00				0		1776	Europe/Andorra	1993-12-23
+3039722	Coll d’ Obac	Coll d' Obac		42.5	1.46667	T	PK	AD		00				0		1678	Europe/Andorra	1993-12-23
+3039723	Torrent dels Nyerros	Torrent dels Nyerros		42.48333	1.46667	H	STM	AD		00				0		1148	Europe/Andorra	1993-12-23
+3039724	Carretera General Número Un	Carretera General Numero Un		42.5	1.53333	R	RD	AD		00				0		1574	Europe/Andorra	1993-12-23
+3039725	Carretera General Número Tres	Carretera General Numero Tres		42.61667	1.48333	R	RD	AD		00				0		2470	Europe/Andorra	1993-12-23
+3039726	Carretera General Número Duo	Carretera General Numero Duo		42.53333	1.73333	R	RD	AD		00				0		2300	Europe/Andorra	1993-12-23
+3039727	Pleta Nova	Pleta Nova		42.61667	1.51667	L	GRAZ	AD		00				0		1716	Europe/Andorra	1993-12-23
+3039728	Obaga de Nou Fonts	Obaga de Nou Fonts		42.46667	1.53333	T	SLP	AD		00				0		2332	Europe/Andorra	1993-12-23
+3039729	Nou Fonts	Nou Fonts		42.46667	1.53333	T	RDGE	AD		00				0		2332	Europe/Andorra	1993-12-23
+3039730	Estany de la Nou	Estany de la Nou		42.47539	1.5756	H	LK	AD		00				0		2349	Europe/Andorra	2011-04-19
+3039731	Pic d’ Ascobes	Pic d' Ascobes	Pic d' Ascobes,Pic de Noe,Pic de Noé,Pic d’ Ascobes	42.61667	1.73333	T	PK	AD		00				0		2508	Europe/Andorra	2011-11-05
+3039732	Basera del Niu de l’Aliga	Basera del Niu de l'Aliga		42.5	1.45	T	CLF	AD		00				0		1840	Europe/Andorra	1993-12-23
+3039733	Collades de Nier	Collades de Nier		42.61667	1.53333	T	PASS	AD		00				0		1609	Europe/Andorra	1993-12-23
+3039734	Bosc de les Neres	Bosc de les Neres		42.53333	1.56667	V	FRST	AD		00				0		1418	Europe/Andorra	1993-12-23
+3039735	Bony de les Neres	Bony de les Neres	Bony de las Neras,Bony de les Neres,Bony de los Neros,Pic de las Neras	42.54761	1.56693	T	MT	AD		00				0		1828	Europe/Andorra	2011-11-05
+3039736	Roques Negres	Roques Negres		42.5	1.46667	T	RKS	AD		00				0		1678	Europe/Andorra	1993-12-23
+3039737	Rocs Negres	Rocs Negres		42.55	1.61667	T	RKS	AD		00				0		2206	Europe/Andorra	1993-12-23
+3039738	Canals Negres	Canals Negres		42.56667	1.68333	H	RVN	AD		00				0		2340	Europe/Andorra	1993-12-23
+3039739	Serrat Negre	Serrat Negre		42.58333	1.48333	T	SPUR	AD		00				0		1809	Europe/Andorra	1993-12-23
+3039740	Riu Negre	Riu Negre		42.45	1.48333	H	STM	AD		00				0		1111	Europe/Andorra	1993-12-23
+3039741	Port de Comallempla	Port de Comallempla	Port Negre,Port de Comallempla	42.56667	1.45	T	PASS	AD		00				0		2137	Europe/Andorra	2011-11-05
+3039742	Pic Negre	Pic Negre	Pic Negre	42.56667	1.45	T	PK	AD		00				0		2137	Europe/Andorra	2011-11-05
+3039743	Pic Negre	Pic Negre	Pic Negre	42.45	1.56667	T	PK	AD		00				0		2558	Europe/Andorra	2011-11-05
+3039744	Forat Negre	Forat Negre		42.5	1.5	H	RVN	AD		00				0		1135	Europe/Andorra	1993-12-23
+3039745	Estany Negre	Estany Negre		42.58333	1.43333	H	LK	AD		00				0		2412	Europe/Andorra	1993-12-23
+3039746	Bosc Negre	Bosc Negre		42.56667	1.55	V	FRST	AD		00				0		1996	Europe/Andorra	1993-12-23
+3039747	Bosc Negre	Bosc Negre		42.53333	1.6	V	FRST	AD		00				0		1888	Europe/Andorra	1993-12-23
+3039748	Bosc Negre	Bosc Negre		42.51667	1.48333	V	FRST	AD		00				0		1839	Europe/Andorra	1993-12-23
+3039749	Bosc Negre	Bosc Negre		42.5	1.56667	V	FRST	AD		00				0		1776	Europe/Andorra	1993-12-23
+3039750	Bosc Negre	Bosc Negre		42.48333	1.51667	V	FRST	AD		00				0		2061	Europe/Andorra	1993-12-23
+3039751	Bony Negre	Bony Negre		42.56667	1.46667	T	SPUR	AD		00				0		1673	Europe/Andorra	1993-12-23
+3039752	Roca Negra	Roca Negra		42.58333	1.58333	T	RK	AD		00				0		1993	Europe/Andorra	1993-12-23
+3039753	Torrent del Nedó	Torrent del Nedo		42.46667	1.5	H	STM	AD		00				0		1383	Europe/Andorra	1993-12-23
+3039754	Font de la Navina	Font de la Navina		42.55	1.56667	H	SPNG	AD		00				0		1828	Europe/Andorra	1993-12-23
+3039755	Llosers de Naudí	Llosers de Naudi		42.56667	1.56667	S	MNQR	AD		00				0		2089	Europe/Andorra	1993-12-23
+3039756	Carretera de Nagol	Carretera de Nagol		42.46667	1.48333	R	RD	AD		00				0		1134	Europe/Andorra	1993-12-23
+3039757	Nagol	Nagol	Nagol	42.47146	1.50314	P	PPL	AD		06				0		1383	Europe/Andorra	2011-11-05
+3039758	Solà de Nadal	Sola de Nadal		42.51667	1.51667	T	SLP	AD		00				0		1265	Europe/Andorra	1993-12-23
+3039759	Obaga de les Mussoles	Obaga de les Mussoles		42.55	1.63333	T	SLP	AD		00				0		2336	Europe/Andorra	1993-12-23
+3039760	Pla Mussola	Pla Mussola		42.53333	1.56667	T	UPLD	AD		00				0		1418	Europe/Andorra	1993-12-23
+3039761	Camí de la Muntanya	Cami de la Muntanya		42.5	1.56667	R	TRL	AD		00				0		1776	Europe/Andorra	1993-12-23
+3039762	Pleta de les Mules	Pleta de les Mules		42.43333	1.53333	L	GRAZ	AD		00				0		2108	Europe/Andorra	1993-12-23
+3039763	Canal del Mulassar	Canal del Mulassar		42.56667	1.51667	H	STM	AD		00				0		1500	Europe/Andorra	1993-12-23
+3039764	Bosc del Mulassar	Bosc del Mulassar		42.56667	1.53333	V	FRST	AD		00				0		1669	Europe/Andorra	1993-12-23
+3039765	Basers del Motxo	Basers del Motxo		42.6	1.63333	T	CLF	AD		00				0		1893	Europe/Andorra	1993-12-23
+3039766	Riu de Mossers	Riu de Mossers		42.45	1.46667	H	STM	AD		00				0		935	Europe/Andorra	1993-12-23
+3039767	Pleta Mosquera	Pleta Mosquera		42.63333	1.51667	L	GRAZ	AD		00				0		1894	Europe/Andorra	1993-12-23
+3039768	Mosquera	Mosquera		42.55	1.58333	P	PPL	AD		03				0		1499	Europe/Andorra	1993-12-23
+3039769	Tosa de Moscatosa	Tosa de Moscatosa		42.55	1.71667	T	UPLD	AD		00				0		2192	Europe/Andorra	1993-12-23
+3039770	Clots de Moscatosa	Clots de Moscatosa		42.55	1.7	H	RVN	AD		00				0		2358	Europe/Andorra	1993-12-23
+3039771	Pont de Mos	Pont de Mos		42.58333	1.63333	S	BDG	AD		00				0		1722	Europe/Andorra	1993-12-23
+3039772	Plana Mortalla	Plana Mortalla		42.58333	1.55	T	UPLD	AD		00				0		2357	Europe/Andorra	1993-12-23
+3039773	Estany Mort	Estany Mort		42.63333	1.61667	H	LK	AD		00				0		2541	Europe/Andorra	1993-12-23
+3039774	Estany Mort	Estany Mort		42.58333	1.71667	H	LK	AD		00				0		2553	Europe/Andorra	1993-12-23
+3039775	Canya dels Moros	Canya dels Moros		42.55	1.51667	S	CAVE	AD		00				0		1397	Europe/Andorra	1993-12-23
+3039776	Pleta de Moretó	Pleta de Moreto		42.55	1.68333	L	GRAZ	AD		00				0		2254	Europe/Andorra	1993-12-23
+3039777	Planell de Moretó	Planell de Moreto		42.55	1.68333	T	UPLD	AD		00				0		2254	Europe/Andorra	1993-12-23
+3039778	Bosc de Moretó	Bosc de Moreto		42.55	1.7	V	FRST	AD		00				0		2358	Europe/Andorra	1993-12-23
+3039779	Estany Moreno	Estany Moreno		42.51667	1.63333	H	LK	AD		00				0		2379	Europe/Andorra	1993-12-23
+3039780	Canal de Mora	Canal de Mora		42.56667	1.58333	H	STM	AD		00				0		1919	Europe/Andorra	1993-12-23
+3039781	Tosal de la Truita	Tosal de la Truita	Pic de Monturull,Pic de Perafita,Tosal de la Truita	42.46667	1.58333	T	PK	AD		00				0		2367	Europe/Andorra	2011-11-05
+3039782	Riu de Montuell	Riu de Montuell		42.51667	1.58333	H	STM	AD		00				0		1994	Europe/Andorra	1993-12-23
+3039783	Cap de Montuell	Cap de Montuell		42.51667	1.61667	T	PK	AD		00				0		2254	Europe/Andorra	1993-12-23
+3039784	Roc de Montmantell	Roc de Montmantell		42.59695	1.47085	T	RDGE	AD		00				0		2421	Europe/Andorra	2011-04-19
+3039785	Riu de Montmantell	Riu de Montmantell		42.6	1.46667	H	STM	AD		00				0		2421	Europe/Andorra	1993-12-23
+3039786	Pleta de Montmantell	Pleta de Montmantell		42.6	1.46667	L	GRAZ	AD		00				0		2421	Europe/Andorra	1993-12-23
+3039787	Obaga de Montmantell	Obaga de Montmantell		42.6	1.46667	T	SLP	AD		00				0		2421	Europe/Andorra	1993-12-23
+3039788	Font de Montmantell	Font de Montmantell		42.6	1.46667	H	SPNG	AD		00				0		2421	Europe/Andorra	1993-12-23
+3039789	Estanys de Montmantell	Estanys de Montmantell		42.6	1.46667	H	LKS	AD		00				0		2421	Europe/Andorra	1993-12-23
+3039790	Collada de Montmantell	Collada de Montmantell		42.6	1.46667	T	PASS	AD		00				0		2421	Europe/Andorra	1993-12-23
+3039791	Camí de Montmantell	Cami de Montmantell		42.6	1.46667	R	TRL	AD		00				0		2421	Europe/Andorra	1993-12-23
+3039792	Montmantell	Montmantell		42.6	1.46667	A	ADMD	AD		00				0		2421	Europe/Andorra	1993-12-23
+3039793	Pic de Montmalús	Pic de Montmalus		42.50902	1.6861	T	PK	AD		00				0		2445	Europe/Andorra	2011-04-19
+3039794	Estany de Montmalús	Estany de Montmalus		42.5	1.68333	H	LK	AD		00				0		2425	Europe/Andorra	1993-12-23
+3039795	Collada de Montmalús	Collada de Montmalus		42.51667	1.7	T	PASS	AD		00				0		2435	Europe/Andorra	1993-12-23
+3039796	Montmalús	Montmalus		42.5	1.7	A	ADMD	AD		00				0		2323	Europe/Andorra	1993-12-23
+3039797	Torrent de Montllobar	Torrent de Montllobar		42.45	1.51667	H	STM	AD		00				0		1790	Europe/Andorra	1993-12-23
+3039798	Font de Montllobar	Font de Montllobar		42.45	1.51667	H	SPNG	AD		00				0		1790	Europe/Andorra	1993-12-23
+3039799	Montllobar	Montllobar		42.45	1.51667	L	LCTY	AD		00				0		1790	Europe/Andorra	1993-12-23
+3039800	Riu de Montaup	Riu de Montaup		42.56667	1.6	H	STM	AD		00				0		1655	Europe/Andorra	1993-12-23
+3039801	Prats de Montaup	Prats de Montaup		42.56667	1.58333	L	GRAZ	AD		00				0		1919	Europe/Andorra	1993-12-23
+3039802	Collet de Montaup	Collet de Montaup		42.56667	1.58333	T	SPUR	AD		00				0		1919	Europe/Andorra	1993-12-23
+3039803	Carrera de Montaup	Carrera de Montaup		42.56667	1.6	R	RD	AD		00				0		1655	Europe/Andorra	1993-12-23
+3039804	Barranc de Montaup	Barranc de Montaup		42.56667	1.6	H	STM	AD		00				0		1655	Europe/Andorra	1993-12-23
+3039805	Montaup	Montaup		42.58333	1.58333	A	ADMD	AD		00				0		1993	Europe/Andorra	1993-12-23
+3039806	Riu Montaner	Riu Montaner		42.52965	1.52042	H	STM	AD		00				0		1361	Europe/Andorra	2011-04-19
+3039807	Collada de Montaner	Collada de Montaner	Col de Montaner,Col de Montanér,Coll de Montane,Coll de Montané,Collada de Montaner	42.51798	1.46716	T	PASS	AD		00				0		1840	Europe/Andorra	2011-11-05
+3039808	Riu de Montalarí	Riu de Montalari		42.53333	1.58333	H	STM	AD		00				0		1571	Europe/Andorra	1993-12-23
+3039809	Bordes de Montalarí	Bordes de Montalari		42.55	1.58333	S	HUTS	AD		00				0		1499	Europe/Andorra	1993-12-23
+3039810	Mónjol de Cabana Sorda	Monjol de Cabana Sorda		42.61667	1.68333	T	SLP	AD		00				0		2406	Europe/Andorra	1993-12-23
+3039811	Canal del Monjo	Canal del Monjo		42.48333	1.5	H	STM	AD		00				0		1631	Europe/Andorra	1993-12-23
+3039812	Tossal Momó	Tossal Momo		42.53333	1.46667	T	PK	AD		00				0		1846	Europe/Andorra	1993-12-23
+3039813	Pleta dels Moltons	Pleta dels Moltons		42.55	1.73333	L	GRAZ	AD		00				0		2100	Europe/Andorra	1993-12-23
+3039814	Obaga de la Mollerra	Obaga de la Mollerra		42.6	1.51667	T	SLP	AD		00				0		1445	Europe/Andorra	1993-12-23
+3039815	Pont de Molleres	Pont de Molleres		42.55	1.58333	S	BDG	AD		00				0		1499	Europe/Andorra	1993-12-23
+3039816	Canal de les Molleres	Canal de les Molleres		42.51667	1.56667	H	STM	AD		00				0		1759	Europe/Andorra	1993-12-23
+3039817	Canal de les Molleres	Canal de les Molleres		42.5	1.55	H	STM	AD		00				0		1566	Europe/Andorra	1993-12-23
+3039818	Bosc de les Molleres	Bosc de les Molleres		42.51667	1.56667	V	FRST	AD		00				0		1759	Europe/Andorra	1993-12-23
+3039819	Molleres	Molleres		42.55103	1.58958	P	PPL	AD		02				0		1640	Europe/Andorra	2011-04-19
+3039820	Molleres	Molleres		42.55	1.58333	L	LCTY	AD		00				0		1499	Europe/Andorra	1993-12-23
+3039821	Bosc de la Mollera	Bosc de la Mollera		42.6	1.51667	V	FRST	AD		00				0		1445	Europe/Andorra	1993-12-23
+3039822	Bordes de la Mollera	Bordes de la Mollera		42.6	1.51667	S	HUTS	AD		00				0		1445	Europe/Andorra	1993-12-23
+3039823	Torrent de la Molina	Torrent de la Molina		42.45	1.51667	H	STM	AD		00				0		1790	Europe/Andorra	1993-12-23
+3039824	Riu de la Molina	Riu de la Molina		42.53333	1.6	H	STM	AD		00				0		1888	Europe/Andorra	1993-12-23
+3039825	Obaga de la Molina	Obaga de la Molina		42.56667	1.48333	T	SLP	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039826	Obaga de la Molina	Obaga de la Molina		42.45	1.51667	T	SLP	AD		00				0		1790	Europe/Andorra	1993-12-23
+3039827	Grau de la Molina	Grau de la Molina		42.53333	1.6	H	RVN	AD		00				0		1888	Europe/Andorra	1993-12-23
+3039828	Canal de la Molina	Canal de la Molina		42.48333	1.56667	H	STM	AD		00				0		2231	Europe/Andorra	1993-12-23
+3039829	Canal de la Molina	Canal de la Molina		42.48333	1.46667	H	STM	AD		00				0		1148	Europe/Andorra	1993-12-23
+3039830	Camí de la Molina	Cami de la Molina		42.53333	1.6	R	TRL	AD		00				0		1888	Europe/Andorra	1993-12-23
+3039831	Bordes de la Molina	Bordes de la Molina		42.51667	1.58333	S	HUT	AD		00				0		1994	Europe/Andorra	1993-12-23
+3039832	Plana Moleta	Plana Moleta		42.56667	1.53333	T	UPLD	AD		00				0		1669	Europe/Andorra	1993-12-23
+3039833	Roc de les Moles	Roc de les Moles		42.53333	1.51667	T	RK	AD		00				0		1361	Europe/Andorra	1993-12-23
+3039834	Pont de les Moles	Pont de les Moles		42.6	1.53333	S	BDG	AD		00				0		1695	Europe/Andorra	1993-12-23
+3039835	Canal de les Moles	Canal de les Moles		42.56667	1.61667	H	RVN	AD		00				0		1920	Europe/Andorra	1993-12-23
+3039836	Pont de la Mola	Pont de la Mola		42.56667	1.66667	S	BDG	AD		00				0		1938	Europe/Andorra	1993-12-23
+3039837	Roc del Moixó	Roc del Moixo		42.56667	1.5	T	RK	AD		00				0		1636	Europe/Andorra	1993-12-23
+3039838	Borda del Moixellaire	Borda del Moixellaire		42.45	1.45	S	HUTS	AD		00				0		1482	Europe/Andorra	1993-12-23
+3039839	Torrent de la Moixella	Torrent de la Moixella		42.43333	1.48333	H	STM	AD		00				0		1228	Europe/Andorra	1993-12-23
+3039840	Riu de la Moixella	Riu de la Moixella		42.45	1.48333	H	STM	AD		00				0		1111	Europe/Andorra	1993-12-23
+3039841	Bosc de la Moixella	Bosc de la Moixella		42.45	1.46667	V	FRST	AD		00				0		935	Europe/Andorra	1993-12-23
+3039842	Serra Mitjana	Serra Mitjana		42.46667	1.61667	T	RDGE	AD		00				0		2448	Europe/Andorra	1993-12-23
+3039843	Serra Mitjana	Serra Mitjana		42.46667	1.58333	T	MT	AD		00				0		2367	Europe/Andorra	1993-12-23
+3039844	Bony de Mitgeu	Bony de Mitgeu		42.55	1.53333	T	SPUR	AD		00				0		1593	Europe/Andorra	1993-12-23
+3039845	Collet de la Mira	Collet de la Mira		42.56667	1.53333	T	SPUR	AD		00				0		1669	Europe/Andorra	1993-12-23
+3039846	Serrat dels Miquelets	Serrat dels Miquelets		42.55	1.6	T	RDGE	AD		00				0		2210	Europe/Andorra	1993-12-23
+3039847	Font dels Miquelets	Font dels Miquelets		42.58333	1.43333	H	SPNG	AD		00				0		2412	Europe/Andorra	1993-12-23
+3039848	Pont de les Mines	Pont de les Mines		42.6	1.53333	S	BDG	AD		00				0		1695	Europe/Andorra	1993-12-23
+3039849	Tosa del Mig	Tosa del Mig		42.58333	1.7	T	UPLD	AD		00				0		2584	Europe/Andorra	1993-12-23
+3039850	Serra del Mig	Serra del Mig		42.58333	1.71667	T	RDGE	AD		00				0		2553	Europe/Andorra	1993-12-23
+3039851	Roc del Mig	Roc del Mig		42.56667	1.71667	T	RK	AD		00				0		2219	Europe/Andorra	1993-12-23
+3039852	Estany del Mig	Estany del Mig		42.63999	1.48612	H	LK	AD		07				0		2254	Europe/Andorra	2007-03-04
+3039853	Coma del Mig	Coma del Mig		42.65	1.53333	T	CRQ	AD		00				0		2564	Europe/Andorra	1993-12-23
+3039854	Collada del Mig	Collada del Mig		42.61667	1.55	T	SPUR	AD		00				0		2007	Europe/Andorra	1993-12-23
+3039855	Clots del Mig	Clots del Mig		42.56667	1.55	H	RVN	AD		00				0		1996	Europe/Andorra	1993-12-23
+3039856	Carrera del Mig	Carrera del Mig		42.56667	1.61667	R	TRL	AD		00				0		1920	Europe/Andorra	1993-12-23
+3039857	Basers del Mig	Basers del Mig		42.58333	1.7	T	CLF	AD		00				0		2584	Europe/Andorra	1993-12-23
+3039858	Clots de Més Amunt de la Pleta	Clots de Mes Amunt de la Pleta		42.6	1.48333	T	TAL	AD		00				0		2441	Europe/Andorra	1993-12-23
+3039859	Pleta de Més Amunt	Pleta de Mes Amunt		42.51667	1.63333	L	GRAZ	AD		00				0		2379	Europe/Andorra	1993-12-23
+3039860	Estany de Més Amunt	Estany de Mes Amunt		42.6457	1.48659	H	LK	AD		07				0		2530	Europe/Andorra	2007-03-04
+3039861	Carretera Meritxell	Carretera Meritxell		42.55	1.58333	R	RD	AD		00				0		1499	Europe/Andorra	1993-12-23
+3039862	Meritxell	Meritxell	Sanctuaire de Meritxeli,Sanctuaire de Meritxell,Santuari de Meritxell	42.55403	1.59087	P	PPL	AD	AD	02				0		1640	Europe/Andorra	2007-04-16
+3039863	Rec de Mereig	Rec de Mereig		42.56667	1.58333	H	CNL	AD		00				0		1919	Europe/Andorra	1993-12-23
+3039864	Pont de Mereig	Pont de Mereig		42.55	1.6	S	BDG	AD		00				0		2210	Europe/Andorra	1993-12-23
+3039865	Planells de Mereig	Planells de Mereig		42.56667	1.58333	T	UPLD	AD		00				0		1919	Europe/Andorra	1993-12-23
+3039866	Bosc de Mereig	Bosc de Mereig		42.56667	1.58333	V	FRST	AD		00				0		1919	Europe/Andorra	1993-12-23
+3039867	Bordes de Mereig	Bordes de Mereig		42.56302	1.58293	S	FRMS	AD		00				0		1637	Europe/Andorra	2011-04-19
+3039868	Mereig	Mereig		42.56667	1.58333	A	ADMD	AD		00				0		1919	Europe/Andorra	1993-12-23
+3039869	Font de la Mentirosa	Font de la Mentirosa		42.43333	1.51667	H	SPNG	AD		00				0		2031	Europe/Andorra	1993-12-23
+3039870	Estany dels Meners de la Coma	Estany dels Meners de la Coma		42.61667	1.61667	H	LK	AD		00				0		2352	Europe/Andorra	1993-12-23
+3039871	Riu dels Meners	Riu dels Meners		42.61667	1.63333	H	STM	AD		00				0		2331	Europe/Andorra	1993-12-23
+3039872	Font dels Meners	Font dels Meners		42.61667	1.6	H	SPNG	AD		00				0		2528	Europe/Andorra	1993-12-23
+3039873	Font dels Meners	Font dels Meners		42.46667	1.48333	H	SPNG	AD		00				0		1134	Europe/Andorra	1993-12-23
+3039874	Collada dels Meners	Collada dels Meners		42.61667	1.6	T	PASS	AD		00				0		2528	Europe/Andorra	1993-12-23
+3039875	Pic de la Menera	Pic de la Menera		42.51667	1.71667	T	PK	AD		00				0		2591	Europe/Andorra	1993-12-23
+3039876	Clots de la Menera	Clots de la Menera		42.51667	1.71667	H	RVN	AD		00				0		2591	Europe/Andorra	1993-12-23
+3039877	Bosc del Menadís	Bosc del Menadis		42.45	1.46667	V	FRST	AD		00				0		935	Europe/Andorra	1993-12-23
+3039878	Meligar d’Emportona	Meligar d'Emportona		42.53333	1.66667	L	LCTY	AD		00				0		2489	Europe/Andorra	1993-12-23
+3039879	Serrat de Meligar	Serrat de Meligar		42.55	1.45	T	RDGE	AD		00				0		1788	Europe/Andorra	1993-12-23
+3039880	Estany del Meligar	Estany del Meligar		42.51667	1.66667	H	LK	AD		00				0		2410	Europe/Andorra	1993-12-23
+3039881	Roc del Melic	Roc del Melic		42.58333	1.58333	T	RK	AD		00				0		1993	Europe/Andorra	1993-12-23
+3039882	Pic de Medécourbe	Pic de Medecourbe	Pic de Madecourbe,Pic de Medecorba,Pic de Medecourbe,Pic de Medécourbe	42.6037	1.44264	T	PK	AD		00				0	2914	2658	Europe/Andorra	2011-02-09
+3039883	Camí dels Matxos	Cami dels Matxos		42.5	1.56667	R	TRL	AD		00				0		1776	Europe/Andorra	1993-12-23
+3039884	Basera Mateu	Basera Mateu		42.5	1.5	T	CLF	AD		00				0		1135	Europe/Andorra	1993-12-23
+3039885	Serrat dels Matets	Serrat dels Matets		42.56667	1.5	T	SPUR	AD		00				0		1636	Europe/Andorra	1993-12-23
+3039886	Bosc del Matet	Bosc del Matet		42.61667	1.53333	V	FRST	AD		00				0		1609	Europe/Andorra	1993-12-23
+3039887	Bosc de les Matelles	Bosc de les Matelles		42.51667	1.48333	V	FRST	AD		00				0		1839	Europe/Andorra	1993-12-23
+3039888	Clot de la Mata	Clot de la Mata		42.56667	1.68333	T	SLP	AD		00				0		2340	Europe/Andorra	1993-12-23
+3039889	Canals de la Mata	Canals de la Mata		42.61667	1.58333	H	RVN	AD		00				0		2374	Europe/Andorra	1993-12-23
+3039890	Canal de la Mata	Canal de la Mata		42.51667	1.51667	H	STM	AD		00				0		1265	Europe/Andorra	1993-12-23
+3039891	Bosc de la Mata	Bosc de la Mata		42.6	1.68333	V	FRST	AD		00				0		2089	Europe/Andorra	1993-12-23
+3039892	Bosc de la Mata	Bosc de la Mata		42.6	1.61667	V	FRST	AD		00				0		2271	Europe/Andorra	1993-12-23
+3039893	Massolina	Massolina		42.5	1.61667	H	RVN	AD		00				0		2560	Europe/Andorra	1993-12-23
+3039894	Riu de Massat	Riu de Massat		42.556	1.68641	H	STM	AD		00				0		2083	Europe/Andorra	2011-04-19
+3039895	Cortal del Masover	Cortal del Masover		42.53333	1.53333	S	CRRL	AD		00				0		1521	Europe/Andorra	1993-12-23
+3039896	Mas de Ribafeta	Mas de Ribafeta		42.56936	1.48837	P	PPL	AD		04				0		1655	Europe/Andorra	2011-04-19
+3039897	Pont del Mas d’En Soler	Pont del Mas d'En Soler		42.56667	1.51667	S	BDG	AD		00				0		1500	Europe/Andorra	1993-12-23
+3039898	Devesa del Mas d’Alins	Devesa del Mas d'Alins		42.45	1.45	L	GRAZ	AD		00				0		1482	Europe/Andorra	1993-12-23
+3039899	Carretera del Mas d’Alins	Carretera del Mas d'Alins		42.45	1.46667	R	RD	AD		00				0		935	Europe/Andorra	1993-12-23
+3039900	Borda del Mas d’Alins	Borda del Mas d'Alins		42.43333	1.45	S	HUTS	AD		00				0		877	Europe/Andorra	1993-12-23
+3039901	Mas d’Alins	Mas d'Alins		42.44126	1.44944	P	PPL	AD		06				0		1197	Europe/Andorra	2011-04-19
+3039902	Mas d’Alins	Mas d'Alins		42.45	1.45	A	ADMD	AD		00				0		1482	Europe/Andorra	1993-12-23
+3039903	Solà del Mas	Sola del Mas		42.45	1.5	T	SLP	AD		00				0		1614	Europe/Andorra	1993-12-23
+3039904	Obac del Mas	Obac del Mas		42.56667	1.5	T	SLP	AD		00				0		1636	Europe/Andorra	1993-12-23
+3039905	Camí del Mas	Cami del Mas		42.45	1.5	R	TRL	AD		00				0		1614	Europe/Andorra	1993-12-23
+3039906	Bordes del Mas	Bordes del Mas		42.45	1.5	S	HUTS	AD		00				0		1614	Europe/Andorra	1993-12-23
+3039907	Allau del Mas	Allau del Mas		42.56667	1.48333	H	STM	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039908	Borda del Marticella	Borda del Marticella		42.58333	1.65	S	HUT	AD		00				0		1767	Europe/Andorra	1993-12-23
+3039909	Cortal del Martí	Cortal del Marti		42.53333	1.53333	S	CRRL	AD		00				0		1521	Europe/Andorra	1993-12-23
+3039910	Collet Martí	Collet Marti		42.5	1.48333	T	SPUR	AD		00				0		1316	Europe/Andorra	1993-12-23
+3039911	Collet Martí	Collet Marti		42.46667	1.46667	T	PK	AD		00				0		1340	Europe/Andorra	1993-12-23
+3039912	Borda del Martí	Borda del Marti		42.56667	1.6	S	HUT	AD		00				0		1655	Europe/Andorra	1993-12-23
+3039913	Marrades Negres	Marrades Negres		42.56667	1.7	T	TAL	AD		00				0		2375	Europe/Andorra	1993-12-23
+3039914	Marrades Negres	Marrades Negres		42.56667	1.55	H	STM	AD		00				0		1996	Europe/Andorra	1993-12-23
+3039915	Camí de les Marrades	Cami de les Marrades		42.55	1.5	R	TRL	AD		00				0		1292	Europe/Andorra	1993-12-23
+3039916	Bony de les Marrades	Bony de les Marrades		42.53333	1.5	T	PK	AD		00				0		1357	Europe/Andorra	1993-12-23
+3039917	Roc de Maria	Roc de Maria		42.5	1.48333	T	RK	AD		00				0		1316	Europe/Andorra	1993-12-23
+3039918	Canal de Maria	Canal de Maria		42.56667	1.48333	H	STM	AD		00				0		1508	Europe/Andorra	1993-12-23
+3039919	Pont de la Margineda	Pont de la Margineda		42.4838	1.49042	S	BDG	AD		00				0		991	Europe/Andorra	2011-04-19
+3039920	Coll de la Manyiga	Coll de la Manyiga		42.46667	1.53333	T	SPUR	AD		00				0		2332	Europe/Andorra	1993-12-23
+3039921	Coll de la Manyiga	Coll de la Manyiga		42.46667	1.48333	T	PASS	AD		00				0		1134	Europe/Andorra	1993-12-23
+3039922	Cortals de Manyat	Cortals de Manyat		42.48333	1.51667	S	HUTS	AD		00				0		2061	Europe/Andorra	1993-12-23
+3039923	Manyat	Manyat		42.47591	1.51661	L	LCTY	AD		00				0		1892	Europe/Andorra	2011-04-19
+3039924	Riu del Manegor	Riu del Manegor		42.6	1.68333	H	STM	AD		00				0		2089	Europe/Andorra	1993-12-23
+3039925	Pleta del Manegor	Pleta del Manegor		42.61667	1.7	L	GRAZ	AD		00				0		2285	Europe/Andorra	1993-12-23
+3039926	Bosc de la Mandurana	Bosc de la Mandurana		42.56667	1.61667	V	FRST	AD		00				0		1920	Europe/Andorra	1993-12-23
+3039927	Borda del Mandicó	Borda del Mandico		42.51667	1.55	S	HUT	AD		00				0		1322	Europe/Andorra	1993-12-23
+3039928	Pont del Mamó	Pont del Mamo		42.55	1.48333	S	BDG	AD		00				0		1548	Europe/Andorra	1993-12-23
+3039929	Font de Mallol	Font de Mallol		42.55	1.55	H	SPNG	AD		00				0		2097	Europe/Andorra	1993-12-23
+3039930	Canals Males	Canals Males		42.46667	1.48333	H	STM	AD		00				0		1134	Europe/Andorra	1993-12-23
+3039931	Canals Males	Canals Males		42.58333	1.46667	H	RVN	AD		00				0		1643	Europe/Andorra	1993-12-23
+3039932	Canal Mala	Canal Mala		42.45	1.5	H	RVN	AD		00				0		1614	Europe/Andorra	1993-12-23
+3039933	Roca Major	Roca Major		42.43333	1.5	T	PK	AD		00				0		1804	Europe/Andorra	1993-12-23
+3039934	Clot dels Mais	Clot dels Mais		42.56667	1.61667	H	RVN	AD		00				0		1920	Europe/Andorra	1993-12-23
+3039935	Planada dels Maians	Planada dels Maians		42.55	1.61667	T	UPLD	AD		00				0		2206	Europe/Andorra	1993-12-23
+3039936	Pic dels Maians	Pic dels Maians		42.55	1.61667	T	PK	AD		00				0		2206	Europe/Andorra	1993-12-23
+3039937	Costa dels Maians	Costa dels Maians		42.55	1.61667	T	SLP	AD		00				0		2206	Europe/Andorra	1993-12-23
+3039938	Canal dels Maians	Canal dels Maians		42.5	1.51667	H	STM	AD		00				0		1410	Europe/Andorra	1993-12-23
+3039939	Bosc dels Maians	Bosc dels Maians		42.48333	1.51667	V	FRST	AD		00				0		2061	Europe/Andorra	1993-12-23
+3039940	Bony dels Maians	Bony dels Maians		42.53333	1.61667	T	SPUR	AD		00				0		2237	Europe/Andorra	1993-12-23
+3039941	Pic de la Maiana	Pic de la Maiana		42.4821	1.60014	T	PK	AD		00				0		2250	Europe/Andorra	2011-04-19
+3039942	Collada de la Maiana	Collada de la Maiana		42.48333	1.6	T	PASS	AD		00				0		2250	Europe/Andorra	1993-12-23
+3039943	Tosa del Maià	Tosa del Maia		42.55	1.71667	T	UPLD	AD		00				0		2192	Europe/Andorra	1993-12-23
+3039944	Pleta del Maià	Pleta del Maia		42.56667	1.73333	L	GRAZ	AD		00				0		2096	Europe/Andorra	1993-12-23
+3039945	Pic del Maià	Pic del Maia	Pic Mata,Pic del Maia,Pic del Maià	42.55	1.71667	T	PK	AD	AD	00				0		2192	Europe/Andorra	2011-11-05
+3039946	Obagot del Maià	Obagot del Maia		42.56667	1.73333	T	SLP	AD		00				0		2096	Europe/Andorra	1993-12-23
+3039947	Riu Madriu	Riu Madriu	Madriu,Riu Madriu	42.49697	1.57954	H	STM	AD		00				0		1888	Europe/Andorra	2011-11-05
+3039948	Basers de Madona	Basers de Madona		42.61667	1.63333	T	CLF	AD		00				0		2331	Europe/Andorra	1993-12-23
+3039949	Canal de Luixent Passader	Canal de Luixent Passader		42.5	1.5	H	STM	AD		00				0		1135	Europe/Andorra	1993-12-23
+3039950	L’Ovella	L'Ovella		42.56667	1.6	L	LCTY	AD		00				0		1655	Europe/Andorra	1993-12-23
+3039951	Los Travessers	Los Travessers		42.56667	1.43333	L	LCTY	AD		00				0		2402	Europe/Andorra	1993-12-23
+3039952	L’Orri Amagat	L'Orri Amagat		42.6	1.6	L	LCTY	AD		00				0		2143	Europe/Andorra	1993-12-23
+3039953	L’Obagueta	L'Obagueta		42.53333	1.55	L	LCTY	AD		00				0		1344	Europe/Andorra	1993-12-23
+3039954	Roc dels Llumeners	Roc dels Llumeners		42.58333	1.46667	T	RK	AD		00				0		1643	Europe/Andorra	1993-12-23
+3039955	Canal dels Llumeners	Canal dels Llumeners		42.58333	1.46667	H	RVN	AD		00				0		1643	Europe/Andorra	1993-12-23
+3039956	Riu de Llumeneres	Riu de Llumeneres		42.46591	1.49579	H	STM	AD		00				0		1232	Europe/Andorra	2011-04-19
+3039957	Plana de Llumeneres	Plana de Llumeneres		42.46667	1.51667	T	UPLD	AD		00				0		1985	Europe/Andorra	1993-12-23
+3039958	Cortal de Llumeneres	Cortal de Llumeneres		42.46667	1.5	S	CRRL	AD		00				0		1383	Europe/Andorra	1993-12-23
+3039959	Llumeneres	Llumeneres	Llumeneres	42.46667	1.51667	P	PPL	AD		06				0		1985	Europe/Andorra	2011-11-05
+3039960	Canals del Lloset	Canals del Lloset		42.53333	1.58333	H	STM	AD		00				0		1571	Europe/Andorra	1993-12-23
+3039961	Canal del Lloset	Canal del Lloset		42.55	1.5	H	STM	AD		00				0		1292	Europe/Andorra	1993-12-23
+3039962	Bordes del Lloset	Bordes del Lloset		42.55	1.6	S	HUT	AD		00				0		2210	Europe/Andorra	1993-12-23
+3039963	Lloset	Lloset		42.53333	1.6	A	ADMD	AD		00				0		1888	Europe/Andorra	1993-12-23
+3039964	Costa del Lloser de Naudí	Costa del Lloser de Naudi		42.56667	1.56667	T	SLP	AD		00				0		2089	Europe/Andorra	1993-12-23
+3039965	Solana del Lloser	Solana del Lloser		42.46667	1.45	T	SLP	AD		00				0		1562	Europe/Andorra	1993-12-23
+3039966	Roc del Lloser	Roc del Lloser		42.58333	1.51667	T	RK	AD		00				0		1722	Europe/Andorra	1993-12-23
+3039967	Canal del Lloser	Canal del Lloser		42.51667	1.51667	H	STM	AD		00				0		1265	Europe/Andorra	1993-12-23
+3039968	Tossal de la Llosada	Tossal de la Llosada	Pic Llosada,Tossal de la Llosada	42.54862	1.64567	T	PK	AD		00				0		2422	Europe/Andorra	2011-11-05
+3039969	Tosa de la Llosada	Tosa de la Llosada		42.55	1.63333	T	UPLD	AD		00				0		2336	Europe/Andorra	1993-12-23
+3039970	Riu de la Llosada	Riu de la Llosada		42.53333	1.6	H	STM	AD		00				0		1888	Europe/Andorra	1993-12-23
+3039971	Obaga de la Llosada	Obaga de la Llosada		42.53333	1.61667	T	SLP	AD		00				0		2237	Europe/Andorra	1993-12-23
+3039972	Emprius de la Llosada	Emprius de la Llosada		42.53333	1.61667	L	CMN	AD		00				0		2237	Europe/Andorra	1993-12-23
+3039973	Basers de la Llosada	Basers de la Llosada		42.55	1.63333	T	CLF	AD		00				0		2336	Europe/Andorra	1993-12-23
+3039974	Riu Llosà	Riu Llosa		42.45	1.46667	H	STM	AD		00				0		935	Europe/Andorra	1993-12-23
+3039975	Grau de la Llosa	Grau de la Llosa		42.61667	1.55	T	SLP	AD		00				0		2007	Europe/Andorra	1993-12-23
+3039976	Collet de la Llosa	Collet de la Llosa		42.56667	1.5	T	PASS	AD		00				0		1636	Europe/Andorra	1993-12-23
+3039977	Clots de la Llosa	Clots de la Llosa		42.61667	1.61667	H	RVN	AD		00				0		2352	Europe/Andorra	1993-12-23
+3039978	Canal de la Llosa	Canal de la Llosa		42.56667	1.5	H	STM	AD		00				0		1636	Europe/Andorra	1993-12-23
+3039979	Llorts	Llorts	Llors,Llorta,Lors	42.59625	1.52658	P	PPL	AD	AD	05				0		1695	Europe/Andorra	2007-04-16
+3039980	Canal de les Llongues	Canal de les Llongues		42.55	1.51667	H	STM	AD		00				0		1397	Europe/Andorra	1993-12-23
+3039981	Bosc de les Llongues	Bosc de les Llongues		42.55	1.51667	V	FRST	AD		00				0		1397	Europe/Andorra	1993-12-23
+3039982	Costa dels Llomassos	Costa dels Llomassos		42.58333	1.45	T	SLP	AD		00				0		2156	Europe/Andorra	1993-12-23
+3039983	Canal dels Llomassos	Canal dels Llomassos		42.55	1.46667	H	STM	AD		00				0		1585	Europe/Andorra	1993-12-23
+3039984	Pleta del Llomar	Pleta del Llomar		42.61667	1.56667	L	GRAZ	AD		00				0		2228	Europe/Andorra	1993-12-23
+3039985	Clot del Llomar	Clot del Llomar		42.53333	1.48333	H	RVN	AD		00				0		1677	Europe/Andorra	1993-12-23
+3039986	Camí de la Llobatera	Cami de la Llobatera		42.55	1.48333	R	TRL	AD		00				0		1548	Europe/Andorra	1993-12-23
+3039987	Canal Llisa	Canal Llisa		42.56667	1.51667	H	STM	AD		00				0		1500	Europe/Andorra	1993-12-23
+3039988	Canal Llisa	Canal Llisa		42.53333	1.6	H	STM	AD		00				0		1888	Europe/Andorra	1993-12-23
+3039989	Torrent Llimois	Torrent Llimois		42.48333	1.43333	H	STM	AD		00				0		1938	Europe/Andorra	1993-12-23
+3039990	Solana dels Llimois	Solana dels Llimois		42.5	1.43333	T	SLP	AD		00				0		1654	Europe/Andorra	1993-12-23
+3039991	Bosc de Llevai	Bosc de Llevai		42.53333	1.55	V	FRST	AD		00				0		1344	Europe/Andorra	1993-12-23
+3039992	Basers de la Llessa	Basers de la Llessa		42.48333	1.5	T	CLF	AD		00				0		1631	Europe/Andorra	1993-12-23
+3039993	Planell de la Llentiga	Planell de la Llentiga		42.6	1.51667	T	UPLD	AD		00				0		1445	Europe/Andorra	1993-12-23
+3039994	Barranc del Llempo	Barranc del Llempo		42.55	1.51667	H	STM	AD		00				0		1397	Europe/Andorra	1993-12-23
+3039995	Casa Llècsia	Casa Llecsia		42.56667	1.6	S	HSE	AD		00				0		1655	Europe/Andorra	1993-12-23
+3039996	Borda del Llècsia	Borda del Llecsia		42.58333	1.6	S	HUT	AD		00				0		1828	Europe/Andorra	1993-12-23
+3039997	Solana dels Llaurers	Solana dels Llaurers		42.48333	1.43333	T	SLP	AD		00				0		1938	Europe/Andorra	1993-12-23
+3039998	Obaga dels Llaurers	Obaga dels Llaurers		42.48333	1.43333	T	SLP	AD		00				0		1938	Europe/Andorra	1993-12-23
+3039999	Bony dels Llaurers	Bony dels Llaurers		42.48333	1.43333	T	PK	AD		00				0		1938	Europe/Andorra	1993-12-23
+3040000	Roc Llarg	Roc Llarg		42.55	1.45	T	SPUR	AD		00				0		1788	Europe/Andorra	1993-12-23
+3040001	Pont del Llarg	Pont del Llarg		42.6	1.68333	S	BDG	AD		00				0		2089	Europe/Andorra	1993-12-23
+3040002	Fontanal Llarg	Fontanal Llarg		42.53333	1.46667	H	SPNG	AD		00				0		1846	Europe/Andorra	1993-12-23
+3040003	Casa Llarg	Casa Llarg		42.55	1.58333	S	HSE	AD		00				0		1499	Europe/Andorra	1993-12-23
+3040004	Canal del Llarg	Canal del Llarg		42.5	1.48333	H	STM	AD		00				0		1316	Europe/Andorra	1993-12-23
+3040005	Solana dels Llampells	Solana dels Llampells		42.51667	1.46667	T	SLP	AD		00				0		1840	Europe/Andorra	1993-12-23
+3040006	Torrent de la Llampa	Torrent de la Llampa		42.56667	1.6	H	STM	AD		00				0		1655	Europe/Andorra	1993-12-23
+3040007	Riu dels Llacs	Riu dels Llacs		42.55	1.43333	H	STM	AD		00				0		1949	Europe/Andorra	1993-12-23
+3040008	Pleta dels Llacs	Pleta dels Llacs		42.6	1.63333	L	GRAZ	AD		00				0		1893	Europe/Andorra	1993-12-23
+3040009	Pic dels Llacs	Pic dels Llacs	Pic dels Llacs	42.53333	1.41667	T	PK	AD		00				0		1948	Europe/Andorra	2011-11-05
+3040010	Pales dels Llacs	Pales dels Llacs		42.6	1.61667	T	CLF	AD		00				0		2271	Europe/Andorra	1993-12-23
+3040011	Font dels Llacs	Font dels Llacs		42.55	1.43333	H	SPNG	AD		00				0		1949	Europe/Andorra	1993-12-23
+3040012	Bosc dels Llacs	Bosc dels Llacs		42.55	1.41667	V	FRST	AD		00				0		2105	Europe/Andorra	1993-12-23
+3040013	L’Hospital	L'Hospital		42.48333	1.55	A	ADMD	AD		00				0		2233	Europe/Andorra	1993-12-23
+3040014	L’Hortó	L'Horto		42.58333	1.63333	L	LCTY	AD		00				0		1722	Europe/Andorra	1993-12-23
+3040015	L’Hortó	L'Horto		42.56667	1.51667	L	LCTY	AD		00				0		1500	Europe/Andorra	1993-12-23
+3040016	L’Hortell	L'Hortell		42.61667	1.51667	L	LCTY	AD		00				0		1716	Europe/Andorra	1993-12-23
+3040017	Les Tres Oles	Les Tres Oles		42.51667	1.55	H	RVN	AD		00				0		1322	Europe/Andorra	1993-12-23
+3040018	Les Tres Fonts	Les Tres Fonts		42.5	1.46667	H	SPNG	AD		00				0		1678	Europe/Andorra	1993-12-23
+3040019	Les Tarteres	Les Tarteres		42.45	1.48333	L	LCTY	AD		00				0		1111	Europe/Andorra	1993-12-23
+3040020	L’Estanyassa	L'Estanyassa		42.58333	1.65	L	LCTY	AD		00				0		1767	Europe/Andorra	1993-12-23
+3040021	Les Tallades	Les Tallades		42.61667	1.55	A	ADMD	AD		00				0		2007	Europe/Andorra	1993-12-23
+3040022	L’Estall	L'Estall		42.5	1.58333	L	LCTY	AD		00				0		1888	Europe/Andorra	1993-12-23
+3040023	Les Sorobilles	Les Sorobilles		42.56667	1.53333	T	CLF	AD		00				0		1669	Europe/Andorra	1993-12-23
+3040024	Les Solanelles	Les Solanelles		42.55	1.6	L	LCTY	AD		00				0		2210	Europe/Andorra	1993-12-23
+3040025	Les Sobiranes	Les Sobiranes		42.61667	1.55	L	LCTY	AD		00				0		2007	Europe/Andorra	1993-12-23
+3040026	Les Salses	Les Salses		42.63333	1.5	T	SLP	AD		00				0		1979	Europe/Andorra	1993-12-23
+3040027	Les Salines	Les Salines		42.60952	1.53697	P	PPL	AD		05				0		1793	Europe/Andorra	2007-04-16
+3040028	Les Ribaltes	Les Ribaltes		42.53333	1.53333	L	LCTY	AD		00				0		1521	Europe/Andorra	1993-12-23
+3040029	Les Rebes	Les Rebes		42.61667	1.61667	T	UPLD	AD		00				0		2352	Europe/Andorra	1993-12-23
+3040030	Les Queroles	Les Queroles		42.6	1.53333	L	LCTY	AD		00				0		1695	Europe/Andorra	1993-12-23
+3040031	Les Portes	Les Portes		42.48333	1.48333	L	LCTY	AD		00				0		981	Europe/Andorra	1993-12-23
+3040032	Les Planes de Sornàs	Les Planes de Sornas		42.56667	1.53333	L	LCTY	AD		00				0		1669	Europe/Andorra	1993-12-23
+3040033	Les Planes	Les Planes		42.63333	1.5	L	LCTY	AD		00				0		1979	Europe/Andorra	1993-12-23
+3040034	Les Planes	Les Planes		42.53333	1.6	A	ADMD	AD		00				0		1888	Europe/Andorra	1993-12-23
+3040035	Les Planades	Les Planades		42.6	1.68333	L	LCTY	AD		00				0		2089	Europe/Andorra	1993-12-23
+3040036	Les Pedrusques	Les Pedrusques		42.51667	1.63333	T	SLP	AD		00				0		2379	Europe/Andorra	1993-12-23
+3040037	Les Pedrasses	Les Pedrasses		42.55	1.51667	L	LCTY	AD		00				0		1397	Europe/Andorra	1993-12-23
+3040038	Les Pardines	Les Pardines		42.58333	1.48333	T	SLP	AD		00				0		1809	Europe/Andorra	1993-12-23
+3040039	Les Pardines	Les Pardines		42.43333	1.46667	S	HUTS	AD		00				0		1113	Europe/Andorra	1993-12-23
+3040040	Les Obagues	Les Obagues		42.58333	1.48333	L	LCTY	AD		00				0		1809	Europe/Andorra	1993-12-23
+3040041	Les Neres	Les Neres		42.55	1.56667	A	ADMD	AD		00				0		1828	Europe/Andorra	1993-12-23
+3040042	Les Molleres	Les Molleres		42.55	1.53333	L	LCTY	AD		00				0		1593	Europe/Andorra	1993-12-23
+3040043	Les Majobarnes	Les Majobarnes		42.55	1.48333	L	LCTY	AD		00				0		1548	Europe/Andorra	1993-12-23
+3040044	Les Llanasques	Les Llanasques		42.58333	1.58333	L	LCTY	AD		00				0		1993	Europe/Andorra	1993-12-23
+3040045	Les Forques	Les Forques		42.55	1.53333	L	LCTY	AD		00				0		1593	Europe/Andorra	1993-12-23
+3040046	Les Fonts	Les Fonts		42.6	1.6	T	UPLD	AD		00				0		2143	Europe/Andorra	1993-12-23
+3040047	Les Fonts	Les Fonts		42.6	1.48333	L	LCTY	AD		00				0		2441	Europe/Andorra	1993-12-23
+3040048	Les Feixes	Les Feixes		42.55	1.43333	L	LCTY	AD		00				0		1949	Europe/Andorra	1993-12-23
+3040049	Les Feixes	Les Feixes		42.53333	1.46667	L	LCTY	AD		00				0		1846	Europe/Andorra	1993-12-23
+3040050	Les Fargues	Les Fargues		42.48333	1.6	S	ANS	AD		00				0		2250	Europe/Andorra	1993-12-23
+3040051	les Escaldes	les Escaldes	Ehskal'des-Ehndzhordani,Escaldes,Escaldes-Engordany,Les Escaldes,esukarudesu=engorudani jiao qu,lai sai si ka er de-en ge er da,ЭÑкальдеÑ-Энджордани,エスカルデスï¼ã‚¨ãƒ³ã‚´ãƒ«ãƒ€ãƒ‹æ•™åŒº,èŠå¡žæ–¯å¡çˆ¾å¾·-æ©æˆˆçˆ¾é”,èŠå¡žæ–¯å¡çˆ¾å¾·ï¼æ©æˆˆçˆ¾é”	42.50729	1.53414	P	PPLA	AD		08				15853		1350	Europe/Andorra	2008-10-15
+3040052	Les Deveses	Les Deveses		42.53333	1.65	A	ADMD	AD		00				0		2508	Europe/Andorra	1993-12-23
+3040053	Les Costes	Les Costes		42.51667	1.55	A	ADMD	AD		00				0		1322	Europe/Andorra	1993-12-23
+3040054	Les Costeres	Les Costeres		42.53333	1.53333	L	LCTY	AD		00				0		1521	Europe/Andorra	1993-12-23
+3040055	Les Comelletes	Les Comelletes		42.48333	1.46667	L	LCTY	AD		00				0		1148	Europe/Andorra	1993-12-23
+3040056	Les Comarques	Les Comarques		42.55	1.51667	L	LCTY	AD		00				0		1397	Europe/Andorra	1993-12-23
+3040057	Les Colleroles	Les Colleroles		42.58333	1.6	T	SLP	AD		00				0		1828	Europe/Andorra	1993-12-23
+3040058	Les Codolles	Les Codolles		42.53333	1.51667	L	LCTY	AD		00				0		1361	Europe/Andorra	1993-12-23
+3040059	L’Escobar	L'Escobar		42.61667	1.68333	L	LCTY	AD		00				0		2406	Europe/Andorra	1993-12-23
+3040060	Les Claperes	Les Claperes		42.55	1.5	L	LCTY	AD		00				0		1292	Europe/Andorra	1993-12-23
+3040061	Les Carboneres	Les Carboneres		42.48333	1.46667	L	LCTY	AD		00				0		1148	Europe/Andorra	1993-12-23
+3040062	Les Canyorques	Les Canyorques		42.58333	1.43333	T	SLP	AD		00				0		2412	Europe/Andorra	1993-12-23
+3040063	Les Canals	Les Canals		42.58333	1.58333	L	LCTY	AD		00				0		1993	Europe/Andorra	1993-12-23
+3040064	Les Canaletes	Les Canaletes		42.6	1.45	T	RKS	AD		00				0		2174	Europe/Andorra	1993-12-23
+3040065	Les Canadilles	Les Canadilles		42.48333	1.48333	H	STM	AD		00				0		981	Europe/Andorra	1993-12-23
+3040066	Les Bordes	Les Bordes		42.58333	1.63333	S	HUTS	AD		00				0		1722	Europe/Andorra	1993-12-23
+3040067	Les Bons	Les Bons	Els Bons	42.53873	1.58649	P	PPL	AD	AD	03				0		1490	Europe/Andorra	2007-04-16
+3040068	Les Bartigues	Les Bartigues		42.6	1.53333	L	LCTY	AD		00				0		1695	Europe/Andorra	1993-12-23
+3040069	Les Barreres	Les Barreres		42.55	1.6	L	LCTY	AD		00				0		2210	Europe/Andorra	1993-12-23
+3040070	Les Barreres	Les Barreres		42.53333	1.53333	L	LCTY	AD		00				0		1521	Europe/Andorra	1993-12-23
+3040071	Les Bagelles	Les Bagelles		42.46667	1.5	L	LCTY	AD		00				0		1383	Europe/Andorra	1993-12-23
+3040072	Les Aubes	Les Aubes		42.51667	1.55	L	LCTY	AD		00				0		1322	Europe/Andorra	1993-12-23
+3040073	Les Aspades	Les Aspades		42.56667	1.55	L	LCTY	AD		00				0		1996	Europe/Andorra	1993-12-23
+3040074	Les Angleves	Les Angleves		42.53333	1.53333	L	LCTY	AD		00				0		1521	Europe/Andorra	1993-12-23
+3040075	Les Allaus	Les Allaus		42.63333	1.53333	L	LCTY	AD		00				0		2072	Europe/Andorra	1993-12-23
+3040076	Les Agols	Les Agols		42.51667	1.6	A	ADMD	AD		00				0		2085	Europe/Andorra	1993-12-23
+3040077	Les Abelletes	Les Abelletes		42.53333	1.73333	L	LCTY	AD		00				0		2300	Europe/Andorra	1993-12-23
+3040078	L’Ensegur	L'Ensegur		42.58333	1.53333	A	ADMD	AD		00				0		1924	Europe/Andorra	1993-12-23
+3040079	L’Avetar	L'Avetar		42.6	1.68333	L	LCTY	AD		00				0		2089	Europe/Andorra	1993-12-23
+3040080	Planell de Laverdú	Planell de Laverdu		42.63333	1.53333	T	UPLD	AD		00				0		2072	Europe/Andorra	1993-12-23
+3040081	Laverdú	Laverdu		42.63333	1.53333	L	LCTY	AD		00				0		2072	Europe/Andorra	1993-12-23
+3040082	La Vaquerissa	La Vaquerissa		42.55	1.45	L	LCTY	AD		00				0		1788	Europe/Andorra	1993-12-23
+3040083	L’Ausany	L'Ausany		42.61667	1.53333	L	LCTY	AD		00				0		1609	Europe/Andorra	1993-12-23
+3040084	La Uïna	La Uina		42.55	1.51667	A	ADMD	AD		00				0		1397	Europe/Andorra	1993-12-23
+3040085	La Trava	La Trava		42.58333	1.61667	L	LCTY	AD		00				0		1707	Europe/Andorra	1993-12-23
+3040086	La Trava	La Trava		42.56667	1.53333	L	GRAZ	AD		00				0		1669	Europe/Andorra	1993-12-23
+3040087	La Tosa	La Tosa		42.56667	1.46667	L	LCTY	AD		00				0		1673	Europe/Andorra	1993-12-23
+3040088	L’Assalador	L'Assalador		42.5	1.43333	L	GRAZ	AD		00				0		1654	Europe/Andorra	1993-12-23
+3040089	L’Asparró	L'Asparro		42.48333	1.5	T	PK	AD		00				0		1631	Europe/Andorra	1993-12-23
+3040090	La Solaneta	La Solaneta		42.53333	1.53333	L	LCTY	AD		00				0		1521	Europe/Andorra	1993-12-23
+3040091	La Solanella	La Solanella		42.46667	1.5	L	LCTY	AD		00				0		1383	Europe/Andorra	1993-12-23
+3040092	La Solana	La Solana		42.58333	1.76667	A	ADMD	AD		00				0		1945	Europe/Andorra	1993-12-23
+3040093	La Serreta	La Serreta		42.58333	1.61667	T	SPUR	AD		00				0		1707	Europe/Andorra	1993-12-23
+3040094	La Serradora	La Serradora		42.46667	1.46667	L	LCTY	AD		00				0		1340	Europe/Andorra	1993-12-23
+3040095	La Serra	La Serra		42.45204	1.49609	S	HUTS	AD		00				0		1270	Europe/Andorra	2011-04-19
+3040096	La Senyoreta	La Senyoreta		42.45	1.48333	A	ADMD	AD		00				0		1111	Europe/Andorra	1993-12-23
+3040097	L’Artiga	L'Artiga		42.56946	1.60243	L	LCTY	AD		00				0		1655	Europe/Andorra	2011-04-19
+3040098	L’Artic	L'Artic		42.5	1.46667	L	LCTY	AD		00				0		1678	Europe/Andorra	1993-12-23
+3040099	La Rovira	La Rovira		42.45	1.46667	L	LCTY	AD		00				0		935	Europe/Andorra	1993-12-23
+3040100	La Roca	La Roca		42.55	1.58333	L	LCTY	AD		00				0		1499	Europe/Andorra	1993-12-23
+3040101	L’Armiana	L'Armiana		42.58333	1.6	A	ADMD	AD		00				0		1828	Europe/Andorra	1993-12-23
+3040102	La Riberola	La Riberola		42.45	1.48333	L	LCTY	AD		00				0		1111	Europe/Andorra	1993-12-23
+3040103	La Rabassa	La Rabassa		42.63333	1.55	A	ADMD	AD		00				0		2053	Europe/Andorra	1993-12-23
+3040104	La Rabassa	La Rabassa	Bois de la Rabassa,Bois de la Rabossa,La Rabassa	42.43333	1.51667	A	ADMD	AD	AD	00				0		2031	Europe/Andorra	2011-11-05
+3040105	La Quera	La Quera		42.51667	1.51667	L	LCTY	AD		00				0		1265	Europe/Andorra	1993-12-23
+3040106	La Presó	La Preso		42.61667	1.56667	L	LCTY	AD		00				0		2228	Europe/Andorra	1993-12-23
+3040107	La Posa	La Posa		42.53333	1.6	L	LCTY	AD		00				0		1888	Europe/Andorra	1993-12-23
+3040108	La Portelleta	La Portelleta		42.48333	1.65	L	LCTY	AD		00				0		2658	Europe/Andorra	1993-12-23
+3040109	La Portella	La Portella		42.55218	1.62761	T	PK	AD		00				0		2364	Europe/Andorra	2011-04-19
+3040110	La Portella	La Portella		42.45	1.5	T	PK	AD		00				0		1614	Europe/Andorra	1993-12-23
+3040111	La Portella	La Portella		42.56667	1.71667	A	ADMD	AD		00				0		2219	Europe/Andorra	1993-12-23
+3040112	La Polguera	La Polguera		42.56667	1.58333	L	LCTY	AD		00				0		1919	Europe/Andorra	1993-12-23
+3040113	La Pleta	La Pleta		42.55	1.43333	S	HUTS	AD		00				0		1949	Europe/Andorra	1993-12-23
+3040114	La Planada	La Planada		42.6	1.6	T	UPLD	AD		00				0		2143	Europe/Andorra	1993-12-23
+3040115	La Plana	La Plana		42.51667	1.51667	T	UPLD	AD		00				0		1265	Europe/Andorra	1993-12-23
+3040116	La Plana	La Plana		42.5	1.55	L	LCTY	AD		00				0		1566	Europe/Andorra	1993-12-23
+3040117	La Pinatella	La Pinatella		42.53333	1.6	L	LCTY	AD		00				0		1888	Europe/Andorra	1993-12-23
+3040118	La Peracaus	La Peracaus		42.56667	1.6	L	LCTY	AD		00				0		1655	Europe/Andorra	1993-12-23
+3040119	La Pera	La Pera		42.56667	1.46667	L	LCTY	AD		00				0		1673	Europe/Andorra	1993-12-23
+3040120	La Peguera	La Peguera		42.45	1.53333	A	ADMD	AD		00				0		1859	Europe/Andorra	1993-12-23
+3040121	La Pedregosa	La Pedregosa		42.58333	1.61667	L	LCTY	AD		00				0		1707	Europe/Andorra	1993-12-23
+3040122	La Passera	La Passera		42.6	1.53333	H	STM	AD		00				0		1695	Europe/Andorra	1993-12-23
+3040123	La Palomera	La Palomera		42.58333	1.78333	L	LCTY	AD		00				0		1694	Europe/Andorra	1993-12-23
+3040124	L’Angonella	L'Angonella		42.6	1.5	A	ADMD	AD		00				0		1923	Europe/Andorra	1993-12-23
+3040125	L’Andorrana	L'Andorrana		42.55	1.43333	L	LCTY	AD		00				0		1949	Europe/Andorra	1993-12-23
+3040126	La Muga	La Muga		42.48333	1.65	L	LCTY	AD		00				0		2658	Europe/Andorra	1993-12-23
+3040127	La Molina	La Molina		42.45	1.51667	L	LCTY	AD		00				0		1790	Europe/Andorra	1993-12-23
+3040128	La Molina	La Molina		42.51667	1.6	A	ADMD	AD		00				0		2085	Europe/Andorra	1993-12-23
+3040129	La Moixella	La Moixella		42.43333	1.46667	S	HUTS	AD		00				0		1113	Europe/Andorra	1993-12-23
+3040130	La Mentirosa	La Mentirosa		42.43333	1.51667	L	LCTY	AD		00				0		2031	Europe/Andorra	1993-12-23
+3040131	Parròquia de la Massana	Parroquia de la Massana	La Massana,Parroquia de la Massana,Parròquia de la Massana,la Massana	42.56667	1.48333	A	ADM1	AD		04				8953		1508	Europe/Andorra	2008-03-17
+3040132	la Massana	la Massana	La Macana,La Massana,La Maçana,La-Massana,la Massana,ma sa na,Ла-МаÑÑана,ラ・マサナ教区,马è¨çº³	42.54499	1.51483	P	PPLA	AD		04				7211		1257	Europe/Andorra	2008-10-15
+3040133	La Margineda	La Margineda		42.48333	1.5	A	ADMD	AD		00				0		1631	Europe/Andorra	1993-12-23
+3040134	La Mandurana	La Mandurana		42.56667	1.61667	A	ADMD	AD		00				0		1920	Europe/Andorra	1993-12-23
+3040135	L’Alzinar	L'Alzinar		42.48333	1.5	L	LCTY	AD		00				0		1631	Europe/Andorra	1993-12-23
+3040136	La Lomera	La Lomera		42.46667	1.48333	L	LCTY	AD		00				0		1134	Europe/Andorra	1993-12-23
+3040137	La Llosada	La Llosada		42.55	1.63333	A	ADMD	AD		00				0		2336	Europe/Andorra	1993-12-23
+3040138	Serrat de La Llonga	Serrat de La Llonga		42.56667	1.51667	T	RDGE	AD		00				0		1500	Europe/Andorra	1993-12-23
+3040139	La Llonga	La Llonga		42.56667	1.51667	A	ADMD	AD		00				0		1500	Europe/Andorra	1993-12-23
+3040140	l'Aldosa	l'Aldosa		42.58333	1.63333	P	PPL	AD		02				195		1722	Europe/Andorra	2007-04-29
+3040141	l'Aldosa	l'Aldosa		42.54391	1.52289	P	PPL	AD		04				594		1397	Europe/Andorra	2007-04-29
+3040142	L’Airola	L'Airola		42.46667	1.46667	L	LCTY	AD		00				0		1340	Europe/Andorra	1993-12-23
+3040143	La Gonarda	La Gonarda		42.55	1.53333	L	LCTY	AD		00				0		1593	Europe/Andorra	1993-12-23
+3040144	La Gonarda	La Gonarda		42.55	1.53333	A	ADMD	AD		00				0		1593	Europe/Andorra	1993-12-23
+3040145	La Ginebrosa	La Ginebrosa		42.55	1.51667	L	LCTY	AD		00				0		1397	Europe/Andorra	1993-12-23
+3040146	La Garganta	La Garganta		42.53333	1.6	L	LCTY	AD		00				0		1888	Europe/Andorra	1993-12-23
+3040147	La Gargallera	La Gargallera		42.48333	1.45	L	LCTY	AD		00				0		1195	Europe/Andorra	1993-12-23
+3040148	La Fonteta	La Fonteta		42.58333	1.46667	L	LCTY	AD		00				0		1643	Europe/Andorra	1993-12-23
+3040149	La Fita	La Fita		42.55	1.45	L	LCTY	AD		00				0		1788	Europe/Andorra	1993-12-23
+3040150	La Cuina	La Cuina		42.56667	1.48333	T	SPUR	AD		00				0		1508	Europe/Andorra	1993-12-23
+3040151	La Creu de les Portes	La Creu de les Portes		42.48333	1.48333	L	LCTY	AD		00				0		981	Europe/Andorra	1993-12-23
+3040152	La Costa	La Costa		42.57834	1.64324	P	PPL	AD		02				0		1737	Europe/Andorra	2011-04-19
+3040153	La Coruvilla	La Coruvilla		42.58333	1.46667	A	ADMD	AD		00				0		1643	Europe/Andorra	1993-12-23
+3040154	La Cortinada	La Cortinada	La Cortinada	42.57601	1.51896	P	PPL	AD		05				0		1722	Europe/Andorra	2011-11-05
+3040155	La Cortada	La Cortada		42.45	1.5	L	LCTY	AD		00				0		1614	Europe/Andorra	1993-12-23
+3040156	La Comella	La Comella		42.51667	1.68333	L	LCTY	AD		00				0		2352	Europe/Andorra	1993-12-23
+3040157	La Comella	La Comella		42.5	1.53333	S	HUTS	AD		00				0		1574	Europe/Andorra	1993-12-23
+3040158	La Comarqueta	La Comarqueta		42.58333	1.71667	L	LCTY	AD		00				0		2553	Europe/Andorra	1993-12-23
+3040159	La Comarqueta	La Comarqueta		42.48333	1.63333	L	LCTY	AD		00				0		2296	Europe/Andorra	1993-12-23
+3040160	La Colilla	La Colilla		42.53333	1.6	L	LCTY	AD		00				0		1888	Europe/Andorra	1993-12-23
+3040161	La Colilla	La Colilla		42.5	1.58333	L	LCTY	AD		00				0		1888	Europe/Andorra	1993-12-23
+3040162	La Clota	La Clota		42.56667	1.53333	L	LCTY	AD		00				0		1669	Europe/Andorra	1993-12-23
+3040163	La Caubella	La Caubella		42.58333	1.48333	L	CLG	AD		00				0		1809	Europe/Andorra	1993-12-23
+3040164	La Castelleta	La Castelleta		42.53333	1.51667	T	SPUR	AD		00				0		1361	Europe/Andorra	1993-12-23
+3040165	La Carbonera	La Carbonera		42.46667	1.63333	L	LCTY	AD		00				0		2619	Europe/Andorra	1993-12-23
+3040166	La Caolla	La Caolla		42.5	1.61667	T	SPUR	AD		00				0		2560	Europe/Andorra	1993-12-23
+3040167	La Callisa	La Callisa		42.56667	1.48333	S	BDG	AD		00				0		1508	Europe/Andorra	1993-12-23
+3040168	La Cabeça	La Cabeca		42.48333	1.45	L	LCTY	AD		00				0		1195	Europe/Andorra	1994-04-16
+3040169	La Cabanella	La Cabanella		42.51667	1.46667	A	ADMD	AD		00				0		1840	Europe/Andorra	1993-12-23
+3040170	La Burna	La Burna		42.6	1.48333	T	CRQ	AD		00				0		2441	Europe/Andorra	1993-12-23
+3040171	La Borda Nova	La Borda Nova		42.61667	1.53333	S	HUT	AD		00				0		1609	Europe/Andorra	1993-12-23
+3040172	La Bor	La Bor		42.55	1.58333	A	ADMD	AD		00				0		1499	Europe/Andorra	1993-12-23
+3040173	La Boixosa	La Boixosa		42.56667	1.53333	L	LCTY	AD		00				0		1669	Europe/Andorra	1993-12-23
+3040174	La Beçosa	La Becosa		42.61667	1.55	L	LCTY	AD		00				0		2007	Europe/Andorra	1994-04-16
+3040175	La Bauma	La Bauma		42.48333	1.61667	S	CAVE	AD		00				0		2217	Europe/Andorra	1993-12-23
+3040176	La Basseta	La Basseta		42.48333	1.65	H	LK	AD		00				0		2658	Europe/Andorra	1993-12-23
+3040177	La Basera	La Basera		42.58333	1.65	L	LCTY	AD		00				0		1767	Europe/Andorra	1993-12-23
+3040178	La Bartra del Ganxo	La Bartra del Ganxo		42.48333	1.46667	L	LCTY	AD		00				0		1148	Europe/Andorra	1993-12-23
+3040179	La Bartra	La Bartra		42.51667	1.55	L	LCTY	AD		00				0		1322	Europe/Andorra	1993-12-23
+3040180	La Bartra	La Bartra		42.46667	1.46667	L	LCTY	AD		00				0		1340	Europe/Andorra	1993-12-23
+3040181	La Bartra	La Bartra	La Barta,La Bartra	42.51667	1.56667	A	ADMD	AD	AD	00				0		1759	Europe/Andorra	2011-11-05
+3040182	L’Abarsetar	L'Abarsetar		42.61667	1.51667	L	LCTY	AD		00				0		1716	Europe/Andorra	1993-12-23
+3040183	L’Abarsetar	L'Abarsetar		42.53333	1.61667	L	LCTY	AD		00				0		2237	Europe/Andorra	1993-12-23
+3040184	La Baladosa	La Baladosa		42.6	1.68333	L	LCTY	AD		00				0		2089	Europe/Andorra	1993-12-23
+3040185	Tosa de Juclar	Tosa de Juclar		42.6	1.71667	T	UPLD	AD		00				0		2516	Europe/Andorra	1993-12-23
+3040186	Solana de Juclar	Solana de Juclar		42.6	1.7	T	SLP	AD		00				0		2354	Europe/Andorra	1993-12-23
+3040187	Riu de Juclar	Riu de Juclar		42.60124	1.69807	H	STM	AD		00				0		2147	Europe/Andorra	2011-04-19
+3040188	Pleta de Juclar	Pleta de Juclar		42.6	1.71667	L	GRAZ	AD		00				0		2516	Europe/Andorra	1993-12-23
+3040189	Obaga de Juclar	Obaga de Juclar		42.61667	1.7	T	SLP	AD		00				0		2285	Europe/Andorra	1993-12-23
+3040190	Collada de Juclar	Collada de Juclar	Col de Joucla,Col de l' Albe,Col de l’ Albe,Collada de Juclar,Port de Jogela,Port de Joucla	42.61667	1.73333	T	PASS	AD		00				0		2508	Europe/Andorra	2011-11-05
+3040191	Canals de Juclar	Canals de Juclar		42.6	1.71667	H	RVN	AD		00				0		2516	Europe/Andorra	1993-12-23
+3040192	Camí de Juclar	Cami de Juclar		42.6	1.7	R	TRL	AD		00				0		2354	Europe/Andorra	1993-12-23
+3040193	Alt de Juclar	Alt de Juclar		42.61667	1.7	T	RDGE	AD		00				0		2285	Europe/Andorra	1993-12-23
+3040194	Juclar	Juclar		42.6	1.71667	A	ADMD	AD		00				0		2516	Europe/Andorra	1993-12-23
+3040195	Carretera de la Juberrussa	Carretera de la Juberrussa		42.45	1.48333	R	RD	AD		00				0		1111	Europe/Andorra	1993-12-23
+3040196	Canal de la Juberrussa	Canal de la Juberrussa		42.43333	1.48333	H	STM	AD		00				0		1228	Europe/Andorra	1993-12-23
+3040197	Bosc de la Juberrussa	Bosc de la Juberrussa		42.43333	1.48333	V	FRST	AD		00				0		1228	Europe/Andorra	1993-12-23
+3040198	Bordes de la Juberrussa	Bordes de la Juberrussa		42.43333	1.48333	S	FRM	AD		00				0		1228	Europe/Andorra	1993-12-23
+3040199	Juberrussa	Juberrussa		42.43333	1.48333	A	ADMD	AD		00				0		1228	Europe/Andorra	1993-12-23
+3040200	Juberri	Juberri	Juberri,Juverri	42.44069	1.48972	P	PPL	AD		06				0		1460	Europe/Andorra	2011-11-05
+3040201	Font de la Jubanya	Font de la Jubanya		42.58333	1.45	H	SPNG	AD		00				0		2156	Europe/Andorra	1993-12-23
+3040202	Coll Jovell	Coll Jovell		42.45	1.53333	T	SPUR	AD		00				0		1859	Europe/Andorra	1993-12-23
+3040203	Coll Jovell	Coll Jovell		42.5	1.56667	T	PK	AD		00				0		1776	Europe/Andorra	1993-12-23
+3040204	Coll Jovell	Coll Jovell		42.48333	1.48333	T	PK	AD		00				0		981	Europe/Andorra	1993-12-23
+3040205	Jovell	Jovell		42.58333	1.63333	L	LCTY	AD		00				0		1722	Europe/Andorra	1993-12-23
+3040206	Coll de Jou	Coll de Jou		42.51667	1.55	T	SPUR	AD		00				0		1322	Europe/Andorra	1993-12-23
+3040207	Coll de Jou	Coll de Jou		42.45	1.46667	T	PASS	AD		00				0		935	Europe/Andorra	1993-12-23
+3040208	Canal del Jou	Canal del Jou		42.56667	1.5	H	STM	AD		00				0		1636	Europe/Andorra	1993-12-23
+3040209	Camí del Jou	Cami del Jou		42.56667	1.5	R	TRL	AD		00				0		1636	Europe/Andorra	1993-12-23
+3040210	Bosc del Jou	Bosc del Jou		42.56667	1.51667	V	FRST	AD		00				0		1500	Europe/Andorra	1993-12-23
+3040211	Solana del Jordà	Solana del Jorda		42.51667	1.6	T	SLP	AD		00				0		2085	Europe/Andorra	1993-12-23
+3040212	Borda del Joansaus	Borda del Joansaus		42.58333	1.65	S	HUT	AD		00				0		1767	Europe/Andorra	1993-12-23
+3040213	Portella de Joan Antoni	Portella de Joan Antoni		42.51195	1.70887	T	PASS	AD		00				0		2550	Europe/Andorra	2011-04-19
+3040214	Pleta de Jes Agols	Pleta de Jes Agols		42.51667	1.6	L	GRAZ	AD		00				0		2085	Europe/Andorra	1993-12-23
+3040215	Borda del Jep	Borda del Jep	Borda del Gep,Borda del Jep	42.56667	1.58333	S	HUT	AD	AD	00				0		1919	Europe/Andorra	2011-11-05
+3040216	Borda del Jarca	Borda del Jarca		42.56667	1.58333	S	HUT	AD		00				0		1919	Europe/Andorra	1993-12-23
+3040217	Borda del Jarca	Borda del Jarca		42.55	1.6	S	HUT	AD		00				0		2210	Europe/Andorra	1993-12-23
+3040218	Borda del Janramon	Borda del Janramon		42.56667	1.58333	S	HUT	AD		00				0		1919	Europe/Andorra	1993-12-23
+3040219	Turó de Jan	Turo de Jan		42.61667	1.63333	T	SPUR	AD		00				0		2331	Europe/Andorra	1993-12-23
+3040220	Riu de Jan	Riu de Jan		42.61667	1.63333	H	STM	AD		00				0		2331	Europe/Andorra	1993-12-23
+3040221	Pala de Jan	Pala de Jan		42.61667	1.63333	T	CLF	AD		00				0		2331	Europe/Andorra	1993-12-23
+3040222	Cóms de Jan	Coms de Jan		42.61667	1.63333	H	LK	AD		00				0		2331	Europe/Andorra	1993-12-23
+3040223	Collada de Jan	Collada de Jan	Collada de Jan	42.61667	1.63333	T	PASS	AD		00				0		2331	Europe/Andorra	2011-11-05
+3040224	Estany de l’ Isla	Estany de l' Isla		42.61667	1.68333	H	LK	AD		00				0		2406	Europe/Andorra	1993-12-23
+3040225	Font dels Isards	Font dels Isards		42.58333	1.7	H	SPNG	AD		00				0		2584	Europe/Andorra	1993-12-23
+3040226	Costa dels Isards	Costa dels Isards		42.58333	1.6	T	SLP	AD		00				0		1828	Europe/Andorra	1993-12-23
+3040227	Coll dels Isards	Coll dels Isards		42.51831	1.73762	T	PASS	AD		00				0		2659	Europe/Andorra	2011-04-19
+3040228	Canal dels Isards	Canal dels Isards		42.5	1.66667	H	RVN	AD		00				0		2441	Europe/Andorra	1993-12-23
+3040229	Canal de l’ Isard	Canal de l' Isard		42.58333	1.46667	H	STM	AD		00				0		1643	Europe/Andorra	1993-12-23
+3040230	Pla de l’ Ingla	Pla de l' Ingla		42.48333	1.61667	T	UPLD	AD		00				0		2217	Europe/Andorra	1993-12-23
+3040231	Collet de l’ Infern	Collet de l' Infern		42.48333	1.6	T	PASS	AD		00				0		2250	Europe/Andorra	1993-12-23
+3040232	Canal de l’ Infern	Canal de l' Infern		42.53333	1.6	H	STM	AD		00				0		1888	Europe/Andorra	1993-12-23
+3040233	Torrent dels Indrets	Torrent dels Indrets		42.48333	1.45	H	STM	AD		00				0		1195	Europe/Andorra	1993-12-23
+3040234	Riu d’ Incles	Riu d' Incles		42.60159	1.68721	H	STM	AD		00				0		2014	Europe/Andorra	2011-04-19
+3040235	Prats d’ Incles	Prats d' Incles		42.6	1.66667	L	GRAZ	AD		00				0		1858	Europe/Andorra	1993-12-23
+3040236	Port de Fontargente	Port de Fontargente	Port d' Incles,Port de Fontargent,Port de Fontargente,Port d’ Incles	42.61667	1.71667	T	PASS	AD		00				0		2352	Europe/Andorra	2011-11-05
+3040237	Pont d’ Incles	Pont d' Incles		42.58333	1.66667	S	BDG	AD		00				0		2159	Europe/Andorra	1993-12-23
+3040238	Camí d’ Incles	Cami d' Incles		42.6	1.66667	R	TRL	AD		00				0		1858	Europe/Andorra	1993-12-23
+3040239	Bosc d’ Incles	Bosc d' Incles		42.58333	1.66667	V	FRST	AD		00				0		2159	Europe/Andorra	1993-12-23
+3040240	Estany de l’ Illa	Estany de l' Illa		42.49785	1.65852	H	RSV	AD		00				0		2553	Europe/Andorra	2011-04-19
+3040241	Obagues de la Iesca	Obagues de la Iesca		42.48333	1.43333	T	SLP	AD		00				0		1938	Europe/Andorra	1993-12-23
+3040242	Bosc de l’ Hoste	Bosc de l' Hoste		42.55	1.68333	V	FRST	AD		00				0		2254	Europe/Andorra	1993-12-23
+3040243	Bosc de l’ Hostal del Poll	Bosc de l' Hostal del Poll		42.61667	1.63333	V	FRST	AD		00				0		2331	Europe/Andorra	1993-12-23
+3040244	Riu dels Hortons	Riu dels Hortons		42.51667	1.46667	H	STM	AD		00				0		1840	Europe/Andorra	1993-12-23
+3040245	Bosc dels Hortons	Bosc dels Hortons		42.53333	1.55	V	FRST	AD		00				0		1344	Europe/Andorra	1993-12-23
+3040246	Torrent dels Hortells	Torrent dels Hortells		42.45	1.48333	H	STM	AD		00				0		1111	Europe/Andorra	1993-12-23
+3040247	Serra de l’ Hortell	Serra de l' Hortell		42.61667	1.51667	T	MT	AD		00				0		1716	Europe/Andorra	1993-12-23
+3040248	Pic de l’ Hortell	Pic de l' Hortell		42.61914	1.50815	T	PK	AD		00				0		2390	Europe/Andorra	2011-04-19
+3040249	Torrent dels Hortals	Torrent dels Hortals		42.53333	1.58333	H	STM	AD		00				0		1571	Europe/Andorra	1993-12-23
+3040250	Serra de l’ Honor	Serra de l' Honor		42.53333	1.51667	T	SPUR	AD		00				0		1361	Europe/Andorra	1993-12-23
+3040251	Roc del’ Home Dret	Roc del' Home Dret		42.58333	1.66667	T	SPUR	AD		00				0		2159	Europe/Andorra	1993-12-23
+3040252	Roca Herbosa	Roca Herbosa		42.48333	1.56667	T	RK	AD		00				0		2231	Europe/Andorra	1993-12-23
+3040253	Pleta de Guitard	Pleta de Guitard	Pleta de Guitard,Pleta de Guitart	42.53333	1.6	L	GRAZ	AD	AD	00				0		1888	Europe/Andorra	2011-11-05
+3040254	Cortal del Guitard	Cortal del Guitard	Cortal del Guitard,Cortal del Guitart	42.45	1.5	S	CRRL	AD	AD	00				0		1614	Europe/Andorra	2011-11-05
+3040255	Tartera de les Guineus	Tartera de les Guineus		42.56667	1.68333	T	TAL	AD		00				0		2340	Europe/Andorra	1993-12-23
+3040256	Roc de la Guilla	Roc de la Guilla		42.51667	1.56667	T	RK	AD		00				0		1759	Europe/Andorra	1993-12-23
+3040257	Bony de les Guardioles	Bony de les Guardioles		42.55	1.51667	T	SPUR	AD		00				0		1397	Europe/Andorra	1993-12-23
+3040258	Serra de la Guardiola	Serra de la Guardiola		42.58081	1.71111	T	RDGE	AD		00				0		2544	Europe/Andorra	2011-04-19
+3040259	Obaga de la Guardiola	Obaga de la Guardiola		42.56667	1.68333	T	SLP	AD		00				0		2340	Europe/Andorra	1993-12-23
+3040260	Planell de la Guàrdia	Planell de la Guardia		42.61667	1.51667	T	UPLD	AD		00				0		1716	Europe/Andorra	1993-12-23
+3040261	Roques Grosses	Roques Grosses		42.58333	1.66667	T	RKS	AD		00				0		2159	Europe/Andorra	1993-12-23
+3040262	Riba Grossa	Riba Grossa		42.55	1.6	T	TAL	AD		00				0		2210	Europe/Andorra	1993-12-23
+3040263	Roc Gros	Roc Gros		42.58333	1.48333	T	RK	AD		00				0		1809	Europe/Andorra	1993-12-23
+3040264	Riu Gros	Riu Gros		42.56667	1.66667	H	STM	AD		00				0		1938	Europe/Andorra	1993-12-23
+3040265	Alt del Griu	Alt del Griu		42.52534	1.65114	T	MT	AD		00				0		2508	Europe/Andorra	2011-04-19
+3040266	Riu del Grill	Riu del Grill		42.51667	1.6	H	STM	AD		00				0		2085	Europe/Andorra	1993-12-23
+3040267	Font de les Greixes	Font de les Greixes		42.46667	1.45	H	SPNG	AD		00				0		1562	Europe/Andorra	1993-12-23
+3040268	Clot de les Greixes	Clot de les Greixes		42.46667	1.45	H	RVN	AD		00				0		1562	Europe/Andorra	1993-12-23
+3040269	Camí de les Gravades	Cami de les Gravades		42.53333	1.51667	R	TRL	AD		00				0		1361	Europe/Andorra	1993-12-23
+3040270	Clot dels Gravaders	Clot dels Gravaders		42.46667	1.55	H	RVN	AD		00				0		2341	Europe/Andorra	1993-12-23
+3040271	Pleta dels Graus	Pleta dels Graus		42.48333	1.56667	L	GRAZ	AD		00				0		2231	Europe/Andorra	1993-12-23
+3040272	Grau Roig	Grau Roig		42.53333	1.7	S	RSRT	AD		03				0		2357	Europe/Andorra	2010-01-11
+3040273	Obaga de Graupont	Obaga de Graupont		42.48333	1.46667	T	SLP	AD		00				0		1148	Europe/Andorra	1993-12-23
+3040274	Clot de Graupont	Clot de Graupont		42.48333	1.46667	H	RVN	AD		00				0		1148	Europe/Andorra	1993-12-23
+3040275	Grau d’Incles	Grau d'Incles		42.58333	1.65	L	LCTY	AD		00				0		1767	Europe/Andorra	1993-12-23
+3040276	Grau del Cabrer	Grau del Cabrer		42.56667	1.71667	L	LCTY	AD		00				0		2219	Europe/Andorra	1993-12-23
+3040277	Plana del Grau	Plana del Grau		42.58333	1.51667	T	UPLD	AD		00				0		1722	Europe/Andorra	1993-12-23
+3040278	Costa del Grau	Costa del Grau		42.56667	1.6	T	SLP	AD		00				0		1655	Europe/Andorra	1993-12-23
+3040279	Canal del Grau	Canal del Grau		42.56667	1.6	H	STM	AD		00				0		1655	Europe/Andorra	1993-12-23
+3040280	Canal Gran de la Serrera	Canal Gran de la Serrera		42.61667	1.58333	H	RVN	AD		00				0		2374	Europe/Andorra	1993-12-23
+3040281	Canal Gran de la Grella	Canal Gran de la Grella		42.51667	1.51667	H	STM	AD		00				0		1265	Europe/Andorra	1993-12-23
+3040282	Planell Gran de la Cebollera	Planell Gran de la Cebollera		42.63333	1.58333	T	UPLD	AD		00				0		2470	Europe/Andorra	1993-12-23
+3040283	Costa de les Grandalles	Costa de les Grandalles		42.53333	1.7	T	SLP	AD		00				0		2357	Europe/Andorra	1993-12-23
+3040284	Planell Gran	Planell Gran		42.63333	1.55	T	UPLD	AD		00				0		2053	Europe/Andorra	1993-12-23
+3040285	Planell Gran	Planell Gran		42.58333	1.66667	T	UPLD	AD		00				0		2159	Europe/Andorra	1993-12-23
+3040286	Planell Gran	Planell Gran		42.56667	1.46667	T	UPLD	AD		00				0		1673	Europe/Andorra	1993-12-23
+3040287	Planell Gran	Planell Gran		42.55	1.68333	T	UPLD	AD		00				0		2254	Europe/Andorra	1993-12-23
+3040288	Planell Gran	Planell Gran		42.55	1.65	T	UPLD	AD		00				0		2432	Europe/Andorra	1993-12-23
+3040289	Planell Gran	Planell Gran		42.46667	1.56667	T	UPLD	AD		00				0		2365	Europe/Andorra	1993-12-23
+3040290	Costa Gran	Costa Gran		42.61667	1.61667	T	SLP	AD		00				0		2352	Europe/Andorra	1993-12-23
+3040291	Costa Gran	Costa Gran		42.6	1.61667	T	SLP	AD		00				0		2271	Europe/Andorra	1993-12-23
+3040292	Costa Gran	Costa Gran		42.51667	1.48333	T	SLP	AD		00				0		1839	Europe/Andorra	1993-12-23
+3040293	Canal Gran	Canal Gran		42.58333	1.46667	H	STM	AD		00				0		1643	Europe/Andorra	1993-12-23
+3040294	Canal Gran	Canal Gran		42.56667	1.51667	H	STM	AD		00				0		1500	Europe/Andorra	1993-12-23
+3040295	Canal Gran	Canal Gran		42.56667	1.48333	H	STM	AD		00				0		1508	Europe/Andorra	1993-12-23
+3040296	Canal Gran	Canal Gran		42.55	1.58333	H	STM	AD		00				0		1499	Europe/Andorra	1993-12-23
+3040297	Canal Gran	Canal Gran		42.55	1.5	H	STM	AD		00				0		1292	Europe/Andorra	1993-12-23
+3040298	Canal Gran	Canal Gran		42.5	1.63333	H	STM	AD		00				0		2545	Europe/Andorra	1993-12-23
+3040299	Canal Gran	Canal Gran		42.48333	1.48333	H	STM	AD		00				0		981	Europe/Andorra	1993-12-23
+3040300	Canal Gran	Canal Gran		42.46667	1.48333	H	STM	AD		00				0		1134	Europe/Andorra	1993-12-23
+3040301	Basera Gran	Basera Gran		42.5	1.48333	T	CLF	AD		00				0		1316	Europe/Andorra	1993-12-23
+3040302	Plana de Gral	Plana de Gral		42.58333	1.48333	T	UPLD	AD		00				0		1809	Europe/Andorra	1993-12-23
+3040303	Canya de les Grailes	Canya de les Grailes		42.48333	1.51667	S	CAVE	AD		00				0		2061	Europe/Andorra	1993-12-23
+3040304	Canya de les Grailes	Canya de les Grailes		42.46667	1.46667	S	CAVE	AD		00				0		1340	Europe/Andorra	1993-12-23
+3040305	Canal de les Grailes	Canal de les Grailes		42.5	1.61667	H	STM	AD		00				0		2560	Europe/Andorra	1993-12-23
+3040306	Roc del Grailer	Roc del Grailer		42.56667	1.51667	T	RK	AD		00				0		1500	Europe/Andorra	1993-12-23
+3040307	Tartera del Goter	Tartera del Goter		42.56667	1.73333	T	TAL	AD		00				0		2096	Europe/Andorra	1993-12-23
+3040308	Roc del Goter	Roc del Goter		42.56667	1.73333	T	CLF	AD		00				0		2096	Europe/Andorra	1993-12-23
+3040309	Pala del Goter	Pala del Goter		42.56667	1.73333	T	SLP	AD		00				0		2096	Europe/Andorra	1993-12-23
+3040310	Bosc de la Gonarda	Bosc de la Gonarda		42.55	1.53333	V	FRST	AD		00				0		1593	Europe/Andorra	1993-12-23
+3040311	Coll de Gomà	Coll de Goma		42.55	1.55	T	PASS	AD		00				0		2097	Europe/Andorra	1993-12-23
+3040312	Planell de Ginestar	Planell de Ginestar		42.6	1.55	T	UPLD	AD		00				0		2298	Europe/Andorra	1993-12-23
+3040313	Bony de la Ginebrera	Bony de la Ginebrera		42.45	1.46667	T	SPUR	AD		00				0		935	Europe/Andorra	1993-12-23
+3040314	Font del Ginebre	Font del Ginebre		42.43333	1.45	H	SPNG	AD		00				0		877	Europe/Andorra	1993-12-23
+3040315	Prat del Gilet	Prat del Gilet		42.51667	1.6	L	GRAZ	AD		00				0		2085	Europe/Andorra	1993-12-23
+3040316	Pla del Géspit	Pla del Gespit		42.55	1.61667	T	UPLD	AD		00				0		2206	Europe/Andorra	1993-12-23
+3040317	Pala del Géspit	Pala del Gespit		42.61667	1.55	T	SLP	AD		00				0		2007	Europe/Andorra	1993-12-23
+3040318	Pala del Géspit	Pala del Gespit		42.56667	1.55	T	SLP	AD		00				0		1996	Europe/Andorra	1993-12-23
+3040319	Borda del Germà	Borda del Germa		42.45	1.48333	S	FRM	AD		00				0		1111	Europe/Andorra	1993-12-23
+3040320	Costa de les Gerderes	Costa de les Gerderes		42.55	1.6	T	SLP	AD		00				0		2210	Europe/Andorra	1993-12-23
+3040321	Cortal del Genret	Cortal del Genret		42.45	1.5	S	CRRL	AD		00				0		1614	Europe/Andorra	1993-12-23
+3040322	Clot del Gel	Clot del Gel		42.51667	1.48333	H	RVN	AD		00				0		1839	Europe/Andorra	1993-12-23
+3040323	Canal del Gel	Canal del Gel		42.48333	1.46667	H	STM	AD		00				0		1148	Europe/Andorra	1993-12-23
+3040324	Solana de la Gaverna	Solana de la Gaverna		42.51667	1.6	T	SLP	AD		00				0		2085	Europe/Andorra	1993-12-23
+3040325	Font de la Gavatxa	Font de la Gavatxa		42.53333	1.73333	H	SPNG	AD		00				0		2300	Europe/Andorra	1993-12-23
+3040326	Borda del Gastó	Borda del Gasto		42.45	1.46667	S	HUT	AD		00				0		935	Europe/Andorra	1993-12-23
+3040327	Serrat de la Garriga	Serrat de la Garriga		42.53333	1.61667	T	SPUR	AD		00				0		2237	Europe/Andorra	1993-12-23
+3040328	Crestes de Gargantillar	Crestes de Gargantillar		42.50208	1.63061	T	RDGE	AD		00				0		2666	Europe/Andorra	2011-04-19
+3040329	Collades de Gargantillar	Collades de Gargantillar		42.5	1.65	T	PASS	AD		00				0		2542	Europe/Andorra	1993-12-23
+3040330	Clots de Gargantillar	Clots de Gargantillar		42.5	1.65	H	RVN	AD		00				0		2542	Europe/Andorra	1993-12-23
+3040331	Gargantillar	Gargantillar		42.5	1.65	A	ADMD	AD		00				0		2542	Europe/Andorra	1993-12-23
+3040332	Roc de la Garganta	Roc de la Garganta		42.55	1.6	T	RK	AD		00				0		2210	Europe/Andorra	1993-12-23
+3040333	Pas dels Gargalls	Pas dels Gargalls		42.6	1.68333	T	PASS	AD		00				0		2089	Europe/Andorra	1993-12-23
+3040334	Bosc de la Gargallosa	Bosc de la Gargallosa		42.56667	1.51667	V	FRST	AD		00				0		1500	Europe/Andorra	1993-12-23
+3040335	Torrent del Gargallet	Torrent del Gargallet		42.45	1.53333	H	STM	AD		00				0		1859	Europe/Andorra	1993-12-23
+3040336	Font del Gargallet	Font del Gargallet		42.45	1.53333	H	SPNG	AD		00				0		1859	Europe/Andorra	1993-12-23
+3040337	Clotada del Gargallet	Clotada del Gargallet		42.45	1.55	H	RVN	AD		00				0		2457	Europe/Andorra	1993-12-23
+3040338	Solana de Galliner	Solana de Galliner		42.56667	1.46667	T	SLP	AD		00				0		1673	Europe/Andorra	1993-12-23
+3040339	Riu de Galliner	Riu de Galliner		42.56667	1.48333	H	STM	AD		00				0		1508	Europe/Andorra	1993-12-23
+3040340	Galliner	Galliner		42.56667	1.46667	A	ADMD	AD		00				0		1673	Europe/Andorra	1993-12-23
+3040341	Planell de la Gallina	Planell de la Gallina		42.43333	1.5	T	UPLD	AD		00				0		1804	Europe/Andorra	1993-12-23
+3040342	Coll de la Gallina	Coll de la Gallina		42.46667	1.45	T	PASS	AD		00				0		1562	Europe/Andorra	1993-12-23
+3040343	Canal dels Gais	Canal dels Gais		42.48333	1.55	H	STM	AD		00				0		2233	Europe/Andorra	1993-12-23
+3040344	Borda del Gabriel	Borda del Gabriel		42.56667	1.58333	S	HUT	AD		00				0		1919	Europe/Andorra	1993-12-23
+3040345	Canal de la Fusta	Canal de la Fusta		42.55	1.55	H	RVN	AD		00				0		2097	Europe/Andorra	1993-12-23
+3040346	Font Freda	Font Freda		42.63333	1.56667	H	SPNG	AD		00				0		2394	Europe/Andorra	1993-12-23
+3040347	Font Freda	Font Freda		42.56667	1.46667	H	SPNG	AD		00				0		1673	Europe/Andorra	1993-12-23
+3040348	Font Freda	Font Freda		42.45	1.5	H	SPNG	AD		00				0		1614	Europe/Andorra	1993-12-23
+3040349	Font Fred	Font Fred		42.48333	1.6	H	SPNG	AD		00				0		2250	Europe/Andorra	1993-12-23
+3040350	Francolí	Francoli		42.48333	1.43333	A	ADMD	AD		00				0		1938	Europe/Andorra	1993-12-23
+3040351	Obaga Fosca	Obaga Fosca		42.48333	1.43333	T	SLP	AD		00				0		1938	Europe/Andorra	1993-12-23
+3040352	Collada Fosca	Collada Fosca		42.43333	1.5	T	SPUR	AD		00				0		1804	Europe/Andorra	1993-12-23
+3040353	Canal Fosca	Canal Fosca		42.6	1.53333	H	STM	AD		00				0		1695	Europe/Andorra	1993-12-23
+3040354	Canal Fosca	Canal Fosca		42.5	1.56667	H	STM	AD		00				0		1776	Europe/Andorra	1993-12-23
+3040355	Bosc Fosc	Bosc Fosc		42.56667	1.66667	V	FRST	AD		00				0		1938	Europe/Andorra	1993-12-23
+3040356	Pleta del Forquilló	Pleta del Forquillo		42.61667	1.7	L	GRAZ	AD		00				0		2285	Europe/Andorra	1993-12-23
+3040357	Collet de Forns	Collet de Forns		42.48333	1.48333	T	SPUR	AD		00				0		981	Europe/Andorra	1993-12-23
+3040358	Canals de la Forniga	Canals de la Forniga		42.5	1.45	H	STM	AD		00				0		1840	Europe/Andorra	1993-12-23
+3040359	Prat del Fornet	Prat del Fornet		42.55	1.61667	L	GRAZ	AD		00				0		2206	Europe/Andorra	1993-12-23
+3040360	Font del Fornell	Font del Fornell		42.45	1.5	H	SPNG	AD		00				0		1614	Europe/Andorra	1993-12-23
+3040361	Canal del Forn de la Calç	Canal del Forn de la Calc		42.55	1.55	H	STM	AD		00				0		2097	Europe/Andorra	1993-12-23
+3040362	Forn de Cals	Forn de Cals		42.56667	1.6	L	LCTY	AD		00				0		1655	Europe/Andorra	1993-12-23
+3040363	Turó del Forn	Turo del Forn		42.63333	1.58333	T	PK	AD		00				0		2470	Europe/Andorra	1993-12-23
+3040364	Torrent del Forn	Torrent del Forn		42.5	1.51667	H	STM	AD		00				0		1410	Europe/Andorra	1993-12-23
+3040365	Serrat del Forn	Serrat del Forn		42.55	1.51667	T	SPUR	AD		00				0		1397	Europe/Andorra	1993-12-23
+3040366	Roca del Forn	Roca del Forn		42.55	1.61667	T	RK	AD		00				0		2206	Europe/Andorra	1993-12-23
+3040367	Riu del Forn	Riu del Forn		42.55	1.6	H	STM	AD		00				0		2210	Europe/Andorra	1993-12-23
+3040368	Portella del Forn	Portella del Forn		42.63333	1.58333	T	PASS	AD		00				0		2470	Europe/Andorra	1993-12-23
+3040369	Planells del Forn	Planells del Forn		42.55	1.6	T	UPLD	AD		00				0		2210	Europe/Andorra	1993-12-23
+3040370	Obaga del Forn	Obaga del Forn		42.48333	1.43333	T	SLP	AD		00				0		1938	Europe/Andorra	1993-12-23
+3040371	Clots del Forn	Clots del Forn		42.63333	1.58333	H	RVN	AD		00				0		2470	Europe/Andorra	1993-12-23
+3040372	Carretera del Forn	Carretera del Forn		42.55	1.58333	R	RD	AD		00				0		1499	Europe/Andorra	1993-12-23
+3040373	Carrerada del Forn	Carrerada del Forn		42.56667	1.63333	R	TRL	AD		00				0		2016	Europe/Andorra	1993-12-23
+3040374	Canal del Forn	Canal del Forn		42.56667	1.51667	H	STM	AD		00				0		1500	Europe/Andorra	1993-12-23
+3040375	Camí del Forn	Cami del Forn		42.55	1.58333	R	TRL	AD		00				0		1499	Europe/Andorra	1993-12-23
+3040376	Estanys Forcats	Estanys Forcats		42.6	1.45	H	LKS	AD		00				0		2174	Europe/Andorra	1993-12-23
+3040377	Torrent Forcat	Torrent Forcat		42.45	1.51667	H	STM	AD		00				0		1790	Europe/Andorra	1993-12-23
+3040378	Estany Forcat	Estany Forcat		42.49439	1.63825	H	LK	AD		00				0		2538	Europe/Andorra	2011-04-19
+3040379	Estany Forcat	Estany Forcat		42.5	1.63333	H	LK	AD		00				0		2545	Europe/Andorra	1993-12-23
+3040380	Forats de l’Óssa	Forats de l'Ossa		42.61667	1.53333	L	LCTY	AD		00				0		1609	Europe/Andorra	1993-12-23
+3040381	Forat Fosc	Forat Fosc		42.55	1.56667	L	LCTY	AD		00				0		1828	Europe/Andorra	1993-12-23
+3040382	Riu de Forat de Rius	Riu de Forat de Rius		42.58333	1.63333	H	STM	AD		00				0		1722	Europe/Andorra	1993-12-23
+3040383	Collada del Forat de Malhiverns	Collada del Forat de Malhiverns		42.6	1.43333	T	PASS	AD		00				0		2667	Europe/Andorra	1993-12-23
+3040384	Cresta del Forat dels Malhiverns	Cresta del Forat dels Malhiverns	Cresta del Forat dels Malhiverns	42.6	1.43333	T	RDGE	AD		00				0		2667	Europe/Andorra	2011-11-05
+3040385	Forat dels Malhiverns	Forat dels Malhiverns		42.6	1.45	L	LCTY	AD		00				0		2174	Europe/Andorra	1993-12-23
+3040386	Forat dels Clots de Massat	Forat dels Clots de Massat		42.55	1.7	L	LCTY	AD		00				0		2358	Europe/Andorra	1993-12-23
+3040387	Forat d’Arau	Forat d'Arau		42.56667	1.48333	L	LCTY	AD		00				0		1508	Europe/Andorra	1993-12-23
+3040388	Coma del Forat	Coma del Forat		42.61667	1.48333	H	STMH	AD		00				0		2470	Europe/Andorra	1993-12-23
+3040389	Font de Fontverd	Font de Fontverd		42.5	1.6	H	SPNG	AD		00				0		2416	Europe/Andorra	1993-12-23
+3040390	Fontverd	Fontverd		42.48333	1.6	S	RUIN	AD		00				0		2250	Europe/Andorra	1993-12-23
+3040391	Fontverd	Fontverd		42.5	1.6	A	ADMD	AD		00				0		2416	Europe/Andorra	1993-12-23
+3040392	Riu de les Fonts de la Tosa	Riu de les Fonts de la Tosa		42.6	1.68333	H	STM	AD		00				0		2089	Europe/Andorra	1993-12-23
+3040393	Tosa de les Fonts	Tosa de les Fonts		42.55	1.65	T	UPLD	AD		00				0		2432	Europe/Andorra	1993-12-23
+3040394	Pic de les Fonts	Pic de les Fonts		42.6	1.6	T	PK	AD		00				0		2143	Europe/Andorra	1993-12-23
+3040395	Pic de les Fonts	Pic de les Fonts		42.6	1.48333	T	PK	AD		00				0		2441	Europe/Andorra	1993-12-23
+3040396	Estany de les Fonts	Estany de les Fonts		42.51667	1.66667	H	LK	AD		00				0		2410	Europe/Andorra	1993-12-23
+3040397	Comella de les Fonts	Comella de les Fonts		42.45	1.46667	H	STM	AD		00				0		935	Europe/Andorra	1993-12-23
+3040398	Clots de les Fonts	Clots de les Fonts		42.58333	1.68333	H	RVN	AD		00				0		2294	Europe/Andorra	1993-12-23
+3040399	Clot de les Fonts	Clot de les Fonts		42.55	1.65	H	RVN	AD		00				0		2432	Europe/Andorra	1993-12-23
+3040400	Clot de les Fonts	Clot de les Fonts		42.46667	1.45	H	RVN	AD		00				0		1562	Europe/Andorra	1993-12-23
+3040401	Clotada de les Fonts	Clotada de les Fonts		42.6	1.48333	T	SLP	AD		00				0		2441	Europe/Andorra	1993-12-23
+3040402	Canal de les Fonts	Canal de les Fonts		42.61667	1.51667	H	STM	AD		00				0		1716	Europe/Andorra	1993-12-23
+3040403	Bosc de les Fonts	Bosc de les Fonts		42.56667	1.6	V	FRST	AD		00				0		1655	Europe/Andorra	1993-12-23
+3040404	Aspre de les Fonts	Aspre de les Fonts		42.6	1.48333	V	VINS	AD		00				0		2441	Europe/Andorra	1993-12-23
+3040405	Bosc de la Font Roja	Bosc de la Font Roja		42.55	1.45	V	FRST	AD		00				0		1788	Europe/Andorra	1993-12-23
+3040406	Collet de Font Podrida	Collet de Font Podrida		42.58333	1.46667	T	PK	AD		00				0		1643	Europe/Andorra	1993-12-23
+3040407	Canal de la Font Llarga	Canal de la Font Llarga		42.6	1.65	H	STM	AD		00				0		2131	Europe/Andorra	1993-12-23
+3040408	Costa de Font Freda	Costa de Font Freda		42.63333	1.56667	T	SLP	AD		00				0		2394	Europe/Andorra	1993-12-23
+3040409	Font de Fontduí	Font de Fontdui		42.56667	1.68333	H	SPNG	AD		00				0		2340	Europe/Andorra	1993-12-23
+3040410	Obaga de la Font dels Pets	Obaga de la Font dels Pets		42.6	1.48333	T	SLP	AD		00				0		2441	Europe/Andorra	1993-12-23
+3040411	Torrent de la Font dels Pals	Torrent de la Font dels Pals		42.45	1.51667	H	STM	AD		00				0		1790	Europe/Andorra	1993-12-23
+3040412	Font del Solà	Font del Sola		42.58333	1.66667	H	STM	AD		00				0		2159	Europe/Andorra	1993-12-23
+3040413	Costa de la Font dels Miquelets	Costa de la Font dels Miquelets		42.58333	1.43333	T	SLP	AD		00				0		2412	Europe/Andorra	1993-12-23
+3040414	Font dels Comellassos	Font dels Comellassos		42.6	1.68333	H	STM	AD		00				0		2089	Europe/Andorra	1993-12-23
+3040415	Bosc de la Font del Pi	Bosc de la Font del Pi		42.58333	1.53333	V	FRST	AD		00				0		1924	Europe/Andorra	1993-12-23
+3040416	Bosc de la Font del Pascol	Bosc de la Font del Pascol		42.53333	1.55	V	FRST	AD		00				0		1344	Europe/Andorra	1993-12-23
+3040417	Serrat de la Font del Mallol	Serrat de la Font del Mallol		42.55	1.55	T	SPUR	AD		00				0		2097	Europe/Andorra	1993-12-23
+3040418	Bosc de la Font del Mallol	Bosc de la Font del Mallol		42.55	1.55	V	FRST	AD		00				0		2097	Europe/Andorra	1993-12-23
+3040419	Canal de la Font del Llop	Canal de la Font del Llop		42.55	1.46667	H	STM	AD		00				0		1585	Europe/Andorra	1993-12-23
+3040420	Bosc de la Font del Gripal	Bosc de la Font del Gripal		42.51667	1.5	V	FRST	AD		00				0		1688	Europe/Andorra	1993-12-23
+3040421	Canal de la Font del Cuc	Canal de la Font del Cuc		42.48333	1.51667	H	STM	AD		00				0		2061	Europe/Andorra	1993-12-23
+3040422	Canal de la Font del Boix	Canal de la Font del Boix		42.55	1.48333	H	STM	AD		00				0		1548	Europe/Andorra	1993-12-23
+3040423	Bosc de la Font del Bisbe	Bosc de la Font del Bisbe		42.55	1.46667	V	FRST	AD		00				0		1585	Europe/Andorra	1993-12-23
+3040424	Barranc de la Font de la Pauca	Barranc de la Font de la Pauca		42.56667	1.58333	H	STM	AD		00				0		1919	Europe/Andorra	1993-12-23
+3040425	Canal de la Font de l’Angleveta	Canal de la Font de l'Angleveta		42.53333	1.5	H	STM	AD		00				0		1357	Europe/Andorra	1993-12-23
+3040426	Planell de la Font de l’Altar	Planell de la Font de l'Altar		42.55	1.41667	T	UPLD	AD		00				0		2105	Europe/Andorra	1993-12-23
+3040427	Canal de la Font de la Gallina	Canal de la Font de la Gallina		42.48333	1.46667	H	STM	AD		00				0		1148	Europe/Andorra	1993-12-23
+3040428	Oratori de la Font de Joans	Oratori de la Font de Joans		42.48333	1.48333	S	AMTH	AD		00				0		981	Europe/Andorra	1993-12-23
+3040429	Basers de la Font de Joans	Basers de la Font de Joans		42.48333	1.48333	T	CLF	AD		00				0		981	Europe/Andorra	1993-12-23
+3040430	Canal de la Font de Gambada	Canal de la Font de Gambada		42.56667	1.5	H	STM	AD		00				0		1636	Europe/Andorra	1993-12-23
+3040431	Fontauzina	Fontauzina		42.58333	1.63333	T	SLP	AD		00				0		1722	Europe/Andorra	1993-12-23
+3040432	Camí de Fontargent	Cami de Fontargent		42.61667	1.71667	R	TRL	AD		00				0		2352	Europe/Andorra	1993-12-23
+3040433	Barranc de la Font Antiga	Barranc de la Font Antiga		42.55	1.48333	H	STM	AD		00				0		1548	Europe/Andorra	1993-12-23
+3040434	Canal de les Fontanelles	Canal de les Fontanelles		42.45	1.48333	H	STM	AD		00				0		1111	Europe/Andorra	1993-12-23
+3040435	Pont de Fontaneda	Pont de Fontaneda		42.46667	1.48333	S	BDG	AD		00				0		1134	Europe/Andorra	1993-12-23
+3040436	Boscarró de Fontaneda	Boscarro de Fontaneda		42.45	1.48333	V	FRST	AD		00				0		1111	Europe/Andorra	1993-12-23
+3040437	Fontaneda	Fontaneda		42.45432	1.46402	P	PPL	AD		06				0		1253	Europe/Andorra	2011-04-19
+3040438	Fontanals del Pui	Fontanals del Pui		42.46667	1.48333	H	STM	AD		00				0		1134	Europe/Andorra	1993-12-23
+3040439	Bosc dels Fontanals	Bosc dels Fontanals		42.55	1.58333	V	FRST	AD		00				0		1499	Europe/Andorra	1993-12-23
+3040440	Font del Fontanal	Font del Fontanal		42.46667	1.5	H	SPNG	AD		00				0		1383	Europe/Andorra	1993-12-23
+3040441	Canal del Fontanal	Canal del Fontanal		42.58333	1.65	H	RVN	AD		00				0		1767	Europe/Andorra	1993-12-23
+3040442	Riu de Font Amagada	Riu de Font Amagada		42.53333	1.53333	H	STM	AD		00				0		1521	Europe/Andorra	1993-12-23
+3040443	Planell de la Font	Planell de la Font		42.56667	1.66667	T	UPLD	AD		00				0		1938	Europe/Andorra	1993-12-23
+3040444	Costa de la Font	Costa de la Font		42.53333	1.51667	T	SLP	AD		00				0		1361	Europe/Andorra	1993-12-23
+3040445	Clot de la Font	Clot de la Font		42.6	1.66667	H	RVN	AD		00				0		1858	Europe/Andorra	1993-12-23
+3040446	Canal de la Font	Canal de la Font		42.58333	1.48333	H	STM	AD		00				0		1809	Europe/Andorra	1993-12-23
+3040447	Clots Fondos	Clots Fondos		42.55	1.61667	H	RVN	AD		00				0		2206	Europe/Andorra	1993-12-23
+3040448	Collada Fonda	Collada Fonda	Collada Fonda	42.46667	1.63333	T	PASS	AD		00				0		2619	Europe/Andorra	2011-11-05
+3040449	Canal Fonda	Canal Fonda		42.53333	1.61667	H	STM	AD		00				0		2237	Europe/Andorra	1993-12-23
+3040450	Cortal del Folc	Cortal del Folc		42.45	1.5	S	CRRL	AD		00				0		1614	Europe/Andorra	1993-12-23
+3040451	Planell del Fogal	Planell del Fogal		42.63333	1.56667	T	UPLD	AD		00				0		2394	Europe/Andorra	1993-12-23
+3040452	Planell de les Flores	Planell de les Flores		42.61667	1.5	T	UPLD	AD		00				0		2390	Europe/Andorra	1993-12-23
+3040453	Canal de les Flamies	Canal de les Flamies		42.58333	1.61667	H	RVN	AD		00				0		1707	Europe/Andorra	1993-12-23
+3040454	Roc del Fiter	Roc del Fiter		42.45	1.51667	T	CLF	AD		00				0		1790	Europe/Andorra	1993-12-23
+3040455	Borda del Fiter	Borda del Fiter		42.58333	1.61667	S	HUT	AD		00				0		1707	Europe/Andorra	1993-12-23
+3040456	Canal de la Fita	Canal de la Fita		42.53333	1.6	H	STM	AD		00				0		1888	Europe/Andorra	1993-12-23
+3040457	Bosc de la Fita	Bosc de la Fita		42.56667	1.53333	V	FRST	AD		00				0		1669	Europe/Andorra	1993-12-23
+3040458	Coll de Finestres	Coll de Finestres		42.43333	1.55	T	SPUR	AD		00				0		2178	Europe/Andorra	1993-12-23
+3040459	Coll de la Finestra	Coll de la Finestra		42.5	1.53333	T	SPUR	AD		00				0		1574	Europe/Andorra	1993-12-23
+3040460	Bosc de la Finestra	Bosc de la Finestra		42.55	1.46667	V	FRST	AD		00				0		1585	Europe/Andorra	1993-12-23
+3040461	Canal de les Fijoles	Canal de les Fijoles		42.58333	1.48333	H	RVN	AD		00				0		1809	Europe/Andorra	1993-12-23
+3040462	Canal de Fhasa	Canal de Fhasa		42.51667	1.56667	H	CNL	AD		00				0		1759	Europe/Andorra	1993-12-23
+3040463	Font de Ferrús	Font de Ferrus		42.51667	1.51667	H	SPNG	AD		00				0		1265	Europe/Andorra	1993-12-23
+3040464	Font de Ferro del Planell Gran	Font de Ferro del Planell Gran		42.53333	1.6	H	SPNG	AD		00				0		1888	Europe/Andorra	1993-12-23
+3040465	Font de Ferro	Font de Ferro		42.58333	1.58333	H	SPNG	AD		00				0		1993	Europe/Andorra	1993-12-23
+3040466	Font de Ferro	Font de Ferro		42.46667	1.48333	H	SPNG	AD		00				0		1134	Europe/Andorra	1993-12-23
+3040467	Solana de Ferreroles	Solana de Ferreroles		42.6	1.56667	T	SLP	AD		00				0		2513	Europe/Andorra	1993-12-23
+3040468	Riu de Ferreroles	Riu de Ferreroles		42.6	1.53333	H	STM	AD		00				0		1695	Europe/Andorra	1993-12-23
+3040469	Planells de Ferreroles	Planells de Ferreroles		42.6	1.55	T	UPLD	AD		00				0		2298	Europe/Andorra	1993-12-23
+3040470	Collada de Ferreroles	Collada de Ferreroles		42.61667	1.56667	T	PASS	AD		00				0		2228	Europe/Andorra	1993-12-23
+3040471	Clots de Ferreroles	Clots de Ferreroles		42.6	1.56667	H	RVN	AD		00				0		2513	Europe/Andorra	1993-12-23
+3040472	Pont de Ferreres	Pont de Ferreres		42.6	1.53333	S	BDG	AD		00				0		1695	Europe/Andorra	1993-12-23
+3040473	Font del Feritxet	Font del Feritxet		42.51667	1.6	H	SPNG	AD		00				0		2085	Europe/Andorra	1993-12-23
+3040474	Feritxet	Feritxet		42.51667	1.6	A	ADMD	AD		00				0		2085	Europe/Andorra	1993-12-23
+3040475	Font del Fenoll	Font del Fenoll		42.58333	1.46667	H	SPNG	AD		00				0		1643	Europe/Andorra	1993-12-23
+3040476	Barranc del Fenetau	Barranc del Fenetau		42.6	1.66667	H	STM	AD		00				0		1858	Europe/Andorra	1993-12-23
+3040477	Bosc dels Feners	Bosc dels Feners		42.43333	1.5	V	FRST	AD		00				0		1804	Europe/Andorra	1993-12-23
+3040478	Bordes dels Fenerols	Bordes dels Fenerols		42.53333	1.5	S	HUTS	AD		00				0		1357	Europe/Andorra	1993-12-23
+3040479	Fener Llong	Fener Llong		42.55	1.55	L	LCTY	AD		00				0		2097	Europe/Andorra	1993-12-23
+3040480	Canal de Fener de Cussols	Canal de Fener de Cussols		42.53333	1.51667	H	STM	AD		00				0		1361	Europe/Andorra	1993-12-23
+3040481	Fener de Comadejó	Fener de Comadejo		42.5	1.48333	L	LCTY	AD		00				0		1316	Europe/Andorra	1993-12-23
+3040482	Canal del Fener Blanc	Canal del Fener Blanc		42.48333	1.48333	H	STM	AD		00				0		981	Europe/Andorra	1993-12-23
+3040483	Torrent dels Fenerals	Torrent dels Fenerals		42.5	1.45	H	STM	AD		00				0		1840	Europe/Andorra	1993-12-23
+3040484	Obagues dels Fenerals	Obagues dels Fenerals		42.5	1.45	T	SLP	AD		00				0		1840	Europe/Andorra	1993-12-23
+3040485	Bosc de les Fenemores	Bosc de les Fenemores		42.53333	1.48333	V	FRST	AD		00				0		1677	Europe/Andorra	1993-12-23
+3040486	Borda del Fenemars	Borda del Fenemars		42.58333	1.63333	S	HUT	AD		00				0		1722	Europe/Andorra	1993-12-23
+3040487	Barraca de Fembra Morta	Barraca de Fembra Morta		42.56667	1.71667	S	HUT	AD		00				0		2219	Europe/Andorra	1993-12-23
+3040488	Bosc del Felegrill	Bosc del Felegrill		42.53333	1.53333	V	FRST	AD		00				0		1521	Europe/Andorra	1993-12-23
+3040489	Bosc de les Feixes	Bosc de les Feixes		42.55	1.43333	V	FRST	AD		00				0		1949	Europe/Andorra	1993-12-23
+3040490	Feixar de Setut	Feixar de Setut		42.46667	1.63333	L	LCTY	AD		00				0		2619	Europe/Andorra	1993-12-23
+3040491	Feixar del Baladre	Feixar del Baladre		42.65	1.55	L	LCTY	AD		00				0		2181	Europe/Andorra	1993-12-23
+3040492	Pic del Feixar	Pic del Feixar		42.46667	1.63333	T	PK	AD		00				0		2619	Europe/Andorra	1993-12-23
+3040493	Font del Feixar	Font del Feixar		42.46667	1.63333	H	SPNG	AD		00				0		2619	Europe/Andorra	1993-12-23
+3040494	Feixants de Xixerella	Feixants de Xixerella		42.55	1.48333	L	LCTY	AD		00				0		1548	Europe/Andorra	1993-12-23
+3040495	Feixa de l’Escobar	Feixa de l'Escobar		42.43333	1.5	L	LCTY	AD		00				0		1804	Europe/Andorra	1993-12-23
+3040496	Clots de la Febrerrussa	Clots de la Febrerrussa		42.46667	1.55	H	RVN	AD		00				0		2341	Europe/Andorra	1993-12-23
+3040497	Canal del Favar	Canal del Favar		42.51667	1.53333	H	STM	AD		00				0		1460	Europe/Andorra	1993-12-23
+3040498	Solà de Faucellers	Sola de Faucellers		42.45	1.5	T	SLP	AD		00				0		1614	Europe/Andorra	1993-12-23
+3040499	Pont de Faucellers	Pont de Faucellers		42.45	1.5	S	BDG	AD		00				0		1614	Europe/Andorra	1993-12-23
+3040500	Faucellers	Faucellers		42.45	1.5	L	LCTY	AD		00				0		1614	Europe/Andorra	1993-12-23
+3040501	Serrat de la Farga	Serrat de la Farga		42.45	1.53333	T	RDGE	AD		00				0		1859	Europe/Andorra	1993-12-23
+3040502	Pont de la Farga	Pont de la Farga		42.61667	1.53333	S	BDG	AD		00				0		1609	Europe/Andorra	1993-12-23
+3040503	Barraca de la Farga	Barraca de la Farga		42.5	1.6	S	HUT	AD		00				0		2416	Europe/Andorra	1993-12-23
+3040504	Roc del Far	Roc del Far		42.43333	1.5	T	RK	AD		00				0		1804	Europe/Andorra	1993-12-23
+3040505	Plana del Far	Plana del Far		42.48333	1.41667	T	SLP	AD		00				0		1920	Europe/Andorra	1993-12-23
+3040506	Fangots de Moretó	Fangots de Moreto		42.55	1.68333	L	LCTY	AD		00				0		2254	Europe/Andorra	1993-12-23
+3040507	Fangots dels Maians	Fangots dels Maians		42.55	1.61667	L	LCTY	AD		00				0		2206	Europe/Andorra	1993-12-23
+3040508	Fangot Gran	Fangot Gran		42.53333	1.63333	L	LCTY	AD		00				0		2360	Europe/Andorra	1993-12-23
+3040509	Canya de les Falgueres	Canya de les Falgueres		42.45	1.46667	S	CAVE	AD		00				0		935	Europe/Andorra	1993-12-23
+3040510	Falconeres	Falconeres		42.55	1.7	L	LCTY	AD		00				0		2358	Europe/Andorra	1993-12-23
+3040511	Serra de Falcobí	Serra de Falcobi		42.63333	1.55	T	MT	AD		00				0		2053	Europe/Andorra	1993-12-23
+3040512	Canya dels Eucassers	Canya dels Eucassers		42.55	1.61667	S	CAVE	AD		00				0		2206	Europe/Andorra	1993-12-23
+3040513	Cabana de l' Eucasser	Cabana de l' Eucasser		42.63086	1.48341	S	HUT	AD		07				0		2111	Europe/Andorra	2007-03-04
+3040514	Cabana de l’ Eucasser	Cabana de l' Eucasser		42.61667	1.56667	S	HUT	AD		00				0		2228	Europe/Andorra	1993-12-23
+3040515	Cabana de l’ Eucasser	Cabana de l' Eucasser		42.58333	1.61667	S	HUT	AD		00				0		1707	Europe/Andorra	1993-12-23
+3040516	Pala Estreta	Pala Estreta		42.55	1.7	T	SLP	AD		00				0		2358	Europe/Andorra	1993-12-23
+3040517	Torrent Estret	Torrent Estret		42.53333	1.56667	H	STM	AD		00				0		1418	Europe/Andorra	1993-12-23
+3040518	Cortal de Estevet	Cortal de Estevet		42.5	1.53333	S	CRRL	AD		00				0		1574	Europe/Andorra	1993-12-23
+3040519	Collada dels Estanys Forcats	Collada dels Estanys Forcats	Collada dels Estanys Forcats	42.6	1.45	T	PK	AD		00				0		2174	Europe/Andorra	2011-11-05
+3040520	Clots de l’ Estany Segon	Clots de l' Estany Segon		42.61667	1.73333	H	RVN	AD		00				0		2508	Europe/Andorra	1993-12-23
+3040521	Estanys de Tristaina	Estanys de Tristaina		42.64217	1.48615	H	LKS	AD		07				0		2530	Europe/Andorra	2007-04-29
+3040522	Estanys del Pessons	Estanys del Pessons		42.51667	1.66667	L	LCTY	AD		00				0		2410	Europe/Andorra	1993-12-23
+3040523	Estanys de l’Obac	Estanys de l'Obac		42.51667	1.66667	L	LCTY	AD		00				0		2410	Europe/Andorra	1993-12-23
+3040524	Estanys de la Solana	Estanys de la Solana		42.53333	1.66667	L	LCTY	AD		00				0		2489	Europe/Andorra	1993-12-23
+3040525	Estanys de Juclar	Estanys de Juclar		42.61667	1.71667	L	LCTY	AD		00				0		2352	Europe/Andorra	1993-12-23
+3040526	Serra dels Estanys	Serra dels Estanys		42.58333	1.58333	T	RDGE	AD		00				0		1993	Europe/Andorra	1993-12-23
+3040527	Riu dels Estanys	Riu dels Estanys		42.6	1.61667	H	STM	AD		00				0		2271	Europe/Andorra	1993-12-23
+3040528	Collada dels Estanys	Collada dels Estanys		42.58333	1.58333	T	PASS	AD		00				0		1993	Europe/Andorra	1993-12-23
+3040529	Camí dels Estanys	Cami dels Estanys		42.48333	1.65	R	TRL	AD		00				0		2658	Europe/Andorra	1993-12-23
+3040530	Cabana dels Estanys	Cabana dels Estanys		42.48333	1.65	S	HUT	AD		00				0		2658	Europe/Andorra	1993-12-23
+3040531	Estanyons de Banyell	Estanyons de Banyell		42.65	1.56667	L	LCTY	AD		00				0		2471	Europe/Andorra	1993-12-23
+3040532	Pic dels Estanyons	Pic dels Estanyons	Pic dels Estanyons,Toseta de la Colilla,Tosseta de la Caulla,Tosseta de la Caülla	42.46667	1.61667	T	PK	AD		00				0		2448	Europe/Andorra	2011-11-05
+3040533	Estanys dels Estanyons	Estanys dels Estanyons		42.46667	1.61667	H	LKS	AD		00				0		2448	Europe/Andorra	1993-12-23
+3040534	Canals Tancades dels Estanyons	Canals Tancades dels Estanyons		42.46667	1.63333	H	RVN	AD		00				0		2619	Europe/Andorra	1993-12-23
+3040535	Bosc dels Estanyons	Bosc dels Estanyons		42.48333	1.63333	V	FRST	AD		00				0		2296	Europe/Andorra	1993-12-23
+3040536	Serra de l’ Estanyó	Serra de l' Estanyo		42.6	1.58333	T	MT	AD		00				0		2461	Europe/Andorra	1993-12-23
+3040537	Saleres de l’ Estanyó	Saleres de l' Estanyo		42.61667	1.56667	L	SALT	AD		00				0		2228	Europe/Andorra	1993-12-23
+3040538	Riu de l’ Estanyó	Riu de l' Estanyo		42.61667	1.56667	H	STM	AD		00				0		2228	Europe/Andorra	1993-12-23
+3040539	Pic de l’ Estanyó	Pic de l' Estanyo	Montagne de l' Estanyo,Montagne de l’ Estanyó,Pic de l' Estanyo,Pic de l’ Estanyó	42.6088	1.59235	T	PK	AD		00				0		2709	Europe/Andorra	2011-11-05
+3040540	Estret de l’ Estanyó	Estret de l' Estanyo		42.61667	1.56667	T	PASS	AD		00				0		2228	Europe/Andorra	1993-12-23
+3040541	Estany de l’ Estanyó	Estany de l' Estanyo		42.61667	1.58333	H	LK	AD		00				0		2374	Europe/Andorra	1993-12-23
+3040542	Clots de l’ Estanyó	Clots de l' Estanyo		42.61667	1.58333	H	STMH	AD		00				0		2374	Europe/Andorra	1993-12-23
+3040543	Canals de l’ Estanyó	Canals de l' Estanyo		42.6	1.58333	H	RVN	AD		00				0		2461	Europe/Andorra	1993-12-23
+3040544	Serrat de l’ Estany Negre	Serrat de l' Estany Negre		42.58333	1.43333	T	SPUR	AD		00				0		2412	Europe/Andorra	1993-12-23
+3040545	Rocs de l’ Estany Negre	Rocs de l' Estany Negre		42.58333	1.43333	T	RKS	AD		00				0		2412	Europe/Andorra	1993-12-23
+3040546	Basses de l’ Estany Negre	Basses de l' Estany Negre		42.58333	1.43333	H	LK	AD		00				0		2412	Europe/Andorra	1993-12-23
+3040547	Bony de l’ Estany Mort	Bony de l' Estany Mort		42.61667	1.61667	T	SPUR	AD		00				0		2352	Europe/Andorra	1993-12-23
+3040548	Roc de l’ Estany Moreno	Roc de l' Estany Moreno		42.51667	1.63333	T	CLF	AD		00				0		2379	Europe/Andorra	1993-12-23
+3040549	Pala de l’ Estany Gran	Pala de l' Estany Gran		42.6	1.58333	T	CLF	AD		00				0		2461	Europe/Andorra	1993-12-23
+3040550	Riu de l' Estany Esbalçat	Riu de l' Estany Esbalcat		42.63612	1.51736	H	STM	AD		07				0		2334	Europe/Andorra	2007-03-04
+3040551	Port de l’ Estany Esbalçat	Port de l' Estany Esbalcat		42.65	1.51667	T	PASS	AD		00				0		2546	Europe/Andorra	1993-12-23
+3040552	Costa de l’ Estany de Més Avall	Costa de l' Estany de Mes Avall		42.6	1.48333	T	SPUR	AD		00				0		2441	Europe/Andorra	1993-12-23
+3040553	Clots de l’ Estany de Més Avall	Clots de l' Estany de Mes Avall		42.6	1.48333	T	TAL	AD		00				0		2441	Europe/Andorra	1993-12-23
+3040554	Costa de l’ Estany de Més Amunt	Costa de l' Estany de Mes Amunt		42.61667	1.48333	T	SLP	AD		00				0		2470	Europe/Andorra	1993-12-23
+3040555	Costa de l’ Estany del Mig	Costa de l' Estany del Mig		42.65	1.48333	T	SLP	AD		00				0		2341	Europe/Andorra	1993-12-23
+3040556	Canals d’Amunt de l’ Estany del Mig	Canals d'Amunt de l' Estany del Mig		42.6	1.48333	H	RVN	AD		00				0		2441	Europe/Andorra	1993-12-23
+3040557	Riu de l’ Estany de l’Isla	Riu de l' Estany de l'Isla		42.61667	1.7	H	STM	AD		00				0		2285	Europe/Andorra	1993-12-23
+3040558	Canal de l’ Estany de l’Isla	Canal de l' Estany de l'Isla		42.61667	1.68333	H	RVN	AD		00				0		2406	Europe/Andorra	1993-12-23
+3040559	Solana de l’ Estany de l’Illa	Solana de l' Estany de l'Illa		42.5	1.65	T	SLP	AD		00				0		2542	Europe/Andorra	1993-12-23
+3040560	Baser de l’ Estany de les Truites	Baser de l' Estany de les Truites		42.56667	1.43333	T	CLF	AD		00				0		2402	Europe/Andorra	1993-12-23
+3040561	Turó de l’ Estany de la Nou	Turo de l' Estany de la Nou		42.46667	1.58333	T	SPUR	AD		00				0		2367	Europe/Andorra	1993-12-23
+3040562	Camí de l’ Estany de la Nou	Cami de l' Estany de la Nou		42.48333	1.58333	R	TRL	AD		00				0		2349	Europe/Andorra	1993-12-23
+3040563	Riu de l' Estany de Creussans	Riu de l' Estany de Creussans		42.63292	1.47976	H	STM	AD		07				0		2283	Europe/Andorra	2007-03-04
+3040564	Vial de l’ Estany Blau	Vial de l' Estany Blau		42.5	1.61667	R	RD	AD		00				0		2560	Europe/Andorra	1993-12-23
+3040565	Turo de l’ Estany Blau	Turo de l' Estany Blau		42.5	1.61667	T	PK	AD		00				0		2560	Europe/Andorra	1993-12-23
+3040566	Riu de l’ Estany Blau	Riu de l' Estany Blau		42.5	1.61667	H	STM	AD		00				0		2560	Europe/Andorra	1993-12-23
+3040567	Canals de l’ Estany Blau	Canals de l' Estany Blau		42.5	1.6	H	RVN	AD		00				0		2416	Europe/Andorra	1993-12-23
+3040568	Serrat de l’ Estany	Serrat de l' Estany		42.51667	1.56667	T	MT	AD		00				0		1759	Europe/Andorra	1993-12-23
+3040569	Riu de l’ Estany	Riu de l' Estany		42.58333	1.43333	H	STM	AD		00				0		2412	Europe/Andorra	1993-12-23
+3040570	Pla de l’ Estany	Pla de l' Estany		42.6	1.46667	T	UPLD	AD		00				0		2421	Europe/Andorra	1993-12-23
+3040571	Pleta de l’ Estall Serrer	Pleta de l' Estall Serrer		42.48333	1.61667	L	GRAZ	AD		00				0		2217	Europe/Andorra	1993-12-23
+3040572	Planells de l’ Estall Serrer	Planells de l' Estall Serrer		42.48333	1.61667	T	UPLD	AD		00				0		2217	Europe/Andorra	1993-12-23
+3040573	Canals de l’ Estall Serrer	Canals de l' Estall Serrer		42.48333	1.61667	H	RVN	AD		00				0		2217	Europe/Andorra	1993-12-23
+3040574	Bosc de l’ Estall Serrer	Bosc de l' Estall Serrer		42.48333	1.61667	V	FRST	AD		00				0		2217	Europe/Andorra	1993-12-23
+3040575	Estall Serrer	Estall Serrer		42.48333	1.61667	L	LCTY	AD		00				0		2217	Europe/Andorra	1993-12-23
+3040576	Estall Serrer	Estall Serrer		42.46667	1.61667	A	ADMD	AD		00				0		2448	Europe/Andorra	1993-12-23
+3040577	Roc de l’ Estall	Roc de l' Estall		42.5	1.58333	T	RK	AD		00				0		1888	Europe/Andorra	1993-12-23
+3040578	Planell de l’ Estall	Planell de l' Estall		42.55	1.55	T	UPLD	AD		00				0		2097	Europe/Andorra	1993-12-23
+3040579	Collada de l’ Estall	Collada de l' Estall		42.55	1.55	T	PASS	AD		00				0		2097	Europe/Andorra	1993-12-23
+3040580	Carretera de l’ Estall	Carretera de l' Estall		42.53333	1.53333	R	RD	AD		00				0		1521	Europe/Andorra	1993-12-23
+3040581	Borda de l’ Estall	Borda de l' Estall		42.55	1.55	S	HUT	AD		00				0		2097	Europe/Andorra	1993-12-23
+3040582	Pleta de l’ Estaleritx	Pleta de l' Estaleritx		42.61667	1.55	L	GRAZ	AD		00				0		2007	Europe/Andorra	1993-12-23
+3040583	Collada de l’ Estaleritx	Collada de l' Estaleritx		42.61667	1.55	T	SPUR	AD		00				0		2007	Europe/Andorra	1993-12-23
+3040584	Pont dels Esquirols	Pont dels Esquirols		42.56667	1.48333	S	BDG	AD		00				0		1508	Europe/Andorra	1993-12-23
+3040585	Roc de les Esquiroles	Roc de les Esquiroles		42.46667	1.5	T	RK	AD		00				0		1383	Europe/Andorra	1993-12-23
+3040586	Roc de les Esquiroles	Roc de les Esquiroles		42.45	1.48333	T	RK	AD		00				0		1111	Europe/Andorra	1993-12-23
+3040587	Canal de les Esquiroles	Canal de les Esquiroles		42.45	1.48333	H	STM	AD		00				0		1111	Europe/Andorra	1993-12-23
+3040588	Roc d’ Esquers	Roc d' Esquers		42.5	1.55	T	RK	AD		00				0		1566	Europe/Andorra	1993-12-23
+3040589	Roc de l’ Espluga	Roc de l' Espluga		42.46667	1.46667	T	RK	AD		00				0		1340	Europe/Andorra	1993-12-23
+3040590	Planell de l’ Espluga	Planell de l' Espluga		42.46667	1.46667	T	UPLD	AD		00				0		1340	Europe/Andorra	1993-12-23
+3040591	Tosa dels Espiolets	Tosa dels Espiolets		42.55	1.65	T	UPLD	AD		00				0		2432	Europe/Andorra	1993-12-23
+3040592	Espeluga	Espeluga		42.56667	1.43333	L	LCTY	AD		00				0		2402	Europe/Andorra	1993-12-23
+3040593	Espeluga	Espeluga		42.53333	1.55	A	ADMD	AD		00				0		1344	Europe/Andorra	1993-12-23
+3040594	Pont de l’ Espalmera	Pont de l' Espalmera		42.55	1.46667	S	BDG	AD		00				0		1585	Europe/Andorra	1993-12-23
+3040595	Riu de l’ Escobet	Riu de l' Escobet		42.46667	1.51667	H	STM	AD		00				0		1985	Europe/Andorra	1993-12-23
+3040596	Escobet	Escobet		42.46667	1.51667	L	LCTY	AD		00				0		1985	Europe/Andorra	1993-12-23
+3040597	Cylindre d’ Ascobes	Cylindre d' Ascobes	Cylindre d' Ascobes,Cylindre d’ Ascobes,Pic d' Escobes,Pic d’ Escobes	42.6	1.73333	T	PK	AD		00				0		2383	Europe/Andorra	2011-11-05
+3040598	Comella dels Esclops	Comella dels Esclops		42.43333	1.48333	H	STM	AD		00				0		1228	Europe/Andorra	1993-12-23
+3040599	Solà d’ Escàs	Sola d' Escas		42.55	1.5	T	SLP	AD		00				0		1292	Europe/Andorra	1993-12-23
+3040600	Camí d’ Escàs	Cami d' Escas		42.55	1.5	R	TRL	AD		00				0		1292	Europe/Andorra	1993-12-23
+3040601	Escàs	Escas		42.54643	1.50895	P	PPL	AD		04				0		1257	Europe/Andorra	2011-04-19
+3040602	Roc d’ Escalluquer	Roc d' Escalluquer		42.55	1.5	T	CLF	AD		00				0		1292	Europe/Andorra	1993-12-23
+3040603	Bosc d’ Escalluquer	Bosc d' Escalluquer		42.55	1.5	V	FRST	AD		00				0		1292	Europe/Andorra	1993-12-23
+3040604	Escalluquer	Escalluquer		42.55	1.5	A	ADMD	AD		00				0		1292	Europe/Andorra	1993-12-23
+3040605	Canal de l’ Escalella	Canal de l' Escalella		42.5	1.45	H	STM	AD		00				0		1840	Europe/Andorra	1993-12-23
+3040606	Vial de l’ Escala	Vial de l' Escala		42.48333	1.5	R	RD	AD		00				0		1631	Europe/Andorra	1993-12-23
+3040607	Estany Esbalçat	Estany Esbalcat		42.64002	1.51371	H	LK	AD		07				0		2130	Europe/Andorra	2007-03-04
+3040608	Obac d’ Erts	Obac d' Erts		42.56667	1.5	T	SLP	AD		00				0		1636	Europe/Andorra	1993-12-23
+3040609	Erts	Erts	Ercs,Ercz,Erez	42.56218	1.4968	P	PPL	AD	AD	04				0		1430	Europe/Andorra	2007-04-16
+3040610	Costa de les Eroles	Costa de les Eroles		42.56667	1.45	T	SLP	AD		00				0		2137	Europe/Andorra	1993-12-23
+3040611	Solana de l’ Era de Mitges	Solana de l' Era de Mitges		42.46667	1.45	T	SLP	AD		00				0		1562	Europe/Andorra	1993-12-23
+3040612	Refugi d’ Envalira	Refugi d' Envalira		42.53333	1.68333	S	RSRT	AD		00				0		2322	Europe/Andorra	1993-12-23
+3040613	Port d’ Envalira	Port d' Envalira	Port d' Envalira,Port d’ Envalira,Puerto d' Envalira,Puerto d’ Envalira	42.54041	1.71897	T	PASS	AD		00				0		2230	Europe/Andorra	2011-11-05
+3040615	Bordes d’ Envalira	Bordes d' Envalira	Bordas d' Envalira,Bordas d’ Envalira,Bordes d' Envalira,Bordes d’ Envalira	42.56667	1.68333	S	HUTS	AD		00				0		2340	Europe/Andorra	2011-11-05
+3040616	Envalira	Envalira		42.53333	1.7	A	ADMD	AD		00				0		2357	Europe/Andorra	1993-12-23
+3040617	Solà d’ Entremesaiqües	Sola d' Entremesaiques		42.50094	1.55771	T	SLP	AD		00				0		1621	Europe/Andorra	2011-04-19
+3040618	Pont d’ Entremesaigües	Pont d' Entremesaigues		42.5	1.56667	S	BDG	AD		00				0		1776	Europe/Andorra	1993-12-23
+3040619	Entremesaigües	Entremesaigues		42.49692	1.55577	L	LCTY	AD		00				0		1566	Europe/Andorra	2011-04-19
+3040620	Roca Entravessada	Roca Entravessada		42.5986	1.44241	T	SPUR	AD		00				0		2406	Europe/Andorra	2011-04-19
+3040621	Font de les Entrades	Font de les Entrades		42.56667	1.48333	H	SPNG	AD		00				0		1508	Europe/Andorra	1993-12-23
+3040622	Bosc de l’ Entrada	Bosc de l' Entrada		42.55	1.46667	V	FRST	AD		00				0		1585	Europe/Andorra	1993-12-23
+3040623	Camí d’ Entor	Cami d' Entor		42.58333	1.63333	R	TRL	AD		00				0		1722	Europe/Andorra	1993-12-23
+3040624	Bosc d’ Entor	Bosc d' Entor		42.58333	1.65	V	FRST	AD		00				0		1767	Europe/Andorra	1993-12-23
+3040625	Entor	Entor		42.6	1.65	A	ADMD	AD		00				0		2131	Europe/Andorra	1993-12-23
+3040626	Collada d’ Entinyola	Collada d' Entinyola		42.51667	1.65	T	PASS	AD		00				0		2633	Europe/Andorra	1993-12-23
+3040627	Clots d’ Entinyac	Clots d' Entinyac		42.58333	1.68333	H	STMH	AD		00				0		2294	Europe/Andorra	1993-12-23
+3040628	Tarteres d’ Entalàs	Tarteres d' Entalas		42.53333	1.63333	T	TAL	AD		00				0		2360	Europe/Andorra	1993-12-23
+3040629	Canals d’ Entalàs	Canals d' Entalas		42.53333	1.63333	H	RVN	AD		00				0		2360	Europe/Andorra	1993-12-23
+3040630	Bosc d’ En Som	Bosc d' En Som		42.56667	1.58333	V	FRST	AD		00				0		1919	Europe/Andorra	1993-12-23
+3040631	Serra de l’ Ensegur	Serra de l' Ensegur		42.58333	1.55	T	RDGE	AD		00				0		2357	Europe/Andorra	1993-12-23
+3040632	Riu de l’ Ensegur	Riu de l' Ensegur		42.6	1.53333	H	STM	AD		00				0		1695	Europe/Andorra	1993-12-23
+3040633	Obaga de l’ Ensegur	Obaga de l' Ensegur		42.58333	1.55	T	SLP	AD		00				0		2357	Europe/Andorra	1993-12-23
+3040634	Collada de l’ Ensegur	Collada de l' Ensegur		42.58333	1.53333	T	PASS	AD		00				0		1924	Europe/Andorra	1993-12-23
+3040635	Clot de l’ Ensegur	Clot de l' Ensegur		42.58333	1.55	H	RVN	AD		00				0		2357	Europe/Andorra	1993-12-23
+3040636	Camí de l’ Ensegur	Cami de l' Ensegur		42.58333	1.53333	R	TRL	AD		00				0		1924	Europe/Andorra	1993-12-23
+3040637	Bosc de l’ Ensegur	Bosc de l' Ensegur		42.6	1.55	V	FRST	AD		00				0		2298	Europe/Andorra	1993-12-23
+3040638	Bordes de l’ Ensegur	Bordes de l' Ensegur		42.58333	1.55	S	HUTS	AD		00				0		2357	Europe/Andorra	1993-12-23
+3040639	Aspres de l’ Ensegur	Aspres de l' Ensegur		42.58333	1.55	V	VINS	AD		00				0		2357	Europe/Andorra	1993-12-23
+3040640	Solana d’ Ensagents	Solana d' Ensagents		42.51667	1.63333	T	SLP	AD		00				0		2379	Europe/Andorra	1993-12-23
+3040641	Riu d’ Ensagents	Riu d' Ensagents		42.52752	1.6099	H	STM	AD		00				0		2101	Europe/Andorra	2011-04-19
+3040642	Obaga d’ Ensagents	Obaga d' Ensagents		42.51667	1.63333	T	SLP	AD		00				0		2379	Europe/Andorra	1993-12-23
+3040643	Estanys d’ Ensagents	Estanys d' Ensagents		42.52041	1.64793	H	LKS	AD		00				0		2627	Europe/Andorra	2011-04-19
+3040644	Ensagents	Ensagents		42.51667	1.65	A	ADMD	AD		00				0		2633	Europe/Andorra	1993-12-23
+3040645	Costa d’ Enradort	Costa d' Enradort		42.53333	1.66667	T	SLP	AD		00				0		2489	Europe/Andorra	1993-12-23
+3040646	Collada d’ Enradort	Collada d' Enradort		42.53333	1.66667	T	PASS	AD		00				0		2489	Europe/Andorra	1993-12-23
+3040647	Rec d’ Engordany	Rec d' Engordany		42.51667	1.53333	H	CNL	AD		00				0		1460	Europe/Andorra	1993-12-23
+3040648	Engordany	Engordany	Engordany	42.51115	1.54118	P	PPL	AD		08				0		1139	Europe/Andorra	2007-04-05
+3040649	Pla d’ Engolasters	Pla d' Engolasters		42.5	1.56667	T	UPLD	AD		00				0		1776	Europe/Andorra	1993-12-23
+3040650	Estany d'Engolasters	Estany d'Engolasters		42.51966	1.56772	H	LK	AD		07				0	1615	1759	Europe/Andorra	2007-04-05
+3040651	Carretera d’ Engolasters	Carretera d' Engolasters		42.51667	1.56667	R	RD	AD		00				0		1759	Europe/Andorra	1993-12-23
+3040652	Canal d’ Engolasters	Canal d' Engolasters		42.53333	1.6	H	CNL	AD		00				0		1888	Europe/Andorra	1993-12-23
+3040653	Engolasters	Engolasters		42.5	1.56667	A	ADMD	AD		00				0		1776	Europe/Andorra	1993-12-23
+3040654	Estany d’ Engaït	Estany d' Engait		42.51667	1.71667	H	LK	AD		00				0		2591	Europe/Andorra	1993-12-23
+3040655	Engaït	Engait		42.51667	1.71667	A	ADMD	AD		00				0		2591	Europe/Andorra	1993-12-23
+3040656	Serrat de l’ Enfreu	Serrat de l' Enfreu		42.56667	1.56667	T	RDGE	AD		00				0		2089	Europe/Andorra	1993-12-23
+3040657	Riu de l’ Enfreu	Riu de l' Enfreu		42.56667	1.55	H	STM	AD		00				0		1996	Europe/Andorra	1993-12-23
+3040658	Bosc de l’ Enfreu	Bosc de l' Enfreu		42.56667	1.55	V	FRST	AD		00				0		1996	Europe/Andorra	1993-12-23
+3040659	Borda d’ Endrieta	Borda d' Endrieta		42.55	1.58333	S	HUT	AD		00				0		1499	Europe/Andorra	1993-12-23
+3040660	Obaga d’ Encortesa	Obaga d' Encortesa		42.45	1.51667	T	SLP	AD		00				0		1790	Europe/Andorra	1993-12-23
+3040661	Carretera Encortesa	Carretera Encortesa		42.45	1.5	R	RD	AD		00				0		1614	Europe/Andorra	1993-12-23
+3040662	Encortesa	Encortesa		42.45	1.51667	L	LCTY	AD		00				0		1790	Europe/Andorra	1993-12-23
+3040663	Camí d’ Encodina	Cami d' Encodina		42.63333	1.53333	R	TRL	AD		00				0		2072	Europe/Andorra	1993-12-23
+3040664	Encodina	Encodina		42.63333	1.53333	L	LCTY	AD		00				0		2072	Europe/Andorra	1993-12-23
+3040665	Serra d’ Enclar	Serra d' Enclar		42.51408	1.48329	T	RDGE	AD		00				0		2069	Europe/Andorra	2011-04-19
+3040666	Riu d’ Enclar	Riu d' Enclar		42.49067	1.49562	H	STM	AD		00				0		1353	Europe/Andorra	2011-04-19
+3040667	Prat d’ Enclar	Prat d' Enclar		42.5	1.48333	L	GRAZ	AD		00				0		1316	Europe/Andorra	1993-12-23
+3040668	Bony de Garci	Bony de Garci	Bony de Garci,Pic d' Enclar,Pic d’ Enclar,Puig de Ancla,Puig de Anclá	42.51667	1.46667	T	PK	AD		00				0		1840	Europe/Andorra	2011-11-05
+3040669	Camí d’ Enclar	Cami d' Enclar		42.5	1.48333	R	TRL	AD		00				0		1316	Europe/Andorra	1993-12-23
+3040670	Bosc d’ Enclar	Bosc d' Enclar		42.48333	1.48333	V	FRST	AD		00				0		981	Europe/Andorra	1993-12-23
+3040671	Font de l’ Enciam	Font de l' Enciam		42.61667	1.51667	H	SPNG	AD		00				0		1716	Europe/Andorra	1993-12-23
+3040672	Carrerons d’ Encenrera	Carrerons d' Encenrera		42.53333	1.68333	R	TRL	AD		00				0		2322	Europe/Andorra	1993-12-23
+3040673	Encenrera	Encenrera		42.53333	1.68333	L	LCTY	AD		00				0		2322	Europe/Andorra	1993-12-23
+3040674	Clots d’ Encarners	Clots d' Encarners		42.6	1.58333	H	RVN	AD		00				0		2461	Europe/Andorra	1993-12-23
+3040675	Basera d’ Encarners	Basera d' Encarners		42.5	1.45	T	CLF	AD		00				0		1840	Europe/Andorra	1993-12-23
+3040676	Basera d’ Encarners	Basera d' Encarners		42.46667	1.55	T	CLF	AD		00				0		2341	Europe/Andorra	1993-12-23
+3040677	Solà d’ Encampadana	Sola d' Encampadana		42.55	1.61667	T	SLP	AD		00				0		2206	Europe/Andorra	1993-12-23
+3040678	Pic d’ Encampadana	Pic d' Encampadana	Pic d' Encampadana,Pic d’ Encampadana,Tossa d' Encampdana,Tossa d’ Encampdana,Tossal d' Encampdana,Tossal d’ Encampdana	42.5552	1.63153	T	PK	AD		00				0		2364	Europe/Andorra	2011-11-05
+3040679	Obaga d’ Encampadana	Obaga d' Encampadana		42.55	1.61667	T	SLP	AD		00				0		2206	Europe/Andorra	1993-12-23
+3040680	Font d’ Encampadana	Font d' Encampadana		42.55	1.61667	H	SPNG	AD		00				0		2206	Europe/Andorra	1993-12-23
+3040681	Clot d’ Encampadana	Clot d' Encampadana		42.55	1.61667	H	RVN	AD		00				0		2206	Europe/Andorra	1993-12-23
+3040682	Encampadana	Encampadana		42.56667	1.61667	A	ADMD	AD		00				0		1920	Europe/Andorra	1993-12-23
+3040683	Serra d’ Encamp	Serra d' Encamp		42.53333	1.55	T	RDGE	AD		00				0		1344	Europe/Andorra	1993-12-23
+3040684	Parròquia d'Encamp	Parroquia d'Encamp	Encamp,Parroquia d'Encamp,Parròquia d'Encamp	42.53333	1.63333	A	ADM1	AD		03				13685		2360	Europe/Andorra	2008-03-17
+3040685	Estany d’ Encamp	Estany d' Encamp		42.5	1.65	H	LK	AD		00				0		2542	Europe/Andorra	1993-12-23
+3040686	Encamp	Encamp	Ehnkam,Encamp,en kan pu,enkanpu jiao qu,Энкам,エンカンプ教区,æ©åŽæ™®	42.53451	1.5767	P	PPLA	AD		03				11223		1309	Europe/Andorra	2011-11-05
+3040687	Borda d’ En Cadena	Borda d' En Cadena		42.53333	1.56667	S	HUT	AD		00				0		1418	Europe/Andorra	1993-12-23
+3040688	Pleta d’ Emportona	Pleta d' Emportona		42.53333	1.65	L	GRAZ	AD		00				0		2508	Europe/Andorra	1993-12-23
+3040689	Emportona	Emportona		42.53333	1.65	A	ADMD	AD		00				0		2508	Europe/Andorra	1993-12-23
+3040690	Riu de l’ Empallador	Riu de l' Empallador		42.53333	1.7	H	STM	AD		00				0		2357	Europe/Andorra	1993-12-23
+3040691	Canal de l’ Embut	Canal de l' Embut		42.58333	1.48333	H	RVN	AD		00				0		1809	Europe/Andorra	1993-12-23
+3040692	Clots d’ Embolcar	Clots d' Embolcar		42.61667	1.61667	T	CRQS	AD		00				0		2352	Europe/Andorra	1993-12-23
+3040693	Basers d’ Embolcar	Basers d' Embolcar		42.61667	1.61667	T	CLF	AD		00				0		2352	Europe/Andorra	1993-12-23
+3040694	El Vilar	El Vilar	El Vilar	42.57226	1.60781	P	PPL	AD		02				0		1655	Europe/Andorra	2011-11-05
+3040695	El Vedat	El Vedat		42.46667	1.48333	A	ADMD	AD		00				0		1134	Europe/Andorra	1993-12-23
+3040696	El Turer	El Turer		42.56667	1.53333	T	SPUR	AD		00				0		1669	Europe/Andorra	1993-12-23
+3040697	El Trillar	El Trillar		42.46667	1.5	L	LCTY	AD		00				0		1383	Europe/Andorra	1993-12-23
+3040698	El Tremat	El Tremat		42.53348	1.58056	P	PPL	AD		03				0		1309	Europe/Andorra	2011-04-19
+3040699	El Torrentill	El Torrentill		42.45	1.48333	H	STM	AD		00				0		1111	Europe/Andorra	1993-12-23
+3040700	El Tarter	El Tarter		42.58026	1.64902	P	PPL	AD		02				0		1737	Europe/Andorra	2007-04-16
+3040701	El Tamany	El Tamany		42.63333	1.51667	T	SLP	AD		00				0		1894	Europe/Andorra	1993-12-23
+3040702	Els Tarters	Els Tarters		42.56667	1.68333	L	LCTY	AD		00				0		2340	Europe/Andorra	1993-12-23
+3040703	Els Tarterals	Els Tarterals		42.48333	1.51667	L	LCTY	AD		00				0		2061	Europe/Andorra	1993-12-23
+3040704	Els Rebolians	Els Rebolians		42.53333	1.51667	L	LCTY	AD		00				0		1361	Europe/Andorra	1993-12-23
+3040705	Els Quatre Rocs	Els Quatre Rocs		42.46667	1.53333	T	RKS	AD		00				0		2332	Europe/Andorra	1993-12-23
+3040706	Els Pujols	Els Pujols		42.48333	1.48333	L	LCTY	AD		00				0		981	Europe/Andorra	1993-12-23
+3040707	Els Puiols	Els Puiols		42.56667	1.63333	L	LCTY	AD		00				0		2016	Europe/Andorra	1993-12-23
+3040708	Els Pletius	Els Pletius		42.61667	1.68333	L	LCTY	AD		00				0		2406	Europe/Andorra	1993-12-23
+3040709	Els Plans	Els Plans		42.58142	1.63273	P	PPL	AD		02				0		1722	Europe/Andorra	2011-04-19
+3040710	Els Plans	Els Plans		42.45084	1.50641	L	LCTY	AD		00				0		1394	Europe/Andorra	2011-04-19
+3040711	Els Plans	Els Plans		42.45	1.5	S	FRM	AD		00				0		1614	Europe/Andorra	1993-12-23
+3040712	Els Plans	Els Plans		42.53333	1.51667	A	ADMD	AD		00				0		1361	Europe/Andorra	1993-12-23
+3040713	Els Pessons	Els Pessons		42.51667	1.66667	A	ADMD	AD		00				0		2410	Europe/Andorra	1993-12-23
+3040714	Els Pallers	Els Pallers		42.56667	1.75	L	LCTY	AD		00				0		1923	Europe/Andorra	1993-12-23
+3040715	Els Pallerils	Els Pallerils		42.58333	1.53333	L	LCTY	AD		00				0		1924	Europe/Andorra	1993-12-23
+3040716	Els Padals	Els Padals		42.58333	1.66667	L	LCTY	AD		00				0		2159	Europe/Andorra	1993-12-23
+3040717	Els Orris	Els Orris		42.55	1.46667	L	LCTY	AD		00				0		1585	Europe/Andorra	1993-12-23
+3040718	Els Oriosos	Els Oriosos		42.53333	1.53333	L	LCTY	AD		00				0		1521	Europe/Andorra	1993-12-23
+3040719	El Solà	El Sola		42.55	1.53333	L	LCTY	AD		00				0		1593	Europe/Andorra	1993-12-23
+3040720	Els Obacs	Els Obacs		42.6	1.5	T	SLP	AD		00				0		1923	Europe/Andorra	1993-12-23
+3040721	Els Obacs	Els Obacs		42.55	1.48333	L	LCTY	AD		00				0		1548	Europe/Andorra	1993-12-23
+3040722	Els Maians	Els Maians		42.55	1.61667	L	LCTY	AD		00				0		2206	Europe/Andorra	1993-12-23
+3040723	Els Llacs	Els Llacs		42.53333	1.41667	T	UPLD	AD		00				0		1948	Europe/Andorra	1993-12-23
+3040724	Els Indrets	Els Indrets		42.48333	1.45	L	LCTY	AD		00				0		1195	Europe/Andorra	1993-12-23
+3040725	Els Hortells	Els Hortells		42.45	1.5	L	LCTY	AD		00				0		1614	Europe/Andorra	1993-12-23
+3040726	Els Graus	Els Graus		42.56667	1.73333	L	LCTY	AD		00				0		2096	Europe/Andorra	1993-12-23
+3040727	Els Graus	Els Graus		42.48333	1.56667	A	ADMD	AD		00				0		2231	Europe/Andorra	1993-12-23
+3040728	Els Fontanals	Els Fontanals		42.56667	1.68333	L	LCTY	AD		00				0		2340	Europe/Andorra	1993-12-23
+3040729	Els Feners	Els Feners		42.58333	1.66667	T	SLP	AD		00				0		2159	Europe/Andorra	1993-12-23
+3040730	Els Feners	Els Feners		42.48333	1.48333	L	LCTY	AD		00				0		981	Europe/Andorra	1993-12-23
+3040731	Els Fenerols	Els Fenerols		42.53333	1.56667	L	LCTY	AD		00				0		1418	Europe/Andorra	1993-12-23
+3040732	Els Fenerols	Els Fenerols		42.53333	1.5	L	LCTY	AD		00				0		1357	Europe/Andorra	1993-12-23
+3040733	Els Fenerals	Els Fenerals		42.5	1.46667	A	ADMD	AD		00				0		1678	Europe/Andorra	1993-12-23
+3040734	Els Estanys	Els Estanys		42.48333	1.63333	A	ADMD	AD		00				0		2296	Europe/Andorra	1993-12-23
+3040735	Els Estanyons	Els Estanyons		42.46667	1.61667	L	LCTY	AD		00				0		2448	Europe/Andorra	1993-12-23
+3040736	Els Espiolets	Els Espiolets		42.56667	1.66667	L	LCTY	AD		00				0		1938	Europe/Andorra	1993-12-23
+3040737	El Serrat	El Serrat	Lo Serrat	42.6183	1.53912	P	PPL	AD	AD	05				0		1704	Europe/Andorra	2007-04-16
+3040738	El Seig	El Seig		42.53333	1.58333	A	ADMD	AD		00				0		1571	Europe/Andorra	1993-12-23
+3040739	Els Cuiners	Els Cuiners		42.63333	1.55	L	CLG	AD		00				0		2053	Europe/Andorra	1993-12-23
+3040740	Els Cortals	Els Cortals		42.53333	1.61667	A	ADMD	AD		00				0		2237	Europe/Andorra	1993-12-23
+3040741	Els Corralets	Els Corralets		42.56667	1.55	L	LCTY	AD		00				0		1996	Europe/Andorra	1993-12-23
+3040742	Els Colls	Els Colls		42.55	1.51667	L	LCTY	AD		00				0		1397	Europe/Andorra	1993-12-23
+3040743	Els Collets	Els Collets		42.53333	1.51667	L	LCTY	AD		00				0		1361	Europe/Andorra	1993-12-23
+3040744	Els Collells	Els Collells		42.56667	1.48333	T	PK	AD		00				0		1508	Europe/Andorra	1993-12-23
+3040745	Els Colells	Els Colells		42.6	1.7	L	LCTY	AD		00				0		2354	Europe/Andorra	1993-12-23
+3040746	Els Colells	Els Colells		42.51667	1.7	A	ADMD	AD		00				0		2435	Europe/Andorra	1993-12-23
+3040747	Els Clots	Els Clots		42.56667	1.6	T	SLP	AD		00				0		1655	Europe/Andorra	1993-12-23
+3040748	Els Clots	Els Clots		42.55	1.43333	L	LCTY	AD		00				0		1949	Europe/Andorra	1993-12-23
+3040749	Els Caubets	Els Caubets		42.55	1.48333	T	VAL	AD		00				0		1548	Europe/Andorra	1993-12-23
+3040750	Els Carabedius	Els Carabedius		42.46667	1.45	L	LCTY	AD		00				0		1562	Europe/Andorra	1993-12-23
+3040751	Els Canalons	Els Canalons		42.48333	1.48333	H	RVN	AD		00				0		981	Europe/Andorra	1993-12-23
+3040752	Els Botaders	Els Botaders		42.48333	1.56667	L	LCTY	AD		00				0		2231	Europe/Andorra	1993-12-23
+3040753	Els Bedres	Els Bedres		42.55	1.48333	L	LCTY	AD		00				0		1548	Europe/Andorra	1993-12-23
+3040754	Els Beçolans	Els Becolans		42.63333	1.55	L	LCTY	AD		00				0		2053	Europe/Andorra	1993-12-23
+3040755	Els Bassots	Els Bassots		42.55	1.65	H	LKS	AD		00				0		2432	Europe/Andorra	1993-12-23
+3040756	Els Aubells	Els Aubells		42.56667	1.53333	L	LCTY	AD		00				0		1669	Europe/Andorra	1993-12-23
+3040757	Els Astrells	Els Astrells		42.5	1.56667	L	LCTY	AD		00				0		1776	Europe/Andorra	1993-12-23
+3040758	Els Assaladors	Els Assaladors		42.55	1.66667	L	LCTY	AD		00				0		2224	Europe/Andorra	1993-12-23
+3040759	Els Aspres	Els Aspres		42.56667	1.45	T	RKS	AD		00				0		2137	Europe/Andorra	1993-12-23
+3040760	Els Aspedius	Els Aspedius		42.48333	1.53333	L	LCTY	AD		00				0		2255	Europe/Andorra	1993-12-23
+3040761	El Saquet	El Saquet		42.6	1.53333	A	ADMD	AD		00				0		1695	Europe/Andorra	1993-12-23
+3040762	Els Amorriadors	Els Amorriadors		42.51667	1.6	L	LCTY	AD		00				0		2085	Europe/Andorra	1993-12-23
+3040763	Els Alabars	Els Alabars		42.5	1.48333	L	LCTY	AD		00				0		1316	Europe/Andorra	1993-12-23
+3040764	El Riguer	El Riguer		42.48333	1.55	A	ADMD	AD		00				0		2233	Europe/Andorra	1993-12-23
+3040765	El Remugar	El Remugar		42.56667	1.53333	L	LCTY	AD		00				0		1669	Europe/Andorra	1993-12-23
+3040766	El Ramer	El Ramer		42.56667	1.48333	L	LCTY	AD		00				0		1508	Europe/Andorra	1993-12-23
+3040767	El Pui	El Pui		42.54731	1.51439	P	PPL	AD		04				0		1257	Europe/Andorra	2011-04-19
+3040768	El Pouader	El Pouader	El Ponader,El Pouader	42.53333	1.6	T	RK	AD	AD	00				0		1888	Europe/Andorra	2011-11-05
+3040769	El Pletiu	El Pletiu		42.5	1.6	L	GRAZ	AD		00				0		2416	Europe/Andorra	1993-12-23
+3040770	El Planellet	El Planellet		42.61667	1.53333	L	LCTY	AD		00				0		1609	Europe/Andorra	1993-12-23
+3040771	El Pla	El Pla		42.53333	1.58333	L	LCTY	AD		00				0		1571	Europe/Andorra	1993-12-23
+3040772	El Pas Mal	El Pas Mal		42.53333	1.65	T	PASS	AD		00				0		2508	Europe/Andorra	1993-12-23
+3040773	El Pardal	El Pardal		42.55	1.5	A	ADMD	AD		00				0		1292	Europe/Andorra	1993-12-23
+3040774	El Palinqueró	El Palinquero		42.58333	1.66667	L	LCTY	AD		00				0		2159	Europe/Andorra	1993-12-23
+3040775	El Noguer	El Noguer		42.51667	1.55	A	ADMD	AD		00				0		1322	Europe/Andorra	1993-12-23
+3040776	El Mollar	El Mollar		42.53333	1.6	L	LCTY	AD		00				0		1888	Europe/Andorra	1993-12-23
+3040777	El Moixeret	El Moixeret		42.58333	1.51667	L	LCTY	AD		00				0		1722	Europe/Andorra	1993-12-23
+3040778	El Meligar	El Meligar		42.5	1.61667	T	RDGE	AD		00				0		2560	Europe/Andorra	1993-12-23
+3040779	El Mas	El Mas		42.56667	1.5	A	ADMD	AD		00				0		1636	Europe/Andorra	1993-12-23
+3040780	El Maià	El Maia		42.56667	1.73333	A	ADMD	AD		00				0		2096	Europe/Andorra	1993-12-23
+3040781	El Madriu	El Madriu		42.48333	1.58333	A	ADMD	AD		00				0		2349	Europe/Andorra	1993-12-23
+3040782	El Llempo	El Llempo		42.58333	1.6	L	LCTY	AD		00				0		1828	Europe/Andorra	1993-12-23
+3040783	El Jou	El Jou		42.56667	1.5	A	ADMD	AD		00				0		1636	Europe/Andorra	1993-12-23
+3040784	El Griu	El Griu		42.53333	1.63333	A	ADMD	AD		00				0		2360	Europe/Andorra	1993-12-23
+3040785	El Fornet	El Fornet		42.55	1.6	L	LCTY	AD		00				0		2210	Europe/Andorra	1993-12-23
+3040786	El Fornell	El Fornell		42.51667	1.51667	L	LCTY	AD		00				0		1265	Europe/Andorra	1993-12-23
+3040787	El Fornell	El Fornell		42.45	1.5	L	LCTY	AD		00				0		1614	Europe/Andorra	1993-12-23
+3040788	El Forn	El Forn		42.55	1.6	A	ADMD	AD		00				0		2210	Europe/Andorra	1993-12-23
+3040789	El Fontanal	El Fontanal		42.46667	1.5	L	LCTY	AD		00				0		1383	Europe/Andorra	1993-12-23
+3040790	El Cubil	El Cubil		42.43333	1.55	L	LCTY	AD		00				0		2178	Europe/Andorra	1993-12-23
+3040791	El Cubil	El Cubil		42.53333	1.68333	A	ADMD	AD		00				0		2322	Europe/Andorra	1993-12-23
+3040792	El Cresper	El Cresper		42.51667	1.56667	A	ADMD	AD		00				0		1759	Europe/Andorra	1993-12-23
+3040793	El Cortalet	El Cortalet		42.55	1.51667	L	LCTY	AD		00				0		1397	Europe/Andorra	1993-12-23
+3040794	El Cortalet	El Cortalet		42.51667	1.56667	L	LCTY	AD		00				0		1759	Europe/Andorra	1993-12-23
+3040795	El Corbater	El Corbater		42.55	1.71667	L	LCTY	AD		00				0		2192	Europe/Andorra	1993-12-23
+3040796	El Confòs	El Confos		42.51667	1.58333	A	ADMD	AD		00				0		1994	Europe/Andorra	1993-12-23
+3040797	El Collell	El Collell		42.45	1.5	T	PASS	AD		00				0		1614	Europe/Andorra	1993-12-23
+3040798	El Colitx	El Colitx		42.58333	1.51667	L	LCTY	AD		00				0		1722	Europe/Andorra	1993-12-23
+3040799	El Clos	El Clos		42.53333	1.6	L	LCTY	AD		00				0		1888	Europe/Andorra	1993-12-23
+3040800	El Castellar	El Castellar		42.63333	1.51667	A	ADMD	AD		00				0		1894	Europe/Andorra	1993-12-23
+3040801	El Carregador	El Carregador		42.56667	1.53333	L	LCTY	AD		00				0		1669	Europe/Andorra	1993-12-23
+3040802	El Cardemeller	El Cardemeller		42.55	1.46667	L	LCTY	AD		00				0		1585	Europe/Andorra	1993-12-23
+3040803	El Campús	El Campus		42.43333	1.48333	T	UPLD	AD		00				0		1228	Europe/Andorra	1993-12-23
+3040804	El Bullidor	El Bullidor		42.55	1.71667	L	LCTY	AD		00				0		2192	Europe/Andorra	1993-12-23
+3040805	El Brossós	El Brossos		42.61667	1.53333	A	ADMD	AD		00				0		1609	Europe/Andorra	1993-12-23
+3040806	El Braibal	El Braibal		42.5	1.58333	L	LCTY	AD		00				0		1888	Europe/Andorra	1993-12-23
+3040807	El Bosquet	El Bosquet		42.56667	1.53333	L	LCTY	AD		00				0		1669	Europe/Andorra	1993-12-23
+3040808	El Boscarró	El Boscarro		42.46667	1.48333	L	LCTY	AD		00				0		1134	Europe/Andorra	1993-12-23
+3040809	El Barrerol	El Barrerol		42.43333	1.45	L	LCTY	AD		00				0		877	Europe/Andorra	1993-12-23
+3040810	Camí d’ Easagents	Cami d' Easagents		42.53333	1.61667	R	TRL	AD		00				0		2237	Europe/Andorra	1993-12-23
+3040811	Pleta de Duedra	Pleta de Duedra		42.63333	1.5	L	GRAZ	AD		00				0		1979	Europe/Andorra	1993-12-23
+3040812	Pleta de Duedra	Pleta de Duedra		42.61667	1.56667	L	GRAZ	AD		00				0		2228	Europe/Andorra	1993-12-23
+3040813	Plana Duedra	Plana Duedra		42.58333	1.51667	T	UPLD	AD		00				0		1722	Europe/Andorra	1993-12-23
+3040814	Planella del Duc	Planella del Duc		42.45	1.5	T	SLP	AD		00				0		1614	Europe/Andorra	1993-12-23
+3040815	Canal del Duc	Canal del Duc		42.58333	1.61667	H	RVN	AD		00				0		1707	Europe/Andorra	1993-12-23
+3040816	Canal Dreta	Canal Dreta		42.55	1.53333	H	STM	AD		00				0		1593	Europe/Andorra	1993-12-23
+3040817	Canal Dreta	Canal Dreta		42.51667	1.48333	H	STM	AD		00				0		1839	Europe/Andorra	1993-12-23
+3040818	Port Dret	Port Dret		42.57454	1.70316	T	PASS	AD		00				0		2375	Europe/Andorra	2011-04-19
+3040819	Mas del Diumenge	Mas del Diumenge		42.51667	1.53333	S	FRM	AD		00				0		1460	Europe/Andorra	1993-12-23
+3040820	Canal del Diumenge	Canal del Diumenge		42.51667	1.53333	H	STM	AD		00				0		1460	Europe/Andorra	1993-12-23
+3040821	Clot del Diable	Clot del Diable		42.56667	1.7	T	CRQ	AD		00				0		2375	Europe/Andorra	1993-12-23
+3040822	Devesassa	Devesassa		42.56667	1.6	L	LCTY	AD		00				0		1655	Europe/Andorra	1993-12-23
+3040823	Costa de la Devesa	Costa de la Devesa		42.55	1.45	T	SLP	AD		00				0		1788	Europe/Andorra	1993-12-23
+3040824	Bosc de la Devesa	Bosc de la Devesa		42.5	1.55	V	FRST	AD		00				0		1566	Europe/Andorra	1993-12-23
+3040825	Bony de les Deu Hores	Bony de les Deu Hores		42.53333	1.65	T	SPUR	AD		00				0		2508	Europe/Andorra	1993-12-23
+3040826	Pleta de Dalt	Pleta de Dalt		42.48333	1.43333	L	GRAZ	AD		00				0		1938	Europe/Andorra	1993-12-23
+3040827	Carrera de Dalt	Carrera de Dalt		42.55	1.61667	R	TRL	AD		00				0		2206	Europe/Andorra	1993-12-23
+3040828	Serra del Cussol	Serra del Cussol		42.45	1.45	T	RDGE	AD		00				0		1482	Europe/Andorra	1993-12-23
+3040829	Bosc del Cussol	Bosc del Cussol		42.45	1.45	V	FRST	AD		00				0		1482	Europe/Andorra	1993-12-23
+3040830	Borda de la Cultiassa	Borda de la Cultiassa		42.53333	1.58333	S	HUT	AD		00				0		1571	Europe/Andorra	1993-12-23
+3040831	Obaga del Cultiar	Obaga del Cultiar		42.55	1.6	T	SLP	AD		00				0		2210	Europe/Andorra	1993-12-23
+3040832	Cultiar	Cultiar		42.55	1.58333	L	LCTY	AD		00				0		1499	Europe/Andorra	1993-12-23
+3040833	Canal del Cul	Canal del Cul		42.48333	1.43333	H	STM	AD		00				0		1938	Europe/Andorra	1993-12-23
+3040834	Serrat dels Cuiners	Serrat dels Cuiners		42.63333	1.55	T	SPUR	AD		00				0		2053	Europe/Andorra	1993-12-23
+3040835	Roc dels Cuiners	Roc dels Cuiners		42.63333	1.55	T	RK	AD		00				0		2053	Europe/Andorra	1993-12-23
+3040836	Canal del Cuinal	Canal del Cuinal		42.45	1.48333	H	STM	AD		00				0		1111	Europe/Andorra	1993-12-23
+3040837	Bosc del Cúbol	Bosc del Cubol		42.6	1.7	V	FRST	AD		00				0		2354	Europe/Andorra	1993-12-23
+3040838	Costa del Cubil d’Erts	Costa del Cubil d'Erts		42.58333	1.48333	T	SLP	AD		00				0		1809	Europe/Andorra	1993-12-23
+3040839	Bony del Cubil d’Erts	Bony del Cubil d'Erts		42.58333	1.48333	T	PK	AD		00				0		1809	Europe/Andorra	1993-12-23
+3040840	Solana del Cubil	Solana del Cubil		42.56667	1.46667	T	SLP	AD		00				0		1673	Europe/Andorra	1993-12-23
+3040841	Serrat del Cubil	Serrat del Cubil		42.55	1.66667	T	RDGE	AD		00				0		2224	Europe/Andorra	1993-12-23
+3040842	Riu del Cubil	Riu del Cubil		42.56667	1.46667	H	STM	AD		00				0		1673	Europe/Andorra	1993-12-23
+3040843	Riu del Cubil	Riu del Cubil		42.55568	1.68579	H	STM	AD		00				0		2083	Europe/Andorra	2011-04-19
+3040844	Pla del Cubil	Pla del Cubil		42.53333	1.66667	T	UPLD	AD		00				0		2489	Europe/Andorra	1993-12-23
+3040845	Pic del Cubil	Pic del Cubil	Pic del Cubil	42.53333	1.45	T	PK	AD		00				0		2130	Europe/Andorra	2011-11-05
+3040846	Pic Baix del Cubil	Pic Baix del Cubil		42.53333	1.68333	T	PK	AD		00				0		2322	Europe/Andorra	1993-12-23
+3040847	Pic Alt del Cubil	Pic Alt del Cubil	Pic Alt del Cubil,Pic de Cuvil,Pic de Suvil	42.52808	1.66868	T	PK	AD		00				0		2489	Europe/Andorra	2011-11-05
+3040848	Obaga del Cubil	Obaga del Cubil		42.56667	1.46667	T	SLP	AD		00				0		1673	Europe/Andorra	1993-12-23
+3040849	Llac del Cubil	Llac del Cubil		42.53647	1.66891	H	LK	AD		00				0		2335	Europe/Andorra	2011-04-19
+3040850	Port de Caraussans	Port de Caraussans	Port de Caraussans,Port de Creussans	42.63333	1.46667	T	PASS	AD		00				0		2324	Europe/Andorra	2011-11-05
+3040851	Estany de Creussans	Estany de Creussans		42.63484	1.47697	H	LK	AD		07				0		2291	Europe/Andorra	2007-03-04
+3040852	Creussans	Creussans		42.63355	1.47785	L	LCTY	AD		07				0		2291	Europe/Andorra	2007-03-04
+3040853	Planell de la Creueta	Planell de la Creueta		42.55	1.55	T	UPLD	AD		00				0		2097	Europe/Andorra	1993-12-23
+3040854	Creu de Noral	Creu de Noral		42.56667	1.55	L	LCTY	AD		00				0		1996	Europe/Andorra	1993-12-23
+3040855	Serra de la Creu	Serra de la Creu		42.48333	1.51667	T	MT	AD		00				0		2061	Europe/Andorra	1993-12-23
+3040856	Roc de la Creu	Roc de la Creu		42.56667	1.48333	T	RK	AD		00				0		1508	Europe/Andorra	1993-12-23
+3040857	Borda del Cresper	Borda del Cresper		42.53333	1.56667	S	HUT	AD		00				0		1418	Europe/Andorra	1993-12-23
+3040858	Rocs del Cresp	Rocs del Cresp		42.58333	1.51667	T	RKS	AD		00				0		1722	Europe/Andorra	1993-12-23
+3040859	Roc del Cresp	Roc del Cresp		42.56667	1.48333	T	SPUR	AD		00				0		1508	Europe/Andorra	1993-12-23
+3040860	Canal del Cresp	Canal del Cresp		42.58333	1.51667	H	STM	AD		00				0		1722	Europe/Andorra	1993-12-23
+3040861	Font del Crau	Font del Crau		42.6	1.51667	H	SPNG	AD		00				0		1445	Europe/Andorra	1993-12-23
+3040862	Pla de la Cot	Pla de la Cot		42.53333	1.46667	T	UPLD	AD		00				0		1846	Europe/Andorra	1993-12-23
+3040863	Canal de les Costes	Canal de les Costes		42.51667	1.53333	H	STM	AD		00				0		1460	Europe/Andorra	1993-12-23
+3040864	Canal de Costa Verda	Canal de Costa Verda		42.5	1.56667	H	STM	AD		00				0		1776	Europe/Andorra	1993-12-23
+3040865	Collet de Costasseda	Collet de Costasseda		42.48333	1.5	T	PASS	AD		00				0		1631	Europe/Andorra	1993-12-23
+3040866	Bosc de la Costassa	Bosc de la Costassa		42.55	1.51667	V	FRST	AD		00				0		1397	Europe/Andorra	1993-12-23
+3040867	Barranc de la Costa Rodona	Barranc de la Costa Rodona		42.56667	1.73333	H	STM	AD		00				0		2096	Europe/Andorra	1993-12-23
+3040868	Font de la Costa Gran	Font de la Costa Gran		42.51667	1.48333	H	SPNG	AD		00				0		1839	Europe/Andorra	1993-12-23
+3040869	Bony de la Costa del Sodorn	Bony de la Costa del Sodorn		42.5	1.45	T	PK	AD		00				0		1840	Europe/Andorra	1993-12-23
+3040870	Canal de la Costa de les Salineres	Canal de la Costa de les Salineres		42.5	1.46667	H	STM	AD		00				0		1678	Europe/Andorra	1993-12-23
+3040871	Costa de les Neres	Costa de les Neres		42.55	1.55	A	ADMD	AD		00				0		2097	Europe/Andorra	1993-12-23
+3040872	Canal de la Costa de les Gerderes	Canal de la Costa de les Gerderes		42.55	1.6	H	RVN	AD		00				0		2210	Europe/Andorra	1993-12-23
+3040873	Collet de la Costa del Bony Roig	Collet de la Costa del Bony Roig		42.6	1.61667	T	PASS	AD		00				0		2271	Europe/Andorra	1993-12-23
+3040874	Ras de la Costa de l’Avier	Ras de la Costa de l'Avier		42.58333	1.5	T	SLP	AD		00				0		1595	Europe/Andorra	1993-12-23
+3040875	Font de la Costa de l’Avier	Font de la Costa de l'Avier		42.56667	1.53333	H	SPNG	AD		00				0		1669	Europe/Andorra	1993-12-23
+3040876	Canal de Costa de l’Avier	Canal de Costa de l'Avier		42.58333	1.48333	H	STM	AD		00				0		1809	Europe/Andorra	1993-12-23
+3040877	Roc de la Costa	Roc de la Costa		42.56667	1.48333	T	RK	AD		00				0		1508	Europe/Andorra	1993-12-23
+3040878	Solana del Cosp	Solana del Cosp		42.56667	1.61667	T	SLP	AD		00				0		1920	Europe/Andorra	1993-12-23
+3040879	Roca Cosconera	Roca Cosconera		42.48333	1.56667	T	RK	AD		00				0		2231	Europe/Andorra	1993-12-23
+3040880	Font de la Coruvilla	Font de la Coruvilla		42.58333	1.46667	H	SPNG	AD		00				0		1643	Europe/Andorra	1993-12-23
+3040881	Canals de la Coruvilla	Canals de la Coruvilla		42.58333	1.46667	H	RVN	AD		00				0		1643	Europe/Andorra	1993-12-23
+3040882	Borda de la Coruvilla	Borda de la Coruvilla		42.58333	1.46667	S	FRM	AD		00				0		1643	Europe/Andorra	1993-12-23
+3040883	Corts d’Ern	Corts d'Ern	Corts d'Aern,Corts d'Ern,Corts d’Aern,Corts d’Ern	42.5	1.48333	L	LCTY	AD		00				0		1316	Europe/Andorra	2011-11-05
+3040884	Bosc de les Corts	Bosc de les Corts		42.56667	1.53333	V	FRST	AD		00				0		1669	Europe/Andorra	1993-12-23
+3040885	Cort d’Esteve	Cort d'Esteve		42.6	1.51667	L	LCTY	AD		00				0		1445	Europe/Andorra	1993-12-23
+3040886	Cort de Rossell	Cort de Rossell		42.46667	1.48333	L	LCTY	AD		00				0		1134	Europe/Andorra	1993-12-23
+3040887	Cort Cremada	Cort Cremada		42.56667	1.53333	L	LCTY	AD		00				0		1669	Europe/Andorra	1993-12-23
+3040888	Bosc de les Cortanbelles	Bosc de les Cortanbelles		42.46667	1.45	V	FRST	AD		00				0		1562	Europe/Andorra	1993-12-23
+3040889	Canal del Cortal Vell	Canal del Cortal Vell		42.58333	1.46667	H	STM	AD		00				0		1643	Europe/Andorra	1993-12-23
+3040890	Cortal Vell	Cortal Vell		42.58333	1.46667	L	LCTY	AD		00				0		1643	Europe/Andorra	1993-12-23
+3040891	Cortals de Sispony	Cortals de Sispony	Corfots de Sispony,Cortals de Sispony	42.53333	1.5	L	LCTY	AD	AD	00				0		1357	Europe/Andorra	2011-11-05
+3040892	Cortals de Fontaneda	Cortals de Fontaneda		42.45	1.46667	L	LCTY	AD		00				0		935	Europe/Andorra	1993-12-23
+3040893	Riu dels Cortals	Riu dels Cortals		42.53333	1.58333	H	STM	AD		00				0		1571	Europe/Andorra	1993-12-23
+3040894	Riu dels Cortals	Riu dels Cortals		42.53333	1.51667	H	STM	AD		00				0		1361	Europe/Andorra	1993-12-23
+3040895	Carretera dels Cortals	Carretera dels Cortals		42.53333	1.58333	R	RD	AD		00				0		1571	Europe/Andorra	1993-12-23
+3040896	Camí dels Cortals	Cami dels Cortals		42.53333	1.53333	R	TRL	AD		00				0		1521	Europe/Andorra	1993-12-23
+3040897	Canal del Cortalet	Canal del Cortalet		42.48333	1.48333	H	STM	AD		00				0		981	Europe/Andorra	1993-12-23
+3040898	Camí del Cortal de la Serra	Cami del Cortal de la Serra		42.53333	1.5	R	TRL	AD		00				0		1357	Europe/Andorra	1993-12-23
+3040899	Mas del Cortal	Mas del Cortal		42.56667	1.6	S	FRM	AD		00				0		1655	Europe/Andorra	1993-12-23
+3040900	Canal del Cortà	Canal del Corta		42.5	1.48333	H	STM	AD		00				0		1316	Europe/Andorra	1993-12-23
+3040901	Canal de la Corruga	Canal de la Corruga		42.58333	1.63333	H	RVN	AD		00				0		1722	Europe/Andorra	1993-12-23
+3040902	Bosc de la Corruga	Bosc de la Corruga		42.56667	1.65	V	FRST	AD		00				0		1988	Europe/Andorra	1993-12-23
+3040903	Font del Correus	Font del Correus		42.56667	1.71667	H	SPNG	AD		00				0		2219	Europe/Andorra	1993-12-23
+3040904	Corrals de la Mentirosa	Corrals de la Mentirosa		42.43333	1.51667	S	RUIN	AD		00				0		2031	Europe/Andorra	1993-12-23
+3040905	Serra dels Corrals	Serra dels Corrals		42.55	1.45	T	MT	AD		00				0		1788	Europe/Andorra	1993-12-23
+3040906	Serrat de Corpalanca	Serrat de Corpalanca		42.53333	1.46667	T	RDGE	AD		00				0		1846	Europe/Andorra	1993-12-23
+3040907	Canal de Cordabalba	Canal de Cordabalba		42.48333	1.55	H	STM	AD		00				0		2233	Europe/Andorra	1993-12-23
+3040908	Roc dels Corbs	Roc dels Corbs		42.5	1.51667	T	RK	AD		00				0		1410	Europe/Andorra	1993-12-23
+3040909	Torrent de les Corbelles	Torrent de les Corbelles		42.55	1.6	H	STM	AD		00				0		2210	Europe/Andorra	1993-12-23
+3040910	Roca Corba	Roca Corba		42.51667	1.53333	T	RK	AD		00				0		1460	Europe/Andorra	1993-12-23
+3040911	Font del Corb	Font del Corb		42.55	1.46667	H	SPNG	AD		00				0		1585	Europe/Andorra	1993-12-23
+3040912	Canal del Corb	Canal del Corb		42.55	1.5	H	STM	AD		00				0		1292	Europe/Andorra	1993-12-23
+3040913	Canal del Corb	Canal del Corb		42.53333	1.56667	H	STM	AD		00				0		1418	Europe/Andorra	1993-12-23
+3040914	Camí del Corb	Cami del Corb		42.63333	1.51667	R	TRL	AD		00				0		1894	Europe/Andorra	1993-12-23
+3040915	Basers del Corb	Basers del Corb		42.63333	1.51667	T	CLF	AD		00				0		1894	Europe/Andorra	1993-12-23
+3040916	Serrat del Corantell	Serrat del Corantell		42.53333	1.5	T	RDGE	AD		00				0		1357	Europe/Andorra	1993-12-23
+3040917	Roc de la Copa	Roc de la Copa		42.5	1.46667	T	RK	AD		00				0		1678	Europe/Andorra	1993-12-23
+3040918	Font de Conxa	Font de Conxa		42.6	1.65	H	SPNG	AD		00				0		2131	Europe/Andorra	1993-12-23
+3040919	Bosc de Conxa	Bosc de Conxa		42.6	1.65	V	FRST	AD		00				0		2131	Europe/Andorra	1993-12-23
+3040920	Solana del Contador	Solana del Contador		42.48333	1.43333	T	SLP	AD		00				0		1938	Europe/Andorra	1993-12-23
+3040921	Font del Coniol	Font del Coniol		42.5	1.58333	H	SPNG	AD		00				0		1888	Europe/Andorra	1993-12-23
+3040922	Costa del Congost	Costa del Congost		42.6	1.46667	T	SLP	AD		00				0		2421	Europe/Andorra	1993-12-23
+3040923	Conangle	Conangle		42.43333	1.51667	L	LCTY	AD		00				0		2031	Europe/Andorra	1993-12-23
+3040924	Cóms de Jan	Coms de Jan		42.63333	1.61667	L	LCTY	AD		00				0		2541	Europe/Andorra	1993-12-23
+3040925	Roc dels Cóms	Roc dels Coms		42.63333	1.63333	T	CLF	AD		00				0		2603	Europe/Andorra	1993-12-23
+3040926	Planells dels Cóms	Planells dels Coms		42.55	1.6	T	UPLD	AD		00				0		2210	Europe/Andorra	1993-12-23
+3040927	Obaga dels Cóms	Obaga dels Coms		42.48333	1.41667	T	SLP	AD		00				0		1920	Europe/Andorra	1993-12-23
+3040928	Font dels Cóms	Font dels Coms		42.45	1.45	H	SPNG	AD		00				0		1482	Europe/Andorra	1993-12-23
+3040929	Canal dels Cóms	Canal dels Coms		42.48333	1.41667	H	STM	AD		00				0		1920	Europe/Andorra	1993-12-23
+3040930	Bosc dels Cóms	Bosc dels Coms		42.53333	1.46667	V	FRST	AD		00				0		1846	Europe/Andorra	1993-12-23
+3040931	Riu del Comís Vell	Riu del Comis Vell		42.63667	1.52189	H	STM	AD		07				0		2334	Europe/Andorra	2007-03-04
+3040932	Comís Vell	Comis Vell		42.63748	1.52098	L	LCTY	AD		07				0		2334	Europe/Andorra	2007-03-04
+3040933	Borda del Comet	Borda del Comet		42.56667	1.58333	S	HUT	AD		00				0		1919	Europe/Andorra	1993-12-23
+3040934	Comes de Banyàs	Comes de Banyas		42.55	1.51667	L	LCTY	AD		00				0		1397	Europe/Andorra	1993-12-23
+3040935	Bosc de Comes Beçoses	Bosc de Comes Becoses		42.51667	1.58333	V	FRST	AD		00				0		1994	Europe/Andorra	1993-12-23
+3040936	Solà de les Comes	Sola de les Comes		42.58333	1.5	T	SLP	AD		00				0		1595	Europe/Andorra	1993-12-23
+3040937	Portell de les Comes	Portell de les Comes		42.5	1.46667	T	PASS	AD		00				0		1678	Europe/Andorra	1993-12-23
+3040938	Canals de les Comes	Canals de les Comes		42.56667	1.51667	H	RVN	AD		00				0		1500	Europe/Andorra	1993-12-23
+3040939	Borda de les Comes	Borda de les Comes		42.53333	1.58333	S	FRM	AD		00				0		1571	Europe/Andorra	1993-12-23
+3040940	Bosc dels Comellassos	Bosc dels Comellassos		42.6	1.66667	V	FRST	AD		00				0		1858	Europe/Andorra	1993-12-23
+3040941	Costa del Comellar Llarg	Costa del Comellar Llarg		42.56667	1.61667	T	SLP	AD		00				0		1920	Europe/Andorra	1993-12-23
+3040942	Camí del Comellar Llarg	Cami del Comellar Llarg		42.56667	1.61667	R	TRL	AD		00				0		1920	Europe/Andorra	1993-12-23
+3040943	Clot de la Comellada	Clot de la Comellada		42.53333	1.45	H	RVN	AD		00				0		2130	Europe/Andorra	1993-12-23
+3040944	Riu de la Comella	Riu de la Comella		42.5	1.53333	H	STM	AD		00				0		1574	Europe/Andorra	1993-12-23
+3040945	Carretera de la Comella	Carretera de la Comella		42.51667	1.55	R	RD	AD		00				0		1322	Europe/Andorra	1993-12-23
+3040946	Bony de Comascura	Bony de Comascura		42.5	1.55	T	SPUR	AD		00				0		1566	Europe/Andorra	1993-12-23
+3040947	Canal de Coma Sansa	Canal de Coma Sansa		42.48333	1.46667	H	STM	AD		00				0		1148	Europe/Andorra	1993-12-23
+3040948	Riu de la Comarqueta d’Incles	Riu de la Comarqueta d'Incles		42.6	1.61667	H	STM	AD		00				0		2271	Europe/Andorra	1993-12-23
+3040949	Collet de la Comarqueta d’Incles	Collet de la Comarqueta d'Incles		42.6	1.61667	T	PASS	AD		00				0		2271	Europe/Andorra	1993-12-23
+3040950	Comarqueta d’Incles	Comarqueta d'Incles		42.6	1.61667	L	LCTY	AD		00				0		2271	Europe/Andorra	1993-12-23
+3040951	Tosa de la Comarqueta	Tosa de la Comarqueta		42.56667	1.73333	T	UPLD	AD		00				0		2096	Europe/Andorra	1993-12-23
+3040952	Font de la Comarqueta	Font de la Comarqueta		42.56667	1.71667	H	SPNG	AD		00				0		2219	Europe/Andorra	1993-12-23
+3040953	Basers de la Comarqueta	Basers de la Comarqueta		42.58333	1.71667	T	CLF	AD		00				0		2553	Europe/Andorra	1993-12-23
+3040954	Riu de la Comarca de les Fonts	Riu de la Comarca de les Fonts		42.6	1.61667	H	STM	AD		00				0		2271	Europe/Andorra	1993-12-23
+3040955	Obaga de la Comarca	Obaga de la Comarca		42.56667	1.46667	T	SLP	AD		00				0		1673	Europe/Andorra	1993-12-23
+3040956	Font de la Comarca	Font de la Comarca		42.56667	1.45	H	SPNG	AD		00				0		2137	Europe/Andorra	1993-12-23
+3040957	Riu de Coma Pedrosa	Riu de Coma Pedrosa		42.58333	1.46667	H	STM	AD		00				0		1643	Europe/Andorra	1993-12-23
+3040958	Pleta de Coma Pedrosa	Pleta de Coma Pedrosa		42.58333	1.45	L	GRAZ	AD		00				0		2156	Europe/Andorra	1993-12-23
+3040959	Pic de Coma Pedrosa	Pic de Coma Pedrosa	Pic Alt de la Coma Pedrosa,Pic Alt de la Pedrosa,Pic de Coma Pedrosa	42.5917	1.44428	T	PK	AD		00				0		2406	Europe/Andorra	2011-11-05
+3040960	Obaga de Coma Pedrosa	Obaga de Coma Pedrosa		42.58387	1.44182	T	SLP	AD		00				0		2350	Europe/Andorra	2011-04-19
+3040961	Grau de Coma Pedrosa	Grau de Coma Pedrosa		42.58333	1.46667	T	SLP	AD		00				0		1643	Europe/Andorra	1993-12-23
+3040962	Collet de Coma Pedrosa	Collet de Coma Pedrosa		42.58333	1.45	T	PK	AD		00				0		2156	Europe/Andorra	1993-12-23
+3040963	Camí de Coma Pedrosa	Cami de Coma Pedrosa		42.58333	1.48333	R	TRL	AD		00				0		1809	Europe/Andorra	1993-12-23
+3040964	Coma Pedrosa	Coma Pedrosa	Coma Pedrosa	42.58333	1.43333	A	ADMD	AD		00				0		2412	Europe/Andorra	2011-11-05
+3040965	Serra de Coma Obaga i Ferreroles	Serra de Coma Obaga i Ferreroles		42.61667	1.56667	T	RDGE	AD		00				0		2228	Europe/Andorra	1993-12-23
+3040966	Solana de Coma Obaga	Solana de Coma Obaga		42.61667	1.55	T	SLP	AD		00				0		2007	Europe/Andorra	1993-12-23
+3040967	Serra de Coma Obaga	Serra de Coma Obaga		42.61667	1.56667	T	RDGE	AD		00				0		2228	Europe/Andorra	1993-12-23
+3040968	Pala de Coma Obaga	Pala de Coma Obaga		42.61667	1.56667	T	SLP	AD		00				0		2228	Europe/Andorra	1993-12-23
+3040969	Coma Obaga	Coma Obaga		42.6	1.55	A	ADMD	AD		00				0		2298	Europe/Andorra	1993-12-23
+3040970	Comangerra	Comangerra		42.56667	1.5	T	SPUR	AD		00				0		1636	Europe/Andorra	1993-12-23
+3040971	Canal de Coma Llonga	Canal de Coma Llonga		42.56667	1.6	H	STM	AD		00				0		1655	Europe/Andorra	1993-12-23
+3040972	Riu de Comallempla	Riu de Comallempla		42.56667	1.48333	H	STM	AD		00				0		1508	Europe/Andorra	1993-12-23
+3040973	Portella de Comallempla	Portella de Comallempla		42.56667	1.45	T	PASS	AD		00				0		2137	Europe/Andorra	1993-12-23
+3040974	Camí de Comallempla	Cami de Comallempla		42.56667	1.48333	R	TRL	AD		00				0		1508	Europe/Andorra	1993-12-23
+3040975	Bordes de Comallempla	Bordes de Comallempla		42.56667	1.46667	S	HUTS	AD		00				0		1673	Europe/Andorra	1993-12-23
+3040976	Comallempla	Comallempla		42.56667	1.46667	A	ADMD	AD		00				0		1673	Europe/Andorra	1993-12-23
+3040977	Canal de Coma Fregona	Canal de Coma Fregona		42.58333	1.51667	H	STM	AD		00				0		1722	Europe/Andorra	1993-12-23
+3040978	Canal de Coma Fregona	Canal de Coma Fregona		42.56667	1.51667	H	STM	AD		00				0		1500	Europe/Andorra	1993-12-23
+3040979	Coma Estreta	Coma Estreta		42.58333	1.55	L	LCTY	AD		00				0		2357	Europe/Andorra	1993-12-23
+3040980	Coma Estremera	Coma Estremera		42.51667	1.68333	A	ADMD	AD		00				0		2352	Europe/Andorra	1993-12-23
+3040981	Col de la Portaneille	Col de la Portaneille	Col de la Portaneille,Portella de la Coma de Varilles	42.61667	1.65	T	PASS	AD		00				0		2567	Europe/Andorra	2011-11-05
+3040982	Pics de la Portaneille	Pics de la Portaneille	Passada,Pic de la Coma de Varilles,Pic de la Passada,Picos de la Passade,Pics de la Passade,Pics de la Portaneille	42.61667	1.66667	T	PKS	AD		00				0		2536	Europe/Andorra	2011-11-05
+3040983	Rocs de Coma de Teix	Rocs de Coma de Teix		42.46667	1.46667	T	RKS	AD		00				0		1340	Europe/Andorra	1993-12-23
+3040984	Coma de Ransol	Coma de Ransol		42.6	1.63333	A	ADMD	AD		00				0		1893	Europe/Andorra	1993-12-23
+3040985	Solana de la Coma dels Llops	Solana de la Coma dels Llops		42.51667	1.63333	T	SLP	AD		00				0		2379	Europe/Andorra	1993-12-23
+3040986	Riu de la Coma dels Llops	Riu de la Coma dels Llops		42.52722	1.60965	H	STM	AD		00				0		2101	Europe/Andorra	2011-04-19
+3040987	Pleta de la Coma dels Llops	Pleta de la Coma dels Llops		42.51667	1.61667	L	GRAZ	AD		00				0		2254	Europe/Andorra	1993-12-23
+3040988	Cap de la Coma dels Llops	Cap de la Coma dels Llops		42.50769	1.62525	T	RDGE	AD		00				0		2666	Europe/Andorra	2011-04-19
+3040989	Bosc de la Coma dels Llops	Bosc de la Coma dels Llops		42.51667	1.61667	V	FRST	AD		00				0		2254	Europe/Andorra	1993-12-23
+3040990	Coma dels Llops	Coma dels Llops		42.51667	1.61667	A	ADMD	AD		00				0		2254	Europe/Andorra	1993-12-23
+3040991	Clot de la Coma del Prat	Clot de la Coma del Prat		42.51667	1.46667	H	RVN	AD		00				0		1840	Europe/Andorra	1993-12-23
+3040992	Clot de la Coma del Pou	Clot de la Coma del Pou		42.51667	1.48333	H	RVN	AD		00				0		1839	Europe/Andorra	1993-12-23
+3040993	Riu de la Coma del Mig	Riu de la Coma del Mig		42.63333	1.51667	H	STM	AD		00				0		1894	Europe/Andorra	1993-12-23
+3040994	Riu de la Coma del Forat	Riu de la Coma del Forat		42.63333	1.5	H	STM	AD		00				0		1979	Europe/Andorra	1993-12-23
+3040995	Costa de la Coma del Forat	Costa de la Coma del Forat		42.63333	1.48333	T	SLP	AD		00				0		2283	Europe/Andorra	1993-12-23
+3040996	Coma del Favar	Coma del Favar		42.51667	1.58333	L	LCTY	AD		00				0		1994	Europe/Andorra	1993-12-23
+3040997	Clot de la Coma de la Sella	Clot de la Coma de la Sella		42.51667	1.48333	H	RVN	AD		00				0		1839	Europe/Andorra	1993-12-23
+3040998	Solà de la Coma de Cardes	Sola de la Coma de Cardes		42.58333	1.61667	T	SLP	AD		00				0		1707	Europe/Andorra	1993-12-23
+3040999	Tosa de Coma Bella	Tosa de Coma Bella		42.58333	1.68333	T	UPLD	AD		00				0		2294	Europe/Andorra	1993-12-23
+3041000	Font de Coma Bella	Font de Coma Bella		42.45	1.5	H	SPNG	AD		00				0		1614	Europe/Andorra	1993-12-23
+3041001	Canal de Coma Bella	Canal de Coma Bella		42.45	1.48333	H	STM	AD		00				0		1111	Europe/Andorra	1993-12-23
+3041002	Coma Bella	Coma Bella		42.45	1.5	L	LCTY	AD		00				0		1614	Europe/Andorra	1993-12-23
+3041003	Planell de Coma Aubosa	Planell de Coma Aubosa		42.58333	1.5	T	UPLD	AD		00				0		1595	Europe/Andorra	1993-12-23
+3041004	Clot de Coma Aubosa	Clot de Coma Aubosa		42.58333	1.48333	H	RVN	AD		00				0		1809	Europe/Andorra	1993-12-23
+3041005	Roc de la Coma	Roc de la Coma		42.51667	1.55	T	RK	AD		00				0		1322	Europe/Andorra	1993-12-23
+3041006	Riu de la Coma	Riu de la Coma		42.58333	1.63333	H	STM	AD		00				0		1722	Europe/Andorra	1993-12-23
+3041007	Riu de la Coma	Riu de la Coma		42.45	1.46667	H	STM	AD		00				0		935	Europe/Andorra	1993-12-23
+3041008	Prats de Coma	Prats de Coma		42.56667	1.46667	L	GRAZ	AD		00				0		1673	Europe/Andorra	1993-12-23
+3041009	Pont de la Coma	Pont de la Coma		42.55	1.46667	S	BDG	AD		00				0		1585	Europe/Andorra	1993-12-23
+3041010	Collet de la Coma	Collet de la Coma		42.65	1.51667	T	PASS	AD		00				0		2546	Europe/Andorra	1993-12-23
+3041011	Collada de la Coma	Collada de la Coma		42.6	1.61667	T	PASS	AD		00				0		2271	Europe/Andorra	1993-12-23
+3041012	Canal de la Coma	Canal de la Coma		42.61667	1.55	H	STM	AD		00				0		2007	Europe/Andorra	1993-12-23
+3041013	Canal de la Coma	Canal de la Coma		42.55	1.55	H	STM	AD		00				0		2097	Europe/Andorra	1993-12-23
+3041014	Canal de la Coma	Canal de la Coma		42.6	1.51667	H	RVN	AD		00				0		1445	Europe/Andorra	1993-12-23
+3041015	Bosc de Coma	Bosc de Coma		42.53333	1.55	V	FRST	AD		00				0		1344	Europe/Andorra	1993-12-23
+3041016	Bony de la Coma	Bony de la Coma		42.55	1.53333	T	SPUR	AD		00				0		1593	Europe/Andorra	1993-12-23
+3041017	Rocs del Colomer	Rocs del Colomer		42.53333	1.51667	T	SPUR	AD		00				0		1361	Europe/Andorra	1993-12-23
+3041018	Collet dels Colls	Collet dels Colls		42.55	1.51667	T	PASS	AD		00				0		1397	Europe/Andorra	1993-12-23
+3041019	Solana de Coll Pa	Solana de Coll Pa		42.55	1.43333	T	SLP	AD		00				0		1949	Europe/Andorra	1993-12-23
+3041020	Serrat de Coll Pa	Serrat de Coll Pa		42.55	1.43333	T	RDGE	AD		00				0		1949	Europe/Andorra	1993-12-23
+3041021	Planell de Coll Pa	Planell de Coll Pa		42.48333	1.56667	T	UPLD	AD		00				0		2231	Europe/Andorra	1993-12-23
+3041022	Pic de Coll Pa	Pic de Coll Pa		42.51667	1.48333	T	PK	AD		00				0		1839	Europe/Andorra	1993-12-23
+3041023	Bosc de Coll Pa	Bosc de Coll Pa		42.48333	1.55	V	FRST	AD		00				0		2233	Europe/Andorra	1993-12-23
+3041024	Basers de Coll Pa	Basers de Coll Pa		42.48333	1.56667	T	CLF	AD		00				0		2231	Europe/Andorra	1993-12-23
+3041025	Canal de Coll Jovell	Canal de Coll Jovell		42.5	1.56667	H	STM	AD		00				0		1776	Europe/Andorra	1993-12-23
+3041026	Canal de Collet Purgat	Canal de Collet Purgat		42.46667	1.48333	H	STM	AD		00				0		1134	Europe/Andorra	1993-12-23
+3041027	Clot del Collet de Font Podrida	Clot del Collet de Font Podrida		42.58333	1.46667	T	SLP	AD		00				0		1643	Europe/Andorra	1993-12-23
+3041028	Camí dels Collells	Cami dels Collells		42.56667	1.46667	R	TRL	AD		00				0		1673	Europe/Andorra	1993-12-23
+3041029	Carretera del Coll d’Ordino	Carretera del Coll d'Ordino		42.55	1.53333	R	RD	AD		00				0		1593	Europe/Andorra	1993-12-23
+3041030	Camí del Coll d’Ordino	Cami del Coll d'Ordino		42.55	1.56667	R	TRL	AD		00				0		1828	Europe/Andorra	1993-12-23
+3041031	Font del Coll de Vista	Font del Coll de Vista		42.48333	1.45	H	SPNG	AD		00				0		1195	Europe/Andorra	1993-12-23
+3041032	Canal del Coll de Vista	Canal del Coll de Vista		42.48333	1.45	H	STM	AD		00				0		1195	Europe/Andorra	1993-12-23
+3041033	Canal del Coll de Turer	Canal del Coll de Turer		42.56667	1.46667	H	STM	AD		00				0		1673	Europe/Andorra	1993-12-23
+3041034	Camí de Coll de Turer	Cami de Coll de Turer		42.56667	1.46667	R	TRL	AD		00				0		1673	Europe/Andorra	1993-12-23
+3041035	Canal de Coll d’Eres	Canal de Coll d'Eres		42.5	1.51667	H	STM	AD		00				0		1410	Europe/Andorra	1993-12-23
+3041036	Camí del Coll dels Isards	Cami del Coll dels Isards		42.51667	1.73333	R	TRL	AD		00				0		2484	Europe/Andorra	1993-12-23
+3041037	Canal del Coll de l’Obac	Canal del Coll de l'Obac		42.48333	1.48333	H	STM	AD		00				0		981	Europe/Andorra	1993-12-23
+3041038	Canal del Coll de les Cases	Canal del Coll de les Cases		42.56667	1.5	H	STM	AD		00				0		1636	Europe/Andorra	1993-12-23
+3041039	Bony del Coll de l’Era	Bony del Coll de l'Era		42.48333	1.45	T	SPUR	AD		00				0		1195	Europe/Andorra	1993-12-23
+3041040	Riu del Coll de l’Aquell	Riu del Coll de l'Aquell		42.48333	1.43333	H	STM	AD		00				0		1938	Europe/Andorra	1993-12-23
+3041041	Canal del Coll de l’Acaumader	Canal del Coll de l'Acaumader		42.48333	1.46667	H	STM	AD		00				0		1148	Europe/Andorra	1993-12-23
+3041042	Canal del Coll de la Cauba	Canal del Coll de la Cauba		42.58333	1.61667	H	STM	AD		00				0		1707	Europe/Andorra	1993-12-23
+3041043	Canal del Coll de la Basera	Canal del Coll de la Basera		42.58333	1.65	H	RVN	AD		00				0		1767	Europe/Andorra	1993-12-23
+3041044	Camí del Coll d’Arenes	Cami del Coll d'Arenes		42.6	1.58333	R	TRL	AD		00				0		2461	Europe/Andorra	1993-12-23
+3041045	Pala de Coll Carnisser	Pala de Coll Carnisser		42.6	1.46667	T	SLP	AD		00				0		2421	Europe/Andorra	1993-12-23
+3041046	Collada de Coll Carnisser	Collada de Coll Carnisser		42.6	1.48333	T	PASS	AD		00				0		2441	Europe/Andorra	1993-12-23
+3041047	Prats de Collart	Prats de Collart		42.58333	1.65	L	GRAZ	AD		00				0		1767	Europe/Andorra	1993-12-23
+3041048	Canal de Collart	Canal de Collart		42.58333	1.65	H	STM	AD		00				0		1767	Europe/Andorra	1993-12-23
+3041049	Bosc de Collart	Bosc de Collart		42.56667	1.66667	V	FRST	AD		00				0		1938	Europe/Andorra	1993-12-23
+3041050	La Colladeta	La Colladeta		42.55	1.55	T	PASS	AD		00				0		2097	Europe/Andorra	1993-12-23
+3041051	Bony de les Collades	Bony de les Collades		42.55	1.51667	T	MT	AD		00				0		1397	Europe/Andorra	1993-12-23
+3041052	Canal de la Collada Gran	Canal de la Collada Gran		42.5	1.48333	H	STM	AD		00				0		1316	Europe/Andorra	1993-12-23
+3041053	Camí de la Collada de Sanfons	Cami de la Collada de Sanfons		42.58333	1.45	R	TRL	AD		00				0		2156	Europe/Andorra	1993-12-23
+3041054	Camí de la Collada d’Enradort	Cami de la Collada d'Enradort		42.53333	1.61667	R	TRL	AD		00				0		2237	Europe/Andorra	1993-12-23
+3041055	Camí de la Collada de la Maiana	Cami de la Collada de la Maiana		42.48333	1.61667	R	TRL	AD		00				0		2217	Europe/Andorra	1993-12-23
+3041056	Camí de la Collada de Ferreroles	Cami de la Collada de Ferreroles		42.6	1.56667	R	TRL	AD		00				0		2513	Europe/Andorra	1993-12-23
+3041057	Clots de la Collada	Clots de la Collada		42.61667	1.61667	H	RVN	AD		00				0		2352	Europe/Andorra	1993-12-23
+3041058	Bosc de la Collada	Bosc de la Collada		42.51667	1.46667	V	FRST	AD		00				0		1840	Europe/Andorra	1993-12-23
+3041059	Planell del Colitx	Planell del Colitx		42.61667	1.51667	T	UPLD	AD		00				0		1716	Europe/Andorra	1993-12-23
+3041060	Font de la Colilla	Font de la Colilla		42.51667	1.6	H	SPNG	AD		00				0		2085	Europe/Andorra	1993-12-23
+3041061	Canal de la Colilla	Canal de la Colilla		42.48333	1.58333	H	STM	AD		00				0		2349	Europe/Andorra	1993-12-23
+3041062	Canal de la Colija	Canal de la Colija		42.58333	1.65	H	STM	AD		00				0		1767	Europe/Andorra	1993-12-23
+3041063	Bosc de la Colija	Bosc de la Colija		42.58333	1.65	V	FRST	AD		00				0		1767	Europe/Andorra	1993-12-23
+3041064	Riu dels Colells	Riu dels Colells		42.53333	1.7	H	STM	AD		00				0		2357	Europe/Andorra	1993-12-23
+3041065	Circ dels Colells	Circ dels Colells		42.51667	1.7	T	CRQ	AD		00				0		2435	Europe/Andorra	1993-12-23
+3041066	Bosc dels Colells	Bosc dels Colells		42.53333	1.7	V	FRST	AD		00				0		2357	Europe/Andorra	1993-12-23
+3041067	Cortal del Coix	Cortal del Coix		42.55	1.55	S	HUTS	AD		00				0		2097	Europe/Andorra	1993-12-23
+3041068	Tartera del Coferony	Tartera del Coferony		42.5	1.45	T	SLP	AD		00				0		1840	Europe/Andorra	1993-12-23
+3041069	Coferony	Coferony		42.48333	1.45	L	LCTY	AD		00				0		1195	Europe/Andorra	1993-12-23
+3041070	Canal de les Codolles	Canal de les Codolles		42.53333	1.51667	H	STM	AD		00				0		1361	Europe/Andorra	1993-12-23
+3041071	Serrat de Codinet	Serrat de Codinet		42.51667	1.6	T	SPUR	AD		00				0		2085	Europe/Andorra	1993-12-23
+3041072	Collada del Clot Sord	Collada del Clot Sord		42.6	1.65	T	PASS	AD		00				0		2131	Europe/Andorra	1993-12-23
+3041073	Pala dels Clots d’Entinyac	Pala dels Clots d'Entinyac		42.58333	1.68333	T	SLP	AD		00				0		2294	Europe/Andorra	1993-12-23
+3041074	Tosa dels Clots de Massat	Tosa dels Clots de Massat		42.56667	1.7	T	UPLD	AD		00				0		2375	Europe/Andorra	1993-12-23
+3041075	Pala dels Clots de Massat	Pala dels Clots de Massat		42.56667	1.7	T	SLP	AD		00				0		2375	Europe/Andorra	1993-12-23
+3041076	Bassot dels Clots de Massat	Bassot dels Clots de Massat		42.56667	1.7	H	LK	AD		00				0		2375	Europe/Andorra	1993-12-23
+3041077	Clots de Massat	Clots de Massat		42.56667	1.68333	A	ADMD	AD		00				0		2340	Europe/Andorra	1993-12-23
+3041078	Clots de l’Ós	Clots de l'Os		42.58333	1.68333	A	ADMD	AD		00				0		2294	Europe/Andorra	1993-12-23
+3041079	Font dels Clots de la Llosa	Font dels Clots de la Llosa		42.61667	1.61667	H	SPNG	AD		00				0		2352	Europe/Andorra	1993-12-23
+3041080	Font dels Clots	Font dels Clots		42.55	1.71667	H	SPNG	AD		00				0		2192	Europe/Andorra	1993-12-23
+3041081	Canal del Clot del Mener	Canal del Clot del Mener		42.5	1.53333	H	STM	AD		00				0		1574	Europe/Andorra	1993-12-23
+3041082	Barranc del Clot de les Deveses	Barranc del Clot de les Deveses		42.53333	1.51667	H	STM	AD		00				0		1361	Europe/Andorra	1993-12-23
+3041083	Pic del Clot del Cavall	Pic del Clot del Cavall		42.6	1.5	T	PK	AD		00				0		1923	Europe/Andorra	1993-12-23
+3041084	Barranc del Clot d’Aixades	Barranc del Clot d'Aixades		42.56667	1.58333	H	STM	AD		00				0		1919	Europe/Andorra	1993-12-23
+3041085	Font de la Closa	Font de la Closa		42.5	1.56667	H	SPNG	AD		00				0		1776	Europe/Andorra	1993-12-23
+3041086	Pas de la Clau	Pas de la Clau		42.51667	1.61667	T	PASS	AD		00				0		2254	Europe/Andorra	1993-12-23
+3041087	Riu de Claror i Perafita	Riu de Claror i Perafita		42.5	1.55	H	STM	AD		00				0		1566	Europe/Andorra	1993-12-23
+3041088	Riu de Claror	Riu de Claror		42.47897	1.56898	H	STM	AD		00				0		2231	Europe/Andorra	2011-04-19
+3041089	Pleta de Claror	Pleta de Claror		42.48333	1.56667	L	GRAZ	AD		00				0		2231	Europe/Andorra	1993-12-23
+3041090	Camí de Claror	Cami de Claror		42.48333	1.56667	R	TRL	AD		00				0		2231	Europe/Andorra	1993-12-23
+3041091	Cabana de Claror	Cabana de Claror		42.46667	1.56667	S	HUT	AD		00				0		2365	Europe/Andorra	1993-12-23
+3041092	Claror	Claror		42.46667	1.56667	A	ADMD	AD		00				0		2365	Europe/Andorra	1993-12-23
+3041093	Riu de les Claperes	Riu de les Claperes		42.55	1.51667	H	STM	AD		00				0		1397	Europe/Andorra	1993-12-23
+3041094	Canal de les Claperes	Canal de les Claperes		42.55	1.5	H	STM	AD		00				0		1292	Europe/Andorra	1993-12-23
+3041095	Civòs	Civos		42.45	1.48333	L	LCTY	AD		00				0		1111	Europe/Andorra	1993-12-23
+3041096	Canal de la Cirera	Canal de la Cirera		42.5	1.5	H	STM	AD		00				0		1135	Europe/Andorra	1993-12-23
+3041097	Conreu de Certers	Conreu de Certers		42.48333	1.5	V	CULT	AD		00				0		1631	Europe/Andorra	1993-12-23
+3041098	Certers	Certers	Certers,Certes,Certés,Sertes	42.47468	1.50575	P	PPL	AD		06				0		1383	Europe/Andorra	2011-11-05
+3041099	Coll del Cerc	Coll del Cerc		42.46667	1.5	T	PASS	AD		00				0		1383	Europe/Andorra	1993-12-23
+3041100	Cementiri	Cementiri		42.56667	1.73333	L	LCTY	AD		00				0		2096	Europe/Andorra	1993-12-23
+3041101	Costa dell Cell	Costa dell Cell		42.48333	1.48333	T	SLP	AD		00				0		981	Europe/Andorra	1993-12-23
+3041102	Tarteres de la Cebollera	Tarteres de la Cebollera		42.63333	1.6	T	TAL	AD		00				0		2635	Europe/Andorra	1993-12-23
+3041103	Riu de la Cebollera	Riu de la Cebollera		42.61667	1.56667	H	STM	AD		00				0		2228	Europe/Andorra	1993-12-23
+3041104	Portella de la Cebollera	Portella de la Cebollera	Portella de la Cebollera	42.63333	1.6	T	PASS	AD		00				0		2635	Europe/Andorra	2011-11-05
+3041105	Pleta de la Cebollera	Pleta de la Cebollera		42.63333	1.58333	L	GRAZ	AD		00				0		2470	Europe/Andorra	1993-12-23
+3041106	Basses de la Cebollera	Basses de la Cebollera		42.63333	1.58333	H	LKS	AD		00				0		2470	Europe/Andorra	1993-12-23
+3041107	Aspres de la Cebollera	Aspres de la Cebollera		42.63333	1.58333	V	VINS	AD		00				0		2470	Europe/Andorra	1993-12-23
+3041108	Riu de les Cebes	Riu de les Cebes		42.61667	1.58333	H	STM	AD		00				0		2374	Europe/Andorra	1993-12-23
+3041109	Clot del Cavall	Clot del Cavall		42.6	1.5	T	SLP	AD		00				0		1923	Europe/Andorra	1993-12-23
+3041110	Costa del Caup	Costa del Caup		42.6	1.66667	T	SLP	AD		00				0		1858	Europe/Andorra	1993-12-23
+3041111	Solana de la Caülla	Solana de la Caulla		42.48333	1.53333	T	SLP	AD		00				0		2255	Europe/Andorra	1993-12-23
+3041112	Collada de la Caülla	Collada de la Caulla		42.48333	1.53333	T	PASS	AD		00				0		2255	Europe/Andorra	1993-12-23
+3041113	Clots de la Caülla	Clots de la Caulla		42.48333	1.53333	H	RVN	AD		00				0		2255	Europe/Andorra	1993-12-23
+3041114	Bosc de la Caülla	Bosc de la Caulla		42.48333	1.53333	V	FRST	AD		00				0		2255	Europe/Andorra	1993-12-23
+3041115	Planell de la Caubella	Planell de la Caubella		42.53333	1.48333	T	UPLD	AD		00				0		1677	Europe/Andorra	1993-12-23
+3041116	Torrent de la Cauba	Torrent de la Cauba		42.55	1.51667	H	STM	AD		00				0		1397	Europe/Andorra	1993-12-23
+3041117	Roc de la Cauba	Roc de la Cauba		42.56114	1.51506	T	RK	AD		00				0		1551	Europe/Andorra	2011-04-19
+3041118	Coll de la Cauba	Coll de la Cauba		42.58333	1.61667	T	PASS	AD		00				0		1707	Europe/Andorra	1993-12-23
+3041119	Bosc de la Cauba	Bosc de la Cauba		42.56667	1.51667	V	FRST	AD		00				0		1500	Europe/Andorra	1993-12-23
+3041120	Catolla la Guineu	Catolla la Guineu		42.46667	1.46667	L	LCTY	AD		00				0		1340	Europe/Andorra	1993-12-23
+3041121	Pic de Cataverdis	Pic de Cataverdis	Pic de Cataperdis,Pic de Cataperdís,Pic de Cataverdis	42.61667	1.46667	T	PK	AD		00				0		2442	Europe/Andorra	2011-11-05
+3041122	Roc dels Castells	Roc dels Castells		42.51667	1.55	T	RK	AD		00				0		1322	Europe/Andorra	1993-12-23
+3041123	Riu de les Castelletes	Riu de les Castelletes		42.45	1.53333	H	STM	AD		00				0		1859	Europe/Andorra	1993-12-23
+3041124	Roc de la Castelleta	Roc de la Castelleta		42.56667	1.5	T	RK	AD		00				0		1636	Europe/Andorra	1993-12-23
+3041125	Canal de la Castelleta	Canal de la Castelleta		42.53333	1.5	H	STM	AD		00				0		1357	Europe/Andorra	1993-12-23
+3041126	Bosc de la Castelleta	Bosc de la Castelleta		42.53333	1.5	V	FRST	AD		00				0		1357	Europe/Andorra	1993-12-23
+3041127	Bony de la Castelleta	Bony de la Castelleta		42.53333	1.53333	T	SPUR	AD		00				0		1521	Europe/Andorra	1993-12-23
+3041128	Font del Casteller	Font del Casteller		42.53333	1.55	H	SPNG	AD		00				0		1344	Europe/Andorra	1993-12-23
+3041129	Castell de Sant Vicenç	Castell de Sant Vicenc	Castell de Sant Vicenc,Castell de Sant Vicens,Castell de Sant Vicenç	42.49658	1.48686	S	RUIN	AD		00				0		1027	Europe/Andorra	2011-11-05
+3041130	Castell dels Moros	Castell dels Moros	Castel dels Moros La Meca,Castell dels Moros,La Meca	42.55	1.53333	T	PROM	AD	AD	00				0		1593	Europe/Andorra	2011-11-05
+3041131	Pleta del Castellar	Pleta del Castellar		42.63333	1.51667	L	GRAZ	AD		00				0		1894	Europe/Andorra	1993-12-23
+3041132	Canal del Castellar	Canal del Castellar	Canal del Castella,Canal del Castellar,Canal del Castellà	42.6	1.63333	H	STM	AD	AD	00				0		1893	Europe/Andorra	2011-11-05
+3041133	Bosc del Castellar	Bosc del Castellar		42.63333	1.51667	V	FRST	AD		00				0		1894	Europe/Andorra	1993-12-23
+3041134	Bordes del Castellar	Bordes del Castellar		42.53031	1.60873	S	HUTS	AD		00				0		2101	Europe/Andorra	2011-04-19
+3041135	Rocs de Castell	Rocs de Castell		42.46667	1.45	T	RKS	AD		00				0		1562	Europe/Andorra	1993-12-23
+3041136	Roc del Castell	Roc del Castell		42.46667	1.46667	T	RK	AD		00				0		1340	Europe/Andorra	1993-12-23
+3041137	Canal del Castell	Canal del Castell		42.58333	1.51667	H	STM	AD		00				0		1722	Europe/Andorra	1993-12-23
+3041138	Bosc del Castell	Bosc del Castell		42.58333	1.51667	V	FRST	AD		00				0		1722	Europe/Andorra	1993-12-23
+3041139	Coll de les Cases	Coll de les Cases		42.58333	1.5	T	PK	AD		00				0		1595	Europe/Andorra	1993-12-23
+3041140	Canal de les Casasses	Canal de les Casasses		42.55	1.5	H	STM	AD		00				0		1292	Europe/Andorra	1993-12-23
+3041141	Serra de Casamanya	Serra de Casamanya		42.58877	1.57163	T	RDGE	AD		00				0		2423	Europe/Andorra	2011-04-19
+3041142	Riu de Casamanya	Riu de Casamanya		42.55	1.55	H	STM	AD		00				0		2097	Europe/Andorra	1993-12-23
+3041143	Pic de Casamanya	Pic de Casamanya	Pic de Camanya,Pic de Casamanya	42.58619	1.56971	T	PK	AD		00				0		2423	Europe/Andorra	2011-11-05
+3041144	Camí de Casamanya	Cami de Casamanya		42.56667	1.55	R	TRL	AD		00				0		1996	Europe/Andorra	1993-12-23
+3041145	Casamanya	Casamanya		42.56667	1.55	A	ADMD	AD		00				0		1996	Europe/Andorra	1993-12-23
+3041146	Borda del Casadet	Borda del Casadet		42.56667	1.6	S	HUT	AD		00				0		1655	Europe/Andorra	1993-12-23
+3041147	Bordes de la Casa	Bordes de la Casa	Bordes,Bordes de la Casa	42.53333	1.61667	S	HUTS	AD	AD	00				0		2237	Europe/Andorra	2011-11-05
+3041148	Bordes de la Casa	Bordes de la Casa		42.53333	1.6	S	HUTS	AD		00				0		1888	Europe/Andorra	1993-12-23
+3041149	Pic de Carroi	Pic de Carroi		42.51667	1.5	T	PK	AD		00				0		1688	Europe/Andorra	1993-12-23
+3041150	Roc del Carret	Roc del Carret		42.56667	1.48333	T	RK	AD		00				0		1508	Europe/Andorra	1993-12-23
+3041151	Roc del Carrador	Roc del Carrador		42.56667	1.48333	T	RK	AD		00				0		1508	Europe/Andorra	1993-12-23
+3041152	Bosc del Carpider	Bosc del Carpider		42.51667	1.5	V	FRST	AD		00				0		1688	Europe/Andorra	1993-12-23
+3041153	Canal Carnissera	Canal Carnissera		42.58333	1.46667	H	STM	AD		00				0		1643	Europe/Andorra	1993-12-23
+3041154	Canal Carnissera	Canal Carnissera		42.5	1.6	H	STM	AD		00				0		2416	Europe/Andorra	1993-12-23
+3041155	Canal Carnissera	Canal Carnissera		42.65	1.55	H	RVN	AD		00				0		2181	Europe/Andorra	1993-12-23
+3041156	Roca de Carmenús	Roca de Carmenus		42.55	1.61667	T	RK	AD		00				0		2206	Europe/Andorra	1993-12-23
+3041157	Clots de Carmenús	Clots de Carmenus		42.55	1.61667	H	RVN	AD		00				0		2206	Europe/Andorra	1993-12-23
+3041158	Riu del Cardemeller	Riu del Cardemeller		42.55	1.46667	H	STM	AD		00				0		1585	Europe/Andorra	1993-12-23
+3041159	Borda del Cardago	Borda del Cardago		42.53333	1.58333	S	HUT	AD		00				0		1571	Europe/Andorra	1993-12-23
+3041160	Roca de Carcamanyà	Roca de Carcamanya		42.53333	1.56667	T	RK	AD		00				0		1418	Europe/Andorra	1993-12-23
+3041161	Barranc del Carcabanyat	Barranc del Carcabanyat		42.55	1.48333	H	STM	AD		00				0		1548	Europe/Andorra	1993-12-23
+3041162	Carboneres de Ferrer	Carboneres de Ferrer		42.48333	1.6	L	LCTY	AD		00				0		2250	Europe/Andorra	1993-12-23
+3041163	Bosc de les Carboneres	Bosc de les Carboneres		42.51667	1.51667	V	FRST	AD		00				0		1265	Europe/Andorra	1993-12-23
+3041164	Bony de les Carboneres	Bony de les Carboneres		42.56667	1.65	T	SPUR	AD		00				0		1988	Europe/Andorra	1993-12-23
+3041165	Pleta Carbona	Pleta Carbona		42.63333	1.5	L	GRAZ	AD		00				0		1979	Europe/Andorra	1993-12-23
+3041166	Planell del Carbó	Planell del Carbo		42.53333	1.48333	T	UPLD	AD		00				0		1677	Europe/Andorra	1993-12-23
+3041167	Canal de la Carbassa	Canal de la Carbassa		42.51667	1.48333	H	STM	AD		00				0		1839	Europe/Andorra	1993-12-23
+3041168	Tosa de Caraup	Tosa de Caraup		42.6	1.65	T	UPLD	AD		00				0		2131	Europe/Andorra	1993-12-23
+3041169	Planells de Caraup	Planells de Caraup		42.61667	1.65	T	UPLD	AD		00				0		2567	Europe/Andorra	1993-12-23
+3041170	Clots de Caraup	Clots de Caraup		42.61667	1.65	H	RVN	AD		00				0		2567	Europe/Andorra	1993-12-23
+3041171	Riu de Cap Torrent	Riu de Cap Torrent		42.53333	1.56667	H	STM	AD		00				0		1418	Europe/Andorra	1993-12-23
+3041172	Pont de Capigol	Pont de Capigol		42.56667	1.66667	S	BDG	AD		00				0		1938	Europe/Andorra	1993-12-23
+3041173	Font dels Capellans	Font dels Capellans		42.6	1.63333	H	SPNG	AD		00				0		1893	Europe/Andorra	1993-12-23
+3041174	Cortal del Capdevila	Cortal del Capdevila		42.53333	1.53333	S	CRRL	AD		00				0		1521	Europe/Andorra	1993-12-23
+3041175	Estany del Cap dels Pessons	Estany del Cap dels Pessons		42.51996	1.67873	H	LK	AD		00				0		2352	Europe/Andorra	2011-04-19
+3041176	Tosa del Cap del Siscaró	Tosa del Cap del Siscaro		42.58333	1.71667	T	UPLD	AD		00				0		2553	Europe/Andorra	1993-12-23
+3041177	Cap dels Clots de Massat	Cap dels Clots de Massat		42.56667	1.7	L	LCTY	AD		00				0		2375	Europe/Andorra	1993-12-23
+3041178	Collada del Cap dels Clots	Collada del Cap dels Clots		42.55	1.63333	T	PASS	AD		00				0		2336	Europe/Andorra	1993-12-23
+3041179	Cap dels Clots	Cap dels Clots		42.55	1.63333	L	LCTY	AD		00				0		2336	Europe/Andorra	1993-12-23
+3041180	Canal del Cap dels Camp	Canal del Cap dels Camp		42.55	1.53333	H	STM	AD		00				0		1593	Europe/Andorra	1993-12-23
+3041181	Clot del Cap del Maià	Clot del Cap del Maia		42.55	1.71667	H	RVN	AD		00				0		2192	Europe/Andorra	1993-12-23
+3041182	Pala del Cap de les Tallades	Pala del Cap de les Tallades		42.61667	1.55	T	SLP	AD		00				0		2007	Europe/Andorra	1993-12-23
+3041183	Planada del Cap de les Canals dels Obacs	Planada del Cap de les Canals dels Obacs		42.6	1.5	T	UPLD	AD		00				0		1923	Europe/Andorra	1993-12-23
+3041184	Cap de les Agols	Cap de les Agols		42.5	1.61667	L	LCTY	AD		00				0		2560	Europe/Andorra	1993-12-23
+3041185	Cap del Bosc de Moretó	Cap del Bosc de Moreto		42.53333	1.68333	L	LCTY	AD		00				0		2322	Europe/Andorra	1993-12-23
+3041186	Cap del Bosc dels Plans	Cap del Bosc dels Plans		42.58333	1.61667	L	LCTY	AD		00				0		1707	Europe/Andorra	1993-12-23
+3041187	Cap de la Solana del Forn	Cap de la Solana del Forn		42.53333	1.65	L	LCTY	AD		00				0		2508	Europe/Andorra	1993-12-23
+3041188	Cap de la Montada	Cap de la Montada		42.56667	1.58333	L	LCTY	AD		00				0		1919	Europe/Andorra	1993-12-23
+3041189	Tarteres del Cap de la Coma	Tarteres del Cap de la Coma		42.61667	1.48333	T	TAL	AD		00				0		2470	Europe/Andorra	1993-12-23
+3041190	Serra del Cap de la Coma	Serra del Cap de la Coma		42.61667	1.48333	T	RDGE	AD		00				0		2470	Europe/Andorra	1993-12-23
+3041191	Alt de la Capa	Alt de la Capa		42.56293	1.45424	T	PK	AD		00				0		2173	Europe/Andorra	2011-04-19
+3041192	Font de les Canyorques	Font de les Canyorques		42.58333	1.43333	H	SPNG	AD		00				0		2412	Europe/Andorra	1993-12-23
+3041193	Coves de la Canya Gran	Coves de la Canya Gran		42.48333	1.46667	S	CAVE	AD		00				0		1148	Europe/Andorra	1993-12-23
+3041194	Planell de la Canya	Planell de la Canya		42.58333	1.6	T	UPLD	AD		00				0		1828	Europe/Andorra	1993-12-23
+3041195	Bosc de la Canya	Bosc de la Canya		42.58333	1.6	V	FRST	AD		00				0		1828	Europe/Andorra	1993-12-23
+3041196	Font de Cantallops	Font de Cantallops		42.56667	1.5	H	SPNG	AD		00				0		1636	Europe/Andorra	1993-12-23
+3041197	Canal de Cantallops	Canal de Cantallops		42.56667	1.5	H	STM	AD		00				0		1636	Europe/Andorra	1993-12-23
+3041198	Roc de Canomala	Roc de Canomala		42.56667	1.68333	T	RK	AD		00				0		2340	Europe/Andorra	1993-12-23
+3041199	Santuari de Canòlic	Santuari de Canolic		42.46667	1.45	S	CH	AD		00				0		1562	Europe/Andorra	1993-12-23
+3041200	Conreu de Canòlic	Conreu de Canolic		42.48333	1.45	V	CULT	AD		00				0		1195	Europe/Andorra	1993-12-23
+3041201	Carretera de Canòlic	Carretera de Canolic		42.48333	1.46667	R	RD	AD		00				0		1148	Europe/Andorra	1993-12-23
+3041202	Canòlic	Canolic		42.46667	1.45	A	ADMD	AD		00				0		1562	Europe/Andorra	1993-12-23
+3041203	Parròquia de Canillo	Parroquia de Canillo	Canillo,Parroquia de Canillo,Parròquia de Canillo	42.58333	1.66667	A	ADM1	AD	AD	02				5067		2159	Europe/Andorra	2011-11-05
+3041204	Canillo	Canillo	Canillo,Kanil'o,ka ni e,kaniryo jiao qu,Канильо,カニーリョ教区,å¡å°¼ç•¥	42.5669	1.59556	P	PPLA	AD		02				3292		1640	Europe/Andorra	2011-11-05
+3041205	Estany de les Canals Roges	Estany de les Canals Roges		42.58333	1.71667	H	LK	AD		00				0		2553	Europe/Andorra	1993-12-23
+3041206	Cap de les Canals de Ribanelles	Cap de les Canals de Ribanelles		42.58333	1.46667	T	PK	AD		00				0		1643	Europe/Andorra	1993-12-23
+3041207	Pic de les Canals de Montmantell	Pic de les Canals de Montmantell		42.6	1.48333	T	PK	AD		00				0		2441	Europe/Andorra	1993-12-23
+3041208	Canals dels Planells de Baell	Canals dels Planells de Baell		42.5	1.6	L	LCTY	AD		00				0		2416	Europe/Andorra	1993-12-23
+3041209	Canals dels Obacs	Canals dels Obacs		42.6	1.5	H	RVN	AD		00				0		1923	Europe/Andorra	1993-12-23
+3041210	Canals del Pla de l’Ingla	Canals del Pla de l'Ingla		42.48333	1.61667	L	LCTY	AD		00				0		2217	Europe/Andorra	1993-12-23
+3041211	Canals del Maià	Canals del Maia		42.56667	1.73333	L	LCTY	AD		00				0		2096	Europe/Andorra	1993-12-23
+3041212	Canals de la Rabassa	Canals de la Rabassa		42.63333	1.56667	L	LCTY	AD		00				0		2394	Europe/Andorra	1993-12-23
+3041213	Canals de la Comarqueta	Canals de la Comarqueta		42.48333	1.61667	L	LCTY	AD		00				0		2217	Europe/Andorra	1993-12-23
+3041214	Canals de la Burna	Canals de la Burna		42.58333	1.5	L	LCTY	AD		00				0		1595	Europe/Andorra	1993-12-23
+3041215	Serra de les Canals de Falcobí	Serra de les Canals de Falcobi		42.63333	1.55	T	RDGE	AD		00				0		2053	Europe/Andorra	1993-12-23
+3041216	Canals de Falcobí	Canals de Falcobi		42.63333	1.55	L	LCTY	AD		00				0		2053	Europe/Andorra	1993-12-23
+3041217	Canals de Comascura	Canals de Comascura		42.5	1.55	L	LCTY	AD		00				0		1566	Europe/Andorra	1993-12-23
+3041218	Riu de les Canals	Riu de les Canals		42.58333	1.63333	H	STM	AD		00				0		1722	Europe/Andorra	1993-12-23
+3041219	Camí de les Canals	Cami de les Canals		42.58333	1.63333	R	TRL	AD		00				0		1722	Europe/Andorra	1993-12-23
+3041220	Bosc de les Canals	Bosc de les Canals		42.58333	1.63333	V	FRST	AD		00				0		1722	Europe/Andorra	1993-12-23
+3041221	Bosc de la Canal Llisa	Bosc de la Canal Llisa		42.56667	1.5	V	FRST	AD		00				0		1636	Europe/Andorra	1993-12-23
+3041222	Serrat de la Canal de Nicolau	Serrat de la Canal de Nicolau		42.61667	1.55	T	RDGE	AD		00				0		2007	Europe/Andorra	1993-12-23
+3041223	Solana de la Canal	Solana de la Canal		42.48333	1.43333	T	SLP	AD		00				0		1938	Europe/Andorra	1993-12-23
+3041224	Torrent de la Canadilla	Torrent de la Canadilla		42.53333	1.58333	H	STM	AD		00				0		1571	Europe/Andorra	1993-12-23
+3041225	Camps de Sispony	Camps de Sispony		42.53333	1.51667	L	LCTY	AD		00				0		1361	Europe/Andorra	1993-12-23
+3041226	Torrent dels Camps de Pardellà	Torrent dels Camps de Pardella		42.46667	1.5	H	STM	AD		00				0		1383	Europe/Andorra	1993-12-23
+3041227	Camps de Pardellà	Camps de Pardella		42.46667	1.51667	L	LCTY	AD		00				0		1985	Europe/Andorra	1993-12-23
+3041228	Camp Ramonet	Camp Ramonet		42.47397	1.53854	L	LCTY	AD		00				0		2541	Europe/Andorra	2011-04-19
+3041229	Planell de Campillar	Planell de Campillar		42.58333	1.68333	T	UPLD	AD		00				0		2294	Europe/Andorra	1993-12-23
+3041230	Fonts del Campeà	Fonts del Campea		42.51667	1.61667	H	SPNG	AD		00				0		2254	Europe/Andorra	1993-12-23
+3041231	Bosc del Campeà	Bosc del Campea		42.53333	1.63333	V	FRST	AD		00				0		2360	Europe/Andorra	1993-12-23
+3041232	Camp de Vassalló	Camp de Vassallo		42.58333	1.53333	L	LCTY	AD		00				0		1924	Europe/Andorra	1993-12-23
+3041233	Camp del Sastre	Camp del Sastre		42.45	1.53333	L	LCTY	AD		00				0		1859	Europe/Andorra	1993-12-23
+3041234	Camp del Remugar	Camp del Remugar		42.56667	1.53333	L	LCTY	AD		00				0		1669	Europe/Andorra	1993-12-23
+3041235	Camp del Cortal	Camp del Cortal		42.56667	1.5	L	LCTY	AD		00				0		1636	Europe/Andorra	1993-12-23
+3041236	Camp de la Trava	Camp de la Trava		42.56667	1.53333	L	LCTY	AD		00				0		1669	Europe/Andorra	1993-12-23
+3041237	Camp de la Llosa	Camp de la Llosa		42.56667	1.51667	L	LCTY	AD		00				0		1500	Europe/Andorra	1993-12-23
+3041238	Bosc del Camp de la Finestra	Bosc del Camp de la Finestra		42.5	1.53333	V	FRST	AD		00				0		1574	Europe/Andorra	1993-12-23
+3041239	Pedrusques del Camp de Claror	Pedrusques del Camp de Claror		42.48333	1.55	T	TAL	AD		00				0		2233	Europe/Andorra	1993-12-23
+3041240	Camp de Claror	Camp de Claror		42.46667	1.55	L	LCTY	AD		00				0		2341	Europe/Andorra	1993-12-23
+3041241	Camp Borrut	Camp Borrut		42.45	1.56667	L	LCTY	AD		00				0		2558	Europe/Andorra	1993-12-23
+3041242	Pont del Camp	Pont del Camp		42.55	1.48333	S	BDG	AD		00				0		1548	Europe/Andorra	1993-12-23
+3041243	Camí del Canal	Cami del Canal		42.51667	1.58333	H	CNL	AD		00				0		1994	Europe/Andorra	1993-12-23
+3041244	Cal Toni	Cal Toni		42.55	1.6	S	HSE	AD		00				0		2210	Europe/Andorra	1993-12-23
+3041245	Cal Serra	Cal Serra		42.46667	1.5	S	FRM	AD		00				0		1383	Europe/Andorra	1993-12-23
+3041246	Cal Ponet	Cal Ponet		42.56667	1.6	S	HSE	AD		00				0		1655	Europe/Andorra	1993-12-23
+3041247	Cal Patxeta	Cal Patxeta		42.56667	1.6	S	HSE	AD		00				0		1655	Europe/Andorra	1993-12-23
+3041248	Cal Jaumina	Cal Jaumina		42.55	1.58333	S	HSE	AD		00				0		1499	Europe/Andorra	1993-12-23
+3041249	Canal de la Calcinera	Canal de la Calcinera	Canal de la Calcinera,Canal de la Calzinera	42.53333	1.58333	H	STM	AD	AD	00				0		1571	Europe/Andorra	2011-11-05
+3041250	Cal Call	Cal Call		42.55	1.6	S	HSE	AD		00				0		2210	Europe/Andorra	1993-12-23
+3041251	Cal Borronet	Cal Borronet		42.56667	1.6	S	HSE	AD		00				0		1655	Europe/Andorra	1993-12-23
+3041252	Cal Borró	Cal Borro		42.55	1.6	S	HSE	AD		00				0		2210	Europe/Andorra	1993-12-23
+3041253	Cal Becaina	Cal Becaina		42.55	1.58333	S	HSE	AD		00				0		1499	Europe/Andorra	1993-12-23
+3041254	Cal Bartreta	Cal Bartreta		42.55	1.6	S	HSE	AD		00				0		2210	Europe/Andorra	1993-12-23
+3041255	Solà de Calaup	Sola de Calaup		42.58333	1.65	T	SLP	AD		00				0		1767	Europe/Andorra	1993-12-23
+3041256	Font de la Caitanta	Font de la Caitanta		42.48333	1.63333	H	SPNG	AD		00				0		2296	Europe/Andorra	1993-12-23
+3041257	Serrat del Caire Forc	Serrat del Caire Forc		42.53333	1.61667	T	SPUR	AD		00				0		2237	Europe/Andorra	1993-12-23
+3041258	Riu del Caire Forc	Riu del Caire Forc		42.53333	1.61667	H	STM	AD		00				0		2237	Europe/Andorra	1993-12-23
+3041259	Caire Forc	Caire Forc		42.53333	1.63333	L	LCTY	AD		00				0		2360	Europe/Andorra	1993-12-23
+3041260	Torrent dels Càcols	Torrent dels Cacols		42.56667	1.48333	H	STM	AD		00				0		1508	Europe/Andorra	1993-12-23
+3041261	Roc de la Cacarulla	Roc de la Cacarulla		42.55	1.48333	T	RK	AD		00				0		1548	Europe/Andorra	1993-12-23
+3041262	Collado de Cabris	Collado de Cabris	Collado de Cabris,Port de Cabus,Port de Cabús	42.55	1.41667	T	PASS	AD		00				0		2105	Europe/Andorra	2011-11-05
+3041263	Solana de Caborreu	Solana de Caborreu		42.43333	1.55	T	SLP	AD		00				0		2178	Europe/Andorra	1993-12-23
+3041264	Riu de Caborreu	Riu de Caborreu		42.45	1.53333	H	STM	AD		00				0		1859	Europe/Andorra	1993-12-23
+3041265	Bosc de la Cabeça	Bosc de la Cabeca		42.5	1.45	V	FRST	AD		00				0		1840	Europe/Andorra	1993-12-23
+3041266	Pleta de la Cabaneta	Pleta de la Cabaneta		42.6	1.61667	L	GRAZ	AD		00				0		2271	Europe/Andorra	1993-12-23
+3041267	Pic de la Cabaneta	Pic de la Cabaneta		42.61667	1.6	T	PK	AD		00				0		2528	Europe/Andorra	1993-12-23
+3041268	Pic de la Cabanette	Pic de la Cabanette	Cabaneta,Pic de la Cabaneta,Pic de la Cabanette	42.58333	1.73333	T	PK	AD		00				0		2378	Europe/Andorra	2011-11-05
+3041269	Bosc de la Cabanella	Bosc de la Cabanella		42.51667	1.46667	V	FRST	AD		00				0		1840	Europe/Andorra	1993-12-23
+3041270	Serra de Cabana Sorda	Serra de Cabana Sorda		42.61667	1.66667	T	RDGE	AD		00				0		2536	Europe/Andorra	1993-12-23
+3041271	Riu de Cabana Sorda	Riu de Cabana Sorda		42.6	1.68333	H	STM	AD		00				0		2089	Europe/Andorra	1993-12-23
+3041272	Pleta de Cabana Sorda	Pleta de Cabana Sorda		42.61667	1.68333	L	GRAZ	AD		00				0		2406	Europe/Andorra	1993-12-23
+3041273	Pales de Cabana Sorda	Pales de Cabana Sorda		42.61667	1.66667	T	CLF	AD		00				0		2536	Europe/Andorra	1993-12-23
+3041274	Estany de Cabana Sorda	Estany de Cabana Sorda		42.61667	1.66667	H	LK	AD		00				0		2536	Europe/Andorra	1993-12-23
+3041275	Cabana Sorda	Cabana Sorda		42.61667	1.66667	A	ADMD	AD		00				0		2536	Europe/Andorra	1993-12-23
+3041276	Font de la Cabana de l’Eucasser	Font de la Cabana de l'Eucasser		42.58333	1.61667	H	SPNG	AD		00				0		1707	Europe/Andorra	1993-12-23
+3041277	Pic de Cabayrou	Pic de Cabayrou	Cabairu,Cabairú,Pic de Cabagnau,Pic de Cabairu,Pic de Cabairú,Pic de Cabayrou	42.63333	1.46667	T	PK	AD		00				0		2324	Europe/Andorra	2011-11-05
+3041278	Font de la Ca	Font de la Ca		42.45	1.5	H	SPNG	AD		00				0		1614	Europe/Andorra	1993-12-23
+3041279	Pla de Buscalls	Pla de Buscalls	Pla de Buscalls,Pla de Busoalls	42.56667	1.65	T	UPLD	AD	AD	00				0		1988	Europe/Andorra	2011-11-05
+3041280	Serrat de la Burna	Serrat de la Burna		42.6	1.48333	T	SPUR	AD		00				0		2441	Europe/Andorra	1993-12-23
+3041281	Pic de la Burna	Pic de la Burna		42.6	1.48333	T	PK	AD		00				0		2441	Europe/Andorra	1993-12-23
+3041282	Clot de la Burna	Clot de la Burna		42.6	1.48333	T	SLP	AD		00				0		2441	Europe/Andorra	1993-12-23
+3041283	Font dels Bullidors	Font dels Bullidors		42.46667	1.45	H	SPNG	AD		00				0		1562	Europe/Andorra	1993-12-23
+3041284	Toll Bullidor	Toll Bullidor		42.58333	1.61667	L	GRAZ	AD		00				0		1707	Europe/Andorra	1993-12-23
+3041285	Barranc del Bullidor	Barranc del Bullidor		42.53333	1.71667	H	STM	AD		00				0		2400	Europe/Andorra	1993-12-23
+3041286	Roc de Bruna Roja	Roc de Bruna Roja		42.63333	1.53333	T	RK	AD		00				0		2072	Europe/Andorra	1993-12-23
+3041287	Pleta del Bruig	Pleta del Bruig		42.63333	1.5	L	GRAZ	AD		00				0		1979	Europe/Andorra	1993-12-23
+3041288	Marrades del Bruig	Marrades del Bruig		42.63333	1.5	R	TRL	AD		00				0		1979	Europe/Andorra	1993-12-23
+3041289	Basers del Bruig	Basers del Bruig		42.65	1.5	T	CLF	AD		00				0		2455	Europe/Andorra	1993-12-23
+3041290	Bruig	Bruig		42.65	1.5	T	SLP	AD		00				0		2455	Europe/Andorra	1993-12-23
+3041291	Pic del Brossós	Pic del Brossos		42.61667	1.51667	T	PK	AD		00				0		1716	Europe/Andorra	1993-12-23
+3041292	Canals del Brossós	Canals del Brossos		42.61667	1.53333	H	RVN	AD		00				0		1609	Europe/Andorra	1993-12-23
+3041293	Camí del Brossós	Cami del Brossos		42.61667	1.53333	R	TRL	AD		00				0		1609	Europe/Andorra	1993-12-23
+3041294	Cortal del Bringuer	Cortal del Bringuer		42.45	1.46667	S	CRRL	AD		00				0		935	Europe/Andorra	1993-12-23
+3041295	Cortal de Bringuer	Cortal de Bringuer		42.53333	1.53333	S	CRRL	AD		00				0		1521	Europe/Andorra	1993-12-23
+3041296	Borda del Bringuer	Borda del Bringuer		42.45	1.48333	S	HUTS	AD		00				0		1111	Europe/Andorra	1993-12-23
+3041297	Clot dels Brillons	Clot dels Brillons		42.46667	1.46667	H	RVN	AD		00				0		1340	Europe/Andorra	1993-12-23
+3041298	Brancs de la Farga	Brancs de la Farga		42.48333	1.61667	L	LCTY	AD		00				0		2217	Europe/Andorra	1993-12-23
+3041299	Tosa del Braibal	Tosa del Braibal	Tosa del Braibal,Tossal Braibal	42.50302	1.59836	T	PK	AD		00				0		2436	Europe/Andorra	2011-11-05
+3041300	Planells del Braibal	Planells del Braibal		42.51667	1.58333	T	UPLD	AD		00				0		1994	Europe/Andorra	1993-12-23
+3041301	Font del Braibal	Font del Braibal		42.51667	1.58333	H	SPNG	AD		00				0		1994	Europe/Andorra	1993-12-23
+3041302	Estany de la Bova	Estany de la Bova		42.48333	1.65	H	LK	AD		00				0		2658	Europe/Andorra	1993-12-23
+3041303	Canal de la Bova	Canal de la Bova		42.5	1.58333	H	STM	AD		00				0		1888	Europe/Andorra	1993-12-23
+3041304	Costa de Bou Mort	Costa de Bou Mort		42.46667	1.56667	T	SLP	AD		00				0		2365	Europe/Andorra	1993-12-23
+3041305	Plana del Bou	Plana del Bou		42.45	1.46667	T	UPLD	AD		00				0		935	Europe/Andorra	1993-12-23
+3041306	Cortal del Bou	Cortal del Bou		42.45	1.46667	S	HUT	AD		00				0		935	Europe/Andorra	1993-12-23
+3041307	Roc de la Botiffarra	Roc de la Botiffarra		42.48333	1.48333	T	RK	AD		00				0		981	Europe/Andorra	1993-12-23
+3041308	Col de la Botella	Col de la Botella		42.55	1.46667	T	PASS	AD		00				0		1585	Europe/Andorra	1993-12-23
+3041309	Canal del Botàs	Canal del Botas		42.55	1.58333	H	STM	AD		00				0		1499	Europe/Andorra	1993-12-23
+3041310	Canal dels Botaders	Canal dels Botaders		42.5	1.48333	H	STM	AD		00				0		1316	Europe/Andorra	1993-12-23
+3041311	Canal del Bosc Nou	Canal del Bosc Nou		42.58333	1.65	H	STM	AD		00				0		1767	Europe/Andorra	1993-12-23
+3041312	Canal del Bosc Negre	Canal del Bosc Negre		42.55	1.46667	H	STM	AD		00				0		1585	Europe/Andorra	1993-12-23
+3041313	Bosc del Coll d’Ordino	Bosc del Coll d'Ordino		42.55	1.55	A	ADMD	AD		00				0		2097	Europe/Andorra	1993-12-23
+3041314	Canal del Bosc de Coma	Canal del Bosc de Coma		42.53333	1.53333	H	STM	AD		00				0		1521	Europe/Andorra	1993-12-23
+3041315	Boscarró	Boscarro		42.55	1.46667	L	LCTY	AD		00				0		1585	Europe/Andorra	1993-12-23
+3041316	Torrent del Bosc	Torrent del Bosc		42.48333	1.45	H	STM	AD		00				0		1195	Europe/Andorra	1993-12-23
+3041317	Pla del Bosc	Pla del Bosc		42.56667	1.66667	T	UPLD	AD		00				0		1938	Europe/Andorra	1993-12-23
+3041318	Pla del Bosc	Pla del Bosc		42.55	1.6	T	UPLD	AD		00				0		2210	Europe/Andorra	1993-12-23
+3041319	Clot del Bosc	Clot del Bosc		42.43333	1.51667	T	SLP	AD		00				0		2031	Europe/Andorra	1993-12-23
+3041320	Barranc del Bosc	Barranc del Bosc		42.56667	1.58333	H	STM	AD		00				0		1919	Europe/Andorra	1993-12-23
+3041321	Boïgues de Borró	Boigues de Borro		42.55	1.6	V	CULT	AD		00				0		2210	Europe/Andorra	1993-12-23
+3041322	Pleta dels Borrecs	Pleta dels Borrecs		42.58333	1.68333	L	GRAZ	AD		00				0		2294	Europe/Andorra	1993-12-23
+3041323	Borrassica	Borrassica		42.5	1.48333	L	LCTY	AD		00				0		1316	Europe/Andorra	1993-12-23
+3041324	Pla de Borràs	Pla de Borras		42.53333	1.48333	T	UPLD	AD		00				0		1677	Europe/Andorra	1993-12-23
+3041325	Bosc del Bornal	Bosc del Bornal		42.53333	1.46667	V	FRST	AD		00				0		1846	Europe/Andorra	1993-12-23
+3041326	Pla de Bordetes	Pla de Bordetes		42.6	1.66667	T	UPLD	AD		00				0		1858	Europe/Andorra	1993-12-23
+3041327	Bordes de Ramonet	Bordes de Ramonet		42.51667	1.55	A	ADMD	AD		00				0		1322	Europe/Andorra	1993-12-23
+3041328	Canal de les Bordes	Canal de les Bordes		42.56667	1.61667	H	RVN	AD		00				0		1920	Europe/Andorra	1993-12-23
+3041329	Palanca de la Borda del Sabater	Palanca de la Borda del Sabater		42.45	1.48333	S	BDG	AD		00				0		1111	Europe/Andorra	1993-12-23
+3041330	Bosc de la Borda del Rauquet	Bosc de la Borda del Rauquet		42.6	1.63333	V	FRST	AD		00				0		1893	Europe/Andorra	1993-12-23
+3041331	Borda del Molines	Borda del Molines		42.5	1.43333	S	RUIN	AD		00				0		1654	Europe/Andorra	1993-12-23
+3041332	Borda del Ferrer Nou	Borda del Ferrer Nou		42.45	1.48333	S	RUIN	AD		00				0		1111	Europe/Andorra	1993-12-23
+3041333	Camí de la Borda del Cosp	Cami de la Borda del Cosp		42.45	1.48333	R	TRL	AD		00				0		1111	Europe/Andorra	1993-12-23
+3041334	Basera de la Borda de l’Arena	Basera de la Borda de l'Arena		42.48333	1.46667	T	CLF	AD		00				0		1148	Europe/Andorra	1993-12-23
+3041335	Borda de l’Alma	Borda de l'Alma		42.58333	1.65	S	RUIN	AD		00				0		1767	Europe/Andorra	1993-12-23
+3041336	Riu de la Bor	Riu de la Bor		42.58333	1.63333	H	STM	AD		00				0		1722	Europe/Andorra	1993-12-23
+3041337	Gorges de la Bor	Gorges de la Bor		42.55	1.58333	T	GRGE	AD		00				0		1499	Europe/Andorra	1993-12-23
+3041338	Costa del Bony Roig	Costa del Bony Roig		42.6	1.61667	T	SLP	AD		00				0		2271	Europe/Andorra	1993-12-23
+3041339	Planades del Bony Negre	Planades del Bony Negre		42.56667	1.45	T	UPLD	AD		00				0		2137	Europe/Andorra	1993-12-23
+3041340	Canal Gran del Bony de la Pica	Canal Gran del Bony de la Pica		42.5	1.46667	H	STM	AD		00				0		1678	Europe/Andorra	1993-12-23
+3041341	Obaga del Bony de la Costa	Obaga del Bony de la Costa		42.56667	1.53333	T	SLP	AD		00				0		1669	Europe/Andorra	1993-12-23
+3041342	Obaga del Bony	Obaga del Bony		42.5	1.58333	T	SLP	AD		00				0		1888	Europe/Andorra	1993-12-23
+3041343	Boïga del Bony	Boiga del Bony		42.56667	1.48333	V	CULT	AD		00				0		1508	Europe/Andorra	1993-12-23
+3041344	Boïga del Bony	Boiga del Bony		42.45	1.53333	V	CULT	AD		00				0		1859	Europe/Andorra	1993-12-23
+3041345	Canal de les Bons	Canal de les Bons		42.53333	1.58333	H	STM	AD		00				0		1571	Europe/Andorra	1993-12-23
+3041346	Canal de la Boneta	Canal de la Boneta		42.5	1.5	H	STM	AD		00				0		1135	Europe/Andorra	1993-12-23
+3041347	Pont de Bonavida	Pont de Bonavida		42.6	1.68333	S	BDG	AD		00				0		2089	Europe/Andorra	1993-12-23
+3041348	Bombal	Bombal		42.6	1.53333	L	LCTY	AD		00				0		1695	Europe/Andorra	1993-12-23
+3041349	Bosc de la Boixera	Bosc de la Boixera		42.53333	1.51667	V	FRST	AD		00				0		1361	Europe/Andorra	1993-12-23
+3041350	Canal de la Boïgueta	Canal de la Boigueta		42.56667	1.51667	H	STM	AD		00				0		1500	Europe/Andorra	1993-12-23
+3041351	Costa de les Boïgues	Costa de les Boigues		42.48333	1.46667	T	SLP	AD		00				0		1148	Europe/Andorra	1993-12-23
+3041352	Canal de les Boïgues	Canal de les Boigues		42.55	1.46667	H	STM	AD		00				0		1585	Europe/Andorra	1993-12-23
+3041353	Boïgots	Boigots		42.55	1.55	L	LCTY	AD		00				0		2097	Europe/Andorra	1993-12-23
+3041354	Font del Boïgot	Font del Boigot		42.5	1.56667	H	SPNG	AD		00				0		1776	Europe/Andorra	1993-12-23
+3041355	Canal del Boïgot	Canal del Boigot		42.5	1.55	H	STM	AD		00				0		1566	Europe/Andorra	1993-12-23
+3041356	Bosc de Boïga Plana	Bosc de Boiga Plana		42.45	1.45	V	FRST	AD		00				0		1482	Europe/Andorra	1993-12-23
+3041357	Font de la Boïga Mitgera	Font de la Boiga Mitgera		42.5	1.46667	H	SPNG	AD		00				0		1678	Europe/Andorra	1993-12-23
+3041358	Canal dels Boïgals	Canal dels Boigals		42.51667	1.5	H	STM	AD		00				0		1688	Europe/Andorra	1993-12-23
+3041359	Serrat de Boïga Gran	Serrat de Boiga Gran		42.48333	1.48333	T	SPUR	AD		00				0		981	Europe/Andorra	1993-12-23
+3041360	Font de la Boïga del Roi	Font de la Boiga del Roi		42.5	1.48333	H	SPNG	AD		00				0		1316	Europe/Andorra	1993-12-23
+3041361	Roc de Boïga Curta	Roc de Boiga Curta		42.51667	1.55	T	RK	AD		00				0		1322	Europe/Andorra	1993-12-23
+3041362	Estany Blau	Estany Blau		42.49666	1.62069	H	LK	AD		00				0		2560	Europe/Andorra	2011-04-19
+3041363	Roques Blanques	Roques Blanques		42.48333	1.48333	T	CLF	AD		00				0		981	Europe/Andorra	1993-12-23
+3041364	Rocs Blancs	Rocs Blancs		42.45	1.45	T	SLP	AD		00				0		1482	Europe/Andorra	1993-12-23
+3041365	Roca Blanca	Roca Blanca		42.56667	1.48333	T	RK	AD		00				0		1508	Europe/Andorra	1993-12-23
+3041366	La Porteille Blanche	La Porteille Blanche	La Porteille Blanche,Porteille Blanche d'Andorra,Porteille Blanche d'Andorre,Porteille Blanche d’Andorra,Porteille Blanche d’Andorre,Portella Blanca	42.5	1.73333	T	PASS	AD		00				0		2686	Europe/Andorra	2011-11-05
+3041367	Font Blanca	Font Blanca		42.65	1.53333	H	SPNG	AD		00				0		2564	Europe/Andorra	1993-12-23
+3041368	Font Blanca	Font Blanca		42.58333	1.58333	H	SPNG	AD		00				0		1993	Europe/Andorra	1993-12-23
+3041369	Canal Blanca	Canal Blanca		42.55	1.61667	H	RVN	AD		00				0		2206	Europe/Andorra	1993-12-23
+3041370	Vial Blanc	Vial Blanc		42.48333	1.5	R	RD	AD		00				0		1631	Europe/Andorra	1993-12-23
+3041371	Roc Blanc	Roc Blanc		42.48333	1.53333	T	RK	AD		00				0		2255	Europe/Andorra	1993-12-23
+3041372	Riu Blanc	Riu Blanc		42.53333	1.58333	H	STM	AD		00				0		1571	Europe/Andorra	1993-12-23
+3041373	Coll Blanc	Coll Blanc		42.52961	1.72014	T	PK	AD		00				0		2400	Europe/Andorra	2011-04-19
+3041374	Carretera de Bixessarri	Carretera de Bixessarri		42.48333	1.46667	R	RD	AD		00				0		1148	Europe/Andorra	1993-12-23
+3041375	Bixessarri	Bixessarri	Bicisarri,Bixessarri,Bixisarri,Biçisarri,Vixesarri	42.48238	1.45949	P	PPL	AD		06				0		1178	Europe/Andorra	2011-11-05
+3041376	Bissets	Bissets		42.53333	1.53333	L	LCTY	AD		00				0		1521	Europe/Andorra	1993-12-23
+3041377	Bisset	Bisset		42.55	1.53333	L	LCTY	AD		00				0		1593	Europe/Andorra	1993-12-23
+3041378	Planell del Bisbe	Planell del Bisbe		42.48333	1.58333	T	UPLD	AD		00				0		2349	Europe/Andorra	1993-12-23
+3041379	Font del Bisbe	Font del Bisbe		42.55	1.45	H	SPNG	AD		00				0		1788	Europe/Andorra	1993-12-23
+3041380	Font de la Birena	Font de la Birena		42.51667	1.51667	H	SPNG	AD		00				0		1265	Europe/Andorra	1993-12-23
+3041381	Fontanal del Besurt	Fontanal del Besurt		42.53333	1.48333	H	SPNG	AD		00				0		1677	Europe/Andorra	1993-12-23
+3041382	Roc del Bessó	Roc del Besso		42.45	1.46667	T	RK	AD		00				0		935	Europe/Andorra	1993-12-23
+3041383	Font del Bessó	Font del Besso		42.45	1.48333	H	SPNG	AD		00				0		1111	Europe/Andorra	1993-12-23
+3041384	Costa de Bescaran	Costa de Bescaran		42.43333	1.5	T	SLP	AD		00				0		1804	Europe/Andorra	1993-12-23
+3041385	Portella de Besalí	Portella de Besali		42.63333	1.55	T	PASS	AD		00				0		2053	Europe/Andorra	1993-12-23
+3041386	Pla de Besalí	Pla de Besali		42.63333	1.55	T	UPLD	AD		00				0		2053	Europe/Andorra	1993-12-23
+3041387	Pic de Besalí	Pic de Besali		42.63333	1.53333	T	PK	AD		00				0		2072	Europe/Andorra	1993-12-23
+3041388	Basers de Besalí	Basers de Besali		42.63333	1.55	T	CLF	AD		00				0		2053	Europe/Andorra	1993-12-23
+3041389	Besalí	Besali		42.63333	1.53333	A	ADMD	AD		00				0		2072	Europe/Andorra	1993-12-23
+3041390	Clot de les Berques	Clot de les Berques		42.55	1.48333	H	RVN	AD		00				0		1548	Europe/Andorra	1993-12-23
+3041391	Coma Bella	Coma Bella		42.58333	1.68333	T	VAL	AD		00				0		2294	Europe/Andorra	1993-12-23
+3041392	Riu de la Beixellosa	Riu de la Beixellosa		42.53333	1.53333	H	STM	AD		00				0		1521	Europe/Andorra	1993-12-23
+3041393	Bosc de la Beixellosa	Bosc de la Beixellosa		42.53333	1.53333	V	FRST	AD		00				0		1521	Europe/Andorra	1993-12-23
+3041394	Collada de Beixalís	Collada de Beixalis		42.53333	1.55	T	PASS	AD		00				0		1344	Europe/Andorra	1993-12-23
+3041395	Carretera Beixalís	Carretera Beixalis		42.53333	1.56667	R	RD	AD		00				0		1418	Europe/Andorra	1993-12-23
+3041396	Bosc de Beixalís	Bosc de Beixalis		42.53333	1.55	V	FRST	AD		00				0		1344	Europe/Andorra	1993-12-23
+3041397	Bordes de Beixalís	Bordes de Beixalis		42.53333	1.55	S	FRMS	AD		00				0		1344	Europe/Andorra	1993-12-23
+3041398	Beixalís	Beixalis		42.53333	1.55	A	ADMD	AD		00				0		1344	Europe/Andorra	1993-12-23
+3041399	Comes Beçoses	Comes Becoses		42.51667	1.6	H	RVN	AD		00				0		2085	Europe/Andorra	1993-12-23
+3041400	Planell dels Beços	Planell dels Becos		42.61667	1.56667	T	UPLD	AD		00				0		2228	Europe/Andorra	1993-12-23
+3041401	Bosc del Becet	Bosc del Becet		42.56667	1.53333	V	FRST	AD		00				0		1669	Europe/Andorra	1993-12-23
+3041402	Vial del Beç	Vial del Bec		42.53333	1.46667	R	TRL	AD		00				0		1846	Europe/Andorra	1993-12-23
+3041403	Solana dels Batallats	Solana dels Batallats		42.56667	1.51667	T	SLP	AD		00				0		1500	Europe/Andorra	1993-12-23
+3041404	Roc dels Batallassos	Roc dels Batallassos		42.56667	1.6	T	RK	AD		00				0		1655	Europe/Andorra	1993-12-23
+3041405	Coll del Bast	Coll del Bast		42.48333	1.48333	T	PK	AD		00				0		981	Europe/Andorra	1993-12-23
+3041406	Basses del Siscaró	Basses del Siscaro		42.58333	1.7	L	LCTY	AD		00				0		2584	Europe/Andorra	1993-12-23
+3041407	Basses dels Basers	Basses dels Basers		42.58333	1.7	L	LCTY	AD		00				0		2584	Europe/Andorra	1993-12-23
+3041408	Pales de les Basses de les Salamandres	Pales de les Basses de les Salamandres		42.61667	1.66667	T	SLP	AD		00				0		2536	Europe/Andorra	1993-12-23
+3041409	Basses de la Burna	Basses de la Burna		42.6	1.48333	T	RKS	AD		00				0		2441	Europe/Andorra	1993-12-23
+3041410	Planell de les Basses	Planell de les Basses		42.55	1.58333	T	UPLD	AD		00				0		1499	Europe/Andorra	1993-12-23
+3041411	Pla de la Bassa de les Granotes	Pla de la Bassa de les Granotes		42.58333	1.43333	T	UPLD	AD		00				0		2412	Europe/Andorra	1993-12-23
+3041412	Basers de l’Estany de Més Amunt	Basers de l'Estany de Mes Amunt		42.6	1.46667	L	LCTY	AD		00				0		2421	Europe/Andorra	1993-12-23
+3041413	Basers de Font Blanca	Basers de Font Blanca		42.65	1.55	L	LCTY	AD		00				0		2181	Europe/Andorra	1993-12-23
+3041414	Coll de Basers	Coll de Basers		42.5	1.48333	T	RDGE	AD		00				0		1316	Europe/Andorra	1993-12-23
+3041415	Baser Negre	Baser Negre		42.63333	1.46667	L	LCTY	AD		00				0		2324	Europe/Andorra	1993-12-23
+3041416	Cap del Baser de la Llonga	Cap del Baser de la Llonga		42.56667	1.51667	T	SPUR	AD		00				0		1500	Europe/Andorra	1993-12-23
+3041417	Cap del Baser	Cap del Baser		42.56667	1.51667	T	SPUR	AD		00				0		1500	Europe/Andorra	1993-12-23
+3041418	Canal de Bartreta	Canal de Bartreta		42.55	1.6	H	RVN	AD		00				0		2210	Europe/Andorra	1993-12-23
+3041419	Bosc de les Bartres	Bosc de les Bartres		42.51667	1.5	V	FRST	AD		00				0		1688	Europe/Andorra	1993-12-23
+3041420	Bosc de la Bartra	Bosc de la Bartra		42.5	1.51667	V	FRST	AD		00				0		1410	Europe/Andorra	1993-12-23
+3041421	Pont dels Barrons	Pont dels Barrons		42.61667	1.55	S	BDG	AD		00				0		2007	Europe/Andorra	1993-12-23
+3041422	Torrent del Barreró	Torrent del Barrero		42.48333	1.46667	H	STM	AD		00				0		1148	Europe/Andorra	1993-12-23
+3041423	Bosc del Barrer d’Areny	Bosc del Barrer d'Areny		42.58333	1.46667	V	FRST	AD		00				0		1643	Europe/Andorra	1993-12-23
+3041424	Barrer d’Areny	Barrer d'Areny		42.58333	1.46667	L	GRAZ	AD		00				0		1643	Europe/Andorra	1993-12-23
+3041425	Roc del Barrer	Roc del Barrer		42.55	1.53333	T	SPUR	AD		00				0		1593	Europe/Andorra	1993-12-23
+3041426	Canal del Barrer	Canal del Barrer		42.56667	1.51667	H	STM	AD		00				0		1500	Europe/Andorra	1993-12-23
+3041427	Riu de la Barraca Cremada	Riu de la Barraca Cremada		42.53333	1.7	H	STM	AD		00				0		2357	Europe/Andorra	1993-12-23
+3041428	Solà de Barra	Sola de Barra		42.58333	1.65	T	SLP	AD		00				0		1767	Europe/Andorra	1993-12-23
+3041429	Prats de la Baronia	Prats de la Baronia		42.53333	1.61667	L	GRAZ	AD		00				0		2237	Europe/Andorra	1993-12-23
+3041430	Planell de la Baronia	Planell de la Baronia		42.53333	1.61667	T	UPLD	AD		00				0		2237	Europe/Andorra	1993-12-23
+3041431	Borda de la Baronia	Borda de la Baronia		42.53333	1.61667	S	HUT	AD		00				0		2237	Europe/Andorra	1993-12-23
+3041432	Canal dels Banys	Canal dels Banys		42.53333	1.5	H	STM	AD		00				0		1357	Europe/Andorra	1993-12-23
+3041433	Bosc dels Banys	Bosc dels Banys		42.53333	1.5	V	FRST	AD		00				0		1357	Europe/Andorra	1993-12-23
+3041434	Port de Banyell	Port de Banyell		42.64218	1.5777	T	PASS	AD		00				0		2365	Europe/Andorra	2011-04-19
+3041435	Font de Banyell	Font de Banyell		42.63333	1.56667	H	SPNG	AD		00				0		2394	Europe/Andorra	1993-12-23
+3041436	Banyell	Banyell		42.63333	1.56667	L	LCTY	AD		00				0		2394	Europe/Andorra	1993-12-23
+3041437	Bosc dels Bancs	Bosc dels Bancs		42.48333	1.46667	V	FRST	AD		00				0		1148	Europe/Andorra	1993-12-23
+3041438	Riu del Bancal Vedeller	Riu del Bancal Vedeller		42.6	1.46667	H	STM	AD		00				0		2421	Europe/Andorra	1993-12-23
+3041439	Basers del Bancal Vedeller	Basers del Bancal Vedeller		42.6	1.45	T	CLF	AD		00				0		2174	Europe/Andorra	1993-12-23
+3041440	Plana del Banc	Plana del Banc		42.43333	1.5	T	UPLD	AD		00				0		1804	Europe/Andorra	1993-12-23
+3041441	Font del Baladre	Font del Baladre		42.58333	1.48333	H	SPNG	AD		00				0		1809	Europe/Andorra	1993-12-23
+3041442	Pont de la Baladosa	Pont de la Baladosa		42.6	1.68333	S	BDG	AD		00				0		2089	Europe/Andorra	1993-12-23
+3041443	Baladosa	Baladosa		42.58333	1.68333	L	LCTY	AD		00				0		2294	Europe/Andorra	1993-12-23
+3041444	Collades Baixes d’Emportona	Collades Baixes d'Emportona		42.51667	1.65	T	PASS	AD		00				0		2633	Europe/Andorra	1993-12-23
+3041445	Collades Baixes	Collades Baixes		42.5	1.63333	T	PASS	AD		00				0		2545	Europe/Andorra	1993-12-23
+3041446	Baixant	Baixant		42.55	1.48333	L	LCTY	AD		00				0		1548	Europe/Andorra	1993-12-23
+3041447	Pleta de Baix	Pleta de Baix		42.51667	1.61667	L	GRAZ	AD		00				0		2254	Europe/Andorra	1993-12-23
+3041448	Pleta de Baix	Pleta de Baix		42.48333	1.43333	L	GRAZ	AD		00				0		1938	Europe/Andorra	1993-12-23
+3041449	Estany de Baix	Estany de Baix		42.58333	1.7	H	LK	AD		00				0		2584	Europe/Andorra	1993-12-23
+3041450	Font de Baiter	Font de Baiter		42.55	1.51667	H	SPNG	AD		00				0		1397	Europe/Andorra	1993-12-23
+3041451	Port de Baiau	Port de Baiau	Port de Baiau	42.6	1.43333	T	PASS	AD		00				0		2667	Europe/Andorra	2011-11-05
+3041452	Pic de Baiau	Pic de Baiau	Pic de Baiau	42.58333	1.43333	T	PK	AD		00				0		2412	Europe/Andorra	2011-11-05
+3041453	Agulla de Baiau	Agulla de Baiau	Agulla de Baiau	42.58333	1.43333	T	PK	AD		00				0		2412	Europe/Andorra	2011-11-05
+3041454	Planells de Baell	Planells de Baell		42.48333	1.6	T	UPLD	AD		00				0		2250	Europe/Andorra	1993-12-23
+3041455	Font de Baell	Font de Baell		42.5	1.61667	H	SPNG	AD		00				0		2560	Europe/Andorra	1993-12-23
+3041456	Pleta de les Bacives	Pleta de les Bacives		42.5	1.65	L	GRAZ	AD		00				0		2542	Europe/Andorra	1993-12-23
+3041457	Serra de l’ Avier	Serra de l' Avier		42.6	1.5	T	RDGE	AD		00				0		1923	Europe/Andorra	1993-12-23
+3041458	Plana de l’ Avier	Plana de l' Avier		42.6	1.5	T	UPLD	AD		00				0		1923	Europe/Andorra	1993-12-23
+3041459	Obaga de l’ Avier	Obaga de l' Avier		42.48333	1.53333	T	SLP	AD		00				0		2255	Europe/Andorra	1993-12-23
+3041460	Costa de l’ Avier	Costa de l' Avier		42.56667	1.55	T	SLP	AD		00				0		1996	Europe/Andorra	1993-12-23
+3041461	Camí de l’ Avier	Cami de l' Avier		42.48333	1.55	R	TRL	AD		00				0		2233	Europe/Andorra	1993-12-23
+3041462	Bosc de l’ Avier	Bosc de l' Avier		42.48333	1.53333	V	FRST	AD		00				0		2255	Europe/Andorra	1993-12-23
+3041463	Torrent de l’ Aviar	Torrent de l' Aviar		42.53333	1.56667	H	STM	AD		00				0		1418	Europe/Andorra	1993-12-23
+3041464	Canal de l’ Avetar	Canal de l' Avetar		42.58333	1.65	H	STM	AD		00				0		1767	Europe/Andorra	1993-12-23
+3041465	Canal de l’ Avetar	Canal de l' Avetar		42.5	1.48333	H	STM	AD		00				0		1316	Europe/Andorra	1993-12-23
+3041466	Bosc de l’ Avetar	Bosc de l' Avetar		42.55	1.55	V	FRST	AD		00				0		2097	Europe/Andorra	1993-12-23
+3041467	Costa de Avet	Costa de Avet		42.5	1.56667	T	SLP	AD		00				0		1776	Europe/Andorra	1993-12-23
+3041468	Canal de l’ Avet	Canal de l' Avet		42.55	1.5	H	STM	AD		00				0		1292	Europe/Andorra	1993-12-23
+3041469	Costa de l’ Ave Maria	Costa de l' Ave Maria		42.61667	1.53333	T	SLP	AD		00				0		1609	Europe/Andorra	1993-12-23
+3041470	Solà de l’ Avellanet	Sola de l' Avellanet		42.58333	1.48333	T	SLP	AD		00				0		1809	Europe/Andorra	1993-12-23
+3041471	Costa dels Avellaners	Costa dels Avellaners		42.56667	1.61667	T	SLP	AD		00				0		1920	Europe/Andorra	1993-12-23
+3041472	Canal dels Avellaners	Canal dels Avellaners		42.58333	1.46667	H	RVN	AD		00				0		1643	Europe/Andorra	1993-12-23
+3041473	Coma Aubosa	Coma Aubosa		42.58333	1.5	T	SLP	AD		00				0		1595	Europe/Andorra	1993-12-23
+3041474	Riu d’ Aubinyà	Riu d' Aubinya		42.45	1.48333	H	STM	AD		00				0		1111	Europe/Andorra	1993-12-23
+3041475	Aubinyà	Aubinya	Aubinya,Aubinyà,Auvinya	42.45447	1.48761	P	PPL	AD		06				0		1159	Europe/Andorra	2011-11-05
+3041476	Aubinyà	Aubinya		42.45	1.5	A	ADMD	AD		00				0		1614	Europe/Andorra	1993-12-23
+3041477	Roc de les Aubes	Roc de les Aubes		42.51667	1.55	T	SPUR	AD		00				0		1322	Europe/Andorra	1993-12-23
+3041478	Riu de les Aubes	Riu de les Aubes		42.55	1.53333	H	STM	AD		00				0		1593	Europe/Andorra	1993-12-23
+3041479	Costa de l’ Aubell	Costa de l' Aubell		42.56667	1.53333	T	SLP	AD		00				0		1669	Europe/Andorra	1993-12-23
+3041480	Aubaderes	Aubaderes		42.5	1.48333	L	LCTY	AD		00				0		1316	Europe/Andorra	1993-12-23
+3041481	Roc de l’ Auba	Roc de l' Auba		42.56667	1.5	T	RK	AD		00				0		1636	Europe/Andorra	1993-12-23
+3041482	Canal dels Astrells	Canal dels Astrells		42.48333	1.56667	H	STM	AD		00				0		2231	Europe/Andorra	1993-12-23
+3041483	Riu de l’ Astrell	Riu de l' Astrell		42.55	1.55	H	STM	AD		00				0		2097	Europe/Andorra	1993-12-23
+3041484	Cap de l’ Astrell	Cap de l' Astrell		42.56667	1.56667	T	SPUR	AD		00				0		2089	Europe/Andorra	1993-12-23
+3041485	Bosc de l’ Astrell	Bosc de l' Astrell		42.56667	1.56667	V	FRST	AD		00				0		2089	Europe/Andorra	1993-12-23
+3041486	Canals dels Assaladors dels Pletius	Canals dels Assaladors dels Pletius		42.5	1.46667	H	STM	AD		00				0		1678	Europe/Andorra	1993-12-23
+3041487	Assaladors del Planell Gran	Assaladors del Planell Gran		42.56667	1.46667	L	LCTY	AD		00				0		1673	Europe/Andorra	1993-12-23
+3041488	Assaladors de Cabana Sorda	Assaladors de Cabana Sorda		42.6	1.66667	T	PKS	AD		00				0		1858	Europe/Andorra	1993-12-23
+3041489	Canal de l’ Assalador de Rei	Canal de l' Assalador de Rei		42.55	1.46667	H	STM	AD		00				0		1585	Europe/Andorra	1993-12-23
+3041490	Bosc de l’ Assalador	Bosc de l' Assalador		42.61667	1.65	V	FRST	AD		00				0		2567	Europe/Andorra	1993-12-23
+3041491	Bosc de l’ Assalador	Bosc de l' Assalador		42.56667	1.6	V	FRST	AD		00				0		1655	Europe/Andorra	1993-12-23
+3041492	Bosc de l’ Assalador	Bosc de l' Assalador		42.56667	1.58333	V	FRST	AD		00				0		1919	Europe/Andorra	1993-12-23
+3041493	Serra del Cap dels Aspres de Banyell	Serra del Cap dels Aspres de Banyell		42.63333	1.56667	T	MT	AD		00				0		2394	Europe/Andorra	1993-12-23
+3041494	Aspres de Banyell	Aspres de Banyell		42.63333	1.56667	L	LCTY	AD		00				0		2394	Europe/Andorra	1993-12-23
+3041495	Pic dels Aspres	Pic dels Aspres	Pic dels Aspres	42.56667	1.45	T	PK	AD		00				0	2562	2137	Europe/Andorra	2011-11-05
+3041496	Rocs de l’ Aspra	Rocs de l' Aspra		42.53333	1.53333	T	RKS	AD		00				0		1521	Europe/Andorra	1993-12-23
+3041497	Riu de l’ Aspra	Riu de l' Aspra		42.55	1.55	H	STM	AD		00				0		2097	Europe/Andorra	1993-12-23
+3041498	Clots de l’ Aspra	Clots de l' Aspra		42.51667	1.63333	H	RVN	AD		00				0		2379	Europe/Andorra	1993-12-23
+3041499	Alt de l’ Aspra	Alt de l' Aspra		42.51667	1.65	T	SLP	AD		00				0		2633	Europe/Andorra	1993-12-23
+3041500	Canals d’ Aspones	Canals d' Aspones		42.61667	1.7	H	RVN	AD		00				0		2285	Europe/Andorra	1993-12-23
+3041501	Torrent dels Aspedius	Torrent dels Aspedius		42.48333	1.53333	H	STM	AD		00				0		2255	Europe/Andorra	1993-12-23
+3041502	Comella Ascura	Comella Ascura		42.45	1.45	H	RVN	AD		00				0		1482	Europe/Andorra	1993-12-23
+3041503	Rocs de l’ Ascobet	Rocs de l' Ascobet		42.61667	1.51667	T	RKS	AD		00				0		1716	Europe/Andorra	1993-12-23
+3041504	Ascobar de Puntal	Ascobar de Puntal		42.63333	1.55	L	LCTY	AD		00				0		2053	Europe/Andorra	1993-12-23
+3041505	Canal de les Asclades	Canal de les Asclades		42.5	1.53333	H	STM	AD		00				0		1574	Europe/Andorra	1993-12-23
+3041506	Serrat de l’ Ascladella	Serrat de l' Ascladella		42.53333	1.46667	T	RDGE	AD		00				0		1846	Europe/Andorra	1993-12-23
+3041507	Fontanal de l’ Ascladella	Fontanal de l' Ascladella		42.53333	1.46667	H	SPNG	AD		00				0		1846	Europe/Andorra	1993-12-23
+3041508	Coma de l’ Ascladella	Coma de l' Ascladella		42.56667	1.48333	T	SLP	AD		00				0		1508	Europe/Andorra	1993-12-23
+3041509	Torrent dels Artics	Torrent dels Artics		42.55	1.58333	H	STM	AD		00				0		1499	Europe/Andorra	1993-12-23
+3041510	Artic de Capell	Artic de Capell		42.58333	1.6	L	LCTY	AD		00				0		1828	Europe/Andorra	1993-12-23
+3041511	Font de l’ Artic	Font de l' Artic		42.48333	1.5	H	SPNG	AD		00				0		1631	Europe/Andorra	1993-12-23
+3041512	Bony de l’ Artic	Bony de l' Artic		42.55	1.55	T	SPUR	AD		00				0		2097	Europe/Andorra	1993-12-23
+3041513	Serrat de l’ Arna Tova	Serrat de l' Arna Tova		42.58333	1.46667	T	SPUR	AD		00				0		1643	Europe/Andorra	1993-12-23
+3041514	Arna Tova	Arna Tova		42.58333	1.46667	L	LCTY	AD		00				0		1643	Europe/Andorra	1993-12-23
+3041515	Bordes de l’ Armiana	Bordes de l' Armiana		42.58333	1.6	S	HUTS	AD		00				0		1828	Europe/Andorra	1993-12-23
+3041516	Font de l’ Arinsola	Font de l' Arinsola		42.48333	1.41667	H	SPNG	AD		00				0		1920	Europe/Andorra	1993-12-23
+3041517	Riu d’ Arinsal	Riu d' Arinsal	Riu d' Arinsal,Riu d' Arinsul,Riu d’ Arinsal,Riu d’ Arinsul	42.5456	1.51523	H	STM	AD		00				0		1257	Europe/Andorra	2011-11-05
+3041518	Port d’ Arinsal	Port d' Arinsal	Port d' Arinsal,Port d’ Arinsal	42.6	1.46667	T	PASS	AD		00				0		2421	Europe/Andorra	2011-11-05
+3041519	Arinsal	Arinsal	Arinsal,ÐринÑал	42.57205	1.48453	P	PPL	AD		04				1419		1655	Europe/Andorra	2010-01-29
+3041520	Canal de Argelagosa	Canal de Argelagosa		42.51667	1.53333	H	STM	AD		00				0		1460	Europe/Andorra	1993-12-23
+3041521	Roc de l’ Areny	Roc de l' Areny		42.56667	1.6	T	PROM	AD		00				0		1655	Europe/Andorra	1993-12-23
+3041522	Rec d’ Areny	Rec d' Areny		42.58333	1.46667	H	STM	AD		00				0		1643	Europe/Andorra	1993-12-23
+3041523	Coll d’ Arenes	Coll d' Arenes		42.6	1.58333	T	PASS	AD		00				0		2461	Europe/Andorra	1993-12-23
+3041524	Bassa de l’ Arena	Bassa de l' Arena		42.51667	1.63333	H	STMH	AD		00				0		2379	Europe/Andorra	1993-12-23
+3041525	Font d’ Arduix	Font d' Arduix	Font d' Arduix,Font d’ Arduix	42.45	1.45	H	SPNG	AD		00				0		1482	Europe/Andorra	2011-11-05
+3041526	Solana d’ Arcavell	Solana d' Arcavell	Solana d' Arcabell,Solana d' Arcavell,Solana d’ Arcabell,Solana d’ Arcavell	42.43333	1.51667	T	SLP	AD		00				0		2031	Europe/Andorra	2011-11-05
+3041527	Serra d’ Arcalís	Serra d' Arcalis		42.61667	1.5	T	RDGE	AD		00				0		2390	Europe/Andorra	1993-12-23
+3041528	Roc d’ Arcalís	Roc d' Arcalis		42.63333	1.5	T	RK	AD		00				0		1979	Europe/Andorra	1993-12-23
+3041529	Riu d’ Arcalís	Riu d' Arcalis		42.63333	1.5	H	STM	AD		00				0		1979	Europe/Andorra	1993-12-23
+3041530	Portella d’ Arcalís	Portella d' Arcalis		42.61667	1.48333	T	PASS	AD		00				0		2470	Europe/Andorra	1993-12-23
+3041531	Pont d’ Arcalís	Pont d' Arcalis		42.63333	1.5	S	BDG	AD		00				0		1979	Europe/Andorra	1993-12-23
+3041532	Pic d’ Arcalís	Pic d' Arcalis		42.61575	1.4904	T	PK	AD		00				0		2431	Europe/Andorra	2011-04-19
+3041533	Feixans d’ Arcalís	Feixans d' Arcalis		42.63333	1.5	V	CULT	AD		00				0		1979	Europe/Andorra	1993-12-23
+3041534	Collades d’ Arcalís	Collades d' Arcalis		42.63333	1.51667	T	PASS	AD		00				0		1894	Europe/Andorra	1993-12-23
+3041535	Cabana d’ Arcalís	Cabana d' Arcalis		42.63333	1.5	S	HUT	AD		00				0		1979	Europe/Andorra	1993-12-23
+3041536	Basers d’ Arcalís	Basers d' Arcalis		42.61667	1.5	T	CLF	AD		00				0		2390	Europe/Andorra	1993-12-23
+3041537	Port de l’ Albeille	Port de l' Albeille	Port de l' Albeille,Port de l' Albelle,Port de l' Arbeille,Port de l' Arbella,Port de l’ Albeille,Port de l’ Albelle,Port de l’ Arbeille,Port de l’ Arbella,Porteille de l' Albelle,Porteille de l’ Albelle	42.65	1.5	T	PASS	AD		00				0		2455	Europe/Andorra	2011-11-05
+3041538	Canal de l’ Arbeguer	Canal de l' Arbeguer		42.48333	1.43333	H	STM	AD		00				0		1938	Europe/Andorra	1993-12-23
+3041539	Pont d’ Arans	Pont d' Arans		42.58333	1.51667	S	BDG	AD		00				0		1722	Europe/Andorra	1993-12-23
+3041540	Canals d’ Arans	Canals d' Arans		42.58333	1.5	H	RVN	AD		00				0		1595	Europe/Andorra	1993-12-23
+3041541	Arans	Arans	Arans	42.58226	1.51844	P	PPL	AD		05				0		1722	Europe/Andorra	2011-11-05
+3041542	Coll de la Quell	Coll de la Quell	Coll de l' Aquell,Coll de la Quell,Coll de l’ Aquell	42.48333	1.41667	T	PASS	AD		00				0		1920	Europe/Andorra	2011-11-05
+3041543	Anyós	Anyos		42.53458	1.52672	P	PPL	AD		04				0		1491	Europe/Andorra	2011-04-19
+3041544	Bosc Gran de l’ Any de la Part	Bosc Gran de l' Any de la Part		42.55	1.51667	V	FRST	AD		00				0		1397	Europe/Andorra	1993-12-23
+3041545	Planell d’ Antònia	Planell d' Antonia		42.51667	1.55	T	UPLD	AD		00				0		1322	Europe/Andorra	1993-12-23
+3041546	Ansalonga	Ansalonga	Ansalonga	42.56919	1.52285	P	PPL	AD		05				0		1500	Europe/Andorra	2011-11-05
+3041547	Serra d’ Anrodat	Serra d' Anrodat		42.61667	1.68333	T	RDGE	AD		00				0		2406	Europe/Andorra	1993-12-23
+3041548	Pic de la Coume d’Enfer	Pic de la Coume d'Enfer	Pic d' Anrodat,Pic de la Coume d'Enfer,Pic de la Coume d’Enfer,Pic d’ Anrodat	42.61667	1.68333	T	RDGE	AD		00				0		2406	Europe/Andorra	2011-11-05
+3041549	Estany d’ Anrodat	Estany d' Anrodat		42.61667	1.68333	H	LK	AD		00				0		2406	Europe/Andorra	1993-12-23
+3041550	Coll d’ Anrodat	Coll d' Anrodat	Coll d' Anrodat,Coll d’ Anrodat	42.61667	1.7	T	PASS	AD		00				0		2285	Europe/Andorra	2011-11-05
+3041551	Anrodat	Anrodat		42.61667	1.68333	A	ADMD	AD		00				0		2406	Europe/Andorra	1993-12-23
+3041552	Riu de l’ Angonella	Riu de l' Angonella		42.6	1.53333	H	STM	AD		00				0		1695	Europe/Andorra	1993-12-23
+3041553	Port de l’ Angonella	Port de l' Angonella	Port de l' Angonella,Port de l’ Angonella	42.61667	1.46667	T	PASS	AD		00				0		2442	Europe/Andorra	2011-11-05
+3041554	Pleta de l’ Angonella	Pleta de l' Angonella		42.6	1.5	L	GRAZ	AD		00				0		1923	Europe/Andorra	1993-12-23
+3041555	Pas de l’ Angonella	Pas de l' Angonella		42.6	1.5	T	PASS	AD		00				0		1923	Europe/Andorra	1993-12-23
+3041556	Estret del l’ Angonella	Estret del l' Angonella		42.6	1.5	T	GRGE	AD		00				0		1923	Europe/Andorra	1993-12-23
+3041557	Estanys de l’ Angonella	Estanys de l' Angonella		42.6	1.48333	H	LKS	AD		00				0		2441	Europe/Andorra	1993-12-23
+3041558	Basers de l’ Angonella	Basers de l' Angonella		42.61667	1.5	T	CLF	AD		00				0		2390	Europe/Andorra	1993-12-23
+3041559	Pic de les Angleves	Pic de les Angleves		42.55	1.53333	T	PK	AD		00				0		1593	Europe/Andorra	1993-12-23
+3041560	Solà de Angleva	Sola de Angleva		42.56667	1.48333	T	SLP	AD		00				0		1508	Europe/Andorra	1993-12-23
+3041561	Planell de Andreuet	Planell de Andreuet		42.51667	1.6	T	UPLD	AD		00				0		2085	Europe/Andorra	1993-12-23
+3041562	Barranc de l’ Andorrana	Barranc de l' Andorrana		42.55	1.43333	H	STM	AD		00				0		1949	Europe/Andorra	1993-12-23
+3041563	Andorra la Vella	Andorra la Vella	Ando-la-Vyey,Andora,Andora la Vela,Andora la Velja,Andora lja Vehl'ja,Andoro Malnova,Andorra,Andorra Tuan,Andorra a Vella,Andorra la Biella,Andorra la Vella,Andorra la Vielha,Andorra-a-Velha,Andorra-la-Vel'ja,Andorra-la-Vielye,Andorre-la-Vieille,Andò-la-Vyèy,Andòrra la Vièlha,an dao er cheng,andolalabeya,andwra la fyla,ΑνδόÏÏα,Ðндора ла ВелÑ,Ðндора ла Веља,Ðндора Ð»Ñ Ð’ÑльÑ,Ðндорра-ла-ВельÑ,×נדורה לה וולה,أندورا لا Ùيلا,አንዶራ ላ ቬላ,アンドラ・ラ・ヴェリャ,安é“爾城,안ë„ë¼ë¼ë² ì•¼	42.50779	1.52109	P	PPLC	AD		07				20430		1073	Europe/Andorra	2010-05-30
+3041564	Rec d’ Andorra	Rec d' Andorra		42.51667	1.51667	H	CNL	AD		00				0		1265	Europe/Andorra	1993-12-23
+3041565	Principality of Andorra	Principality of Andorra	Andora,Andoro,Andorra,Andorre,Andorrë,Andurra,Andòrra,Andóra,L'Andorre,Landoraen,Landorän,Les Vallees d' Andorre,Les Vallées d’ Andorre,L’Andorre,Principado de Andorra,Principality of Andorra,Principat d' Andorra,Principat d’ Andorra,Principaute d' Andorre,Principauté d’ Andorre,Vallees et Suzerainetes d' Andorre,Valls d' Andorra,Valls d’ Andorra,Vallées et Suzerainetés d’ Andorre,an dao er,andola,andora,andwra,antora,endora,prathes xandxrra,xandxrra,yandwrra,ΑνδόÏα,ΑνδόÏÏα,Ðндора,Ðндорра,Ô±Õ¶Õ¤Õ¸Ö€Õ¡,×נדורה,آندورا,أندورا,ئاندوررا,اندورا,انډورا,ÜܢܕܘܪÜ,अंडोरा,अनà¥à¤¡à¥‹à¤°à¤¾,à¤à¤£à¥à¤¡à¥‹à¤°à¤¾,অà§à¦¯à¦¾à¦¨à§à¦¡à§‹à¦°à¦¾,আনà§à¦¡à§‹à¦°à¦¾,à¦à¦¨à§à¦¡à§‹à¦°à¦¾,அனà¯à®Ÿà¯‹à®°à®¾,à´…à´¨àµâ€à´Ÿàµ‹à´±,ประเทศอันดอร์รา,อันดอร์รา,ອັນດà»àº¥àº²,ཨེན་ཌོ་རà¼,áƒáƒœáƒ“áƒáƒ áƒ,አንዶራ,អង់ដូររា,អានដូរ៉ា,アンドラ,安é“å°”,安é“爾,안ë„ë¼	42.5	1.5	A	PCLI	AD		00				84000		1135	Europe/Andorra	2011-11-05
+3041566	Parròquia d'Andorra la Vella	Parroquia d'Andorra la Vella	Andorra la Vella,Andorre-la-Vieille,Parroquia d'Andorra la Vella,Parròquia d'Andorra la Vella	42.5045	1.49414	A	ADM1	AD		07				24211		1236	Europe/Andorra	2008-03-17
+3041567	Canal Ampla	Canal Ampla		42.48333	1.63333	H	STM	AD		00				0		2296	Europe/Andorra	1993-12-23
+3041568	Canal Ampla	Canal Ampla		42.48333	1.6	H	STM	AD		00				0		2250	Europe/Andorra	1993-12-23
+3041569	Bosc dels Amorriadors	Bosc dels Amorriadors		42.53333	1.48333	V	FRST	AD		00				0		1677	Europe/Andorra	1993-12-23
+3041570	Solana de l’ Alzinar	Solana de l' Alzinar		42.45	1.48333	T	SLP	AD		00				0		1111	Europe/Andorra	1993-12-23
+3041571	Serrat de l’ Alzinar	Serrat de l' Alzinar		42.48333	1.46667	T	MT	AD		00				0		1148	Europe/Andorra	1993-12-23
+3041572	Canal de l’ Alzina	Canal de l' Alzina		42.5	1.5	H	STM	AD		00				0		1135	Europe/Andorra	1993-12-23
+3041573	Rocs Alts	Rocs Alts		42.56667	1.43333	T	RKS	AD		00				0		2402	Europe/Andorra	1993-12-23
+3041574	Costa de l’ Alt de la Capa	Costa de l' Alt de la Capa		42.56667	1.46667	T	SLP	AD		00				0		1673	Europe/Andorra	1993-12-23
+3041575	Pala Alta	Pala Alta		42.6	1.63333	T	CLF	AD		00				0		1893	Europe/Andorra	1993-12-23
+3041576	Terregalls de l’ Alt	Terregalls de l' Alt		42.56667	1.45	T	TAL	AD		00				0		2137	Europe/Andorra	1993-12-23
+3041577	Terregalls de l’ Alt	Terregalls de l' Alt		42.58333	1.43333	T	SPUR	AD		00				0		2412	Europe/Andorra	1993-12-23
+3041578	Font de l’ Alt	Font de l' Alt		42.56667	1.46667	H	SPNG	AD		00				0		1673	Europe/Andorra	1993-12-23
+3041579	Cresta de l’ Alt	Cresta de l' Alt		42.58333	1.45	T	SPUR	AD		00				0		2156	Europe/Andorra	1993-12-23
+3041580	Costes de l’ Alt	Costes de l' Alt		42.56667	1.45	T	SLP	AD		00				0		2137	Europe/Andorra	1993-12-23
+3041581	Canals de l’ Alt	Canals de l' Alt		42.58333	1.43333	H	RVN	AD		00				0		2412	Europe/Andorra	1993-12-23
+3041582	Canal de l’ Alt	Canal de l' Alt		42.58333	1.45	H	STM	AD		00				0		2156	Europe/Andorra	1993-12-23
+3041583	Planell dels Alls	Planell dels Alls		42.5	1.6	T	UPLD	AD		00				0		2416	Europe/Andorra	1993-12-23
+3041584	Roca de les Allaus	Roca de les Allaus		42.63333	1.53333	T	RKS	AD		00				0		2072	Europe/Andorra	1993-12-23
+3041585	Bosc de les Allaus	Bosc de les Allaus		42.53333	1.6	V	FRST	AD		00				0		1888	Europe/Andorra	1993-12-23
+3041586	Solà de l’ Allau	Sola de l' Allau		42.6	1.53333	T	SLP	AD		00				0		1695	Europe/Andorra	1993-12-23
+3041587	Planell de l’ Allau	Planell de l' Allau		42.56667	1.68333	T	UPLD	AD		00				0		2340	Europe/Andorra	1993-12-23
+3041588	Planell de l’ Allau	Planell de l' Allau		42.51667	1.58333	T	UPLD	AD		00				0		1994	Europe/Andorra	1993-12-23
+3041589	Camí de l’ Allau	Cami de l' Allau		42.58333	1.61667	R	TRL	AD		00				0		1707	Europe/Andorra	1993-12-23
+3041590	Roc de l’ Àliga	Roc de l' Aliga		42.55	1.5	T	RK	AD		00				0		1292	Europe/Andorra	1993-12-23
+3041591	Roc de l’ Àliga	Roc de l' Aliga		42.55	1.46667	T	RK	AD		00				0		1585	Europe/Andorra	1993-12-23
+3041592	Roc de l’ Àliga	Roc de l' Aliga		42.46667	1.5	T	RK	AD		00				0		1383	Europe/Andorra	1993-12-23
+3041593	Pic de l’ Àliga	Pic de l' Aliga		42.5	1.66667	T	PK	AD		00				0		2441	Europe/Andorra	1993-12-23
+3041594	Canal dels Alegrets	Canal dels Alegrets		42.5	1.48333	H	STM	AD		00				0		1316	Europe/Andorra	1993-12-23
+3041595	Pont de l’ Aldosa	Pont de l' Aldosa		42.55	1.51667	S	BDG	AD		00				0		1397	Europe/Andorra	1993-12-23
+3041596	Carretera de l’ Aldosa	Carretera de l' Aldosa		42.58333	1.63333	R	RD	AD		00				0		1722	Europe/Andorra	1993-12-23
+3041597	Pont d’ Aixovall	Pont d' Aixovall		42.48333	1.48333	S	BDG	AD		00				0		981	Europe/Andorra	1993-12-23
+3041598	Aixovall	Aixovall	Aixovall	42.46667	1.48333	P	PPL	AD		06				0		1134	Europe/Andorra	2011-11-05
+3041599	Solà d’ Aixirivall	Sola d' Aixirivall		42.46667	1.5	T	SLP	AD		00				0		1383	Europe/Andorra	1993-12-23
+3041600	Riu d’ Aixirivall	Riu d' Aixirivall		42.46667	1.5	H	STM	AD		00				0		1383	Europe/Andorra	1993-12-23
+3041601	Conreu d’ Aixirivall	Conreu d' Aixirivall		42.46667	1.5	V	CULT	AD		00				0		1383	Europe/Andorra	1993-12-23
+3041602	Carretera d’ Aixirivall	Carretera d' Aixirivall		42.45	1.48333	R	RD	AD		00				0		1111	Europe/Andorra	1993-12-23
+3041603	Bordes d’ Aixirivall	Bordes d' Aixirivall		42.45	1.51667	S	FRMS	AD		00				0		1790	Europe/Andorra	1993-12-23
+3041604	Aixirivall	Aixirivall	Aixirivali,Aixirivall,Aixirvall,Eixirivall	42.46321	1.5029	P	PPL	AD		06				0		1357	Europe/Andorra	2011-11-05
+3041605	Riu Aixec	Riu Aixec		42.58333	1.66667	H	STM	AD		00				0		2159	Europe/Andorra	1993-12-23
+3041606	Riu Aixec	Riu Aixec		42.55	1.58333	H	STM	AD		00				0		1499	Europe/Andorra	1993-12-23
+3041607	Riu d’ Aixàs	Riu d' Aixas		42.48333	1.46667	H	STM	AD		00				0		1148	Europe/Andorra	1993-12-23
+3041608	Bosc d’ Aixàs	Bosc d' Aixas		42.48333	1.46667	V	FRST	AD		00				0		1148	Europe/Andorra	1993-12-23
+3041609	Aixàs	Aixas		42.48333	1.46667	P	PPL	AD		06				0		1148	Europe/Andorra	1993-12-23
+3041610	Aixàs	Aixas		42.48333	1.46667	A	ADMD	AD		00				0		1148	Europe/Andorra	1993-12-23
+3041611	Clots d’ Aixades	Clots d' Aixades		42.58333	1.58333	H	RVN	AD		00				0		1993	Europe/Andorra	1993-12-23
+3041612	Coll de l’ Airola	Coll de l' Airola		42.48333	1.46667	T	PK	AD		00				0		1148	Europe/Andorra	1993-12-23
+3041613	Bosc de l’ Airola	Bosc de l' Airola		42.56667	1.56667	V	FRST	AD		00				0		2089	Europe/Andorra	1993-12-23
+3041614	Solà d’ Airet	Sola d' Airet		42.45	1.45	T	SLP	AD		00				0		1482	Europe/Andorra	1993-12-23
+3041615	Bosc d’ Aigües Juntes	Bosc d' Aigues Juntes		42.58333	1.46667	V	FRST	AD		00				0		1643	Europe/Andorra	1993-12-23
+3041616	Aigües Juntes	Aigues Juntes		42.58333	1.46667	H	CNFL	AD		00				0		1643	Europe/Andorra	1993-12-23
+3041617	Pont de l’ Aiguerola	Pont de l' Aiguerola		42.53333	1.61667	S	BDG	AD		00				0		2237	Europe/Andorra	1993-12-23
+3041618	Clots d’ Aigua Vella	Clots d' Aigua Vella		42.6	1.63333	H	RVN	AD		00				0		1893	Europe/Andorra	1993-12-23
+3041619	Canal d’ Aigua Vella	Canal d' Aigua Vella		42.6	1.63333	H	STM	AD		00				0		1893	Europe/Andorra	1993-12-23
+3041620	Basers d’ Aigua Vella	Basers d' Aigua Vella		42.6	1.63333	T	CLF	AD		00				0		1893	Europe/Andorra	1993-12-23
+3041621	Riu d’ Aiguarebre	Riu d' Aiguarebre		42.6	1.51667	H	STM	AD		00				0		1445	Europe/Andorra	1993-12-23
+3041622	Planell d’ Aiguarebre	Planell d' Aiguarebre		42.61667	1.51667	T	UPLD	AD		00				0		1716	Europe/Andorra	1993-12-23
+3041623	Costa d’ Aiguarebre	Costa d' Aiguarebre		42.61667	1.51667	T	SLP	AD		00				0		1716	Europe/Andorra	1993-12-23
+3041624	Costa dels Aiguaders	Costa dels Aiguaders		42.5	1.43333	T	SLP	AD		00				0		1654	Europe/Andorra	1993-12-23
+3041625	Presa d’ Aigua	Presa d' Aigua		42.5	1.58333	S	DAM	AD		00				0		1888	Europe/Andorra	1993-12-23
+3041626	Font de Les Agunes	Font de Les Agunes		42.58333	1.46667	H	SPNG	AD		00				0		1643	Europe/Andorra	1993-12-23
+3041627	Borda de les Agunes	Borda de les Agunes		42.58333	1.46667	S	FRM	AD		00				0		1643	Europe/Andorra	1993-12-23
+3041628	Planell de les Agudelles	Planell de les Agudelles		42.56667	1.55	T	UPLD	AD		00				0		1996	Europe/Andorra	1993-12-23
+3041629	Pont dels Agrels	Pont dels Agrels		42.55	1.48333	S	BDG	AD		00				0		1548	Europe/Andorra	1993-12-23
+3041630	Canal dels Agrels	Canal dels Agrels		42.55	1.48333	H	STM	AD		00				0		1548	Europe/Andorra	1993-12-23
+3041631	Serrat de l’ Agraullet	Serrat de l' Agraullet		42.53333	1.55	T	RDGE	AD		00				0		1344	Europe/Andorra	1993-12-23
+3041632	Riu de les Agols	Riu de les Agols		42.53125	1.59204	H	STM	AD		00				0		1756	Europe/Andorra	2011-04-19
+3041633	Coll dels Abòs	Coll dels Abos		42.61667	1.53333	T	PK	AD		00				0		1609	Europe/Andorra	1993-12-23
+3041634	Riu de l’ Abeurador	Riu de l' Abeurador		42.58333	1.65	H	STM	AD		00				0		1767	Europe/Andorra	1993-12-23
+3041635	Clot de les Abelletes	Clot de les Abelletes		42.53333	1.73333	T	CRQ	AD		00				0		2300	Europe/Andorra	1993-12-23
+3041636	Serra del Cap de l’ Abarsetar de Rialb	Serra del Cap de l' Abarsetar de Rialb		42.63333	1.55	T	RDGE	AD		00				0		2053	Europe/Andorra	1993-12-23
+3041637	Abarsetar de Rialb	Abarsetar de Rialb		42.65	1.55	L	LCTY	AD		00				0		2181	Europe/Andorra	1993-12-23
+3041638	Abarsetar de la Coma	Abarsetar de la Coma		42.62793	1.48624	L	LCTY	AD		07				0		2111	Europe/Andorra	2007-03-04
+3041639	Abarsetar de Ferreroles	Abarsetar de Ferreroles		42.6	1.55	L	LCTY	AD		00				0		2298	Europe/Andorra	1993-12-23
+3041640	Abarsetar d'Arcalís	Abarsetar d'Arcalis		42.62657	1.50148	L	LCTY	AD		07				0		1979	Europe/Andorra	2007-03-04
+3041641	Clots de l’ Abarsetar	Clots de l' Abarsetar		42.63333	1.55	H	RVN	AD		00				0		2053	Europe/Andorra	1993-12-23
+3041642	Abarsetar	Abarsetar		42.55	1.65	L	LCTY	AD		00				0		2432	Europe/Andorra	1993-12-23
+3041643	Pla de l’ Abarsa	Pla de l' Abarsa		42.43333	1.51667	T	UPLD	AD		00				0		2031	Europe/Andorra	1993-12-23
+3041644	Costa de l’ Abalançc	Costa de l' Abalancc		42.53333	1.53333	T	SLP	AD		00				0		1521	Europe/Andorra	1993-12-23
+3109332	Riu d’ Ós	Riu d' Os	Rio Saturia,Rio Seturia,Rio de Os,Riu d' Os,Riu d’ Ós,Río Saturia,Río Seturia,Río de Os	42.46667	1.5	H	STM	AD		00				0		1383	Europe/Andorra	2011-11-06
+3338529	Parròquia d'Escaldes-Engordany	Parroquia d'Escaldes-Engordany	Escaldes-Engordany,Parroquia d'Escaldes-Engordany,Parròquia d'Escaldes-Engordany	42.5	1.56667	A	ADM1	AD		08				16391		1776	Europe/Andorra	2008-03-17
+6463133	Eurotel	Eurotel		42.51286	1.53552	S	HTL	AD		07				0		1139	Europe/Andorra	2011-04-02
+6463689	Panorama Hotel	Panorama Hotel		42.5069	1.5361	S	HTL	AD		07				0		1350	Europe/Andorra	2007-04-13
+6463694	Roc Blanc Hotel	Roc Blanc Hotel		42.50927	1.53862	S	HTL	AD		07				0		1139	Europe/Andorra	2011-04-02
+6464858	Ahotels Prisma	Ahotels Prisma		42.5087	1.5364	S	HTL	AD		07				0		1139	Europe/Andorra	2007-04-13
+6465286	Andorra Park Hotel	Andorra Park Hotel		42.5092	1.5244	S	HTL	AD		07				0		1097	Europe/Andorra	2007-04-13
+6473225	Coma	Coma		42.559	1.533	S	HTL	AD		07				0		1514	Europe/Andorra	2007-04-17
+6473433	Magic La Massana	Magic La Massana		42.546	1.518	S	HTL	AD		07				0		1397	Europe/Andorra	2007-04-17
+6473450	Art	Art		42.5062	1.5216	S	HTL	AD		07				0		1073	Europe/Andorra	2007-04-17
+6473528	Andorra Center	Andorra Center		42.5074	1.52	S	HTL	AD		07				0		1073	Europe/Andorra	2007-04-17
+6473529	Hotel Cervol	Hotel Cervol		42.50262	1.51288	S	HTL	AD		07				0		1011	Europe/Andorra	2011-04-02
+6473530	Novotel Andorra	Novotel Andorra		42.50762	1.52734	S	HTL	AD		07				0		1205	Europe/Andorra	2011-04-03
+6473617	Roc del Sola	Roc del Sola		42.505	1.514	S	HTL	AD		07				0		1011	Europe/Andorra	2007-04-17
+6473823	L'Angel Blanc	L'Angel Blanc		42.577	1.667	S	HTL	AD		07				0		2159	Europe/Andorra	2007-04-17
+6473970	Refugi dels Isards	Refugi dels Isards		42.50616	1.5289	S	HTL	AD		07				0		1205	Europe/Andorra	2007-04-17
+6474116	Panorama	Panorama		42.507	1.535	S	HTL	AD		07				0		1350	Europe/Andorra	2007-04-17
+6474117	Font d'Argent	Font d'Argent		42.542	1.732	S	HTL	AD						0		2100	Europe/Paris	2009-09-07
+6474170	Reial Pirineus	Reial Pirineus		42.5405	1.7313	S	HTL	AD						0		2187	Europe/Paris	2009-06-28
+6474171	Hotansa Himalaya	Hotansa Himalaya		42.5405	1.7313	S	HTL	AD						0		2187	Europe/Paris	2009-09-07
+6474212	F and G La Cabana	F and G La Cabana		42.551	1.533	S	HTL	AD		07				0		1340	Europe/Andorra	2007-04-17
+6474230	Pic Maia	Pic Maia		42.5075	1.5218	S	HTL	AD		07				0		1073	Europe/Andorra	2007-04-17
+6474307	Marco Polo	Marco Polo		42.5411	1.5191	S	HTL	AD		07				0		1343	Europe/Andorra	2007-04-17
+6474384	Confort Pas de la Casa	Confort Pas de la Casa		42.5413	1.7326	S	HTL	AD						0		2187	Europe/Paris	2009-09-07
+6474447	Magic Pas	Magic Pas		42.54303	1.73188	S	HTL	AD						0		2100	Europe/Paris	2011-04-02
+6474512	Font del Marge	Font del Marge		42.5043	1.5146	S	HTL	AD		07				0		1011	Europe/Andorra	2007-04-17
+6474514	Sant Jordi	Sant Jordi		42.5043	1.5146	S	HTL	AD		07				0		1011	Europe/Andorra	2007-04-17
+6474580	AnyósPark	AnyosPark		42.53417	1.52528	S	HTL	AD		04				0		1491	Europe/Andorra	2011-04-02
+6474582	Alaska	Alaska		42.61877	1.53922	S	HTL	AD		07				0		1704	Europe/Andorra	2007-04-17
+6474659	President	President		42.502	1.512	S	HTL	AD		07				0		1011	Europe/Andorra	2007-04-17
+6474661	Himalaia Soldeu	Himalaia Soldeu		42.57647	1.66873	S	HTL	AD		07				0		2159	Europe/Andorra	2011-04-02
+6474662	Piolets	Piolets		42.5769	1.66776	S	HTL	AD		07				0		2159	Europe/Andorra	2007-04-17
+6475094	Servissim	Servissim		42.566	1.596	S	HTL	AD		07				0		1677	Europe/Andorra	2007-04-17
+6475095	Artic	Artic		42.5054	1.5183	S	HTL	AD		07				0		1073	Europe/Andorra	2007-04-17
+6475301	Magic Ski	Magic Ski		42.54557	1.51746	S	HTL	AD		07				0		1397	Europe/Andorra	2007-04-17
+6475379	Sporting	Sporting		42.5075	1.5218	S	HTL	AD		07				0		1073	Europe/Andorra	2007-04-17
+6483664	Hotel Apsis Florida	Hotel Apsis Florida		42.50811	1.52255	S	HTL	AD		07				0		1073	Europe/Andorra	2007-04-15
+6483820	Hotel Apsis Art Hotel	Hotel Apsis Art Hotel		42.5	1.51667	S	HTL	AD		07				0		1410	Europe/Andorra	2007-04-15
+6491562	Hotel Delfos	Hotel Delfos		42.50682	1.5344	S	HTL	AD		07				0		1350	Europe/Andorra	2011-04-02
+6491674	Hotel Fénix	Hotel Fenix		42.5086	1.5394	S	HTL	AD		07				0		1139	Europe/Andorra	2007-04-15
+6491682	Hotel Comtes d'Urgell	Hotel Comtes d'Urgell		42.51028	1.54099	S	HTL	AD		07				0		1139	Europe/Andorra	2011-04-02
+6492312	Ahotels Princep	Ahotels Princep		42.5073	1.5316	S	HTL	AD		07				0		1205	Europe/Andorra	2007-04-15
+6493558	Club Dorada el Tarter	Club Dorada el Tarter		42.5793	1.6414	S	HTL	AD		07				0		1727	Europe/Andorra	2007-04-14
+6493930	Residence Deusol	Residence Deusol		42.6195	1.5389	S	HTL	AD		07				0		1704	Europe/Andorra	2007-04-14
+6493995	HUSA Centric	HUSA Centric		42.5087	1.5283	S	HTL	AD		07				0		1041	Europe/Andorra	2007-04-14
+6494290	HUSA Xalet Besoli	HUSA Xalet Besoli		42.5718	1.4843	S	HTL	AD		07				0		1655	Europe/Andorra	2007-04-14
+6494345	HUSA Imperial	HUSA Imperial		42.472	1.4923	S	HTL	AD		06				0		1164	Europe/Andorra	2010-01-12
+6494491	Hotel Marco Polo	Hotel Marco Polo		42.5423	1.5174	S	HTL	AD		07				0		1397	Europe/Andorra	2007-04-14
+6494790	Husa Xalet Verdu	Husa Xalet Verdu		42.571	1.4715	S	HTL	AD		07				0		1673	Europe/Andorra	2007-04-14
+6494958	Apsis Arthotel	Apsis Arthotel		42.5072	1.5261	S	HTL	AD		07				0		1205	Europe/Andorra	2007-04-14
+6495028	Hotel Himalaia Pas	Hotel Himalaia Pas		42.5403	1.7311	S	HTL	AD						0		2187	Europe/Paris	2009-07-01
+6495053	Hotel Font	Hotel Font		42.5436	1.5117	S	HTL	AD		07				0		1257	Europe/Andorra	2007-04-14
+6495582	Hotel Austria	Hotel Austria		42.5788	1.6686	S	HTL	AD		07				0		2159	Europe/Andorra	2007-04-14
+6495704	Hotel Magic Canillo	Hotel Magic Canillo		42.5638	1.5965	S	HTL	AD		07				0		1677	Europe/Andorra	2007-04-14
+6495769	Hotel F&G La Cabana	Hotel F&G La Cabana		42.5538	1.5319	S	HTL	AD		07				0		1340	Europe/Andorra	2007-04-14
+6495790	Hotel Princesa Parc	Hotel Princesa Parc		42.574	1.4824	S	HTL	AD		07				0		1508	Europe/Andorra	2007-04-14
+6495826	Hotel Magic La Massana	Hotel Magic La Massana		42.5448	1.5163	S	HTL	AD		07				0		1257	Europe/Andorra	2007-04-14
+6497139	AHotels Confort Patagonia	AHotels Confort Patagonia		42.5627	1.4952	S	HTL	AD		07				0		1430	Europe/Andorra	2007-04-14
+6497391	Hotel Magic Andorra	Hotel Magic Andorra		42.5091	1.5297	S	HTL	AD		07				0		1041	Europe/Andorra	2007-04-14
+6497888	Ahotels Piolets Park & Spa	Ahotels Piolets Park & Spa		42.5772	1.6652	S	HTL	AD		07				0		1925	Europe/Andorra	2007-04-14
+6498453	Hotel Carlton Plaza	Hotel Carlton Plaza		42.508	1.5254	S	HTL	AD		07				0		1205	Europe/Andorra	2007-04-14
+6498890	Hotel Plaza	Hotel Plaza		42.5062	1.5296	S	HTL	AD		07				0		1205	Europe/Andorra	2007-04-14
+6499190	Hotel Ski Plaza	Hotel Ski Plaza		42.5657	1.5973	S	HTL	AD		07				0		1677	Europe/Andorra	2007-04-14
+6500087	Hesperia Andorra la Vella	Hesperia Andorra la Vella		42.5089	1.5289	S	HTL	AD		07				0		1041	Europe/Andorra	2007-04-14
+6501396	Ahotels El Serrat	Ahotels El Serrat		42.61652	1.53833	S	HTL	AD		07				0		1793	Europe/Andorra	2009-06-25
+6502212	Hotel Euro Esqui	Hotel Euro Esqui		42.5794	1.6585	S	HTL	AD		07				0		1925	Europe/Andorra	2007-04-14
+6504216	Abba Xalet Suites	Abba Xalet Suites		42.5331	1.516	S	HTL	AD		07				0		1211	Europe/Andorra	2007-04-14
+6505101	Hotel Valira	HOTEL VALIRA		42.5	1.5333	S	HTL	AD		07				0		1574	Europe/Andorra	2007-04-13
+6508304	Mercure Andorra 4	MERCURE ANDORRA 4		42.5	1.5166	S	HTL	AD		07				0		1230	Europe/Andorra	2007-04-13
+6520395	Ahotels Confort Patagonia	AHOTELS CONFORT PATAGONIA		42.5124	1.5389	S	HTL	AD		07				0		1139	Europe/Andorra	2007-04-15
+6521569	Ahotels El Serrat	AHOTELS EL SERRAT		42.5575	1.5325	S	HTL	AD		07				0		1340	Europe/Andorra	2007-04-15
+6522685	Salvia D Or Hotel	SALVIA D OR HOTEL		42.5	1.5166	S	HTL	AD		07				0		1230	Europe/Andorra	2007-04-14
+6525900	Roc De Caldes	ROC DE CALDES		42.51065	1.54706	S	HTL	AD		07				0		1227	Europe/Andorra	2011-04-03
+6529214	Metropolis	METROPOLIS		42.5166	1.55	S	HTL	AD		07				0		1498	Europe/Andorra	2007-04-14
+6529355	Diplomatic	DIPLOMATIC		42.50521	1.52715	S	HTL	AD		07				0		1205	Europe/Andorra	2011-04-02
+6529413	Eureka	EUREKA		42.50873	1.53989	S	HTL	AD		07				0		1139	Europe/Andorra	2011-04-02
+6529930	Cassany	CASSANY		42.50798	1.5248	S	HTL	AD		07				0		1073	Europe/Andorra	2011-04-02
+6942550	Andorra Palace	ANDORRA PALACE		42.50837	1.52706	S	HTL	AD		07				0		1041	Europe/Andorra	2009-06-24
+6942551	Annapurna	ANNAPURNA		42.55768	1.53255	S	HTL	AD		07				0		1340	Europe/Andorra	2009-06-24
+6942562	Bringue	BRINGUE		42.55768	1.53255	S	HTL	AD		07				0		1340	Europe/Andorra	2009-06-25
+6942564	Co Princeps	CO PRINCEPS		42.46815	1.49329	S	HTL	AD		06				0		1164	Europe/Andorra	2010-01-12
+6942568	Crowne Plaza Andorra	CROWNE PLAZA ANDORRA		42.50574	1.52002	S	HTL	AD		07				0		1073	Europe/Andorra	2009-06-25
+6942569	Diana Parc	Diana Parc		42.5444	1.5115	S	HTL	AD		07				0		1257	Europe/Andorra	2009-06-25
+6942578	Encamp	Encamp		42.53321	1.57914	S	HTL	AD						0		1571	Europe/Andorra	2009-06-25
+6942579	Diana parc	Diana parc		42.61886	1.53939	S	HTL	AD		07				0		1704	Europe/Andorra	2009-06-25
+6942582	Font d'argent Canillo	Font d'argent Canillo		42.57086	1.60782	S	HTL	AD						0		1655	Europe/Andorra	2011-04-03
+6942589	GRAU ROIG	GRAU ROIG		42.5327	1.70116	S	HTL	AD						0		2357	Europe/Andorra	2009-06-25
+6942604	Husa Patagonia	Husa Patagonia		42.57139	1.48566	S	HTL	AD		07				0		1655	Europe/Andorra	2009-06-26
+6942646	Ibis Andorra	Ibis Andorra		42.51252	1.55059	S	HTL	AD		07				0		1498	Europe/Andorra	2011-04-03
+6942647	Lake placid	Lake placid		42.54119	1.73333	S	HTL	AD						0		2187	Europe/Paris	2009-06-28
+6942648	Les closes	Les closes		42.5087	1.53993	S	HTL	AD		07				0		1139	Europe/Andorra	2009-06-28
+6942649	L Isard	L Isard		42.50775	1.52288	S	HTL	AD		07				0		1073	Europe/Andorra	2009-06-28
+6942650	Paris	Paris		42.528	1.56927	S	HTL	AD						0		1418	Europe/Andorra	2009-06-28
+6942651	Pere D Urg	Pere D Urg		42.5347	1.58012	S	HTL	AD						0		1309	Europe/Andorra	2009-06-28
+6942667	Prat de les mines	Prat de les mines		42.59406	1.52684	S	HTL	AD		07				0		1695	Europe/Andorra	2009-06-28
+6942674	Rialb	Rialb		42.61886	1.53939	S	HTL	AD		07				0		1704	Europe/Andorra	2009-06-28
+6942675	Rutllan	Rutllan		42.54738	1.51345	S	HTL	AD		04				0		1257	Europe/Andorra	2009-06-28
+6942676	Sant Eloi	Sant Eloi		42.45793	1.48717	S	HTL	AD						0		1159	Europe/Madrid	2011-04-02
+6942677	Shusski	Shusski		42.5338	1.58577	S	HTL	AD						0		1490	Europe/Andorra	2009-06-28
+6942678	Solana	Solana		42.58099	1.5193	S	HTL	AD						0		1722	Europe/Andorra	2009-06-28
+6942680	Somriu Comapedrosa	Somriu Comapedrosa		42.58099	1.5193	S	HTL	AD						0		1722	Europe/Andorra	2009-06-28
+6942681	Somriu Galanthus	Somriu Galanthus		42.5769	1.66776	S	HTL	AD		07				0		2159	Europe/Andorra	2009-06-28
+6942682	Les Terres	Les Terres		42.57916	1.63023	S	HTL	AD						0		1722	Europe/Andorra	2011-04-02
+6942683	Somriu Segle Xx	SOMRIU SEGLE XX		42.58118	1.63827	S	HTL	AD						0		1727	Europe/Andorra	2009-09-08
+6942684	Somriu Solana Del Ransol	SOMRIU SOLANA DEL RANSOL		42.58002	1.64432	S	HTL	AD		07				0		1737	Europe/Andorra	2009-06-28
+6942685	Somriu Tivoli	SOMRIU TIVOLI		42.5076	1.53223	S	HTL	AD		07				0		1205	Europe/Andorra	2009-06-28
+6942686	Somriu Vall Ski	Somriu Vall Ski		42.57723	1.66752	S	HTL	AD		07				0		2159	Europe/Andorra	2009-06-28
+6942696	Sport	Sport		42.57688	1.66688	S	HTL	AD		07				0		2159	Europe/Andorra	2009-06-29
+6942697	Sport Hermitage	SPORT HERMITAGE		42.57727	1.6672	S	HTL	AD		07				0		2159	Europe/Andorra	2009-06-29
+6942698	Sport Village	Sport Village		42.57688	1.66688	S	HTL	AD		07				0		2159	Europe/Andorra	2009-06-29
+6942740	APARTAMENTS TURISTICS GLAÇ	APARTAMENTS TURISTICS GLAC		42.58	1.64623	S	HTL	AD		07				0		1737	Europe/Andorra	2009-06-30
+6942759	Aparthotel Cosmos	Aparthotel Cosmos		42.51	1.541	S	HTL	AD		07				0		1139	Europe/Andorra	2009-06-30
+6942761	Carlemany	Carlemany		42.509	1.538	S	HTL	AD		07				0		1139	Europe/Andorra	2009-06-30
+6942762	Cims Andorra	Cims Andorra		42.5085	1.5307	S	HTL	AD		07				0		1041	Europe/Andorra	2009-06-30
+6942764	Folch	Folch		42.471	1.493	S	HTL	AD		06				0		1164	Europe/Andorra	2010-01-12
+6942766	Hotel La Solana	Hotel La Solana		42.533	1.593	S	HTL	AD		07				0		1756	Europe/Andorra	2009-09-07
+6942767	Phoebus	Phoebus		42.54103	1.72985	S	HTL	AD						0		2187	Europe/Paris	2009-06-30
+6942768	Pol	Pol		42.465	1.491	S	HTL	AD		06				0		1045	Europe/Andorra	2009-06-30
+6942770	Subira	Subira		42.632	1.5	S	HTL	AD						0		1979	Europe/Andorra	2009-06-30
+6942771	Vip Plus	Vip Plus		42.535	1.588	S	HTL	AD						0		1490	Europe/Andorra	2009-06-30
+6942793	Acta Arthotel	Acta Arthotel		42.50728	1.52609	S	HTL	AD		07				0		1205	Europe/Andorra	2009-07-01
+6942794	Ahotels Patagonia Austral	Ahotels Patagonia Austral		42.57246	1.48436	S	HTL	AD		07				0		1655	Europe/Andorra	2009-07-01
+6942796	Del Tarter	Del Tarter		42.58035	1.64893	S	HTL	AD		07				0		1737	Europe/Andorra	2009-07-01
+6942797	Hotel Llop Gris	Hotel Llop Gris		42.57804	1.64845	S	HTL	AD		07				0		1737	Europe/Andorra	2009-07-01
+6948955	Apartamentos Xixerella	APARTAMENTOS XIXERELLA		42.55309	1.48746	S	HTL	AD		04				0		1520	Europe/Andorra	2009-09-07
+6948956	Apartaments Gla	APARTAMENTS GLA		42.56	1.67396	S	HTL	AD						0		1963	Europe/Andorra	2009-09-07
+6948957	Boston	Boston		42.6083	1.5394	S	HTL	AD		07				0		1821	Europe/Andorra	2009-09-07
+6948958	Coma Bella	COMA BELLA		42.46487	1.49115	S	HTL	AD		06				0		1045	Europe/Andorra	2009-09-07
+6948959	Confort Soldeu	CONFORT SOLDEU		42.5769	1.66776	S	HTL	AD		07				0		2159	Europe/Andorra	2009-09-07
+6948960	Coray	CORAY		42.535	1.584	S	HTL	AD						0		1490	Europe/Andorra	2009-09-07
+6948963	Del Clos	Del Clos		42.6187	1.5392	S	HTL	AD		07				0		1704	Europe/Andorra	2009-09-07
+6948965	El Xalet	EL XALET		42.58035	1.64892	S	HTL	AD		07				0		1737	Europe/Andorra	2009-09-07
+6948966	Els Llacs	ELS LLACS		42.56012	1.68173	S	HTL	AD						0		2094	Europe/Andorra	2009-09-07
+6948967	Els Meners	ELS MENERS		42.566	1.596	S	HTL	AD		07				0		1677	Europe/Andorra	2009-09-07
+6948968	Espel	Espel		42.509	1.542	S	HTL	AD		07				0		1227	Europe/Andorra	2009-09-07
+6948969	Euro Ski	Euro Ski		42.6187	1.5392	S	HTL	AD		07				0		1704	Europe/Andorra	2009-09-07
+6948970	Guillen	Guillen		42.536	1.583	S	HTL	AD						0		1309	Europe/Andorra	2009-09-07
+6948971	Hotansa Austria	HOTANSA AUSTRIA		42.56012	1.68173	S	HTL	AD						0		2094	Europe/Andorra	2009-09-07
+6948972	Iglu	IGLU		42.53263	1.70035	S	HTL	AD						0		2357	Europe/Andorra	2009-09-07
+6948973	Kandahar	KANDAHAR		42.5413	1.7325	S	HTL	AD						0		2187	Europe/Paris	2009-09-07
+6948974	L Obaga Blanca	L OBAGA BLANCA		42.5617	1.60352	S	HTL	AD						0		1969	Europe/Andorra	2009-09-07
+6948975	Montecarlo	MONTECARLO		42.528	1.569	S	HTL	AD						0		1418	Europe/Andorra	2009-09-07
+6948976	Naudi	Naudi		42.6187	1.5392	S	HTL	AD		07				0		1704	Europe/Andorra	2009-09-07
+6948977	Nordic	NORDIC		42.58035	1.64892	S	HTL	AD		07				0		1737	Europe/Andorra	2009-09-07
+6948991	Hotel Oros	Hotel Oros		42.54564	1.73176	S	HTL	AD						0		2100	Europe/Paris	2011-04-03
+6948993	Palome	Palome		42.56148	1.49741	S	HTL	AD		07				0		1430	Europe/Andorra	2009-09-08
+6948995	Paradis Blanc	PARADIS BLANC		42.54152	1.73378	S	HTL	AD						0		2385	Europe/Paris	2009-09-08
+6948996	Rutllan Xalet De Muntanya	RUTLLAN XALET DE MUNTANYA		42.5479	1.51319	S	HTL	AD		04				0		1257	Europe/Andorra	2009-09-08
+6948997	Sant Bernat Apartments	SANT BERNAT APARTMENTS		42.56609	1.5967	S	HTL	AD		07				0		1677	Europe/Andorra	2009-09-08
+6948998	Solana de ransol	Solana de ransol		42.57831	1.6352	S	HTL	AD						0		1727	Europe/Andorra	2009-09-08
+6948999	Soldeu Maistre	Soldeu Maistre		42.5769	1.66776	S	HTL	AD		07				0		2159	Europe/Andorra	2009-09-08
+6949002	St Gothard	ST GOTHARD		42.5723	1.484	S	HTL	AD		07				0		1655	Europe/Andorra	2009-09-08
+6949004	Univers	Univers		42.53647	1.58195	S	HTL	AD						0		1309	Europe/Andorra	2009-09-08
+6949005	Velvet	Velvet		42.7467	1.7081	S	HTL	AD						0		1563	Europe/Paris	2009-09-08
+6949010	Xalet Montana	Xalet Montana		42.57701	1.66627	S	HTL	AD		07				0		1925	Europe/Andorra	2009-09-08
+7114070	andorra magica	andorra magica		42.50727	1.52202	S	HTL	AD		07				0		1073	Europe/Andorra	2009-12-02
+7284857	Apartamentos L Angel Blanc	Apartamentos L Angel Blanc		42.577	1.667	S	HTL	AD		07				0		2159	Europe/Andorra	2010-04-01
+7284858	Apartamentos Giberga	Apartamentos Giberga		42.552	1.51081	S	HTL	AD						0		1400	Europe/Andorra	2010-04-01
+7284859	Aparthotel l'Alba	Aparthotel l'Alba		42.5787	1.6532	S	HTL	AD		02				0		1767	Europe/Andorra	2010-04-01
+7284866	Magic Canillo Apartments	Magic Canillo Apartments		42.5669	1.60041	S	HTL	AD		02				0		1655	Europe/Andorra	2010-04-01
+7284867	Apartaments Sant Romà	Apartaments Sant Roma		42.5736	1.48311	S	HTL	AD						0		1508	Europe/Andorra	2010-04-01
+7284869	Saporo	Saporo		42.5432	1.73327	S	HTL	AD		03				0		2100	Europe/Paris	2010-04-01
+7284872	Aparthotel Casa Vella	Aparthotel Casa Vella		42.5537	1.53215	S	HTL	AD						0		1340	Europe/Andorra	2010-04-01
+7284874	Caldea Centre Termolùdic d' Andorra	Caldea Centre Termoludic d' Andorra		42.5117	1.5367	S	HTL	AD		07				0		1139	Europe/Andorra	2010-04-01
+7284875	Comabella Hotel	Comabella Hotel		42.5	1.5166	S	HTL	AD		07				0		1230	Europe/Andorra	2010-04-01
+7284915	Hotel Galanthus	Hotel Galanthus		42.5829	1.66268	S	HTL	AD		02				0		1925	Europe/Andorra	2010-04-02
+7284920	Hotel Cims Pas	Hotel Cims Pas		42.5428	1.73479	S	HTL	AD		03				0		2230	Europe/Paris	2010-04-02
+7284921	Hotel Cims Pas de La Casa	Hotel Cims Pas de La Casa		42.5428	1.73479	S	HTL	AD		03				0		2230	Europe/Paris	2010-04-02
+7284925	Hotel del Clos	Hotel del Clos		42.5786	1.65121	S	HTL	AD		02				0		1767	Europe/Andorra	2010-04-02
+7284926	Hotel Erts	Hotel Erts		42.5454	1.51909	S	HTL	AD		07				0		1397	Europe/Andorra	2010-04-02
+7284927	Hotel Font De Ferro	Hotel Font De Ferro		42.5902	1.52436	S	HTL	AD						0		1525	Europe/Andorra	2010-04-02
+7284952	Magic Ski La Massana Hotel	Magic Ski La Massana Hotel		42.5457	1.51838	S	HTL	AD		07				0		1397	Europe/Andorra	2010-04-04
+7284953	Hotel Metropolis	Hotel Metropolis		42.5102	1.54086	S	HTL	AD		07				0		1139	Europe/Andorra	2010-04-04
+7284954	Hotel Palarine	Hotel Palarine		42.5763	1.5187	S	HTL	AD						0		1722	Europe/Andorra	2010-04-04
+7284955	Hotel Paris Londres	Hotel Paris Londres		42.5087	1.54083	S	HTL	AD		07				0		1139	Europe/Andorra	2010-04-04
+7284956	Hotel Parma	Hotel Parma		42.5432	1.73297	S	HTL	AD		03				0		2100	Europe/Paris	2010-04-04
+7284957	Residència Daina	Residencia Daina		42.5615	1.49775	S	HTL	AD						0		1430	Europe/Andorra	2010-04-04
+7284958	Hotel Roc del Castell	Hotel Roc del Castell		42.566	1.596	S	HTL	AD		07				0		1677	Europe/Andorra	2010-04-04
+7284959	Hotel Siracusa	Hotel Siracusa		42.509	1.54305	S	HTL	AD		07				0		1227	Europe/Andorra	2010-04-04
+7284960	Somriu Hotel Refugi dels Isards	Somriu Hotel Refugi dels Isards		42.5433	1.73494	S	HTL	AD		03				0		2230	Europe/Paris	2010-04-04
+7284961	Sport Hotel Hermitage & Spa	Sport Hotel Hermitage & Spa		42.5763	1.66991	S	HTL	AD		07				0		2159	Europe/Andorra	2010-04-04
+7284962	Hotel Tristaina	Hotel Tristaina		42.6189	1.53714	S	HTL	AD						0		1704	Europe/Andorra	2010-04-04
+7287697	Hotel Montane	Hotel Montane		42.5454	1.51909	S	HTL	AD		07				0		1397	Europe/Andorra	2010-04-06
+7287698	Hotel Pitiusa	Hotel Pitiusa		42.5089	1.53258	S	HTL	AD		07				0		1041	Europe/Andorra	2010-04-06
+7287699	Hotel Príncep	Hotel Princep		42.5087	1.54083	S	HTL	AD		07				0		1139	Europe/Andorra	2010-04-06
+7287700	Hotel I Termes Carlemany	Hotel I Termes Carlemany		42.5092	1.54409	S	HTL	AD		07				0		1227	Europe/Andorra	2010-04-06
+7287701	Hotel Viena	Hotel Viena		42.5069	1.52004	S	HTL	AD		07				0		1073	Europe/Andorra	2010-04-06
+7302102	La Margineda	La Margineda		42.484	1.49242	P	PPL	AD		07				0		1353	Europe/Andorra	2010-05-26
+7730819	Andorra la Vella Heliport	Andorra la Vella Heliport	ALV	42.5005	1.51712	S	AIRH	AD						0		1073	Europe/Andorra	2011-03-17
+7733010	Grau Roig	Grau Roig		42.53251	1.69923	P	PPL	AD						0		2204	Europe/Andorra	2011-04-07
+251130	WÄdÄ« Siqattah	Wadi Siqattah	Wadi Siqatta,Wadi Siqattah,WÄdÄ« Siqatta,WÄdÄ« Siqattah	25.6225	56.2225	H	WAD	AE		00				0		99	Asia/Dubai	2011-11-06
+286280	Suhaylah	Suhaylah	Suhaylah,Suheila	24.80388	56.19449	P	PPL	AE		02				0		238	Asia/Dubai	2011-11-06
+288716	WÄdÄ« al Bīḩ	Wadi al Bih	Wadi al Bih,WÄdÄ« al Bīḩ	25.77361	56.04389	H	WAD	AE		05				0		49	Asia/Dubai	2011-11-06
+290399	Zuyūd	Zuyud	Zuyud,Zuyūd	25.2	56.21667	L	TRB	AE		04				0		223	Asia/Dubai	2011-11-06
+290400	Z̧uwayhir	Zuwayhir	Dhawaihir,Duwaihir,Zuwayhir,Z̧uwayhir	23.28333	53.2	P	PPL	AE	AE	01				0		170	Asia/Dubai	2011-11-06
+290401	Z̧uwayhir	Zuwayhir	Dhuwaiher,Zuwayhir,Zuweihir,Z̧uwayhir	23.13916	53.6934	P	PPL	AE		01				0		104	Asia/Dubai	2011-11-06
+290402	Zuwayghir	Zuwayghir	Zuwaighar,Zuwayghir	24.08333	55.26667	H	WLL	AE	AE	01				0		167	Asia/Dubai	2011-11-06
+290403	WÄdÄ« Zuqaybah	Wadi Zuqaybah	Wadi Zuqaybah,WÄdÄ« Zuqaybah	25.40753	56.12592	H	WAD	AE		04				0		383	Asia/Dubai	2011-11-06
+290404	Ţawī Z̧ulaymah	Tawi Zulaymah	Dhalaima,Dulaima,Tawi Dhalaima,Tawi Dhelaimah,Tawi Dulaymah,Tawi Zeleimah,Tawi Zulaymah,Ţawī Dhalaima,Ţawī Dulaymah,Ţawī Z̧ulaymah	24.67083	55.53083	H	WLLQ	AE	AE	01				0		208	Asia/Dubai	2011-11-06
+290405	Z̧ulaymah	Zulaymah	Dulaymah,Zulaymah,Z̧ulaymah	24.65	55.53333	T	TRGD	AE	AE	03				0		230	Asia/Dubai	2011-11-06
+290406	Ruqq az Zukum	Ruqq az Zukum	Rak al Lakum,Rak az Zakum,Rig az Zakum,Ruqq az Zaqqum,Ruqq az Zaqqūm,Ruqq az Zukum	24.8	53.7	H	SHOL	AE	AE	01				0		-9999	Asia/Dubai	2011-11-06
+290407	Zubyah	Zubyah	Zabia,Zubyah	24.96667	55.06667	T	SAND	AE	AE	03				0		20	Asia/Dubai	2011-11-06
+290408	Jabal al ‘Azab	Jabal al `Azab	Jabal Azab,Jabal Zubb al `Azab,Jabal Zubb al ‘Azab,Jabal al `Azab,Jabal al ‘Azab	25.16139	55.83861	T	DUNE	AE		06				0		275	Asia/Dubai	2011-11-06
+290409	ZubÄrah	Zubarah	Zubara,Zubarah,ZubÄra,ZubÄrah	25.40487	56.35971	P	PPL	AE		04				0		-9999	Asia/Dubai	2011-11-06
+290410	ZirkÅ«h	Zirkuh	Az Zarqa',Az ZarqÄ’,Jazirat Zarka,Jazirat Zirku,Jaztal Zarakkuh,Jaztal ZarakkÅ«h,JazÄ«rat ZarkÄ,JazÄ«rat ZÄ«rkÅ«,Jezirat Zirko,JezÄ«rat Zirko,Zarakkawh,Zarakkuh,Zarqa,Zirko Island,Zirkuh,ZirkÅ«h	24.88417	53.07222	T	ISL	AE	AE	01				0		161	Asia/Dubai	2011-11-06
+290411	Zirđah	Zira`ah	Zira`ah,Zirđah	24.06667	55.53333	T	SAND	AE		01				0		182	Asia/Dubai	2011-11-06
+290412	Zirđ	Zira`	Zira`,Zirđ	23.76822	54.20993	H	WLL	AE		01				0		116	Asia/Dubai	2011-11-06
+290413	Sabkhat ZinÄd	Sabkhat Zinad	Sabkhat Zinad,Sabkhat ZinÄd	24.48293	55.22135	H	SBKH	AE		01				0		145	Asia/Dubai	2011-11-06
+290414	ZinÄd	Zinad	Zinad,ZinÄd	24.5	55.23333	T	SAND	AE		01				0		151	Asia/Dubai	2011-11-06
+290415	Zimmat Ḩalamah	Zimmat Halamah	Zimmat Halamah,Zimmat Ḩalamah	24.13333	55.48333	T	DUNE	AE		01				0		187	Asia/Dubai	2011-11-06
+290416	Zimmat ‘Ankah	Zimmat `Ankah	Zimmat `Ankah,Zimmat ‘Ankah	24.16667	55.33333	T	DUNE	AE		01				0		173	Asia/Dubai	2011-11-06
+290417	WÄdÄ« Zikt	Wadi Zikt	Wadi Zikt,WÄdÄ« Zikt	25.52536	56.31864	H	WAD	AE		04				0		140	Asia/Dubai	2011-11-06
+290418	Jabal Zikt	Jabal Zikt	Jabal Zikt	25.51435	56.31373	T	HLL	AE		04				0		285	Asia/Dubai	2011-11-06
+290419	Zikt	Zikt	Zikt	25.5118	56.32328	P	PPL	AE		04				0		41	Asia/Dubai	2011-11-06
+290420	Ziffah	Ziffah	Ziffah	25.02385	56.28954	V	CULT	AE		04				0		99	Asia/Dubai	2011-11-06
+290421	Zidm	Zidm	Zidm	25.48333	56.15	S	RUIN	AE		04				0		507	Asia/Dubai	2011-11-06
+290422	Zibara	Zibara	Zibara,Zibarah,ZibÄrah	24.61889	54.63417	T	DUNE	AE		01				0		11	Asia/Dubai	2011-11-06
+290423	Zi‘Äb	Zi`ab	Za`ab,Za‘Äb,Zi`ab,Zi‘Äb	25.03333	56.36667	L	TRB	AE		06				0		-9999	Asia/Dubai	2011-11-06
+290424	ZayqÄt	Zayqat	Zaiqat,Zayqat,ZayqÄt	23.36667	52.15	S	OILW	AE	AE	01				0		52	Asia/Dubai	2011-11-06
+290425	ZayqÄt	Zayqat	Zayqat,ZayqÄt	23.35	52.15	S	CMPQ	AE		01				0		53	Asia/Dubai	2011-11-06
+290426	MÄ«nÄ’ ZÄyid	Mina' Zayid	Mina Zayed,Mina' Zayid,MÄ«nÄ’ ZÄyid,Port Zayed,mynaʾ zayd,ميناء زايد	24.52518	54.38651	L	PRT	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290427	Ramlat Zayd	Ramlat Zayd	Ramlat Zaid,Ramlat Zayd	24.21667	55.2	T	DUNE	AE	AE	01				0		150	Asia/Dubai	2011-11-06
+290428	Bi’r Zayd	Bi'r Zayd	Bada` Zaid,Bada‘ Zaid,Bi'r Zayd,Bir Zaid,Bi’r Zayd	24.2	55.35	H	WLL	AE		01				0		168	Asia/Dubai	2011-11-06
+290429	Bid‘ Zayd	Bid` Zayd	Bada' Zaid,Bada’ Zaid,Bid` Zayd,Bid‘ Zayd	24.21667	55.11667	H	WLL	AE		01				0		118	Asia/Dubai	2011-11-06
+290430	Khawr Zawrah	Khawr Zawrah	Khawr Zawrah,Khawr Zora,Khawr az Zawra,Khawr az ZawrÄ	25.44417	55.47944	H	INLT	AE	AE	02				0		-9999	Asia/Dubai	2011-11-06
+290431	JazÄ«rat Zawrah	Jazirat Zawrah	Al-Zura,Al-ZÅ«rÄ,Jazirat Zawrah,JazÄ«rat Zawrah,Zora Island	25.43778	55.46472	T	ISL	AE	AE	00				0		8	Asia/Dubai	2011-11-06
+290432	Zawr	Zawr	Zawr	25.5275	55.59667	L	LCTY	AE		07				0		19	Asia/Dubai	2011-11-06
+290433	Z̧awÄhir	Zawahir	Dhawahir,DhawÄhir,Dhuwahir,Zawahir,Z̧awÄhir	24.33333	55.58333	L	TRB	AE	AE	01				0		208	Asia/Dubai	2011-11-06
+290434	ZÄrÅ«b	Zarub	Zarub,ZarÅ«b,ZÄrÅ«b	25.01806	56.21	V	CULT	AE	AE	06				0		406	Asia/Dubai	2011-11-06
+290435	ZarqÄ’	Zarqa'	Zarqa',ZarqÄ’	25.34622	55.87143	L	LCTY	AE		07				0		136	Asia/Dubai	2011-11-06
+290436	ZarÄrah	Zararah	Zararah,Zarrara,Zarrarah,Zarta,ZarÄrah	22.66667	54.13333	L	OILF	AE	AE	01				0		145	Asia/Dubai	2011-11-06
+290437	ZarÄrah	Zararah	Zararah,ZarÄrah	22.87043	53.83424	T	DPR	AE		01				0		51	Asia/Dubai	2011-11-06
+290438	ZarÄf	Zaraf	Zaraf,ZarÄf	23.79064	54.21261	H	WLL	AE		01				0		84	Asia/Dubai	2011-11-06
+290439	Qarn Zaqīq	Qarn Zaqiq	Qarn Bu Naidar,Qarn Zaqiq,Qarn Zaqīq,Qarn al Khabta	24.31463	52.59967	T	HLL	AE		01				0		129	Asia/Dubai	2011-11-06
+290440	Jabal az̧ Z̧annah	Jabal az Zannah	Az Zannah,Az̧ Z̧annah,Djebel Dhanna,Jabal Danna,Jabal Dhannah,Jabal Dhanni,Jabal az Zannah,Jabal az̧ Z̧annah	24.1709	52.59488	T	HLL	AE		01				0		111	Asia/Dubai	2011-11-06
+290441	Dawḩat az̧ Z̧annah	Dawhat az Zannah	Dawhat az Zannah,Dawḩat az̧ Z̧annah	24.15252	52.71794	H	BGHT	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290442	Z̧anḩah	Zanhah	Ghob,Zanhah,Z̧anḩah	25.5659	56.20217	P	PPL	AE		04				0		93	Asia/Dubai	2011-11-06
+290443	Ţawī Za‘lah	Tawi Za`lah	Tawi Za`lah,Ţawī Za‘lah	24.34848	55.43925	H	WLLQ	AE		01				0		178	Asia/Dubai	2011-11-06
+290444	Sayḩ Za‘lah	Sayh Za`lah	Sayh Za`lah,Sayḩ Za‘lah	24.33804	55.4268	T	TRGD	AE		01				0		179	Asia/Dubai	2011-11-06
+290445	Sayḩ Za‘lah	Sayh Za`lah	Sayh Za`lah,Sayḩ Za‘lah	24.2894	55.41804	T	TRGD	AE		01				0		175	Asia/Dubai	2011-11-06
+290446	Sayḩ Za‘lah	Sayh Za`lah	Sayh Za`lah,Sayḩ Za‘lah	24.28333	55.41667	T	TRGD	AE		01				0		163	Asia/Dubai	2011-11-06
+290447	ZÄkhir	Zakhir	Zakhir,ZÄkhir	24.11667	55.68333	S	HSE	AE		01				0		246	Asia/Dubai	2011-11-06
+290448	Ţawī ZĒid	Tawi Za'id	Tawi Za'id,Ţawī ZĒid	25.59556	55.88444	H	WLL	AE		07				0		35	Asia/Dubai	2011-11-06
+290449	Z̧ahūriyīn	Zahuriyin	Zahuriyin,Z̧ahūriyīn	26.05	56.13333	L	TRB	AE		05				0		680	Asia/Dubai	2011-11-06
+290450	Zaḩūm	Zahum	Zahum,Zaḩūm,Zihum	25.25	56.06667	L	TRB	AE	AE	00				0		285	Asia/Dubai	2011-11-06
+290451	ZahrÄnÄ«	Zahrani	Zahrani,ZahrÄnÄ«	23.21667	54.18333	H	WLL	AE		01				0		116	Asia/Dubai	2011-11-06
+290452	Bū Ţabr	Bu Tabr	Bu Tabr,Bū Ţabr,Dahar,Dhahar,Zahr,Z̧ahr	24.35944	52.60583	S	FRM	AE	AE	01				0		-9999	Asia/Dubai	2011-11-06
+290453	WÄdÄ« Z̧aḩah	Wadi Zahah	Wadi Zahah,WÄdÄ« Z̧aḩah	25.57036	56.20654	H	WAD	AE		04				0		76	Asia/Dubai	2011-11-06
+290454	Z̧afÄ«r	Zafir	Dafir,Dhafir,DhafÄ«r,Zafir,Z̧afÄ«r,á¸Äfir	23.12732	53.75439	P	PPL	AE		01				0		198	Asia/Dubai	2011-11-06
+290455	Z̧afīr	Zafir	Zafir,Z̧afīr	23.12027	53.75554	T	DPR	AE		01				0		82	Asia/Dubai	2011-11-06
+290456	Qarn Zabut	Qarn Zabut	Qarn Zabut	23.99048	54.07328	T	HLL	AE		01				0		20	Asia/Dubai	2011-11-06
+290457	Za‘bīl	Za`bil	Za`bil,Za‘bīl	25.22402	55.30452	S	PAL	AE		03				0		33	Asia/Dubai	2011-11-06
+290458	Å¢awÄ« Za‘ÄbÄ«yah	Tawi Za`abiyah	Tawi Za`abiyah,Å¢awÄ« Za‘ÄbÄ«yah	25.24917	55.88472	H	WLL	AE		06				0		128	Asia/Dubai	2011-11-06
+290459	Za‘Äb	Za`ab	Za`ab,Za‘Äb	25.68751	55.84732	L	TRB	AE		07				0		16	Asia/Dubai	2011-11-06
+290460	Ţawī Yudayyah	Tawi Yudayyah	Bir Yidayah,Tawi Yudayyah,Yidaiya,Ţawī Yudayyah	24.88904	55.78333	H	WLL	AE		06				0		203	Asia/Dubai	2011-11-06
+290461	Jabal YÄ«s	Jabal Yis	Jabal Yis,Jabal YÄ«s	25.35781	56.1109	T	MT	AE		04				0		683	Asia/Dubai	2011-11-06
+290462	Yinas	Yinas	Yinas	25.73158	56.13405	P	PPL	AE		05				0		1079	Asia/Dubai	2011-11-06
+290463	Yilak	Yilak	Yilak	23.06607	53.67436	T	DPR	AE		01				0		75	Asia/Dubai	2011-11-06
+290464	Yilaiyis	Yilaiyis	Yilaiyis	23.86184	55.43317	T	DUNE	AE		01				0		152	Asia/Dubai	2011-11-06
+290465	WÄdÄ« Yifan	Wadi Yifan	Wadi Yifan,WÄdÄ« Yifan	25.06066	56.30444	H	WAD	AE		04				0		38	Asia/Dubai	2011-11-06
+290466	Jabal Yibir	Jabal Yibir	Jabal Yibir	25.66752	56.13572	T	MT	AE		05				0	1527	1485	Asia/Dubai	2011-02-09
+290467	YÄsÄt ÅžaghÄ«rah	Yasat Saghirah	Yasat Saghirah,YÄsÄt ÅžaghÄ«rah	24.15912	52.0007	T	ISL	AE		01				0		6	Asia/Dubai	2011-11-06
+290468	Al YÄsÄt as Suflá	Al Yasat as Sufla	Al Yasat as Sufla,Al YÄsÄt as Suflá,Yasat Safli,YÄsÄt SaflÄ«	24.18675	51.9948	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290469	Al YÄsÄt al ‘UlyÄ	Al Yasat al `Ulya	Al Yasat al `Ulya,Al YÄsÄt al ‘UlyÄ,Yasat `Ali,YÄsÄt ‘AlÄ«	24.23662	52.01234	T	ISL	AE		01				0		10	Asia/Dubai	2011-11-06
+290470	Yarīrah	Yarirah	Yarirah,Yarīrah	22.8707	54.22656	T	DPR	AE		01				0		94	Asia/Dubai	2011-11-06
+290471	WÄdÄ« YamÄn	Wadi Yaman	Wadi Yaman,WÄdÄ« YamÄn	25.38499	56.32647	H	WAD	AE		06				0		53	Asia/Dubai	2011-11-06
+290472	Å¢awÄ« YÄl	Tawi Yal	Tawi Yal,Å¢awÄ« YÄl	25.42336	56.10511	H	WLL	AE		04				0		302	Asia/Dubai	2011-11-06
+290473	YÄkhÅ«n	Yakhun	Yakhun,YÄkhÅ«n	24.88795	55.72161	T	DUNE	AE		06				0		210	Asia/Dubai	2011-11-06
+290474	Ya‘rid	Ya`rid	Ya`rid,Yairad,Ya‘rid,Yerad	24.80324	55.03569	T	SAND	AE		01				0		28	Asia/Dubai	2011-11-06
+290475	WÄdÄ« Yaif	Wadi Yaif	Wadi Yaif,WÄdÄ« Yaif	24.93722	56.1025	H	WAD	AE		05				0		295	Asia/Dubai	2011-11-06
+290476	Yaif	Yaif	Yaif	24.94	56.09722	V	CULT	AE		05				0		530	Asia/Dubai	2011-11-06
+290477	Yahli	Yahli	Yahli	23.9879	54.70761	H	WLL	AE		01				0		106	Asia/Dubai	2011-11-06
+290478	Ţawī Yaḩfar	Tawi Yahfar	Tawi Yahfar,Ţawī Yaḩfar	25.02556	55.83361	H	WLL	AE		06				0		172	Asia/Dubai	2011-11-06
+290479	Sayḩ YÄfÅ«kh	Sayh Yafukh	Sayh Yafukh,Sayḩ YÄfÅ«kh	23.6458	55.49207	T	TRGD	AE		01				0		167	Asia/Dubai	2011-11-06
+290480	Yafnah	Yafnah	Yafnah,Yifnah	25.73424	56.09919	V	CULT	AE		05				0		766	Asia/Dubai	2011-11-06
+290481	Jazīrat Yabr	Jazirat Yabr	Jazirat Yabr,Jazīrat Yabr	24.31856	52.71939	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290482	Ţawī Yabbah	Tawi Yabbah	Tawi Yabbah,Ţawī Yabbah	25.35306	56.05111	H	WLL	AE		04				0		266	Asia/Dubai	2011-11-06
+290483	Ya‘alla	Ya`alla	Ya`alla,Ya‘alla	23.90646	54.99539	T	DUNE	AE		01				0		135	Asia/Dubai	2011-11-06
+290484	WÄdÄ« Ya’a	Wadi Ya'a	Wadi Ya'a,WÄdÄ« Ya’a	25.09611	56.26028	H	WAD	AE		04				0		224	Asia/Dubai	2011-11-06
+290485	Jabal Wutayd	Jabal Wutayd	Jabal Witeid,Jabal Wutaid,Jabal Wutayd	23.93615	52.29663	T	HLL	AE		01				0		40	Asia/Dubai	2011-11-06
+290486	Å¢awÄ« WushÄḩ	Tawi Wushah	Tawi Wishah,Tawi Wushah,TÄwÄ« Wishah,Wusha,Å¢awÄ« WushÄḩ	25.22834	55.91456	H	WLL	AE		06				0		143	Asia/Dubai	2011-11-06
+290487	WÄdÄ« Wurayyah	Wadi Wurayyah	Wadi Waraiya,Wadi Wurayyah,WÄdÄ« Waraiya,WÄdÄ« Wurayyah	25.38343	56.26224	H	WAD	AE		04				0		415	Asia/Dubai	2011-11-06
+290488	Wuqnah	Wuqnah	Wuqnah	23.0326	53.67397	T	DPR	AE		01				0		86	Asia/Dubai	2011-11-06
+290489	Wuḩaydah	Wuhaydah	Wuhaydah,Wuḩaydah	23.13333	53.93333	L	OAS	AE		01				0		98	Asia/Dubai	2011-11-06
+290490	Wuḩaydah	Wuhaydah	Wuhaida,Wuhaydah,Wuḩaydah	23.11764	53.77261	L	OAS	AE		01				0		194	Asia/Dubai	2011-11-06
+290491	WudibsÄ	Wudibsa	Wudibsa,WudibsÄ	23.73726	52.25189	T	SAND	AE		01				0		32	Asia/Dubai	2011-11-06
+290492	WÄdÄ« WiqÄ’	Wadi Wiqa'	Wadi Wiqa',Wadi al Amlah,WÄdÄ« WiqÄ’,WÄdÄ« al Amlaḩ	25.19143	56.04543	H	WAD	AE		05				0		210	Asia/Dubai	2011-11-06
+290493	Ţawī Widd	Tawi Widd	Tawi Wid,Tawi Widd,Ţawī Wid,Ţawī Widd	25.63296	56.01934	H	WLL	AE		05				0		108	Asia/Dubai	2011-11-06
+290494	Webb Rock	Webb Rock	Webb Rock	24.08437	52.2437	T	RK	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290495	WÄdÄ« Wayqah	Wadi Wayqah	Wadi Wayqah,WÄdÄ« Wayqah	25.32211	56.12861	H	WAD	AE		04				0		403	Asia/Dubai	2011-11-06
+290496	Watigh	Watigh	Watigh	23.68728	55.07948	T	DUNE	AE		01				0		139	Asia/Dubai	2011-11-06
+290497	Watauq	Watauq	Watauq	24.30306	54.91357	H	WLL	AE		01				0		70	Asia/Dubai	2011-11-06
+290498	Raml WÄsiÅ£	Raml Wasit	Raml Wasit,Raml WÄsiÅ£,Wasit,WÄsit	25.3425	55.47167	T	SAND	AE	AE	06				0		21	Asia/Dubai	2011-11-06
+290499	WÄsiÅ£	Wasit	Wasit,WÄsiÅ£	25.72194	55.87806	H	WLL	AE		05				0		32	Asia/Dubai	2011-11-06
+290500	WÄsiÅ£	Wasit	Wasit,WÄsiÅ£	25.60698	56.2548	P	PPL	AE		04				0		9	Asia/Dubai	2011-11-06
+290501	WÄsiÅ£	Wasit	Wasit,WÄsiÅ£	23.0036	53.44942	T	DPR	AE		01				0		176	Asia/Dubai	2011-11-06
+290502	Wasaţ	Wasat	Wasat,Wasaţ	22.80611	53.31093	H	WLL	AE		01				0		70	Asia/Dubai	2011-11-06
+290503	WarÄ«sÄn	Warisan	Warisan,WarÄ«sÄn	25.16744	55.40708	P	PPL	AE		03				0		20	Asia/Dubai	2011-11-06
+290504	Ţawī Waraqah	Tawi Waraqah	Tawi Waraqah,Ţawī Waraqah	24.77782	56.14991	H	WLL	AE		03				0		341	Asia/Dubai	2011-11-06
+290505	Khaţm Waraq	Khatm Waraq	Khatm Waraq,Khaţm Waraq	24.36667	55.45	T	DUNE	AE		01				0		158	Asia/Dubai	2011-11-06
+290506	WarÄq	Waraq	Waraq,WarÄq	22.97996	54.18115	T	DPR	AE		01				0		98	Asia/Dubai	2011-11-06
+290507	WÄdÄ« Wamm	Wadi Wamm	Wadi Wamm,WÄdÄ« Wamm	25.60512	56.22705	H	WAD	AE		04				0		42	Asia/Dubai	2011-11-06
+290508	Jabal Wamm	Jabal Wamm	Jabal Wamm	25.6021	56.19906	T	MT	AE		04				0		490	Asia/Dubai	2011-11-06
+290509	Wamm	Wamm	Wamm	25.60031	56.22838	P	PPL	AE		04				0		42	Asia/Dubai	2011-11-06
+290510	Walters Shoal	Walters Shoal	Walters Shoal	24.39524	52.47387	H	RF	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290511	Sayḩ al Wakrah	Sayh al Wakrah	Sayh al Wakrah,Sayḩ al Wakrah	24.59461	54.96476	T	TRGD	AE		01				0		57	Asia/Dubai	2011-11-06
+290512	Wahala	Wahala	Wahala,Wahlah,Waḩlah	24.90897	56.30316	P	PPL	AE		02				0		71	Asia/Muscat	2011-11-06
+290513	WÄdÄ« WahÄ«yah	Wadi Wahiyah	Wadi Wahiya,Wadi Wahiyah,WÄdÄ« Wahiya,WÄdÄ« WahÄ«yah	24.8183	56.15931	H	WAD	AE		02				0		282	Asia/Dubai	2011-11-06
+290514	Jabal Waḩīd	Jabal Wahid	Jabal Wahid,Jabal Waḩīd,The Hummock	24.31115	52.61141	T	HLL	AE		01				0		45	Asia/Dubai	2011-11-06
+290515	WÄḩid	Wahid	Wahid,WÄḩid	24.82444	56.08111	V	CULT	AE		02				0		339	Asia/Dubai	2011-11-06
+290516	WaharmÄ	Waharma	Waharma,WaharmÄ	22.75519	53.44766	T	DPR	AE		01				0		66	Asia/Dubai	2011-11-06
+290517	Wafd	Wafd	Qutuf,Quţūf,Waafit,Wafd,Wafid,Wefid	23.10262	53.71159	P	PPL	AE		01				0		84	Asia/Dubai	2011-11-06
+290518	Wad Wid	Wad Wid	Wad Wid	25.62515	56.01396	P	PPL	AE		05				0		17	Asia/Dubai	2011-11-06
+290519	WÄdÄ« ShÄ«	Wadi Shi	Wadi Shi,WÄdÄ« ShÄ«	25.35	56.31667	P	PPL	AE		06				0		77	Asia/Dubai	2011-11-06
+290520	Ḩabl WÄdÄ« ÅžafÄ	Habl Wadi Safa	Habl Wadi Safa,Wadi Safa,WÄdÄ« ÅžafÄ,Ḩabl WÄdÄ« ÅžafÄ	25.06092	55.27055	T	DUNE	AE		03				0		33	Asia/Dubai	2011-11-06
+290521	Wadhīl	Wadhil	Wadhil,Wadhīl,Wedheil,Wudhayl	23.0474	54.13332	P	PPL	AE		01				0		99	Asia/Dubai	2011-11-06
+290522	WÄdÄ« Wa‘bayn	Wadi Wa`bayn	Wadi Wa`bayn,WÄdÄ« Wa‘bayn	25.57261	56.12374	H	WAD	AE		04				0		400	Asia/Dubai	2011-11-06
+290523	Wa‘bayn	Wa`bayn	Wa'abain,Wa`bayn,Wa‘bayn,Wa’abain	25.57046	56.13536	P	PPL	AE		04				0		357	Asia/Dubai	2011-11-06
+290524	‘Uzlah	`Uzlah	`Uzalah,`Uzlah,‘Uzalah,‘Uzlah	22.99545	54.16649	T	DPR	AE		01				0		91	Asia/Dubai	2011-11-06
+290525	WÄdÄ« al ‘Uyaynah	Wadi al `Uyaynah	Wadi Ayeina,Wadi al `Uyaynah,WÄdÄ« Ayeina,WÄdÄ« al ‘Uyaynah	25.4742	56.18514	H	WAD	AE		04				0		176	Asia/Dubai	2011-11-06
+290526	GhaffÄt al ‘Uwayyirah	Ghaffat al `Uwayyirah	Ghaffat al `Uwayyirah,GhaffÄt al ‘Uwayyirah	24.46667	55.31667	T	DUNE	AE		01				0		152	Asia/Dubai	2011-11-06
+290527	Jabal al ‘Uwayyin	Jabal al `Uwayyin	Jabal al `Uwayyin,Jabal al ‘Uwayyin	25.36083	56.32395	T	MT	AE		06				0		583	Asia/Dubai	2011-11-06
+290528	Ţawī ‘Uwayyah	Tawi `Uwayyah	Tawi `Uwayyah,Ţawī ‘Uwayyah	25.58472	55.76028	H	WLL	AE		07				0		26	Asia/Dubai	2011-11-06
+290529	‘Uwayyah	`Uwayyah	`Uwayyah,‘Uwayyah	25.11278	55.30367	L	LCTY	AE		03				0		24	Asia/Dubai	2011-11-06
+290530	Qarn ‘Uwayşim	Qarn `Uwaysim	Qarn `Uwaysim,Qarn ‘Uwayşim	23.86667	53.43333	T	HLL	AE		01				0		70	Asia/Dubai	2011-11-06
+290531	‘Uwayşim	`Uwaysim	`Uwaisim,`Uwaysim,‘Uwaisim,‘Uwayşim	23.88333	53.38333	H	WLL	AE		01				0		66	Asia/Dubai	2011-11-06
+290532	Sabkhat ‘Uwayrah	Sabkhat `Uwayrah	Sabkhat `Uwayrah,Sabkhat ‘Uwayrah	23.95006	55.3068	H	SBKH	AE		01				0		131	Asia/Dubai	2011-11-06
+290533	NiqyÄn ‘Uwayrah	Niqyan `Uwayrah	Niqyan `Uwayrah,NiqyÄn ‘Uwayrah	23.96497	55.28962	T	DUNE	AE		01				0		181	Asia/Dubai	2011-11-06
+290534	Ţawī al ‘Uwaynīyah	Tawi al `Uwayniyah	Tawi al `Uwayniyah,Ţawī al ‘Uwaynīyah	25.31667	55.76667	H	WLL	AE		06				0		86	Asia/Dubai	2011-11-06
+290535	‘Uwayjir	`Uwayjir	`Uwayjir,‘Uwayjir	24.71667	55.16667	T	HLL	AE		03				0		87	Asia/Dubai	2011-11-06
+290536	‘Uwayj al ‘Abīd	`Uwayj al `Abid	`Uwayj al `Abid,‘Uwayj al ‘Abīd	24.11667	55.06667	T	DUNE	AE		01				0		119	Asia/Dubai	2011-11-06
+290537	‘Uwayj al ‘Abīd	`Uwayj al `Abid	`Uwayj al `Abid,‘Uwayj al ‘Abīd	24.13333	55.06667	T	DPR	AE		01				0		125	Asia/Dubai	2011-11-06
+290538	Jabal ‘Uwaybil	Jabal `Uwaybil	Jabal `Uwaybil,Jabal ‘Uwaybil	25.44021	55.96991	T	DUNE	AE		05				0		189	Asia/Dubai	2011-11-06
+290539	Jabal al ‘Uţayfah	Jabal al `Utayfah	Jabal al `Utayfah,Jabal al ‘Utayfah,Jabal al ‘Uţayfah	25.51024	56.02862	T	HLL	AE		04				0		278	Asia/Dubai	2011-11-06
+290540	‘Ushsh	`Ushsh	'Ishsh,Al Isha,Jazirat `Ish,Jazīrat ‘Ish,Ushsh,`Ashsh,`Ish,`Ushsh,‘Ashsh,‘Ish,‘Ushsh,’Ishsh	24.30488	52.87712	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290541	WÄdÄ« Ushayl	Wadi Ushayl	Wadi Ushail,Wadi Ushayl,WÄdÄ« Ushail,WÄdÄ« Ushayl	24.90515	56.08586	H	WAD	AE		05				0		287	Asia/Dubai	2011-11-06
+290542	WÄdÄ« ‘Usaylah	Wadi `Usaylah	Wadi `Usaylah,WÄdÄ« ‘Usaylah	25.69178	56.03764	H	WAD	AE		05				0		48	Asia/Dubai	2011-11-06
+290543	Sabkhat al ‘Urūq	Sabkhat al `Uruq	Sabkhat al `Uruq,Sabkhat al ‘Urūq	23.90811	54.18872	H	SBKH	AE		01				0		40	Asia/Dubai	2011-11-06
+290544	‘UrqÅ«b SibÄ’	`Urqub Siba'	Sbaa,Siba`,SibÄ‘,`Urqub Siba',‘UrqÅ«b SibÄ’	25.27824	55.47043	L	LCTY	AE		03				0		24	Asia/Dubai	2011-11-06
+290545	‘Urqūb Juwayzah	`Urqub Juwayzah	`Urqub Juwayzah,‘Urqūb Juwayzah	24.90586	55.4608	T	SCRP	AE		03				0		107	Asia/Dubai	2011-11-06
+290546	‘Urqūb Juwayza	`Urqub Juwayza	`Urqub Juwayza,‘Urqūb Juwayza	24.93333	55.46667	P	PPL	AE		03				0		102	Asia/Dubai	2011-11-06
+290547	‘Uraykah	`Uraykah	`Uraykah,‘Uraykah	24.36667	55.45	T	TRGD	AE		01				0		158	Asia/Dubai	2011-11-06
+290548	‘Uraykah	`Uraykah	'Ureika,`Uraykah,‘Uraykah,’Ureika	24.33333	55.46667	T	HLL	AE		01				0		199	Asia/Dubai	2011-11-06
+290549	WÄdÄ« ‘Urayf	Wadi `Urayf	Wadi `Urayf,WÄdÄ« ‘Urayf	25.49385	56.06209	H	WAD	AE		04				0		147	Asia/Dubai	2011-11-06
+290550	‘Uraybī	`Uraybi	Araibi,`Uraybi,‘Uraybī	25.78945	55.97665	P	PPLX	AE		05				0		20	Asia/Dubai	2011-11-06
+290551	‘Uqayyiq	`Uqayyiq	`Uqayyiq,‘Uqayyiq	24.53333	54.75	H	WLL	AE		01				0		33	Asia/Dubai	2011-11-06
+290552	‘Uqayyiq	`Uqayyiq	`Uqayyiq,‘Uqayyiq	24.52079	54.66317	T	DUNE	AE		01				0		7	Asia/Dubai	2011-11-06
+290553	Ţawī ‘Uqayr	Tawi `Uqayr	Tawi `Uqayr,Ţawī ‘Uqayr	23.78033	55.4708	H	WLL	AE		01				0		146	Asia/Dubai	2011-11-06
+290554	‘Uqayr	`Uqayr	`Uqayr,‘Uqayr	25.28302	56.36435	P	PPL	AE		06				0		23	Asia/Dubai	2011-11-06
+290555	‘UqaydÄt	`Uqaydat	Al `Uqaydat,Al ‘UqaydÄt,`Uqaydat,‘UqaydÄt	24.78556	55.78972	V	TREE	AE		06				0		275	Asia/Dubai	2011-11-06
+290556	Jabal ‘UqaybÄt	Jabal `Uqaybat	Jabal `Uqaybat,Jabal ‘UqaybÄt	25.1275	56.31278	T	HLL	AE		04				0		155	Asia/Dubai	2011-11-06
+290557	United Arab Emirates	United Arab Emirates	Ab'adnanya Arabskia Emiraty,Al Imarat al `Arabiyah al Muttahidah,Al ImÄrÄt al ‘ArabÄ«yah al Muttaḩidah,Aontas na nEimiriochtai Arabacha,Aontas na nÉimíríochtaí Arabacha,Apvienotie Arabu Emirati,Apvienotie ArÄbu EmirÄti,Araabia UEhendemiraadid,Araabia Ãœhendemiraadid,Arab Federation of Gulf States,Arab Gulf Federation,Arabiar Emirerri Batuak,Arabiar Emirrerri Batuak,Arabiemiirikunnat,Birlashgan Arab Amirliglar,Birlesik Arap Emirlikleri,BirleÅŸik Arap Emirlikleri,Cac Tieu Vuong quoc A-rap Thong nhat,Cac Tieu vuong quoc A rap Thong nhat,Các Tiểu VÆ°Æ¡ng quốc A-rập Thống nhất,Các Tiểu vÆ°Æ¡ng quốc Ả rập Thống nhất,Dawlat Ittihad al Imarat al `Arabiyah,Dawlat IttiḩÄd al ImÄrÄt al ‘ArabÄ«yah,De forente arabiske emirater,Dei sameinte arabiske emirata,Egyesuelt Arab Emiratus,Egyesuelt Arab Emirsegek,Egyesült Arab Emirátus,Egyesült Arab Emírségek,Emira Arab Ini,Emirados Arabes Unidos,Emirados Ãrabes Unidos,Emiraethau Arabaidd Unedig,Emiratele Arabe Unite,Emiratet Arabe te Bashkuara,Emiratet e Bashkuara Arabe,Emirati Arabi Uniti,Emirati Gharab Maqghuda,Emirati Għarab Maqgħuda,Emiratos Arabe Unite,Emiratos Arabes Unidos,Emiratos Arabes Unitos,Emiratos Ãrabes Unidos,Emiratos Ãrabes Unidos - الإمارات العربيّة المتّحدة,Emirats Arabes Unis,Emirats Arabis Units,Emirats Arabs Units,Emirats arabes units,Emirats arabos unis,Emirats Àrabs Units,Emiratus Arabi Uniti,Emirelezhiou Arab Unanet,Emirelezhioù Arab Unanet,Emiriah Arab Bersatu,Enomena Arabika Emirata,Falme za Kiarabu,Federation of Arab Emirates,Federation of Arabian Emirates,Federation of Arabian Gulf Emirates,Feriene Arabyske Emiraten,Foerenade Arabemiraten,Forenede Arabiske Emirater,Förenade Arabemiraten,Imaaraadka Carabta ee Midoobay,Jungtiniai Arabu Emyratai,Jungtiniai Arabų Emyratai,Lemiraens Pebaloel Larabaenik,Lemiräns Pebalöl Larabänik,Mirnisinen Erebi yen Yekbuyi,Muugano wa Falme za Nchi za Kiarabu,Mîrnişînên Erebî yên Yekbûyî,OAEH,Ob"edinjonnye Arabskie Ehmiraty,Obedineni Arabski Emirstva,Obedineti Arapski Emirati,Obuedinennye Arabskie Ehmiraty,Ovttastuvvan Arabaemirahtat,Ovttastuvvan Arábaemiráhtat,Pennternasedh Unys Arabek,Sameindu Emirrikini,Sameindu Emirríkini,Sameinudu arabisku furstadaemin,Sameinuðu arabísku furstadæmin,Spojene arabske emiraty,Spojené arabské emiráty,Trucial States,UAE,Ujedineni Arapski Emirati,Ujedinjeni Arapski Emirati,Uni Emirat Arab,Unio dels Emirats Arabs,Union of Arab Emirates,Unionita Araba Emirati,United Arab Emirates,Unió dels Emirats Àrabs,Unuigintaj Arabaj Emirlandoj,Unuigintaj Arabaj Emirlandos,UnuiÄintaj Arabaj Emirlandoj,UnuiÄintaj Arabaj Emirlandos,Vereenegt Arabesch Emirater,Vereenigte Araabsche Emiraten,Vereinegde Arabische Emirate,Vereinigte Arabische Emirate,Verenigde Arabiese Emirate,Verenigde Arabische Emiraten,Zdruzeni arabski emirati,Združeni arabski emirati,Zjednocene Arabske Emiraty,Zjednoczone Emiraty Arabskie,Zjednoćene Arabske Emiraty,a la bo lian he qiu zhang guo,aikkiya arapu amirakam,aikkiya arapu kuttatci,alab-emiliteu,arabetis gaertianebuli saamiroebi,arabu shou zhang guo lian bang,sanyukta arab rastram,sanyukta araba amirata,shrath xahrab xe mi rets,Èmirats arabes units,Èmirats arabos unis,Émirats Arabes Unis,Ηνωμένα ΑÏαβικά ΕμιÏάτα,Ðб'ÑÐ´Ð½Ð°Ð½Ñ‹Ñ ÐрабÑÐºÑ–Ñ Ð­Ð¼Ñ–Ñ€Ð°Ñ‚Ñ‹,Имороти Муттаҳидаи Ðраб,ОÐЭ,Об'єднані ÐрабÑькі Емірати,Обʼєднані ÐрабÑькі Емірати,Обединени ÐрабÑки ЕмирÑтва,Обединети ÐрапÑки Емирати,Объединенные ÐрабÑкие Эмираты,Объединённые ÐрабÑкие Эмираты,Уједињени ÐрапÑки Емирати,Õ„Õ«Õ¡ÖÕµÕ¡Õ¬ Ô±Ö€Õ¡Õ¢Õ¡Õ¯Õ¡Õ¶ Ô·Õ´Õ«Ö€Õ¡Õ©Õ¶Õ¥Ö€,×יחוד ×”×מירויות הערביות,×יחוד נסיכויות ערב,ברית ×”×מירויות הערביות,ئەرەب بىرلەشمە خەلىپىلىكى,الإمارات العربية المتحدة,الامارات العربية المتحدة,امارات متحده عربی,امارات متحدهٔ عربی,عمارات متحده ÛŒ عربی,متحده عرب امارات,Ù…ØªØ­Ø¯Û Ø¹Ø±Ø¨ امارات,ÜÜ¡ÜÜªÜ˜Ü¬Ü Ü¥ÜªÜ’ÜÜ¬Ü ÜšÜ•ÜܬÜ,संयà¥à¤•à¥à¤¤ अरब अमीरात,সংযà§à¦•à§à¦¤ আরব আমিরাত,à®à®•à¯à®•à®¿à®¯ அரப௠அமீரகமà¯,à®à®•à¯à®•à®¿à®¯ அரப௠கூடà¯à®Ÿà®¾à®Ÿà¯à®šà®¿,à´à´•àµà´¯ അറബൠഎമിരേറàµà´±àµà´•à´³àµâ€â€Œ,സംയàµà´•àµà´¤ അറബൠരാഷàµà´Ÿàµà´°à´‚,สหรัà¸à¸­à¸²à¸«à¸£à¸±à¸šà¹€à¸­à¸¡à¸´à¹€à¸£à¸•à¸ªà¹Œ,ສະຫະລັດອາຫລັບເອມິເລດ,ཡུ་ནའི་ཊེཊ་ཨ་ར བ་ཨེ་མི་རེཊསི,ཨ་རབ༠ཨི་མི་རཊ྄༠ཆིག་སྒྲིལ་རྒྱལ་à½à½–à¼,áƒáƒ áƒáƒ‘ეთის გáƒáƒ”რთიáƒáƒœáƒ”ბული ემირáƒáƒ¢áƒ”ბი,áƒáƒ áƒáƒ‘ეთის გáƒáƒ”რთიáƒáƒœáƒ”ბული სáƒáƒáƒ›áƒ˜áƒ áƒáƒ”ბი,የተባበሩት አረብ ኤáˆáˆ¬á‰µáˆµ,អáŸáž˜áž¸ážšáŸ‰áŸ‚ទអារ៉ាប់រួម,アラブ首長国連邦,阿拉伯è”åˆé…‹é•¿å›½,ì•„ëžì—미리트	24	54	A	PCLI	AE		00				4975593		12	Asia/Dubai	2011-11-06
+290558	Umm Åžayd	Umm Sayd	Umm Sayd,Umm Åžayd	22.94932	53.78481	T	DPR	AE		01				0		44	Asia/Dubai	2011-11-06
+290559	Umm Qays	Umm Qays	Umm Qays,Umm Qayz,Umm Qayz̧,Umm Qaz,Umm Qaz̧	22.92996	53.41542	H	WLL	AE		01				0		65	Asia/Dubai	2011-11-06
+290560	Umm Qays	Umm Qays	Umm Qays,Umm Qayz,Umm Qayz̧	22.92593	53.4138	T	DPR	AE		01				0		65	Asia/Dubai	2011-11-06
+290561	Umm JaÅŸÅŸÄr	Umm Jassar	Umm Jassar,Umm JaÅŸÅŸÄr,Umm Qasar,Umm Qassar,Umm QaÅŸÄr,Umm QaÅŸÅŸÄr,`Umm Gassar,‘Umm GaṣṣÄr	24.3914	52.77794	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290562	Umm Minhad	Umm Minhad	Umm Minhad	24.99359	55.3636	T	SAND	AE		03				0		40	Asia/Dubai	2011-11-06
+290564	Fasht Umm Jannah	Fasht Umm Jannah	Fasht Umm Janna,Fasht Umm Jannah,Janna	24.56959	51.5546	H	RF	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290565	Qarn Umm Ḩaşá	Qarn Umm Hasa	Qarn Umm Hasa,Qarn Umm Ḩaşá	25.11385	55.38284	T	DUNE	AE		03				0		49	Asia/Dubai	2011-11-06
+290566	Umm ḨafÄt	Umm Hafat	Umm Hafaf,Umm Hafat,Umm ḨafÄf,Umm ḨafÄt	23.84889	53.67889	H	WLL	AE	AE	01				0		76	Asia/Dubai	2011-11-06
+290567	Umm Khafat	Umm Khafat	Umm Hafaf,Umm Hafat,Umm Kafat,Umm Khafat,Umm ḨafÄf,Umm ḨafÄt	23.85336	53.69666	T	HLL	AE		01				0	95	89	Asia/Dubai	2011-11-06
+290568	Umm ḨÄbil	Umm Habil	Umm Habil,Umm ḨÄbil	22.99242	54.08237	T	DPR	AE		01				0		213	Asia/Dubai	2011-11-06
+290569	Ţawī Umm Ghuwayr	Tawi Umm Ghuwayr	Tawi Umm Ghuwayr,Ţawī Umm Ghuwayr	24.25341	55.0425	H	WLL	AE		01				0		125	Asia/Dubai	2011-11-06
+290570	Umm Ghaythah	Umm Ghaythah	Umm Gaita,Umm Ghaythah	24.10556	55.67583	T	DPR	AE	AE	01				0		244	Asia/Dubai	2011-11-06
+290571	JazÄ«rat Umm YafÄ«nah	Jazirat Umm Yafinah	Jazirat Umm Yafinah,JazÄ«rat Umm YafÄ«nah,Umm Fiyin,Umm FÄ«yÄ«n,jzyrt am yfynt,جزيرة أم ÙŠÙينة	24.48752	54.45009	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290572	Umm Dhuwaylah	Umm Dhuwaylah	Umm Dhuwaylah	24.45801	54.7294	H	WLL	AE		01				0		26	Asia/Dubai	2011-11-06
+290573	Umm Dhuwaylah	Umm Dhuwaylah	Umm Dhuwaylah	24.4692	54.723	T	SAND	AE		01				0		36	Asia/Dubai	2011-11-06
+290574	Umm Dasīs	Umm Dasis	Umm Dasis,Umm Dasīs,Umm Daysis,Umm Daysīs	23.81981	53.53876	L	GVL	AE		01				0		109	Asia/Dubai	2011-11-06
+290575	WÄdÄ« Umm ad Daqqayn	Wadi Umm ad Daqqayn	Wadi Umm Daqqayn,Wadi Umm ad Daqqayn,WÄdÄ« Umm Daqqayn,WÄdÄ« Umm ad Daqqayn	24.80556	56.20722	H	WAD	AE	AE	00				0		271	Asia/Dubai	2011-11-06
+290576	Kharaj Umm BiyÄt	Kharaj Umm Biyat	Kharaj Umm Bayat,Kharaj Umm BayÄt,Kharaj Umm Biyat,Kharaj Umm BiyÄt	25.15	55.4	H	WLL	AE	AE	03				0		7	Asia/Dubai	2011-11-06
+290577	Ţawī Umm Baraz	Tawi Umm Baraz	Tawi Umm Baraz,Ţawī Umm Baraz	22.94415	54.96468	H	WLL	AE		01				0		137	Asia/Dubai	2011-11-06
+290578	Sabkhat Umm Baraz	Sabkhat Umm Baraz	Sabkhat Umm Baraz	22.93458	55.03856	H	SBKH	AE		01				0		99	Asia/Dubai	2011-11-06
+290579	Umm Baraz	Umm Baraz	Umm Baraz	23.05	54.95	H	WLL	AE		01				0		125	Asia/Dubai	2011-11-06
+290580	Umm az Zumūl	Umm az Zumul	Umm Zamul,Umm Zamūl,Umm al Zamul,Umm az Zumul,Umm az Zumūl,Umm az-Zamul	22.70559	55.21205	H	WLL	AE		01				0		121	Asia/Dubai	2011-11-06
+290581	Umm Suqaym	Umm Suqaym	Umm Suqaym,Umm as Suqaym	25.15015	55.20587	P	PPLX	AE		03				0		26	Asia/Dubai	2011-11-06
+290582	Ḩaql Umm ash Shayf	Haql Umm ash Shayf	Haql Umm ash Shayf,Umm Shaif,Umm al-Shayf,Umm al-Sheif,Umm ash Sha'if,Umm ash ShÄ’if,Ḩaql Umm ash Shayf	25.2	53.2	L	OILF	AE	AE	01				0		-9999	Asia/Dubai	2011-11-06
+290583	Umm ar Raml	Umm ar Raml	Umm ar Raml	25.19658	55.41034	L	LCTY	AE		03				0		22	Asia/Dubai	2011-11-06
+290584	Umm ar Raml	Umm ar Raml	Umm ar Raml	25.23333	55.38333	T	DUNE	AE		03				0		29	Asia/Dubai	2011-11-06
+290585	WÄdÄ« Umm an NughÅ«l	Wadi Umm an Nughul	Wadi Umm an Nughul,WÄdÄ« Umm an NughÅ«l	25.34985	55.84553	H	WAD	AE		07				0		111	Asia/Dubai	2011-11-06
+290586	Ţawī Umm an Nughūl	Tawi Umm an Nughul	Tawi Umm an Nughul,Ţawī Umm an Nughūl	25.36333	55.87019	H	WLL	AE		07				0		92	Asia/Dubai	2011-11-06
+290587	Umm an NÄr	Umm an Nar	Jazirat Umm an Nar,JazÄ«rat Umm an NÄr,Umm an Nar,Umm an NÄr,`Umm al-Nar,am alnar,أم النار,‘Umm al-NÄr	24.44248	54.50948	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290588	Umm ‘Amīm	Umm `Amim	Jazirat Umm `min,Jazīrat Umm ‘mīn,Umm `Amim,Umm ‘Amīm	24.24128	53.39448	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290589	Umm al Qurayn	Umm al Qurayn	Umm Grain,Umm al Qurayn,`Umm Qrein,‘Umm Qrein	23.1	53.71667	P	PPL	AE	AE	01				0		181	Asia/Dubai	2011-11-06
+290590	Umm al Qurayn	Umm al Qurayn	Umm al Qurayn	23.09087	53.72477	L	OAS	AE		01				0		101	Asia/Dubai	2011-11-06
+290591	Umm al Qiţa‘	Umm al Qita`	Umm al Qita`,Umm al Qiţa‘	23.05522	53.53183	L	OAS	AE		01				0		127	Asia/Dubai	2011-11-06
+290592	Umm al Qird	Umm al Qird	Umm al Fa'iyah,Umm al FÄ’iyah,Umm al Qird	24.20527	54.79192	T	SAND	AE		01				0		66	Asia/Dubai	2011-11-06
+290593	Khawr Umm al Qaywayn	Khawr Umm al Qaywayn	Khawr Umm al Qaywayn	25.56	55.57972	H	BAY	AE		07				0		-9999	Asia/Dubai	2011-11-06
+290594	Umm al Qaywayn	Umm al Qaywayn	Um al Quweim,Umm al Qaiwain,Umm al Qawain,Umm al Qaywayn,Yumul al Quwain,am alqywyn,أم القيوين	25.56473	55.55517	P	PPLA	AE		07				44411		8	Asia/Dubai	2011-11-06
+290595	Umm al Qaywayn	Umm al Qaywayn	Oumm al Qaiwain,Oumm al Qaïwaïn,Skeikhdom of Umm al Qaiwain,Umm Al Quwain,Umm al Qawain,Umm al Qaywayn,Umm al Qiwain,am alqywyn,أم القيوين	25.5	55.75	A	ADM1	AE		07				56253		65	Asia/Dubai	2011-11-05
+290596	Kharīmat Umm al Muwayghir	Kharimat Umm al Muwayghir	Kharimat Umm al Muwayghir,Kharīmat Umm al Muwayghir	24.10497	54.80762	T	DPR	AE		01				0		74	Asia/Dubai	2011-11-06
+290597	Umm al Kurkum	Umm al Kurkum	Umm Kirkum,Umm Kurkum,Umm al Kurkum	24.39273	52.76497	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290598	Umm al Ḩişn	Umm al Hisn	Umm al Hisn,Umm al Ḩişn	23.01691	53.41646	S	FT	AE		01				0		118	Asia/Dubai	2011-11-06
+290599	Umm al Ḩişn	Umm al Hisn	Umm al Hisn,Umm al Ḩişn	23.01994	53.44238	T	DPR	AE		01				0		89	Asia/Dubai	2011-11-06
+290600	Umm al HiryÄn	Umm al Hiryan	Umm al Hiryan,Umm al HiryÄn	24.31722	55.79722	H	WLL	AE		01				0		280	Asia/Dubai	2011-11-06
+290601	Umm al Ḩaţab	Umm al Hatab	Jazirat Umm al Hatab,Jazīrat Umm al Ḩaţab,Umm al Halab Island,Umm al Hatab,Umm al Ḩaţab,Umm el Halab	24.21623	51.86389	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290602	Umm al Ghurūl	Umm al Ghurul	Umm al Ghurul,Umm al Ghurūl	23.84161	53.21056	L	GVL	AE		01				0		97	Asia/Dubai	2011-11-06
+290603	Umm al GhirbÄn	Umm al Ghirban	Umm al Gharban,Umm al GharbÄn,Umm al Ghirban,Umm al GhirbÄn	23.03841	53.55597	T	DPR	AE		01				0		71	Asia/Dubai	2011-11-06
+290604	WÄdÄ« Umm al GhÄt	Wadi Umm al Ghat	Wadi Umm al Ghat,WÄdÄ« Umm al GhÄt	24.87975	56.2778	H	WAD	AE		05				0		89	Asia/Dubai	2011-11-06
+290605	Jabal Umm al FurfÄr	Jabal Umm al Furfar	Jabal Umm al Farfar,Jabal Umm al FarfÄr,Jabal Umm al Furfar,Jabal Umm al FurfÄr	25.12448	56.22754	T	MT	AE		04				0		735	Asia/Dubai	2011-11-06
+290606	Umm al Birak	Umm al Birak	Umm al Barak,Umm al BarÄk,Umm al Birak,Umm al-Berak	24.56699	54.58394	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290607	Umm al BanÄdÄ«q	Umm al Banadiq	Umm al Banadig,Umm al Banadiq,Umm al BanÄdÄ«q	24.08705	55.27527	H	WLL	AE		01				0		151	Asia/Dubai	2011-11-06
+290608	Umm al AshÅ£Än	Umm al Ashtan	Umm al Ashtan,Umm al AshÅ£Än	23.58333	52.48333	H	WLL	AE		01				0		75	Asia/Dubai	2011-11-06
+290609	Umm al AshÅ£Än	Umm al Ashtan	Umm al Ashtan,Umm al AshÅ£Än,Umm al Lishtan,Umm al LishtÄn	23.76667	52.66667	T	SAND	AE	AE	01				0		79	Asia/Dubai	2011-11-06
+290610	Umm al AshÅ£Än	Umm al Ashtan	Umm al Ashtan,Umm al AshÅ£Än,Umm al Ishtan,Umm al IshÅ£Än	23.65248	52.45047	S	CMPQ	AE		01				0		64	Asia/Dubai	2011-11-06
+290611	Umm ‘Alaqah	Umm `Alaqah	Um `Alaqa,Um ‘Alaqa,Umm `Alaqah,Umm ‘Alaqah	24.0024	53.39048	T	HLL	AE		01				0		23	Asia/Dubai	2011-11-06
+290612	Ruqq Umm al ‘Anbar	Ruqq Umm al `Anbar	Ruqq Um el Umber,Ruqq Umm al `Anbar,Ruqq Umm al ‘Anbar,Umbar	24.60999	51.8836	H	SHOL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290613	Sabkhat Umm al ‘Alqah	Sabkhat Umm al `Alqah	Sabkhat Umm al Alqa,Sabkhat Umm al `Alqah,Sabkhat Umm al ‘Alqah	23.63234	54.85535	H	SBKH	AE		01				0		115	Asia/Dubai	2011-11-06
+290614	Umm al ‘Alqah	Umm al `Alqah	Umm al Alqa,Umm al `Alqah,Umm al ‘Alqah	23.68333	54.83333	H	WLL	AE		01				0		105	Asia/Dubai	2011-11-06
+290615	Umm al Abyaḑ	Umm al Abyad	Umm al Abyad,Umm al Abyaḑ	25.09157	55.2481	H	WLLS	AE		03				0		26	Asia/Dubai	2011-11-06
+290616	Umm ad Dalkh	Umm ad Dalkh	Umm Addalkh,Umm ad Dalkh,Umm al Dalkh	24.53655	54.14598	L	OILF	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290617	Ghuyūţ ‘Ulayyah	Ghuyut `Ulayyah	Ghuyut `Ulayyah,Ghuyūţ ‘Ulayyah	23.85	54.73333	T	DPR	AE		01				0		96	Asia/Dubai	2011-11-06
+290618	‘Ūd Umm KhÄlid	`Ud Umm Khalid	`Ud Umm Khaldi,`Ud Umm Khalid,‘Ūd Umm KhaldÄ«,‘Ūd Umm KhÄlid	25.1375	55.39861	V	TREE	AE		03				0		35	Asia/Dubai	2011-11-06
+290619	Ra’s al ‘Udayd	Ra's al `Udayd	Al Odaid,Al `Udayd,Al ‘Udayd,Ra's al `Udayd,Ra’s al ‘Udayd	24.61667	51.43333	T	PT	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290620	Khawr al ‘Udayd	Khawr al `Udayd	Khawr al Wutayd,Khawr al Wuţayd,Khawr al `Udayd,Khawr al `Uwayd,Khawr al ‘Udayd,Khawr al ‘Uwayd,Khor al Odaid,Khor al Odeid,Khor al Ubeid,Khor al Wutaid,Khor al `Udaid,Khor al `Udeid,Khor al ‘Udaid,Khor al ‘Udeid	24.6	51.4	H	INLT	AE	AE	06				0		-9999	Asia/Dubai	2011-11-06
+290622	‘Ūd al Maţīnah	`Ud al Matinah	Aud al Matina,Awad al Matinah,Matina,Matinah,Matīna,Maţīnah,`Ud al Matinah,‘Ūd al Maţīnah	25.25583	55.44611	H	WLLS	AE		03				0		24	Asia/Dubai	2011-11-06
+290623	‘Ūd al Maţīnah	`Ud al Matinah	`Ud al Matinah,‘Ūd al Maţīnah	25.23815	55.4741	L	LCTY	AE		03				0		39	Asia/Dubai	2011-11-06
+290624	‘Ūd al BayḑÄ’	`Ud al Bayda'	Ud al Beida,`Ud al Bayda',‘Ūd al BayḑÄ’	25.01625	55.45533	P	PPL	AE		03				0		95	Asia/Dubai	2011-11-06
+290625	‘Ūd al Atham	`Ud al Atham	`Ud al Atham,‘Ūd al Atham	25.15	55.71667	V	TREE	AE		06				0		124	Asia/Dubai	2011-11-06
+290626	‘Ubaydil	`Ubaydil	`Ibeidil,`Ubaydhil,`Ubaydil,‘Ibeidil,‘Ubaydhil,‘Ubaydil	24.51667	51.33333	H	WLL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290627	WÄdÄ« Å¢uwayyah	Wadi Tuwayyah	Wadi Tuwayyah,WÄdÄ« Å¢uwayyah	24.25722	55.68778	H	WAD	AE		01				0		267	Asia/Dubai	2011-11-06
+290628	Sayḩ Ţuwayyah	Sayh Tuwayyah	Sayh Tuwayyah,Sayḩ Ţuwayyah	24.39139	55.8275	T	PLN	AE		00				0		327	Asia/Dubai	2011-11-06
+290629	Sayḩ Tuwaysah	Sayh Tuwaysah	Sayh Tuways,Sayh Tuwaysah,Sayḩ Tuways,Sayḩ Tuwaysah	24.29278	55.505	T	DPR	AE	AE	01				0		195	Asia/Dubai	2011-11-06
+290630	Ţawī Ţuwayli‘	Tawi Tuwayli`	Tawi Tawaila,Tawi Tuwayli`,Tawi Tuwayyilah,Ţawī Tawaila,Ţawī Ţuwayli‘,Ţawī Ţuwayyilah	24.97679	55.7508	H	WLL	AE		06				0		171	Asia/Dubai	2011-11-06
+290631	Kharīmat Ţuwaylah	Kharimat Tuwaylah	Kharimat Tawaila,Kharimat Tuwaylah,Kharmat Tuwaylah,Kharmat Ţuwaylah,Kharīmat Ţuwaylah	24.1361	54.82632	T	TRGD	AE		01				0		92	Asia/Dubai	2011-11-06
+290632	Tuwayhil	Tuwayhil	Tuwayhil	23.4594	53.29148	H	WLL	AE		01				0		160	Asia/Dubai	2011-11-06
+290633	Jabal Tu‘ūs	Jabal Tu`us	Jabal Tu`us,Jabal Tus,Jabal Tu‘ūs	25.29958	56.11017	T	HLL	AE		04				0		550	Asia/Dubai	2011-11-06
+290634	Ţurayf	Turayf	Turayf,Ţurayf	23.0742	53.80161	T	DPR	AE		01				0		70	Asia/Dubai	2011-11-06
+290635	Ţunayq	Tunayq	Tanaij,Tanaiq,Tunaij,Tunayq,Ţunayq	25.86476	56.04169	L	TRB	AE		05				0		435	Asia/Dubai	2011-11-06
+290636	Tunayq	Tunayq	Tanaij,Tanaiq,Tunaij,Tunayq	25.26139	55.94944	L	TRB	AE		06				0		156	Asia/Dubai	2011-11-06
+290637	‘Aqabat Tūmaytayn	`Aqabat Tumaytayn	`Aqabat Tumaytayn,‘Aqabat Tūmaytayn	25.47656	56.35033	T	PASS	AE		04				0		17	Asia/Dubai	2011-11-06
+290638	Ţawī Ţubūl	Tawi Tubul	Tawi Tubul,Ţawī Ţubūl	24.22687	55.03957	H	WLL	AE		01				0		133	Asia/Dubai	2011-11-06
+290639	Jabal Ţubayqah	Jabal Tubayqah	Jabal Tubayqah,Jabal Ţubayqah	24.07825	56.00668	T	HLL	AE		01				0		429	Asia/Dubai	2011-11-06
+290640	Trucial Coast	Trucial Coast	Al Sahil,Al SÄḩil,Arab Coast,Pirate Coast,Sahil `Oman,Sahil `Uman,Sahil as Sulh al Bahri,Shamal,ShamÄl,SÄhil ‘OmÄn,SÄhil ‘UmÄn,SÄḩil aÅŸ Åžulḩ al BaḩrÄ«,Trucial Coast,Trucial Oman,Trucial `Uman,Trucial ‘Uman	24	53	L	RGN	AE	AE	00				0		37	Asia/Dubai	2011-11-06
+290641	Tina	Tina	Tina	23.81243	55.37423	T	DUNE	AE		01				0		182	Asia/Dubai	2011-11-06
+290642	Sabkhat Thuwaymah	Sabkhat Thuwaymah	Sabkhat Thuwaymah	24.0275	55.66806	L	SALT	AE		01				0		227	Asia/Dubai	2011-11-06
+290643	‘UrqÅ«b ThurayyÄ	`Urqub Thurayya	`Urqub Thurayya,‘UrqÅ«b ThurayyÄ	24.9	55.46667	T	DUNE	AE		03				0		139	Asia/Dubai	2011-11-06
+290644	Ţawī ath Thuqbah	Tawi ath Thuqbah	Tawi Thuqbah,Tawi ath Thuqbah,Ţawī Thuqbah,Ţawī ath Thuqbah	25.34611	55.84722	H	WLL	AE	AE	07				0		111	Asia/Dubai	2011-11-06
+290645	Ţawī Thuqaybah	Tawi Thuqaybah	Tawi Thuqaybah,Ţawī Thuqaybah	24.94829	55.81388	H	WLL	AE		06				0		190	Asia/Dubai	2011-11-06
+290646	Ţawī Thuqaybah	Tawi Thuqaybah	Tawi Thuqaybah,Ţawī Thuqaybah	23.55	55.28333	H	WLL	AE		01				0		181	Asia/Dubai	2011-11-06
+290647	Sabkhat Thuqaybah	Sabkhat Thuqaybah	Sabkhat Thuqaybah	23.58997	55.19693	H	SBKH	AE		01				0		114	Asia/Dubai	2011-11-06
+290648	Khawr Thumayrīyah	Khawr Thumayriyah	Khawr Thumayriyah,Khawr Thumayrīyah,Themairiyyah	24.15274	53.0014	H	CHNM	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290649	Thumayrīyah	Thumayriyah	Themairiyya,Themeiriyyah,Thimairiyah,Thumayriyah,Thumayrīyah	24.15033	53.0169	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290650	Thubaybah	Thubaybah	Thubaybah	23.68333	54.61667	H	WLL	AE		01				0		117	Asia/Dubai	2011-11-06
+290651	WÄdÄ« Thayb	Wadi Thayb	Wadi Thayb,Wadi Theeb,WÄdÄ« Thayb	25.24914	56.35124	H	WAD	AE		04				0		96	Asia/Dubai	2011-11-06
+290652	Jabal Thayb	Jabal Thayb	Jabal Thaib,Jabal Thayb	25.25854	56.31202	T	MT	AE		04				0		386	Asia/Dubai	2011-11-06
+290653	Thawrīyah	Thawriyah	Thawriyah,Thawrīyah	23.02531	53.9109	T	DPR	AE		01				0		68	Asia/Dubai	2011-11-06
+290654	Thawrīyah	Thawriyah	Thawriyah,Thawrīyah	22.99797	53.76585	T	DPR	AE		01				0		60	Asia/Dubai	2011-11-06
+290655	Khabb ath Thawr	Khabb ath Thawr	Khabb ath Thawr	24.2597	54.64343	T	SAND	AE		01				0		17	Asia/Dubai	2011-11-06
+290656	WÄdÄ« ThawbÄn	Wadi Thawban	Wadi Thauban,Wadi Thawban,WÄdÄ« Thauban,WÄdÄ« ThawbÄn	25.28582	56.04058	H	WAD	AE		04				0		225	Asia/Dubai	2011-11-06
+290657	Jabal ThawbÄn	Jabal Thawban	Jabal Thawban,Jabal ThawbÄn	25.32944	56.10306	T	MT	AE		04				0		726	Asia/Dubai	2011-11-06
+290658	TharwÄnÄ«yah	Tharwaniyah	Tharwaniyah,Tharwaniyya,Tharwaniyyah,TharwÄniyya,TharwÄniyyah,TharwÄnÄ«yah	23.11088	54.01572	P	PPL	AE		01				0		191	Asia/Dubai	2011-11-06
+290659	Thara’awn	Thara'awn	Thara'awn,Thara’awn	22.91004	54.32293	T	DPR	AE		01				0		162	Asia/Dubai	2011-11-06
+290660	Jabal ThÄnÄ«	Jabal Thani	Jabal Thanais,Jabal Thani,Jabal ThÄnÄ«	25.02249	55.78912	T	HLL	AE		06				0		267	Asia/Dubai	2011-11-06
+290661	Thamūd	Thamud	Thamud,Thamūd	24.78333	55.28333	T	TRGD	AE		03				0		88	Asia/Dubai	2011-11-06
+290662	Barqat ThÄmir	Barqat Thamir	Barqat Thamir,Barqat ThÄmir	23.79619	52.66627	T	DUNE	AE		01				0		74	Asia/Dubai	2011-11-06
+290663	Bid‘at ThallÄb	Bid`at Thallab	Bid`at Thallab,Bid`ath Thalab,Bid‘at ThallÄb,Bid‘ath ThalÄb	23.83333	53.3	H	WLL	AE		01				0		89	Asia/Dubai	2011-11-06
+290664	Ţayyibah	Tayyibah	Taiyibah,Tayibah,Tayiban,Tayyibah,Ţayyibah	25.41228	56.17075	P	PPL	AE		04				0		430	Asia/Dubai	2011-11-06
+290665	Å¢awÄ« Å¢ayy	Tawi Tayy	Tawi Tai,Tawi Tayy,Å¢awÄ« Å¢ayy,Å¢ÄwÄ« Tai	25.23333	55.55	H	WLL	AE		03				0		42	Asia/Dubai	2011-11-06
+290666	Ţawī Ţayrī	Tawi Tayri	Tawi Tayri,Ţawī Ţayrī	25.45472	55.60611	H	WLL	AE		07				0		16	Asia/Dubai	2011-11-06
+290667	Ḩadd aţ Ţayr	Hadd at Tayr	Hadd at Tayr,Ḩadd aţ Ţayr	24.3713	51.83258	H	RF	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290668	Nadd aÅ£ Å¢arÅ«sh	Nadd at Tarush	Nadd Tawsha,Nadd Tawshah,Nadd at Tarush,Nadd aÅ£ Å¢arÅ«sh,Nadd Å¢awshah,Nadd Å¢awshÄ	25.14929	55.37296	T	DUNE	AE		03				0		22	Asia/Dubai	2011-11-06
+290669	WÄdÄ« Å¢awÄ«yayn	Wadi Tawiyayn	Wadi Tawiyayn,WÄdÄ« Å¢awÄ«yayn	25.5575	56.07694	H	WAD	AE		04				0		185	Asia/Dubai	2011-11-06
+290670	Ţawīyayn	Tawiyayn	Tawiyain,Tawiyayn,Tawyayn,Tuwiyain,Ţawīyayn	25.55778	56.07667	H	WLL	AE	AE	04				0		185	Asia/Dubai	2011-11-06
+290671	Ţawī Bin ‘Asīl	Tawi Bin `Asil	Tawi Bin `Asil,Ţawī Bin ‘Asīl	24.20368	54.6095	L	LCTY	AE		01				0		31	Asia/Dubai	2011-11-06
+290672	Ţawī Bid‘ Sa‘īd	Tawi Bid` Sa`id	Tawi Bid` Sa`id,Ţawī Bid‘ Sa‘īd	24.45084	54.74048	T	SAND	AE		01				0		33	Asia/Dubai	2011-11-06
+290673	WÄdÄ« TawÄh	Wadi Tawah	Wadi Tawah,WÄdÄ« TawÄh	24.98978	56.1243	H	WAD	AE		05				0		295	Asia/Dubai	2011-11-06
+290674	Jabal TawÄh	Jabal Tawah	Jabal Tawah,Jabal TawÄh	24.99639	56.12398	T	MT	AE		05				0		306	Asia/Dubai	2011-11-06
+290675	Ţawī Tasharawīyah	Tawi Tasharawiyah	Tawi Tasharawiyah,Ţawī Tasharawīyah	25.28942	55.89526	H	WLL	AE		06				0		105	Asia/Dubai	2011-11-06
+290676	Ţarūqah	Taruqah	Taruqah,Ţarūqah	23.02725	53.88404	T	DPR	AE		01				0		69	Asia/Dubai	2011-11-06
+290677	Ţarūfah	Tarufah	Tarufa,Tarufah,Ţarūfah	23.08936	53.83218	L	OAS	AE		01				0		86	Asia/Dubai	2011-11-06
+290678	Ţawī Tarish	Tawi Tarish	Tawi Tarish,Ţawī Tarish	23.58333	54.61667	H	WLL	AE		01				0		140	Asia/Dubai	2011-11-06
+290679	Ţarīqat Ja‘d	Tariqat Ja`d	Tariqat Ja`d,Ţarīqat Ja‘d	25.52852	56.15144	P	PPL	AE		04				0		357	Asia/Dubai	2011-11-06
+290680	Å¢arÄ«f KalbÄ	Tarif Kalba	Tarif Kalba,Å¢arÄ«f KalbÄ	25.0695	56.33115	P	PPL	AE		06				0		30	Asia/Dubai	2011-11-06
+290681	Ţarīf	Tarif	Al-Tarif,Al-Tarīf,At Tarif,At Turayf,Aţ Ţarīf,Aţ Ţurayf,Taraif,Tarif,Ţarīf	24.05399	53.76347	P	PPL	AE		01				0		24	Asia/Dubai	2011-11-06
+290682	Ţarīf	Tarif	Tarif,Ţarīf	24.03333	53.76667	T	HLL	AE		01				0		13	Asia/Dubai	2011-11-06
+290683	Qurayn aţ Ţarib	Qurayn at Tarib	Qurayn at Tarib,Qurayn aţ Ţarib	25.09414	55.81301	T	HLL	AE		06				0		216	Asia/Dubai	2011-11-06
+290684	Sayḩ Å¢arfÄ’	Sayh Tarfa'	Sayh Tarfa',Sayḩ Å¢arfÄ’	23.21358	55.18286	T	DPR	AE		01				0		154	Asia/Dubai	2011-11-06
+290685	MushÄsh Å¢arfÄ’	Mushash Tarfa'	Mushash Tarfa',MushÄsh Å¢arfÄ’	24.04622	51.69787	H	WLL	AE		01				0		34	Asia/Dubai	2011-11-06
+290686	Qarn at Tarb	Qarn at Tarb	Qarn al Tarab,Qarn at Tarb	24.45094	55.67408	T	HLL	AE		01				0		306	Asia/Dubai	2011-11-06
+290687	Ţaraq	Taraq	Taraq,Tereg,Ţaraq	23.11656	53.60697	P	PPL	AE		01				0		181	Asia/Dubai	2011-11-06
+290688	Å¢arÄhÄ«f	Tarahif	Tarahif,Å¢arÄhÄ«f	22.91769	53.33204	T	DPR	AE		01				0		179	Asia/Dubai	2011-11-06
+290689	WÄdÄ« aÅ£ Å¢araf	Wadi at Taraf	Wadi at Taraf,WÄdÄ« aÅ£ Å¢araf	25.41486	56.32874	H	WAD	AE		04				0		69	Asia/Dubai	2011-11-06
+290690	WÄdÄ« Tarabat	Wadi Tarabat	Wadi Tarabat,WÄdÄ« Tarabat	24.10167	55.71444	H	WAD	AE		01				0		235	Asia/Dubai	2011-11-06
+290691	Tall FÄḩah	Tall Fahah	Tall Fahah,Tall FÄḩah	23.95216	52.35299	L	LCTY	AE		01				0		14	Asia/Dubai	2011-11-06
+290692	Ḩadd aţ Ţallah	Hadd at Tallah	Hadd al Tahlei,Hadd at Tahli,Hadd at Tallah,Hadd at Thalei,Ḩadd at Tahlī,Ḩadd aţ Ţallah	24.66667	54.55	H	SHOL	AE	AE	01				0		-9999	Asia/Dubai	2011-11-06
+290693	Dawḩat Tallah	Dawhat Tallah	Dawhat Tallah,Dawhat Tullah,Dawḩat Tallah,Dawḩat Tullah	24.42605	51.3286	H	BGHT	AE		01				0		1	Asia/Dubai	2011-11-06
+290694	Dawḩat Å¢allÄb	Dawhat Tallab	Dawhat Talab,Dawhat Tallab,Dawhat an Nakhlah,Dawḩat an Nakhlah,Dawḩat Å¢alab,Dawḩat Å¢allÄb,Dohat Tallab,Dohat Tullab,Dohat TullÄb,Dohat an Nakhala,Dohat ṬallÄb,Duhat an Nakhalah	24.2708	51.64849	H	BAY	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290695	Sydney Hill	Sydney Hill	Sydney Hill	24.33333	52.6	T	HLL	AE		01				0		124	Asia/Dubai	2011-11-06
+290696	NaqÄ SuwayÅ£ah	Naqa Suwaytah	Naqa Suwaytah,NaqÄ SuwayÅ£ah	24.34939	55.59034	T	DUNE	AE		01				0		247	Asia/Dubai	2011-11-06
+290697	Å¢awÄ« SuwayḩÄn	Tawi Suwayhan	Tawi Suwayhan,Å¢awÄ« SuwayḩÄn	24.43584	55.25248	H	WLL	AE		01				0		123	Asia/Dubai	2011-11-06
+290698	Sayḩ SuwayḩÄn	Sayh Suwayhan	Sayh Suwayhan,Sayḩ SuwayḩÄn	24.44981	55.28275	H	WAD	AE		01				0		158	Asia/Dubai	2011-11-06
+290699	Ramlat SuwayḩÄn	Ramlat Suwayhan	Ramlat Suwaihan,Ramlat Suwayhan,Ramlat SuwayḩÄn	24.46087	55.26387	T	DUNE	AE		01				0		149	Asia/Dubai	2011-11-06
+290700	Ra’s Suwayfah	Ra's Suwayfah	Ra's Suwayfah,Ra’s Suwayfah	25.59594	56.35239	T	PT	AE		04				0		-9999	Asia/Dubai	2011-11-06
+290701	Farīq Suwayfah	Fariq Suwayfah	Fariq Suwayfah,Farīq Suwayfah	25.59256	56.34525	S	CMP	AE		04				0		60	Asia/Dubai	2011-11-06
+290702	Suwayfah	Suwayfah	Suwayfah	25.59043	56.36261	L	LCTY	AE		04				0		-9999	Asia/Dubai	2011-11-06
+290703	SuwaydÄn	Suwaydan	Suwaydan,SuwaydÄn	25.12436	55.7975	L	AREA	AE		06				0		138	Asia/Dubai	2011-11-06
+290704	WÄdÄ« SuwaydÄ’	Wadi Suwayda'	Wadi Suwayda',WÄdÄ« SuwaydÄ’	24.45696	55.54523	T	TRGD	AE		01				0		261	Asia/Dubai	2011-11-06
+290705	Ţawī SuwaydĒ	Tawi Suwayda'	Tawi Suwayda',Ţawī SuwaydĒ	25.14194	55.29972	H	WLL	AE		03				0		24	Asia/Dubai	2011-11-06
+290706	SuwaydÄ’	Suwayda'	Suwaida,Suwayda',SuwaydÄ’	25.11667	55.3	L	LCTY	AE		03				0		6	Asia/Dubai	2011-11-06
+290707	Sut	Sut	Sut	23.71667	54.53333	H	WLL	AE		01				0		133	Asia/Dubai	2011-11-06
+290708	Surayţ	Surayt	Serait,Surayt,Surayţ	23.12132	53.95075	L	OAS	AE		01				0		94	Asia/Dubai	2011-11-06
+290709	WÄdÄ« SÅ«r	Wadi Sur	Wadi Sur,WÄdÄ« SÅ«r	25.08333	56.36667	H	WAD	AE		06				0		-9999	Asia/Dubai	2011-11-06
+290710	Şūr	Sur	Sur,Şūr	25.09406	56.34827	P	PPL	AE		06				0		23	Asia/Dubai	2011-11-06
+290711	Suqayyah	Suqayyah	Suqayyah	24.57218	55.55891	L	LCTY	AE		01				0		253	Asia/Dubai	2011-11-06
+290712	Sunayyim	Sunayyim	Sunayyim	23.96236	55.40653	T	DUNE	AE		01				0		145	Asia/Dubai	2011-11-06
+290713	Baḩr Sunayţ	Bahr Sunayt	Bahr Sunayt,Baḩr Sunayţ	25.61667	56.25	H	WAD	AE		00				0		7	Asia/Dubai	2011-11-06
+290714	Sumbrair	Sumbrair	Sumbrair,Sumbrayir,Åžumbrayir	25.60082	56.2844	P	PPL	AE		04				0		21	Asia/Dubai	2011-11-06
+290715	Ra’s Sumayrah	Ra's Sumayrah	Ra's Sumayrah,Ras Semaira,Ras Sumaira,Ra’s Sumayrah,RÄs Semaira	24.32243	51.44653	T	PT	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290716	ImshÄsh as Sumayrah	Imshash as Sumayrah	Imshash Semaira,Imshash al-Semeirah,Imshash as Sumayrah,ImshÄsh Semaira,ImshÄsh al-Semeirah,ImshÄsh as Sumayrah,Tawi Sumayrah,Å¢awÄ« Sumayrah	24.27873	51.42068	H	WLL	AE		01				0		51	Asia/Dubai	2011-11-06
+290717	Dawḩat as Sumayrah	Dawhat as Sumayrah	Al Sumaira,Dawhat as Sumayrah,Dawḩat as Sumayrah	24.31884	51.54839	H	BAY	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290718	Sayḩ as Sumayḩ	Sayh as Sumayh	Sayh as Sumayh,Sayḩ as Sumayḩ,Sih al-Semeih,Sih as Semeih,Sih as Sumayh,Sumayh,Sumayḩ,Sīḥ al-Semeiḥ,Sīḩ as Sumayḩ	24.7278	54.78697	T	TRGD	AE		01				0		16	Asia/Dubai	2011-11-06
+290719	Birkat Sumayḩ	Birkat Sumayh	Birkat Sumaih,Birkat Sumayh,Birkat Sumayḩ,Samaih,Semaih,Smeih	24.72276	54.77993	H	WLL	AE		01				0		18	Asia/Dubai	2011-11-06
+290720	Å¢awÄ« SulÅ£Än SÄlim	Tawi Sultan Salim	Tawi Sultan Salim,Å¢awÄ« SulÅ£Än SÄlim	25.02181	55.8195	H	WLL	AE		06				0		170	Asia/Dubai	2011-11-06
+290721	JazÄ«rat aÅŸ ŞīlÄ«yÄ	Jazirat as Siliya	Jazirat Sulayyah,Jazirat as Siliya,JazÄ«rat aÅŸ ŞīlÄ«yÄ,JazÄ«rat Åžulayyah	24.18286	52.8921	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290722	Å¢awÄ« SulaymÄt	Tawi Sulaymat	Al-Seleimat,Al-SeleimÄt,Tawi Sulaymat,Tawi Suleimat,TÄwÄ« Suleimat,Å¢awÄ« SulaymÄt	24.23064	55.59382	H	WLL	AE		01				0		251	Asia/Dubai	2011-11-06
+290723	Sayḩ SulaymÄt	Sayh Sulaymat	Sayh Sulaymat,Sayḩ SulaymÄt	24.19639	55.56278	T	TRGD	AE		01				0		224	Asia/Dubai	2011-11-06
+290724	Sayḩ SulaymÄn	Sayh Sulayman	Sayh Sulayman,Sayḩ SulaymÄn	24.67704	55.47051	T	TRGD	AE		03				0		149	Asia/Dubai	2011-11-06
+290725	Ḩadabat Sukhub	Hadabat Sukhub	Hadabat Sakhub,Hadabat Sukhub,Ḩadabat Sakhub,Ḩadabat Sukhub	24.82094	55.53095	T	DUNE	AE		03				0		157	Asia/Dubai	2011-11-06
+290726	Å¢awÄ« Suhaylah	Tawi Suhaylah	Tawi Saheila,Tawi Suhaylah,Tawi Suheila,TÄwÄ« Saheila,TÄwÄ« Suheila,Å¢awÄ« Suhaylah	25.36667	55.95	H	WLL	AE		07				0		184	Asia/Dubai	2011-11-06
+290727	Suḩaybah	Suhaybah	Suhaybah,Suḩaybah	24.93877	56.15754	P	PPL	AE		05				0		351	Asia/Dubai	2011-11-06
+290728	WÄdÄ« Suftah	Wadi Suftah	Wadi Suftah,WÄdÄ« Suftah	25.46364	56.11519	H	WAD	AE		05				0		297	Asia/Dubai	2011-11-06
+290729	Şufayrī	Sufayri	Sufayri,Şufayrī	24.81168	56.12646	P	PPL	AE		02				0		309	Asia/Dubai	2011-11-06
+290730	Sayḩ Şubrah	Sayh Subrah	Sayh Subrah,Sayḩ Şubrah	24.05023	55.49348	T	TRGD	AE		01				0		196	Asia/Dubai	2011-11-06
+290731	‘Aqabat as Subaykhah	`Aqabat as Subaykhah	`Aqabat as Subaykhah,‘Aqabat as Subaykhah	25.5763	56.33881	T	PASS	AE		04				0		35	Asia/Dubai	2011-11-06
+290732	Subaykhah	Subaykhah	Subaykhah	25.5695	56.33907	L	LCTY	AE		04				0		256	Asia/Dubai	2011-11-06
+290733	Subayḩīyah	Subayhiyah	Subayhiyah,Subayḩīyah	25.40028	56.36417	V	CULT	AE		06				0		-9999	Asia/Dubai	2011-11-06
+290734	Stutter Shoal	Stutter Shoal	Stutter Shoal	24.23777	52.42787	H	SHOL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290735	Stokes Bluff	Stokes Bluff	Stokes Bluff	24.3	52.63333	T	CLF	AE		01				0		6	Asia/Dubai	2011-11-06
+290736	Mount Stewart	Mount Stewart	Mount Stewart	24.33288	52.59674	T	HLL	AE		01				0		60	Asia/Dubai	2011-11-06
+290737	South YÄsÄt Channel	South Yasat Channel	South Yasat Channel,South YÄsÄt Channel	24.13797	51.98182	H	CHNM	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290738	South Faraydat	South Faraydat	Faraijdat,Fereijid,South Faraydat,South Furayjidat,South FurayjidÄt	24.38333	51.71667	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290740	Å¢awÄ« Sirrah	Tawi Sirrah	Tawi Sirra,Tawi Sirrah,TÄwÄ« Sirra,Å¢awÄ« Sirrah	25.45114	55.61266	H	WLL	AE		07				0		31	Asia/Dubai	2011-11-06
+290741	Şīr Bū Nu‘ayr	Sir Bu Nu`ayr	Jazirat Siri Bu Naybar,Jazīrat Sīrī Bū Naybar,Jezirat Sir Bu Na`air,Jezirat Sir Bu Na‘air,Sir Abu Nu`air,Sir Abu Nu`ayr,Sir Abu Nu‘air,Sir Bu Nu`air,Sir Bu Nu`ayr,Sir bu Na`air Island,Sīr bu Na‘air Island,Şīr Abū Nu‘ayr,Şīr Bū Nu‘ayr,Ṣīr Bū Nu‘air	25.23305	54.2181	T	ISL	AE		01				0		15	Asia/Dubai	2011-11-06
+290742	Şīr BanÄ« YÄs	Sir Bani Yas	Al Yas,Al YÄs,Jezirat Yas,JezÄ«rat Yas,Sir Bani Yas,Sir Banias,Sir Beni Yas,Yas Island,Şīr BanÄ« YÄs	24.32589	52.60128	T	ISL	AE		01				0		124	Asia/Dubai	2011-11-06
+290743	SÄ«rat al Khawr	Sirat al Khawr	Sirat al Khawr,SÄ«rat al Khawr	25.35326	56.37784	T	ISL	AE		04				0		-9999	Asia/Dubai	2011-11-06
+290744	Suwayfat aş Şīr	Suwayfat as Sir	Suwayfat as Sir,Suwayfat aş Şīr	25.5188	56.36949	T	PT	AE		04				0		-9999	Asia/Dubai	2011-11-06
+290746	WÄdÄ« Sinnah	Wadi Sinnah	Wadi Sinnah,WÄdÄ« Sinnah	25.50796	56.19523	H	WAD	AE		04				0		124	Asia/Dubai	2011-11-06
+290747	Sinnah	Sinnah	Sinnah	25.50868	56.16772	P	PPL	AE		04				0		198	Asia/Dubai	2011-11-06
+290748	SinÄdil	Sinadil	Sinadil,SinÄdil	24.81095	56.02511	P	PPL	AE		02				0		424	Asia/Dubai	2011-11-06
+290749	Nada Sima	Nada Sima	Nada Sima	24.18333	55.36667	T	DUNE	AE		01				0		198	Asia/Dubai	2011-11-06
+290750	Ra’s as Silmīyah	Ra's as Silmiyah	Ra's as Silmiyah,Ra’s as Silmīyah	24.25278	54.52889	T	DUNE	AE		01				0		8	Asia/Dubai	2011-11-06
+290751	Silmīyah	Silmiyah	Silmiya,Silmiyah,Silmīya,Silmīyah	24.2	54.35	T	HLL	AE	AE	01				0		6	Asia/Dubai	2011-11-06
+290752	Silmīyah	Silmiyah	Al-Silaimiyyah,Silmiyah,Silmīyah	24.22404	54.46374	T	DUNE	AE		01				0		13	Asia/Dubai	2011-11-06
+290753	Sayḩ Silm	Sayh Silm	Sayh Silm,Sayḩ Silm	24.41779	55.31025	T	TRGD	AE		01				0		158	Asia/Dubai	2011-11-06
+290754	Jabal Silḩū Bilḩū	Jabal Silhu Bilhu	Jabal Silhu Bilhu,Jabal Silḩū Bilḩū	25.15146	56.06771	T	MT	AE		05				0		607	Asia/Dubai	2011-11-06
+290755	Ra’s as Sila‘	Ra's as Sila`	Ra's as Sil`,Ra's as Sila`,Ra’s as Sila‘,Ra’s as Sil‘	24.05	51.78333	T	PT	AE		01				0		1	Asia/Dubai	2011-11-06
+290756	Dawḩat as Sila‘	Dawhat as Sila`	Dawhat as Sila`,Dawḩat as Sila‘	23.96667	51.93333	H	INLT	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290757	WÄdÄ« SÄ«jÄ«	Wadi Siji	Wadi Nakh,Wadi Siji,Wadi as Siji,WÄdÄ« Nakh,WÄdÄ« SÄ«jÄ«,WÄdÄ« as SÄ«jÄ«	25.2736	56.03543	H	WAD	AE		00				0		225	Asia/Dubai	2011-11-06
+290758	Jabal Sījī	Jabal Siji	Jabal Siji,Jabal Sījī	25.2436	56.12194	T	MT	AE		04				0		664	Asia/Dubai	2011-11-06
+290759	Tawi Siji	Tawi Siji	Siji,Sījī,Tawi Siji,Tawi as Siji,Ţawī as Sījī	25.24764	56.07719	P	PPL	AE		04				0		324	Asia/Dubai	2011-11-06
+290760	Wadi Saha	Wadi Saha	Wadi Saha,Wadi Siha',WÄdÄ« ÅžihÄ’	25.3828	56.29541	H	WAD	AE		00				0		320	Asia/Dubai	2011-11-06
+290761	Jabal ÅžihÄ’	Jabal Siha'	Jabal Siha',Jabal ÅžihÄ’	25.33931	56.276	T	MT	AE		00				0		788	Asia/Dubai	2011-11-06
+290762	WÄdÄ« SifÅ«nÄ«	Wadi Sifuni	Wadi Sifuni,WÄdÄ« SifÅ«nÄ«	25.19333	56.01389	H	WAD	AE		05				0		189	Asia/Dubai	2011-11-06
+290763	Sayḩ as Sidrah	Sayh as Sidrah	Sayh Sadrah,Sayh as Sidrah,Sayḩ Sadrah,Sayḩ as Sidrah,Sidra,Sih as Sidrah,Sīḩ as Sidrah	24.81824	54.9264	T	TRGD	AE		01				0		31	Asia/Dubai	2011-11-06
+290764	WÄdÄ« Sidr	Wadi Sidr	Wadi Sidr,WÄdÄ« Sidr	25.42182	56.09854	H	WAD	AE		04				0		290	Asia/Dubai	2011-11-06
+290765	Jabal Sidr	Jabal Sidr	Jabal Sidr	25.36833	56.34	T	HLL	AE		06				0		11	Asia/Dubai	2011-11-06
+290766	WÄdÄ« Sadakh	Wadi Sadakh	Wadi Sadakh,Wadi Sidakh,WÄdÄ« Sadakh,WÄdÄ« Sidakh	25.56929	56.06399	H	WAD	AE		04				0		132	Asia/Dubai	2011-11-06
+290767	SÄ«bat al AshkharÄt	Sibat al Ashkharat	Sibat al Ashkharat,SÄ«bat al AshkharÄt	25.61667	56.26667	S	CMTY	AE		04				0		19	Asia/Dubai	2011-11-06
+290768	WÄdÄ« Shuway	Wadi Shuway	Wadi Shuway,Wadi Shuwayy,WÄdÄ« Shuway,WÄdÄ« Shuwayy	25.23142	56.20244	H	WAD	AE		04				0		233	Asia/Dubai	2011-11-06
+290769	WÄdÄ« Shuways	Wadi Shuways	Wadi Shuways,WÄdÄ« Shuways	25.18957	56.33323	H	WAD	AE		04				0		347	Asia/Dubai	2011-11-06
+290770	Jabal Shuways	Jabal Shuways	Jabal Shuways	25.17617	56.3459	T	HLL	AE		04				0		171	Asia/Dubai	2011-11-06
+290771	Al Quwayz	Al Quwayz	Al Quwayz,Chuwais,Kuways,Shuways	25.78102	55.9787	P	PPLX	AE		05				0		24	Asia/Dubai	2011-11-06
+290772	JazÄ«rat ShuwayhÄt	Jazirat Shuwayhat	Jazirat Mashat,Jazirat Showayhat,Jazirat Shuwayhat,JazÄ«rat Mashat,JazÄ«rat ShowayhÄt,JazÄ«rat ShuwayhÄt,Shuwaihat,ShuwaihÄt,Shuweihat,ShuweihÄt	24.11391	52.43972	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290773	Bid‘ Shuwaybir	Bid` Shuwaybir	Bid` Shuwaybir,Bida` Shuwaibah,Bidau' Shuaibar,Bidau' Suaibar,Bidau’ Shuaibar,Bidau’ Suaibar,Bida‘ Shuwaibah,Bid‘ Shuwaybir,Budu` Shuaibar,Budu` Shuwaibir,Budu` Shuwaybir,Budu‘ Shuwaibir,Budū‘ Shuaibar,Budū‘ Shuwaybir,Shawaibir	24.0958	54.58919	H	WLL	AE		01				0		46	Asia/Dubai	2011-11-06
+290774	Shū Triyyah	Shu Triyyah	Shu Triyyah,Shū Triyyah	24.24938	54.29978	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290775	Shuray‘ah	Shuray`ah	Shuray`ah,Shuray‘ah	24.3	53.61667	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290776	Shunayyin	Shunayyin	Shunayyin	24.05604	54.46374	T	SAND	AE		01				0		39	Asia/Dubai	2011-11-06
+290777	Shunayyin	Shunayyin	Shunayyin	24.10632	54.39287	L	LCTY	AE		01				0		16	Asia/Dubai	2011-11-06
+290778	Shunayyin	Shunayyin	Shinaiyin,Shunaiyin,Shunayyin	24.21667	54.41667	T	HLL	AE	AE	01				0		3	Asia/Dubai	2011-11-06
+290779	Buday‘ Shumrūkh	Buday` Shumrukh	Buday` Shumrukh,Buday‘ Shumrūkh,Shumrukh,Shumrūkh	24.77232	55.7849	H	WLL	AE		01				0		302	Asia/Dubai	2011-11-06
+290780	Sayḩ Shumayrīkh	Sayh Shumayrikh	Sayh Shumayrikh,Sayḩ Shumayrīkh	24.71557	55.4203	T	TRGD	AE		03				0		145	Asia/Dubai	2011-11-06
+290781	Shumaylīyah	Shumayliyah	Ash Shumailiya,Ash Shumailīya,Ash Shumayliyah,Ash Shumaylīyah,Shamailiya,Shamailiyah,Shamailīyah,Shamayliyah,Shamaylīyah,Shumailiyah,Shumayliyah,Shumaylīyah	25.33333	56.33333	L	AREA	AE	AE	06				0		426	Asia/Dubai	2011-11-06
+290782	Jabal Shughayr	Jabal Shughayr	Jabal Shughayr	25.4	56.33333	T	HLL	AE		04				0		55	Asia/Dubai	2011-11-06
+290783	Ţawī Shubayşī	Tawi Shubaysi	Tawi Shubaysi,Ţawī Shubayşī	24.16278	55.05729	H	WLL	AE		01				0		120	Asia/Dubai	2011-11-06
+290784	Nadd ash ShibÄ	Nadd ash Shiba	Nad Shibeih,Nadd Shubayh,Nadd Shubayḩ,Nadd ash Shiba,Nadd ash ShibÄ	25.14583	55.33417	P	PPL	AE	AE	03				0		23	Asia/Dubai	2011-11-06
+290785	Shu‘ayb ‘Ūd	Shu`ayb `Ud	Shu`ayb `Ud,Shu‘ayb ‘Ūd	24.74968	55.79034	V	TREE	AE		00				0		253	Asia/Dubai	2011-11-06
+290786	Shu‘ayb Şaghīr	Shu`ayb Saghir	Shu`ayb Saghir,Shu‘ayb Şaghīr	24.74944	55.82028	V	TREE	AE		01				0		311	Asia/Dubai	2011-11-06
+290787	Sayḩ Shu‘ayb	Sayh Shu`ayb	Sayh Shu`ayb,Sayh ash Shu`ayb,Sayḩ Shu‘ayb,Sayḩ ash Shu‘ayb,Shu`aib,Shu‘aib,Sih Shu`aib,Sīḩ Shu‘aib	24.89786	54.96311	T	TRGD	AE		00				0		17	Asia/Dubai	2011-11-06
+290788	Raml ash Shu‘ayb	Raml ash Shu`ayb	Raml ash Shu`ayb,Raml ash Shu‘ayb	24.8757	55.0429	T	SAND	AE		03				0		30	Asia/Dubai	2011-11-06
+290789	Shīshah	Shishah	Shisha,Shishah,Shīshah	24.33635	54.94465	T	HLL	AE		01				0		65	Asia/Dubai	2011-11-06
+290790	WÄdÄ« ShÄ«ÅŸah	Wadi Shisah	Wadi Shisa,Wadi Shisah,WÄdÄ« ShÄ«ÅŸah	25.84028	56.1	H	WAD	AE	AE	05				0		321	Asia/Dubai	2011-11-06
+290791	Ramlat Shīşah	Ramlat Shisah	Ramlat Shisah,Ramlat Shīşah	25.38835	56.02995	T	DUNE	AE		04				0		304	Asia/Dubai	2011-11-06
+290792	WÄdÄ« ShÄ«s	Wadi Shis	Wadi Shis,WÄdÄ« ShÄ«s	25.29348	56.24396	H	WAD	AE		06				0		408	Asia/Dubai	2011-11-06
+290793	Shīs	Shis	Shis,Shīs	25.29138	56.24549	P	PPL	AE		06				0		365	Asia/Dubai	2011-11-06
+290794	WÄdÄ« ShimÄl	Wadi Shimal	Wadi Shimal,WÄdÄ« ShimÄl	25.50089	56.18876	H	WAD	AE		04				0		158	Asia/Dubai	2011-11-06
+290795	ShimÄl	Shimal	Shimal,Shimil,ShimÄl	25.8178	56.01088	P	PPL	AE		05				0		19	Asia/Dubai	2011-11-06
+290796	Khaţmat ash Shiklah	Khatmat ash Shiklah	Khaim ash Shikla,Khatm al-Shiklah,Khatmat ash Shiklah,Khaţmat ash Shiklah,Khaṭm al-Shiklah	24.21583	55.95306	T	SPUR	AE	AE	01				0		392	Asia/Dubai	2011-11-06
+290797	Shidq al Kalb	Shidq al Kalb	Shidq al Kalb,Shudeg al-Kalb,Shudq al Kalb	23.13385	53.73315	L	OAS	AE		01				0		93	Asia/Dubai	2011-11-06
+290798	WÄdÄ« ShibḩÄt	Wadi Shibhat	Wadi Shibhat,WÄdÄ« ShibḩÄt	24.45849	55.7685	T	TRGD	AE		01				0		321	Asia/Dubai	2011-11-06
+290799	Shabahanat al Majann	Shabahanat al Majann	Ash Shibhanah,Shabahanat al Majann,Shibhanat al Mijann,ShibhÄnat al Mijann	24.01997	51.735	T	PLAT	AE		01				0		48	Asia/Dubai	2011-11-06
+290800	ShibÄnÄt	Shibanat	Shibanat,ShibÄnÄt	24.91667	55.66667	L	TRB	AE		00				0		190	Asia/Dubai	2011-11-06
+290801	Shi‘b al GhÄf	Shi`b al Ghaf	Shi`b al Ghaf,Shib al Gat,Shi‘b al GhÄf	24.05278	55.71972	P	PPL	AE		01				0		237	Asia/Dubai	2011-11-06
+290802	ShibÄk	Shibak	Shibak,ShibÄk	24.59856	55.52346	L	LCTY	AE		01				0		208	Asia/Dubai	2011-11-06
+290803	Jabal Shi‘Äb ash Shaybah	Jabal Shi`ab ash Shaybah	Jabal Sha`ab ash Shaybah,Jabal Sha‘Äb ash Shaybah,Jabal Shi`ab ash Shaybah,Jabal Shi‘Äb ash Shaybah	25.40026	56.30431	T	MT	AE		04				0		410	Asia/Dubai	2011-11-06
+290804	WÄdÄ« ShÄ«	Wadi Shi	Wadi Shi,WÄdÄ« ShÄ«	25.34983	56.35746	H	WAD	AE		06				0		40	Asia/Dubai	2011-11-06
+290805	Shaykh ad DimÄth	Shaykh ad Dimath	Shaikh al Dimath,Shaykh ad Dimath,Shaykh ad DimÄth	24.01056	53.09528	L	LCTY	AE	AE	01				0		29	Asia/Dubai	2011-11-06
+290806	Shaydad Bin Khanfūr	Shaydad Bin Khanfur	Shaydad Bin Khanfur,Shaydad Bin Khanfūr	24.6932	55.44164	T	SAND	AE		03				0		141	Asia/Dubai	2011-11-06
+290807	‘Urūq ash Shaybah	`Uruq ash Shaybah	Uruq ash Shaibah,Uruq ash Sheibah,`Uruq ash Shaybah,‘Urūq ash Shaybah	22.35	54.91667	T	DUNE	AE		01				0		161	Asia/Dubai	2011-11-06
+290808	Sayḩ Shawyī	Sayh Shawyi	Sayh Shawyi,Sayḩ Shawyī	25.65293	56.01207	T	PLN	AE		05				0		17	Asia/Dubai	2011-11-06
+290809	WÄdÄ« Shawkah	Wadi Shawkah	Wadi Shaukha,Wadi Shawkah,WÄdÄ« Shawkah	25.26761	55.8668	H	WAD	AE		06				0		110	Asia/Dubai	2011-11-06
+290810	Ţawī Shawkah	Tawi Shawkah	Tawi Shawkah,Ţawī Shawkah	25.03056	56.10694	H	WLL	AE		05				0		699	Asia/Dubai	2011-11-06
+290811	Jabal Shawkah	Jabal Shawkah	Jabal Shauka,Jabal Shawkah	25.10014	56.04331	T	HLL	AE		05				0		442	Asia/Dubai	2011-11-06
+290812	Shawkah	Shawkah	Shauka,Shawkah,Shokah	25.1	56.03333	P	PPL	AE	AE	05				0		283	Asia/Dubai	2011-11-06
+290813	WÄdÄ« Shawiyayn	Wadi Shawiyayn	Wadi Shawiyayn,WÄdÄ« Shawiyayn	25.27208	56.06068	H	WAD	AE		04				0		247	Asia/Dubai	2011-11-06
+290814	Shawīyah	Shawiyah	Shawiyah,Shawīyah	25.26792	56.08149	P	PPL	AE		04				0		304	Asia/Dubai	2011-11-06
+290815	Å¢awÄ« ShÄÅ£	Tawi Shat	Tawi Shat,Å¢awÄ« ShÄÅ£	24.48333	55.2	H	WLLQ	AE		01				0		139	Asia/Dubai	2011-11-06
+290816	ShÄt	Shat	Shat,ShÄt	24.48614	55.19806	T	SAND	AE		01				0		117	Asia/Dubai	2011-11-06
+290817	Sharqīyīn	Sharqiyin	Sharqiyan,Sharqiyin,Sharqīyīn	25.25	56.16667	L	TRB	AE	AE	00				0		522	Asia/Dubai	2011-11-06
+290818	Å¢awÄ« SharqÄn	Tawi Sharqan	Tawi Sharqan,Å¢awÄ« SharqÄn	25.38333	55.4	H	WLL	AE		06				0		1	Asia/Dubai	2011-11-06
+290819	Jabal Sharmah	Jabal Sharmah	Jabal Sharmah	25.28406	56.12845	T	MT	AE		04				0		594	Asia/Dubai	2011-11-06
+290820	Sharm	Sharm	Sharam,Sharm	25.47078	56.35319	P	PPL	AE		04				0		14	Asia/Dubai	2011-11-06
+290821	Jabal Sharīyah	Jabal Shariyah	Jabal Shariyah,Jabal Sharīyah	25.29063	56.14987	T	MT	AE		04				0		415	Asia/Dubai	2011-11-06
+290822	Sharīyah	Shariyah	Shariyah,Sharīyah	25.1	56.03333	P	PPL	AE		05				0		283	Asia/Dubai	2011-11-06
+290823	Sharīyah	Shariyah	Shariyah,Sharīyah	24.81512	56.0986	P	PPL	AE		02				0		319	Asia/Dubai	2011-11-06
+290824	Khawr ash ShÄriqah	Khawr ash Shariqah	Khawr ash Shariqah,Khawr ash ShÄriqah,Khor Sharja,Khor Sharjah,Khor ShÄrja	25.33111	55.38222	H	INLT	AE	AE	06				0		9	Asia/Dubai	2011-11-06
+290825	Sayḩ Sharbū	Sayh Sharbu	Sayh Sharbu,Sayḩ Sharbū	24.81667	55.41667	T	TRGD	AE		03				0		125	Asia/Dubai	2011-11-06
+290826	Raml Sharbū	Raml Sharbu	Raml Sharbu,Raml Sharbū	24.83333	55.41667	T	SAND	AE		03				0		130	Asia/Dubai	2011-11-06
+290827	Sharaf Tamī	Sharaf Tami	Sharaf Tami,Sharaf Tamī	23.62836	55.33692	T	DUNE	AE		01				0		207	Asia/Dubai	2011-11-06
+290828	Sharaf Sawqar	Sharaf Sawqar	Sharaf Sawqar	24.33333	55.7	L	LCTY	AE		01				0		275	Asia/Dubai	2011-11-06
+290829	Sharaf Mundassah	Sharaf Mundassah	Sharaf Mundassah	24.58472	55.73722	V	TREE	AE		01				0		308	Asia/Dubai	2011-11-06
+290830	Sharaf JabÄrah	Sharaf Jabarah	Sharaf Jabara,Sharaf Jabarah,Sharaf JabÄrah	24.62804	55.77334	L	LCTY	AE		01				0		337	Asia/Dubai	2011-11-06
+290831	Sharaf Ḩawrah	Sharaf Hawrah	Sharaf Hawrah,Sharaf Ḩawrah	24.54521	55.71675	T	SAND	AE		01				0		318	Asia/Dubai	2011-11-06
+290832	Sharaf á¸abbah	Sharaf Dabbah	Sharaf Dabbah,Sharaf á¸abbah	24.55321	55.65638	L	LCTY	AE		01				0		288	Asia/Dubai	2011-11-06
+290833	Sharaf Bin ‘Ubayd	Sharaf Bin `Ubayd	Sharaf Bin `Ubayd,Sharaf Bin ‘Ubayd	24.91667	55.4	L	LCTY	AE		03				0		100	Asia/Dubai	2011-11-06
+290834	Sharaf al ‘AfÄr	Sharaf al `Afar	Sharaf al `Afar,Sharaf al ‘AfÄr	24.9516	55.44056	L	LCTY	AE		03				0		85	Asia/Dubai	2011-11-06
+290835	WÄdÄ« SharabÄt	Wadi Sharabat	Wadi Sharabat,WÄdÄ« SharabÄt	24.88063	56.21317	H	WAD	AE		05				0		142	Asia/Dubai	2011-11-06
+290836	Jabal Sharab	Jabal Sharab	Jabal Sharab	25.0138	56.0337	T	MT	AE		05				0		664	Asia/Dubai	2011-11-06
+290837	Jabal Sha‘rÄ’	Jabal Sha`ra'	Jabal Sha`arah,Jabal Sha`ra',Jabal Sha‘arah,Jabal Sha‘rÄ’	24.06667	56.26667	T	MT	AE		01				0		995	Asia/Dubai	2011-11-06
+290838	WÄdÄ« Shaqq	Wadi Shaqq	Wadi Shaq,Wadi Shaqq,WÄdÄ« Shaqq	25.84389	56.02139	H	WAD	AE	AE	05				0		26	Asia/Dubai	2011-11-06
+290839	Ţawī Shaqq	Tawi Shaqq	Tawi Shaqq,Ţawī Shaqq	24.38244	55.60894	H	WLL	AE		01				0		232	Asia/Dubai	2011-11-06
+290840	Ramlat Shanţūt Bin ‘UmÄn	Ramlat Shantut Bin `Uman	Ramlat Shantut Bin `Uman,Ramlat Shanţūt Bin ‘UmÄn	24.26667	55.4	T	DUNE	AE		01				0		195	Asia/Dubai	2011-11-06
+290841	Shanţūt Ibn ‘UmÄn	Shantut Ibn `Uman	Shantut Bin `Uman,Shantut Ibn `Uman,Shanţūt Bin ‘UmÄn,Shanţūt Ibn ‘UmÄn	24.25432	55.42166	T	TRGD	AE		01				0		183	Asia/Dubai	2011-11-06
+290842	Shanţūţ	Shantut	Shantut,Shanţūţ	23.38142	55.30225	T	DUNE	AE		01				0		154	Asia/Dubai	2011-11-06
+290843	Shantuba	Shantuba	Shantuba	24.54867	55.73806	L	LCTY	AE		01				0		322	Asia/Dubai	2011-11-06
+290844	Ra’s Shandaghah	Ra's Shandaghah	Ra's Shandaghah,Ra’s Shandaghah	25.26667	55.28333	T	PT	AE		03				0		-9999	Asia/Dubai	2011-11-06
+290845	Shandaghah	Shandaghah	Shandaghah	25.25833	55.28333	P	PPLX	AE		03				0		9	Asia/Dubai	2011-11-06
+290846	‘Araj Shamsī	`Araj Shamsi	`Araj Shamsi,‘Araj Shamsī	24.18944	54.70361	T	DPR	AE		01				0		51	Asia/Dubai	2011-11-06
+290847	ShammÄmÄ«yah	Shammamiyah	Shammamiyah,ShammÄmÄ«yah	23.06374	53.68898	T	DPR	AE		01				0		76	Asia/Dubai	2011-11-06
+290848	ShÄmis	Shamis	Shamis,Shams,ShÄmis	23.93333	53.7	L	GASF	AE	AE	01				0		21	Asia/Dubai	2011-11-06
+290849	ShÄmis	Shamis	Shamis,ShÄmis	23.93854	53.69593	S	FCL	AE		01				0		41	Asia/Dubai	2011-11-06
+290851	WÄdÄ« ash ShÄmah	Wadi ash Shamah	Wadi ash Shamah,WÄdÄ« ash ShÄmah	25.41592	56.33835	H	WAD	AE		00				0		36	Asia/Dubai	2011-11-06
+290852	WÄdÄ« ash Sha‘m	Wadi ash Sha`m	Wadi ash Sha`m,WÄdÄ« ash Sha‘m	26.03111	56.0975	H	WAD	AE		05				0		143	Asia/Dubai	2011-11-06
+290853	WÄdÄ« ash ShÄl	Wadi ash Shal	Wadi ash Shal,WÄdÄ« ash ShÄl	25.38365	55.95383	H	WAD	AE		07				0		155	Asia/Dubai	2011-11-06
+290854	WÄdÄ« Shakhkh	Wadi Shakhkh	Wadi Shakh,Wadi Shakhkh,WÄdÄ« Shakh,WÄdÄ« Shakhkh	25.58769	56.14287	H	WAD	AE		04				0		228	Asia/Dubai	2011-11-06
+290855	Haḑbat Shakhbūţ	Hadbat Shakhbut	Hadbat Shakhbut,Haḑbat Shakhbūţ	24.29583	54.65722	T	HLL	AE		01				0		27	Asia/Dubai	2011-11-06
+290856	Shaitham	Shaitham	Shaitham,Shaithan,ShaithÄn	24.29326	54.85926	H	WLL	AE		01				0		71	Asia/Dubai	2011-11-06
+290857	Sayḩ Sha‘īl	Sayh Sha`il	Sayh Sha`il,Sayḩ Sha‘īl	24.68333	55.45	T	TRGD	AE		03				0		160	Asia/Dubai	2011-11-06
+290858	Qarn ShÄ’i‘	Qarn Sha'i`	Qarn Sha'i`,Qarn ShÄ’i‘	23.83933	53.28344	T	HLL	AE		01				0		91	Asia/Dubai	2011-11-06
+290859	Å¢awÄ« ShÄhÄ«n	Tawi Shahin	Tawi Shahin,Å¢awÄ« ShÄhÄ«n	25.61667	55.88333	H	WLL	AE		05				0		93	Asia/Dubai	2011-11-06
+290860	ShahawÄt	Shahawat	Sahawat,Shahawat,ShahawÄt	25.96667	56.08333	P	PPL	AE	AE	05				0		18	Asia/Dubai	2011-11-06
+290861	ShahÄ’irah	Shaha'irah	Shaha'irah,ShahÄ’irah	25.38333	56.13333	L	TRB	AE		05				0		501	Asia/Dubai	2011-11-06
+290862	WÄdÄ« ShÄh	Wadi Shah	Wadi Shah,WÄdÄ« ShÄh	25.82949	56.10949	H	WAD	AE		05				0		202	Asia/Dubai	2011-11-06
+290863	ShÄh	Shah	Shah,ShÄh	23.13561	53.91652	P	PPL	AE		01				0		170	Asia/Dubai	2011-11-06
+290864	ShÄh	Shah	Shah,ShÄh	22.85	53.91667	L	OILF	AE		01				0		170	Asia/Dubai	2011-11-06
+290865	ShÄh	Shah	Shah,ShÄh	23.13333	53.91667	T	DPR	AE		01				0		158	Asia/Dubai	2011-11-06
+290866	ShÄh	Shah	Shah,ShÄh	25.89861	56.12833	P	PPL	AE		05				0		715	Asia/Dubai	2011-11-06
+290867	Shabqatayn	Shabqatayn	Shabqatayn	23.91665	55.20997	T	DUNE	AE		01				0		178	Asia/Dubai	2011-11-06
+290868	Sha‘bīyah	Sha`biyah	Sha`biya,Sha`biyah,Sha‘biya,Sha‘bīyah	24.28333	54.48333	T	HLL	AE		01				0		12	Asia/Dubai	2011-11-06
+290869	Shabakah	Shabakah	Shabakah	25.03333	56	P	PPL	AE		05				0		322	Asia/Dubai	2011-11-06
+290870	WÄdÄ« Shabak	Wadi Shabak	Wadi Shabak,WÄdÄ« Shabak	25.15157	55.45444	T	TRGD	AE		03				0		22	Asia/Dubai	2011-11-06
+290871	Bid‘ Shabaan	Bid` Shabaan	Bid` Shabaan,Bid‘ Shabaan	23.35	54.36667	H	WLL	AE		01				0		133	Asia/Dubai	2011-11-06
+290872	Sha‘athÄn	Sha`athan	Sha`athan,Sha‘athÄn	24.27375	54.91292	T	SAND	AE		01				0		73	Asia/Dubai	2011-11-06
+290873	WÄdÄ« Sha‘arah	Wadi Sha`arah	Wadi Sha`arah,Wadi Sha`areh,WÄdÄ« Sha‘arah,WÄdÄ« Sha‘areh	25.06667	56.36667	H	WAD	AE		06				0		-9999	Asia/Dubai	2011-11-06
+290874	Sayyidah	Sayyidah	Sayyidah	24.6884	55.70747	V	TREE	AE		01				0		281	Asia/Dubai	2011-11-06
+290875	Ramlat Sayyid	Ramlat Sayyid	Ramlat Saiyin,Ramlat Sayyid,Ramle Sayyid	24.39496	55.60754	T	DUNE	AE		01				0		304	Asia/Dubai	2011-11-06
+290876	WÄdÄ« Sayraq	Wadi Sayraq	Wadi Sayraq,WÄdÄ« Sayraq	25.56185	56.05472	H	WAD	AE		04				0		127	Asia/Dubai	2011-11-06
+290877	Sayḩ MaḩÄrÄ«	Sayh Mahari	Sayh Mahari,Sayḩ MaḩÄrÄ«	25.38333	55.73333	T	DUNE	AE		06				0		51	Asia/Dubai	2011-11-06
+290878	Sayḩ aş Şaqlah	Sayh as Saqlah	Sayh as Saqlah,Sayḩ aş Şaqlah	24.88398	56.16847	P	PPL	AE		05				0		195	Asia/Dubai	2011-11-06
+290879	Sayḩ al Ḩalamah	Sayh al Halamah	Sayh al Halamah,Sayh al Halmah,Sayḩ al Ḩalamah,Sayḩ al Ḩalmah	24.25409	54.6919	T	DPR	AE		01				0		22	Asia/Dubai	2011-11-06
+290880	Sayḩ	Sayh	Sayh,Sayḩ	25.93333	56.06667	P	PPL	AE		05				0		196	Asia/Dubai	2011-11-06
+290881	Ţawī Sayfilman	Tawi Sayfilman	Tawi Sayfilman,Ţawī Sayfilman	23.6	54.6	H	WLL	AE		01				0		113	Asia/Dubai	2011-11-06
+290882	Ţawī Sayf	Tawi Sayf	Tawi Sayf,Ţawī Sayf	25.33472	55.81056	H	WLL	AE		06				0		79	Asia/Dubai	2011-11-06
+290883	Bid‘ Sayf	Bid` Sayf	Bid` Sayf,Bid‘ Sayf	25.06667	55.58333	H	WLL	AE		03				0		107	Asia/Dubai	2011-11-06
+290884	Bid‘ Sayf	Bid` Sayf	Bid` Sayf,Bid‘ Sayf	23.9195	54.93082	T	DPR	AE		01				0		105	Asia/Dubai	2011-11-06
+290885	Barqat Sayf	Barqat Sayf	Barqat Sayf,Burga Seif,Burqat Sayf	24.29874	52.58478	T	HLL	AE		01				0		34	Asia/Dubai	2011-11-06
+290886	Barqat Sayf	Barqat Sayf	Barqat Sayf	24.03307	53.27185	T	HLL	AE		01				0		14	Asia/Dubai	2011-11-06
+290887	WÄdÄ« SaybitalmÄ	Wadi Saybitalma	Wadi Saybitalma,WÄdÄ« SaybitalmÄ	25.23589	56.13664	H	WAD	AE		05				0		437	Asia/Dubai	2011-11-06
+290888	NaqÄ SawÅ£ah	Naqa Sawtah	Naqa Sawtah,NaqÄ SawÅ£ah	24.26611	55.55694	T	DUNE	AE		01				0		197	Asia/Dubai	2011-11-06
+290889	Jabal SawdÄ’	Jabal Sawda'	Jaba Sawda',Jaba SawdÄ’,Jabal Sawda',Jabal SawdÄ’	25.20083	56.33993	T	HLL	AE		04				0		377	Asia/Dubai	2011-11-06
+290890	Ra’s Abyaḑ QaÅ£Ä	Ra's Abyad Qata	Ra's Abyad Qata,Ra's Sawami`,Ra’s Abyaḑ QaÅ£Ä,Ra’s ÅžawÄmi‘	24.14785	53.20015	T	PT	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290891	Jabal SÄÅ£if	Jabal Satif	Jabal Satif,Jabal SÄÅ£if	25.49887	56.04789	T	MT	AE		04				0		374	Asia/Dubai	2011-11-06
+290892	Saţḩ ar RÄzbÅ«t	Sath ar Razbut	Saath al Raazboot,Satah,Sath ar Razbut,Saţḩ ar RÄzbÅ«t	24.83333	53.16667	L	OILF	AE	AE	01				0		-9999	Asia/Dubai	2011-11-06
+290893	SarÅ«q ZÄhir	Saruq Zahir	Saruq Zahir,SarÅ«q ZÄhir	24.21007	54.69939	H	SBKH	AE		01				0		32	Asia/Dubai	2011-11-06
+290894	Sarūq al Jasam	Saruq al Jasam	Saruq al Jasam,Sarūq al Jasam	24.55	55.43333	T	DUNE	AE		01				0		181	Asia/Dubai	2011-11-06
+290895	SÄrÅ«q	Saruq	Saruq,SÄrÅ«q	23.04905	53.73279	T	DPR	AE		01				0		75	Asia/Dubai	2011-11-06
+290896	Sarūj	Saruj	Saruj,Sarūj	25.26636	56.30934	P	PPL	AE		00				0		386	Asia/Dubai	2011-11-06
+290897	Sarūb	Sarub	Sarub,Sarūb	23.07561	53.69655	T	DPR	AE		01				0		164	Asia/Dubai	2011-11-06
+290898	Sayḩ Şarm Sayf	Sayh Sarm Sayf	Sayh Sarm Sayf,Sayḩ Şarm Sayf	23.68333	55.48333	T	DPR	AE		01				0		183	Asia/Dubai	2011-11-06
+290899	Ţawī ŞarmĒ	Tawi Sarma'	Tawi Saramin,Tawi Sarma',Tawi Sarmah,Ţawī Şaramin,Ţawī Şarmah,Ţawī ŞarmĒ	24.98018	55.88064	H	WLL	AE		06				0		203	Asia/Dubai	2011-11-06
+290900	Wuqnat Sarīj	Wuqnat Sarij	Wuqnat Sarij,Wuqnat Sarīj	22.97483	53.77287	T	DPR	AE		01				0		55	Asia/Dubai	2011-11-06
+290901	SarÄmÄ«	Sarami	Sarami,SarÄmÄ«	24.1	54.51667	T	HLL	AE		01				0		33	Asia/Dubai	2011-11-06
+290902	Ţawī Şaram	Tawi Saram	Sarm,Tawi Saram,Şarm,Ţawī Şaram	25.4925	56.02583	H	WLL	AE	AE	05				0		114	Asia/Dubai	2011-11-06
+290903	Ra’s Sarab	Ra's Sarab	Ra's Sarab,Ras as-Sareb,Ra’s Sarab,RÄs as-Sareb	24.26252	51.78124	T	PT	AE		01				0		17	Asia/Dubai	2011-11-06
+290904	ÅžaqwÄn	Saqwan	Saqwan,ÅžaqwÄn	23.76608	53.49031	L	GVL	AE		01				0		105	Asia/Dubai	2011-11-06
+290905	MÄ«nÄ’ Åžaqr	Mina' Saqr	Mina Saqr Harbour,Mina' Saqr,MÄ«nÄ’ Åžaqr,Port Saqr	25.79167	55.95333	H	HBR	AE		05				0		-9999	Asia/Dubai	2011-11-06
+290906	WÄdÄ« Saqamqam	Wadi Saqamqam	Wadi Saqamqam,WÄdÄ« Saqamqam	25.16667	56.33333	H	WAD	AE		04				0		160	Asia/Dubai	2011-11-06
+290907	Sabkhat Saqamqam	Sabkhat Saqamqam	Sabkhat Saqamqam	25.15839	56.35618	H	SBKH	AE		04				0		16	Asia/Dubai	2011-11-06
+290908	Jabal Saqamqam	Jabal Saqamqam	Jabal Sakamkam,Jabal Saqamqam	25.18982	56.31074	T	RDGE	AE		04				0		145	Asia/Dubai	2011-11-06
+290909	Saqamqam	Saqamqam	As Saqamqam,Sakamkam,Saqamqam	25.17467	56.33039	P	PPL	AE		04				0		160	Asia/Dubai	2011-11-06
+290910	Saqal	Saqal	Saqal	25.27976	56.1859	V	GRVP	AE		05				0		386	Asia/Dubai	2011-11-06
+290911	Dawḩat ÅžÄniyah	Dawhat Saniyah	Dawhat Saniyah,Dawḩat ÅžÄniyah	24.15343	51.77192	H	COVE	AE		01				0		1	Asia/Dubai	2011-11-06
+290912	Khabrat Sanad	Khabrat Sanad	Khabrat Sanad	24.08333	51.7	H	WLL	AE		01				0		7	Asia/Dubai	2011-11-06
+290913	Sanad	Sanad	Sanad	24.1	51.7	L	LCTY	AE		01				0		14	Asia/Dubai	2011-11-06
+290914	WÄdÄ« SanÄbil	Wadi Sanabil	Wadi Sanabil,WÄdÄ« SanÄbil	24.66808	55.55881	T	TRGD	AE		00				0		221	Asia/Dubai	2011-11-06
+290915	SamnÄn	Samnan	Samnan,SamnÄn	25.3	55.45	L	LCTY	AE		00				0		13	Asia/Dubai	2011-11-06
+290916	Å¢awÄ« SamḩÄn	Tawi Samhan	Tawi Samhan,Tawi Semhan,Tawi Simhan,Å¢awÄ« SamḩÄn	24.82866	55.44335	H	WLL	AE		03				0		122	Asia/Dubai	2011-11-06
+290917	Raml SamḩÄn	Raml Samhan	Raml Samhan,Raml SamḩÄn	24.83333	55.46667	T	SAND	AE		03				0		129	Asia/Dubai	2011-11-06
+290918	SamḩÄn	Samhan	Al Irma,Samhan,SamḩÄn,Semhan	24.83333	55.46667	T	HLL	AE	AE	03				0		129	Asia/Dubai	2011-11-06
+290919	Å¢awÄ« SamḩÄ’	Tawi Samha'	Tawi Samha',Å¢awÄ« SamḩÄ’	25.56444	55.82694	H	WLL	AE		07				0		46	Asia/Dubai	2011-11-06
+290920	Samarat	Samarat	Samarat	25.68907	56.13698	P	PPL	AE		01				0		1211	Asia/Dubai	2011-11-06
+290921	Sayh Samarah	Sayh Samarah	Sayh Samarah	24.21929	55.43518	T	TRGD	AE		01				0		205	Asia/Dubai	2011-11-06
+290922	WÄdÄ« SamÄḩ	Wadi Samah	Wadi Samah,WÄdÄ« SamÄḩ	25.44621	55.98338	H	WAD	AE		05				0		125	Asia/Dubai	2011-11-06
+290923	Jabal SamÄḩ	Jabal Samah	Jabal Samah,Jabal SamÄḩ	25.12605	56.17094	T	MT	AE		00				0		954	Asia/Dubai	2011-11-06
+290924	Jabal SamÄḩ	Jabal Samah	Jabal Samah,Jabal SamÄḩ	25.4165	55.96082	T	DUNE	AE		05				0		204	Asia/Dubai	2011-11-06
+290925	SamÄḩ	Samah	Samah,SamÄḩ	24.91667	56.3	L	TRB	AE		00				0		108	Asia/Dubai	2011-11-06
+290926	Salwá	Salwa	Salwa,Salwah,Salwá,Sawa,Sawá	25.02031	55.14772	L	LCTY	AE		03				0		20	Asia/Dubai	2011-11-06
+290927	Sayḩ Salmá	Sayh Salma	Sayh Salma,Sayḩ Salmá	24.16132	55.4748	T	TRGD	AE		01				0		237	Asia/Dubai	2011-11-06
+290928	Raml as Salmá	Raml as Salma	Raml as Salma,Raml as Salmá	24.16667	55.48333	T	DUNE	AE		01				0		243	Asia/Dubai	2011-11-06
+290929	Sayḩ as Salm	Sayh as Salm	Sayh as Salam,Sayh as Salm,Sayḩ as Salam,Sayḩ as Salm	24.83631	55.29693	T	TRGD	AE		03				0		88	Asia/Dubai	2011-11-06
+290930	Raml as Salm	Raml as Salm	Raml as Salam,Raml as Salm	24.85099	55.25704	T	DUNE	AE		03				0		55	Asia/Dubai	2011-11-06
+290931	WÄdÄ« Sallah	Wadi Sallah	Wadi Sallah,WÄdÄ« Sallah	24.56329	55.64776	T	TRGD	AE		01				0		279	Asia/Dubai	2011-11-06
+290932	Sayḩ Sallah	Sayh Sallah	Sayh Sallah,Sayḩ Sallah	23.93198	55.47704	T	TRGD	AE		01				0		186	Asia/Dubai	2011-11-06
+290933	Sallah	Sallah	Sallah	24.57314	55.61947	L	LCTY	AE		01				0		250	Asia/Dubai	2011-11-06
+290934	WÄdÄ« Sall	Wadi Sall	Wadi Sal,Wadi Sall,WÄdÄ« Sal,WÄdÄ« Sall	25.84083	56.06722	H	WAD	AE	AE	05				0		75	Asia/Dubai	2011-11-06
+290935	Jabal Sall	Jabal Sall	Jabal Sal,Jabal Sall	25.76028	56.08861	T	MT	AE	AE	05				0		799	Asia/Dubai	2011-11-06
+290936	Sall	Sall	Sal,Sall	25.74903	56.09217	V	CULT	AE		05				0		563	Asia/Dubai	2011-11-06
+290937	Falaj as Salj	Falaj as Salj	Falaj as Salj	25.44825	55.98449	H	STMI	AE		05				0		125	Asia/Dubai	2011-11-06
+290938	WÄdÄ« SalÄ«mah	Wadi Salimah	Wadi Salimah,Wadi Salum,WÄdÄ« SalÄ«mah,WÄdÄ« SalÅ«m	24.72335	55.66265	T	TRGD	AE		00				0		244	Asia/Dubai	2011-11-06
+290939	Shu‘bat Salīmah	Shu`bat Salimah	Shu`bat Salimah,Shu`bat Silima,Shu‘bat Salīmah,Shu‘bat Silima	24.0475	55.83278	H	WAD	AE		01				0		324	Asia/Dubai	2011-11-06
+290940	Baţn Salīmah	Batn Salimah	Batn Salima,Batn Salimah,Baţn Salima,Baţn Salīmah	24.91893	55.54126	T	DPR	AE		03				0		166	Asia/Dubai	2011-11-06
+290941	Salīmah	Salimah	Saleema,Salimah,Salīmah	22.9118	54.3397	T	DPR	AE		01				0		164	Asia/Dubai	2011-11-06
+290942	Ţawī Salīm	Tawi Salim	Tawi Salim,Ţawī Salīm	25.33896	55.82837	H	WLL	AE		07				0		79	Asia/Dubai	2011-11-06
+290943	Sayḩ SalÄ«l	Sayh Salil	Madiq al-Salil,Maá¸Ä«q al-SalÄ«l,Sayh Salil,Sayḩ SalÄ«l,Sih Salil	23.13333	55.41667	T	TRGD	AE		00				0		120	Asia/Dubai	2011-11-06
+290944	ÅžÄliḩīyah	Salihiyah	As Salihiyah,AÅŸ ÅžÄliḩīyah,Salihiyah,ÅžÄliḩīyah	25.72161	55.99081	P	PPL	AE		05				0		21	Asia/Dubai	2011-11-06
+290945	Saleh	Saleh	Saleh,Salih,ÅžÄliḩ	26.13333	55.75	L	OILF	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290946	Jabal SalḩÄl	Jabal Salhal	Jabal Salhal,Jabal SalḩÄl	25.25969	56.18567	T	MT	AE		05				0		634	Asia/Dubai	2011-11-06
+290947	Salbūk	Salbuk	Salbuk,Salbūk	23.87316	53.76852	T	DUNE	AE		01				0		55	Asia/Dubai	2011-11-06
+290948	SalÄn KhalfÄn	Salan Khalfan	Salan Khalfan,SalÄn KhalfÄn	23.93776	53.52864	T	DUNE	AE		01				0		43	Asia/Dubai	2011-11-06
+290949	Sabkhat as Salamīyah	Sabkhat as Salamiyah	As Salamiyah Sabkhat,Sabkhat as Salamiyah,Sabkhat as Salamīyah	23.99347	53.76611	H	SBKH	AE		01				0		12	Asia/Dubai	2011-11-06
+290950	SalÄm	Salam	Salam,SalÄm	25.66583	55.8525	H	WLL	AE		07				0		66	Asia/Dubai	2011-11-06
+290951	Sabkhat Salal	Sabkhat Salal	Sabkhat Salal,Sabkhat Salil,Sabkhat Salīl	23.74706	55.12282	H	SBKH	AE		01				0		103	Asia/Dubai	2011-11-06
+290952	Salala	Salala	As Salahah,AÅŸ ÅžalÄḩah,Salahah,Salala,ÅžalÄḩah	24.1955	53.53829	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290953	Bid‘ Salaal	Bid` Salaal	Bid` Salaal,Bid‘ Salaal	23.9	54.7	H	WLL	AE		01				0		90	Asia/Dubai	2011-11-06
+290954	Sala	Sala	Sala	24.16667	55.4	T	DUNE	AE		01				0		198	Asia/Dubai	2011-11-06
+290955	Jabal Sakhbīrī	Jabal Sakhbiri	Jabal Sakhbiri,Jabal Sakhbīrī	25.10639	56.03417	T	HLL	AE		05				0		366	Asia/Dubai	2011-11-06
+290956	SÄ’if	Sa'if	Sa'if,SÄ’if	25.1842	56.24205	P	PPL	AE		04				0		415	Asia/Dubai	2011-11-06
+290957	Å¢awÄ« Sa‘īdÄ«	Tawi Sa`idi	Tawi Sa`idi,Tawi Zaid,Tawi Zayd,Å¢awÄ« Sa‘īdÄ«,Å¢awÄ« Zayd,Å¢ÄwÄ« Zaid	25.54241	55.89153	H	WLL	AE		05				0		26	Asia/Dubai	2011-11-06
+290958	Å¢awÄ« Sa‘īd Bin HuwaydÄn	Tawi Sa`id Bin Huwaydan	Tawi Sa`id Bin Huwaydan,Å¢awÄ« Sa‘īd Bin HuwaydÄn	24.97961	55.78755	H	WLL	AE		06				0		172	Asia/Dubai	2011-11-06
+290959	Bid‘ Sa‘īd	Bid` Sa`id	Bada Sa`id,Bada Sa‘īd,Bid` Sa`id,Bid‘ Sa‘īd	23.65747	52.49121	H	WLL	AE		01				0		68	Asia/Dubai	2011-11-06
+290960	SÄ’ibah	Sa'ibah	Sa'iba,Sa'ibah,Sa’iba,SÄ’ibah	22.95698	54.27151	T	DPR	AE		01				0		98	Asia/Dubai	2011-11-06
+290961	GhaffÄt SaḩmÄ«	Ghaffat Sahmi	Ghaffat Sahmi,GhaffÄt SaḩmÄ«	24.5	55.26667	T	DUNE	AE		01				0		123	Asia/Dubai	2011-11-06
+290962	Sayḩ Sahmah	Sayh Sahmah	Sayh Sahmah,Sayḩ Sahmah	23.15	55.25	T	DPR	AE		00				0		104	Asia/Dubai	2011-11-06
+290963	Ḩaql Sahl	Haql Sahl	Haql Sahl,Sahal,Sahel,Sahil,Ḩaql Sahl	23.70594	54.32028	L	OILF	AE		01				0		114	Asia/Dubai	2011-11-06
+290964	WÄdÄ« Åžaḩanah	Wadi Sahanah	Wadi Sahanah,WÄdÄ« Åžaḩanah	25.28025	56.32255	H	WAD	AE		00				0		63	Asia/Dubai	2011-11-06
+290965	Saḩanah	Sahanah	Sahanah,Saḩanah	25.28556	56.3084	P	PPL	AE		06				0		99	Asia/Dubai	2011-11-06
+290966	SaḩÄb	Sahab	Sahab,SaḩÄb	25.15848	56.08929	L	LCTY	AE		05				0		286	Asia/Dubai	2011-11-06
+290967	Å¢awÄ« SÄji‘ah	Tawi Saji`ah	Tawi Saghyah,Tawi Saja'a,Tawi Saja`ah,Tawi Saji`ah,TÄwÄ« Saja’a,Å¢awÄ« Saghyah,Å¢awÄ« SÄja‘ah,Å¢awÄ« SÄji‘ah	25.30552	55.58034	H	WLL	AE		06				0		39	Asia/Dubai	2011-11-06
+290968	Saghyah	Saghyah	Saghyah,Sajaa	25.25	55.78333	L	GASF	AE	AE	06				0		113	Asia/Dubai	2011-11-06
+290969	Sagh‘ayn	Sagh`ayn	Sagh'ain,Sagh`ayn,Sagh‘ayn,Sagh’ain	24.53333	51.11667	H	WLL	AE		01				0		3	Asia/Dubai	2011-11-06
+290970	WÄdÄ« aÅŸ ÅžafÅŸaf	Wadi as Safsaf	Wadi as Safsaf,WÄdÄ« aÅŸ ÅžafÅŸaf	25.33999	56.02183	H	WAD	AE		00				0		201	Asia/Dubai	2011-11-06
+290971	Ţawī Şafşaf	Tawi Safsaf	Tawi Safsaf,Ţawī Şafşaf	25.32833	56.01056	H	WLL	AE		02				0		219	Asia/Dubai	2011-11-06
+290972	Jabal ÅžafÅŸaf	Jabal Safsaf	Jabal Safsaf,Jabal ÅžafÅŸaf	25.33111	56.02306	T	HLL	AE		02				0		198	Asia/Dubai	2011-11-06
+290973	WÄdÄ« Åžafad	Wadi Safad	Wadi Safad,WÄdÄ« Åžafad	25.22915	56.35145	H	WAD	AE		04				0		12	Asia/Dubai	2011-11-06
+290974	Jabal Åžafad	Jabal Safad	Jabal Safad,Jabal Åžafad	25.23315	56.28695	T	MT	AE		04				0		640	Asia/Dubai	2011-11-06
+290975	Åžafad	Safad	Safad,Sufad,Åžafad	25.22165	56.3239	P	PPL	AE		04				0		71	Asia/Dubai	2011-11-06
+290976	WÄdÄ« ÅžafÄ	Wadi Safa	Wadi Safa,WÄdÄ« ÅžafÄ	25.06554	55.26203	T	TRGD	AE		03				0		30	Asia/Dubai	2011-11-06
+290977	Ra’s aş Şadr	Ra's as Sadr	Ra's as Sadr,Ra’s aş Şadr	24.6753	54.65663	T	PT	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290978	Ra’s as Sa‘dÄ«yÄt	Ra's as Sa`diyat	Ra's as Sa`diyat,Ras al Sa`diya,Ras al Sa‘diya,Ra’s as Sa‘dÄ«yÄt	24.58542	54.47465	T	PT	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290979	Khawr as Sa‘dÄ«yÄt	Khawr as Sa`diyat	Khawr Essadiyat,Khawr EssÄdiyÄt,Khawr as Sa`diyat,Khawr as Sa‘dÄ«yÄt	24.62296	54.46416	H	CHNM	AE		01				0		-9999	Asia/Dubai	2011-11-06
+290980	Ţawī Sa‘dīyah	Tawi Sa`diyah	Tawi Sa`diyah,Ţawī Sa‘dīyah	24.87867	56.16899	H	WLL	AE		05				0		191	Asia/Dubai	2011-11-06
+290981	Sa‘dīyah	Sa`diyah	Sa`diyah,Sa‘dīyah	23.69426	54.10408	H	WLL	AE		01				0		97	Asia/Dubai	2011-11-06
+290982	Sa‘dÄ«yah	Sa`diyah	Sa`diyah,Sa‘dÄ«yah,Tawi Sa'adiya,TÄwÄ« Sa’adiya	25.1	55.7	V	TREE	AE		06				0		156	Asia/Dubai	2011-11-06
+290983	Å¢awÄ« as SÄdd	Tawi as Sadd	Sa`ad,Sa‘ad,Tawi Sa`d,Tawi as Sadd,Å¢awÄ« Sa‘d,Å¢awÄ« as SÄdd	24.20806	55.50417	H	WLLS	AE	AE	01				0		211	Asia/Dubai	2011-11-06
+290984	Raml as SÄdd	Raml as Sadd	Raml Sa`d,Raml Sa‘d,Raml as Sadd,Raml as SÄdd	24.2	55.48333	T	DUNE	AE	AE	01				0		213	Asia/Dubai	2011-11-06
+290985	Jabal Sa‘d	Jabal Sa`d	Jabal Sa`d,Jabal Sa‘d	25.11355	56.11717	T	MT	AE		05				0		427	Asia/Dubai	2011-11-06
+290986	Sa‘d	Sa`d	Sa`d,Sa‘d	25.26667	56.28333	P	PPL	AE		06				0		213	Asia/Dubai	2011-11-06
+290987	Ramlat Åžabr	Ramlat Sabr	Ramlat Sabar,Ramlat Sabr,Ramlat Åžabr	24.03866	55.4161	T	DUNE	AE		01				0		202	Asia/Dubai	2011-11-06
+290988	Sabkhah	Sabkhah	Sabakha,Sabkhah	23.3	53.85	P	PPL	AE	AE	01				0		136	Asia/Dubai	2011-11-06
+290989	Sabkhah	Sabkhah	Sabkha,Sabkhah,Sabukhah	23.12917	53.98181	P	PPL	AE		01				0		166	Asia/Dubai	2011-11-06
+290990	Sabkah	Sabkah	Sabka,Sabkah	25.29255	55.39472	H	WLL	AE		03				0		8	Asia/Dubai	2011-11-06
+290991	Ţawī Sabarad	Tawi Sabarad	Tawi Sabarad,Ţawī Sabarad	25.29389	55.89944	H	WLL	AE		06				0		144	Asia/Dubai	2011-11-06
+290992	Sabalah	Sabalah	Sabalah,Sablah	22.92596	53.45262	T	DPR	AE		01				0		61	Asia/Dubai	2011-11-06
+290993	SabÄ’is	Saba'is	Saba'is,SabÄ’is	24.83333	55.5	L	TRB	AE		03				0		169	Asia/Dubai	2011-11-06
+290994	Qarn Şa‘bah	Qarn Sa`bah	Qarn Sa`bah,Qarn Şa‘bah	24.46867	55.72733	T	HLL	AE		01				0		330	Asia/Dubai	2011-11-06
+290995	Sa‘abah	Sa`abah	Sa`abah,Sa`bah,Sa‘abah,Sa‘bah	24.99417	56.025	P	PPLQ	AE		05				0		306	Asia/Dubai	2011-11-06
+290996	NaqÄ Sa‘Ädah	Naqa Sa`adah	Naqa Sa`adah,NaqÄ Sa‘Ädah	23.42818	55.38215	T	DUNE	AE		01				0		195	Asia/Dubai	2011-11-06
+290997	Ţawī Ruwayyah	Tawi Ruwayyah	Tawi Mirdif,Tawi Ruwayyah,Ţawī Mirdif,Ţawī Ruwayyah	24.89283	55.66387	H	WLL	AE		03				0		209	Asia/Dubai	2011-11-06
+290998	Ruwayyah	Ruwayyah	Ruwayyah	24.8871	55.66016	L	LCTY	AE		03				0		205	Asia/Dubai	2011-11-06
+290999	Ra’s Ruwaysīyah	Ra's Ruwaysiyah	Ra's Harmiyah,Ra's Ru'aysiyah,Ra's Ruwaysiyah,Ra’s Harmīyah,Ra’s Ruwaysīyah,Ra’s Ru’aysīyah	24.12591	53.44243	T	PT	AE		01				0		-9999	Asia/Dubai	2011-11-06
+291000	Ruwaysīyah	Ruwaysiyah	Ru'aysiyah,Ruwaisiyya,Ruwaysiyah,Ruwaysīyah,Ruweisiya,Ru’aysīyah	24.11927	53.4354	L	LCTY	AE		01				0		1	Asia/Dubai	2011-11-06
+291001	Sayḩ Ruwayshid	Sayh Ruwayshid	Sayh Ruwayshid,Sayḩ Ruwayshid	25.3	55.66667	T	TRGD	AE		06				0		76	Asia/Dubai	2011-11-06
+291002	Khawr ar Ruways	Khawr ar Ruways	Khawr ar Ru'ays,Khawr ar Ruways,Khawr ar Ru’ays	24.13966	52.67385	H	INLT	AE		01				0		1	Asia/Dubai	2011-11-06
+291003	Ruwaymayah	Ruwaymayah	Ruwaymayah	24.80098	55.64042	L	LCTY	AE		03				0		230	Asia/Dubai	2011-11-06
+291004	Jabal Ruwayḑah	Jabal Ruwaydah	Jabal Ruwaydah,Jabal Ruwayḑah	25.50556	56.08844	T	MT	AE		04				0		493	Asia/Dubai	2011-11-06
+291005	Ruwayḑah	Ruwaydah	Riweidah,Riweiá¸ah,Ruwaidha,Ruwaydah,Ruwayḑah	23.1039	53.97789	L	OAS	AE		01				0		98	Asia/Dubai	2011-11-06
+291006	RuwÄlÄ	Ruwala	Ruwala,Ruwalla,RuwÄlÄ	23.2956	54.20134	H	SPNG	AE		01				0		158	Asia/Dubai	2011-11-06
+291007	Ruqayyib	Ruqayyib	Ruqayyib	23.93586	52.88188	H	WLL	AE		01				0		59	Asia/Dubai	2011-11-06
+291008	Ruqayyah	Ruqayyah	Ruqayyah	23.28333	54.65	H	WLL	AE		01				0		126	Asia/Dubai	2011-11-06
+291009	Ruqayyah	Ruqayyah	Ruqayyah	23.03532	53.60933	L	OAS	AE		01				0		159	Asia/Dubai	2011-11-06
+291010	Shaqqat ar Ruqayţuwah	Shaqqat ar Ruqaytuwah	Ruqaytuwwah,Ruqayţuwwah,Shaqqat ar Ruqaytuwah,Shaqqat ar Ruqaytuwwah,Shaqqat ar Ruqayţuwah,Shaqqat ar Ruqayţuwwah	23.38663	55.25216	T	TRGD	AE		01				0		121	Asia/Dubai	2011-11-06
+291011	Sabkhat ar Ruqayţuwah	Sabkhat ar Ruqaytuwah	Sabkhat Bu Satma,Sabkhat Bū Satma,Sabkhat ar Ruqaytuwah,Sabkhat ar Ruqaytuwwah,Sabkhat ar Ruqayţuwah,Sabkhat ar Ruqayţūwwah	23.44808	55.11346	H	SBKH	AE		01				0		110	Asia/Dubai	2011-11-06
+291012	‘Irq Ruqayţuwah	`Irq Ruqaytuwah	`Irq Ruqaytuwah,`Irq Ruqaytuwwah,‘Irq Ruqayţuwah,‘Irq Ruqayţuwwah	23.38449	55.20788	T	DUNE	AE		01				0		187	Asia/Dubai	2011-11-06
+291013	Ţawī Ruqayshah	Tawi Ruqayshah	Tawi Ruqayshah,Ţawī Ruqayshah	24.98485	55.78776	H	WLL	AE		06				0		163	Asia/Dubai	2011-11-06
+291014	Ruq‘at Ya‘Äribah	Ruq`at Ya`aribah	Raq`al al Jarba,Raq‘al al Jarba,Ruq`at Ya`aribah,Ruq‘at Ya‘Äribah	23.89652	55.48556	T	TRGD	AE		01				0		145	Asia/Dubai	2011-11-06
+291015	Ruq‘at Tulūl	Ruq`at Tulul	Ruq`at Tulul,Ruq‘at Tulūl	22.95827	55.20907	T	DPR	AE		01				0		126	Asia/Dubai	2011-11-06
+291016	Ruq‘at Sunayyim	Ruq`at Sunayyim	Ruq`at Sunayyim,Ruq‘at Sunayyim	23.65155	55.29791	T	TRGD	AE		01				0		114	Asia/Dubai	2011-11-06
+291017	Ruq‘at Ruqayţuwah	Ruq`at Ruqaytuwah	Ruq`at Ruqatuwwah,Ruq`at Ruqaytuwah,Ruq`at Ruqaytuwwah,Ruq‘at Ruqayţuwah,Ruq‘at Ruqayţuwwah,Ruq‘at Ruqaţuwwah	23.40594	55.28253	T	DPR	AE		01				0		121	Asia/Dubai	2011-11-06
+291018	Ruq‘at Nuşb	Ruq`at Nusb	Ruq`at Nusb,Ruq`at Nusub,Ruq‘at Nuşb,Ruq‘at Nuşub	24.05	55.41667	T	SAND	AE		01				0		173	Asia/Dubai	2011-11-06
+291019	Ruq‘at Muşallī	Ruq`at Musalli	Ruq`at Musalli,Ruq‘at Muşallī	24.85889	55.57612	T	DUNE	AE		03				0		195	Asia/Dubai	2011-11-06
+291020	Ruq‘at Musajira	Ruq`at Musajira	Ruq`at Musajira,Ruq‘at Musajira	24.84075	55.65624	T	DUNE	AE		03				0		228	Asia/Dubai	2011-11-06
+291021	Muḩayşinah	Muhaysinah	Muhaysinah,Muḩayşinah,Ruq`ah Muhassanah,Ruq`at Muhassanah,Ruq‘ah Muḩaşşanah,Ruq‘at Muḩaşşanah	25.25528	55.39278	L	LCTY	AE	AE	03				0		15	Asia/Dubai	2011-11-06
+291022	Ruq‘at Maghis	Ruq`at Maghis	Ruq`at Maghis,Ruq‘at Maghis	23.61802	55.4466	T	TRGD	AE		01				0		139	Asia/Dubai	2011-11-06
+291023	Ruq‘at Ḩumaydī	Ruq`at Humaydi	Hameidha,Humaydah,Ruq`at Humaydi,Ruq‘at Ḩumaydī,Ḩumayḑah	24.79964	55.70185	T	DUNE	AE		03				0		260	Asia/Dubai	2011-11-06
+291024	Ruq‘at Ḩulayw	Ruq`at Hulayw	Ruq`at Hulayw,Ruq‘at Ḩulayw	23.63585	55.36908	T	TRGD	AE		01				0		157	Asia/Dubai	2011-11-06
+291025	Rafadah	Rafadah	Rafadah,Ruq`at Bu Zafidah,Ruq‘at Bū Z̧afīdah	24.86014	55.65022	T	DUNE	AE		03				0		217	Asia/Dubai	2011-11-06
+291026	Ruq‘at al Mīr	Ruq`at al Mir	Ruq`at al Mir,Ruq‘at al Mīr	22.98574	55.19227	H	SBKH	AE		01				0		100	Asia/Dubai	2011-11-06
+291027	Ruq‘at al ḨÄdh	Ruq`at al Hadh	Ruq`at al Hadh,Ruq‘at al ḨÄdh	22.94468	55.1886	H	SBKH	AE		01				0		105	Asia/Dubai	2011-11-06
+291028	Ruq‘at al ‘ArÄd	Ruq`at al `Arad	Raq`at al-`Arad,Raq‘at al-‘ArÄd,Ruq`at al Ard,Ruq`at al `Arad,Ruq‘at al Ard,Ruq‘at al ‘ArÄd	23.86265	55.50198	T	TRGD	AE		01				0		191	Asia/Dubai	2011-11-06
+291029	WÄdÄ« Rumḩ	Wadi Rumh	Wadi Rumh,WÄdÄ« Rumḩ	25.04006	56.32907	H	WAD	AE		06				0		24	Asia/Dubai	2011-11-06
+291030	Ţawī Rumḩ	Tawi Rumh	Tawi Rumh,Ţawī Rumḩ	25.01667	56.35	H	WLL	AE		06				0		7	Asia/Dubai	2011-11-06
+291031	Jabal Rumḩ	Jabal Rumh	Jabal Rumh,Jabal Rumḩ	25.02953	56.2807	T	MT	AE		04				0		369	Asia/Dubai	2011-11-06
+291032	Rumaylī	Rumayli	Rumayli,Rumaylī	26.02472	56.11083	V	CULT	AE		05				0		565	Asia/Dubai	2011-11-06
+291033	Jabal ar Rumaylah	Jabal ar Rumaylah	Jabal ar Rumaylah	25.43333	56.35	T	HLL	AE		04				0		34	Asia/Dubai	2011-11-06
+291034	Jabal Umayliḩ	Jabal Umaylih	Jabal Rumaylah,Jabal Umaylah,Jabal Umaylih,Jabal Umayliḩ	25.0503	55.82244	T	HLL	AE		06				0		253	Asia/Dubai	2011-11-06
+291035	RÅ«l á¸adnÄ	Rul Dadna	Rol Dadnah,Rol Ḍadnah,Rul Dadna,Rul Dadnah,Rul Dhadna,RÅ«l á¸adnah,RÅ«l á¸adnÄ	25.55481	56.34546	P	PPL	AE		04				0		26	Asia/Dubai	2011-11-06
+291036	Ţawī ar Rūl	Tawi ar Rul	Tawi ar Rul,Ţawī ar Rūl	25.57602	56.35214	H	WLLQ	AE		04				0		-9999	Asia/Dubai	2011-11-06
+291037	RukbÄt	Rukbat	Rukbat,RukbÄt	24.74428	54.65006	T	SPIT	AE		01				0		7	Asia/Dubai	2011-11-06
+291038	Ramlat ar Ruhayl	Ramlat ar Ruhayl	Ramlat ar Ruhayl	23.55	55.46667	T	DUNE	AE		00				0		188	Asia/Dubai	2011-11-06
+291039	Jabal Ruḩam	Jabal Ruham	Jabal Ruham,Jabal Ruḩam	25.13333	56.26667	T	HLL	AE		04				0		93	Asia/Dubai	2011-11-06
+291040	RughaylÄt	Rughaylat	Righeilat,RigheilÄt,Rughaylat,RughaylÄt,Rugheilat	25.10552	56.3563	P	PPL	AE		04				0		23	Asia/Dubai	2011-11-06
+291041	‘Ayn ar Rufayşah	`Ayn ar Rufaysah	`Ayn ar Rufaysah,‘Ayn ar Rufayşah	25.35	56.31667	H	SPNG	AE		06				0		77	Asia/Dubai	2011-11-06
+291042	RufayÅŸah	Rufaysah	Rufaysah,RufayÅŸah	25.16778	56.17722	V	CULT	AE		01				0		389	Asia/Dubai	2011-11-06
+291043	Bandar Rudaym	Bandar Rudaym	Bandar Rudaym	24.06323	53.69867	H	COVE	AE		01				0		-9999	Asia/Dubai	2011-11-06
+291044	Å¢awÄ« Rubayyah	Tawi Rubayyah	Tawi Rabaiya,Tawi Rubayyah,TÄwÄ« Rabaiya,Å¢awÄ« Rubayyah	25.73333	56.05	H	WLL	AE	AE	05				0		361	Asia/Dubai	2011-11-06
+291045	Rubay‘ah	Rubay`ah	Rubay`ah,Rubay‘ah	24.25	55.08333	T	DPR	AE		01				0		123	Asia/Dubai	2011-11-06
+291046	WÄdÄ« RiyÄmah	Wadi Riyamah	Wadi Riyama,Wadi Riyamah,WÄdÄ« RiyÄmah	25.55023	56.04815	H	WAD	AE		04				0		237	Asia/Dubai	2011-11-06
+291047	RiyÄmah	Riyamah	Riyama,Riyamah,RiyÄmah	25.54564	56.05836	P	PPL	AE		04				0		179	Asia/Dubai	2011-11-06
+291048	Jabal RiyÄdir	Jabal Riyadir	Jabal Riyadir,Jabal RiyÄdir	25.3	56.33333	T	MT	AE		06				0		42	Asia/Dubai	2011-11-06
+291049	Å¢awÄ« RiqÄnah	Tawi Riqanah	Riqana,Tawi Riqanah,Å¢awÄ« RiqÄnah	24.3418	55.69959	H	WLL	AE		01				0		310	Asia/Dubai	2011-11-06
+291050	‘Uqlat ar Rimth	`Uqlat ar Rimth	`Uqlat ar Rimth,‘Uqlat ar Rimth	24.43333	51.11667	H	WLL	AE		01				0		6	Asia/Dubai	2011-11-06
+291051	RimÄḩ	Rimah	Al Ramah,Rimah,RimÄḩ	24.79667	55.61194	V	TREE	AE	AE	03				0		224	Asia/Dubai	2011-11-06
+291052	Rimá	Rima	Rima,Rimá	25.16784	56.11795	P	PPL	AE		05				0		519	Asia/Dubai	2011-11-06
+291053	RiḩyÄt	Rihyat	Rihyat,RiḩyÄt	24.88151	55.44885	T	SAND	AE		03				0		152	Asia/Dubai	2011-11-06
+291054	Ţawī Rifđ	Tawi Rifa`	Tawi Rifa`,Ţawī Rifđ	24.96961	55.78658	H	WLL	AE		06				0		173	Asia/Dubai	2011-11-06
+291055	GhalÄ«lat RÄziqÄ«	Ghalilat Raziqi	Ghalilat Raziqi,GhalÄ«lat RÄziqÄ«	25.6	56.28333	H	WADM	AE		04				0		95	Asia/Dubai	2011-11-06
+291056	WÄdÄ« Rayj	Wadi Rayj	Wadi Rayj,WÄdÄ« Rayj	25.0183	55.2567	T	TRGD	AE		03				0		37	Asia/Dubai	2011-11-06
+291057	WÄdÄ« RaybÄ«yah	Wadi Raybiyah	Wadi Raibiya,Wadi Raybiyah,WÄdÄ« Raibiya,WÄdÄ« RaybÄ«yah	25.73472	56.02722	H	WAD	AE	AE	05				0		9	Asia/Dubai	2011-11-06
+291058	Rawḑah	Rawdah	Rawdah,Rawḑah	24.09028	55.56194	T	DPR	AE		01				0		215	Asia/Dubai	2011-11-06
+291059	Ra’s SiyÄd	Ra's Siyad	Ra's Seyad,Ra's Siyad,Ra’s SeyÄd,Ra’s SiyÄd	24.55	51.65	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+291060	Ra’s Qal‘ah	Ra's Qal`ah	Ra's Qal`ah,Ra’s Qal‘ah	24.19222	55.58333	T	DUNE	AE		01				0		235	Asia/Dubai	2011-11-06
+291061	Ar RÄshidÄ«yah	Ar Rashidiyah	Ar Rashidiyah,Ar RÄshidÄ«yah,Rashdia,Rashidiyah,RashÄ«dÄ«yah	25.22365	55.38894	P	PPLX	AE		03				0		20	Asia/Dubai	2011-11-06
+291062	Å¢awÄ« RÄshid Bin Sa‘īd	Tawi Rashid Bin Sa`id	Tawi Rashid Bin Sa`id,Å¢awÄ« RÄshid Bin Sa‘īd	25.65528	55.95861	H	WLL	AE		05				0		22	Asia/Dubai	2011-11-06
+291063	Å¢awÄ« RÄshid Bin Ḩamad	Tawi Rashid Bin Hamad	Tawi Rashid Bin Hamad,Å¢awÄ« RÄshid Bin Ḩamad	25.24	55.91278	H	WLL	AE		06				0		136	Asia/Dubai	2011-11-06
+291064	RÄshid al GharbÄ«	Rashid al Gharbi	Rashid al Gharbi,RÄshid al GharbÄ«,Tawi Rashid Gharbiyah,Tawi Rashid al Gharbiyah,Å¢awÄ« RÄshid GharbÄ«yah,Å¢awÄ« RÄshid al GharbÄ«yah	25.35833	55.70528	H	WLL	AE	AE	06				0		74	Asia/Dubai	2011-11-06
+291065	Å¢awÄ« RÄshidah	Tawi Rashidah	Tawi Rashidah,Å¢awÄ« RÄshidah	25.16667	55.96667	H	WLL	AE		05				0		234	Asia/Dubai	2011-11-06
+291066	Å¢awÄ« RÄshid	Tawi Rashid	Tawi Rashid,Å¢awÄ« RÄshid	25.33333	55.78333	H	WLL	AE		06				0		105	Asia/Dubai	2011-11-06
+291067	Nadd RÄshid	Nadd Rashid	Nadd Rashid,Nadd RÄshid	25.21944	55.40556	T	DUNE	AE		03				0		24	Asia/Dubai	2011-11-06
+291068	MÄ«nÄ’ RÄshid	Mina' Rashid	Mina' Rashid,MÄ«nÄ’ RÄshid,Port Rashid,Rachid Port	25.27056	55.29083	L	PRT	AE		03				0		-9999	Asia/Dubai	2011-11-06
+291069	Ḩaql RÄshid	Haql Rashid	Haql Rashid,Rashid Oilfield,Ḩaql RÄshid	25.23333	55.2	L	OILF	AE	AE	03				0		-9999	Asia/Dubai	2011-11-06
+291070	Barqat RashÄ«d	Barqat Rashid	Bada` Rashid,Bada‘ Rashid,Barqat Rashid,Barqat RashÄ«d,Barqat RÄshid	24.00562	54.01885	T	HLL	AE		01				0		18	Asia/Dubai	2011-11-06
+291071	Raseen	Raseen	Raseen	23.68333	54.78333	H	WLL	AE		01				0		130	Asia/Dubai	2011-11-06
+291072	Rasan	Rasan	Rasan	24.15	55.21667	T	DUNE	AE		01				0		151	Asia/Dubai	2011-11-06
+291073	Khawr Ra’s al Khaymah	Khawr Ra's al Khaymah	Khawr Khaymah,Khawr Ra's al Khaymah,Khawr Ra’s al Khaymah	25.78333	55.95	H	LGN	AE		05				0		-9999	Asia/Dubai	2011-11-06
+291074	Ra’s al Khaymah	Ra's al Khaymah	Julfa,Khaimah,Ra's al Khaymah,Ras al Khaima,Ras al Khaimah,Ra’s al Khaymah,RÄs al Khaima,ras alkhymt,رأس الخيمة	25.78953	55.9432	P	PPLA	AE		05				115949		27	Asia/Dubai	2011-11-06
+291075	Ra’s al Khaymah	Ra's al Khaymah	Jasimi Shaikhdom of Ras al-Khaimah,Ra's al Khaymah,Ran al Khaima,Ras al Khaima,Ras el Khaimah,Ras el Khaïmah,Raʼs al Khaymah,Ra’s al Khaymah,Sheikhdom of Ras al Khaimah,State of Ras Al Khaima,ras alkhymt,رأس الخيمة	25.66667	56	A	ADM1	AE		05				187535		12	Asia/Dubai	2011-11-05
+291076	WÄdÄ« Ra’s	Wadi Ra's	Wadi Ra's,WÄdÄ« Ra’s	25.10156	56.35801	H	WAD	AE		04				0		23	Asia/Dubai	2011-11-06
+291077	Jabal Ra’s	Jabal Ra's	Jabal Ra's,Jabal Ra’s	25.32634	56.3712	T	MT	AE		00				0		331	Asia/Dubai	2011-11-06
+291078	Sayḩ ar Raqq	Sayh ar Raqq	Sayh ar Raq,Sayh ar Raqq,Sayḩ ar Raq,Sayḩ ar Raqq	24.06667	55.83333	T	TRGD	AE	AE	01				0		316	Asia/Dubai	2011-11-06
+291079	Jabal Rams	Jabal Rams	Jabal Rams	25.86917	56.06917	T	MT	AE		05				0		774	Asia/Dubai	2011-11-06
+291080	RamrÄmÄ«yÄt	Ramramiyat	Birkat al Ju,Ramramiyah,Ramramiyat,Ramrammiyat,RamrammiyÄt,RamramÄ«yah,RamrÄmÄ«yÄt	23.98231	53.7953	T	HLL	AE		01				0	68	69	Asia/Dubai	2011-11-06
+291081	Ḩabl RamrÄm	Habl Ramram	Habl Ramram,Ḩabl RamrÄm	24.9	55.3	T	SAND	AE		03				0		62	Asia/Dubai	2011-11-06
+291082	Ḩabl RamrÄm	Habl Ramram	Habl Ramram,Ḩabl RamrÄm	24.91667	55.3	T	DUNE	AE		03				0		74	Asia/Dubai	2011-11-06
+291083	Za‘bīl	Za`bil	Raml Sabil,Raml Za`bil,Raml Za‘bīl,Za`bil,Za‘bīl	25.20408	55.2875	P	PPLX	AE		03				0		6	Asia/Dubai	2011-11-06
+291084	Ramlat ZarÄrah	Ramlat Zararah	Ramlat Zarara,Ramlat Zararah,Ramlat ZarÄrah	22.90077	54.49992	L	AREA	AE		01				0		131	Asia/Dubai	2011-11-06
+291085	Ramlah	Ramlah	Ramla,Ramlah,RamlÄ	25.35979	56.04452	P	PPL	AE		04				0		264	Asia/Dubai	2011-11-06
+291086	JazÄ«rat RamḩÄn	Jazirat Ramhan	Jazirat Ramhan,JazÄ«rat RamḩÄn,Ramhan,RamḩÄn,jzyrt rmhan,جزيرة رمحان	24.53778	54.52419	T	ISL	AE		01				0		10	Asia/Dubai	2011-11-06
+291087	Å¢awÄ« RamÄrÄ«m	Tawi Ramarim	Tawi Ramarim,Tawi Rimarim,Tawi Rumayram,TÄwÄ« Rimarim,Å¢awÄ« RamÄrÄ«m,Å¢awÄ« Rumayram	25.36083	55.535	H	WLL	AE		02				0		12	Asia/Dubai	2011-11-06
+291088	Ramaq	Ramaq	Ramaq	24.93333	55.13333	T	SAND	AE		03				0		42	Asia/Dubai	2011-11-06
+291089	QÅ«r ar RÄkibÄ«	Qur ar Rakibi	Qur ar Rakibi,QÅ«r ar RÄkibÄ«	25.5841	56.32873	T	MT	AE		04				0		357	Asia/Dubai	2011-11-06
+291090	NaqÄ RÄkah	Naqa Rakah	Naqa Rakah,NaqÄ RÄkah	23.95296	55.43484	T	DUNE	AE		01				0		232	Asia/Dubai	2011-11-06
+291091	GhÄf Rakaḑ	Ghaf Rakad	Ghaf Rakad,GhÄf Rakaḑ	24.51667	55.41667	T	HLL	AE		01				0		179	Asia/Dubai	2011-11-06
+291092	Sayḩ ar Rajībah	Sayh ar Rajibah	Sayh ar Rajibah,Sayḩ ar Rajībah	24.8	55.26667	T	TRGD	AE		03				0		71	Asia/Dubai	2011-11-06
+291093	Ḩadabat ar Rajībah	Hadabat ar Rajibah	Hadabat ar Rajibah,Ḩadabat ar Rajībah	24.8042	55.2656	T	HLL	AE		03				0		67	Asia/Dubai	2011-11-06
+291094	Raighnaan	Raighnaan	Raighnaan	23.16667	54.83333	H	WLL	AE		01				0		114	Asia/Dubai	2011-11-06
+291095	Raighan	Raighan	Raighan	23.26667	54.73333	H	WLL	AE		01				0		129	Asia/Dubai	2011-11-06
+291096	Raigha	Raigha	Raigha	24.44577	55.15561	T	SAND	AE		01				0		128	Asia/Dubai	2011-11-06
+291097	Raḩmah	Rahmah	Rahmah,Raḩmah	25.68	55.86361	H	WLL	AE		07				0		61	Asia/Dubai	2011-11-06
+291098	Rahfīyah	Rahfiyah	Rahfiya,Rahfiyah,Rahfīyah	22.79933	54.90797	T	DPR	AE		01				0		173	Asia/Dubai	2011-11-06
+291099	Jabal ar Raḩraḩ	Jabal ar Rahrah	Jabal Rahah,Jabal RÄḩah,Jabal ar Rahrah,Jabal ar Raḩraḩ	25.94833	56.14194	T	MT	AE	AE	05				0		1586	Asia/Dubai	2011-11-06
+291100	WÄdÄ« Raḩbah	Wadi Rahbah	Wadi Rahabah,Wadi Rahbah,WÄdÄ« Raḩabah,WÄdÄ« Raḩbah	25.92472	56.07167	H	WAD	AE	AE	05				0		12	Asia/Dubai	2011-11-06
+291101	Jabal Raḩabah	Jabal Rahabah	Jabal Rahabah,Jabal Raḩabah,Jabal ar Ra`aylah,Jabal ar Ra‘aylah	25.92583	56.11667	T	MT	AE	AE	05				0		1539	Asia/Dubai	2011-11-06
+291102	Raḩabah	Rahabah	Rahabah,Raḩabah	25.98333	56.08333	L	TRB	AE		05				0		37	Asia/Dubai	2011-11-06
+291103	Raḩá	Raha	Raha,Raḩá	24.87639	56.2175	V	CULT	AE		05				0		140	Asia/Dubai	2011-11-06
+291105	NaqÄ Raghwah	Naqa Raghwah	Naqa Raghawa,Naqa Raghwah,NaqÄ Raghawa,NaqÄ Raghwah	24.44834	55.23357	T	DUNE	AE		01				0		141	Asia/Dubai	2011-11-06
+291106	Å¢awÄ« Rafī‘ah	Tawi Rafi`ah	Tawi Rafi`ah,Tawi Rafiya,Tawi Rayifah,TÄwÄ« Rafiya,TÄwÄ« Rayifah,Å¢awÄ« Rafī‘ah	25.31972	55.73583	H	WLL	AE		06				0		79	Asia/Dubai	2011-11-06
+291107	Rafaq	Rafaq	Rafaq	24.87471	56.24559	P	PPL	AE		05				0		321	Asia/Dubai	2011-11-06
+291108	Å¢awÄ« RafÄh	Tawi Rafah	Tawi Rafah,Å¢awÄ« RafÄh	25.28333	55.88333	H	WLL	AE		06				0		110	Asia/Dubai	2011-11-06
+291109	Å¢awÄ« RafÄdah	Tawi Rafadah	Tawi Rafaada,Tawi Rafadah,Å¢awÄ« Rafaada,Å¢awÄ« RafÄdah	24.89462	55.74432	H	WLL	AE		06				0		185	Asia/Dubai	2011-11-06
+291110	Rafđ	Rafa`	Rafa`,Rafđ	25.34701	56.35014	P	PPL	AE		06				0		40	Asia/Dubai	2011-11-06
+291111	Radūm	Radum	Radum,Radūm	23.09185	53.61473	L	OAS	AE		01				0		172	Asia/Dubai	2011-11-06
+291112	RadÄt Ramz	Radat Ramz	Radat Ramz,Radath Ramz,RadÄt Ramz	25.5	56.1	H	WLL	AE	AE	05				0		218	Asia/Dubai	2011-11-06
+291113	Jabal ar Rabbah	Jabal ar Rabbah	Jabal ar Rabbah	25.39071	56.06641	T	HLL	AE		05				0		322	Asia/Dubai	2011-11-06
+291114	Sayḩ Rab‘	Sayh Rab`	Sayh Rab`,Sayḩ Rab‘	24.61506	54.86516	T	TRGD	AE		01				0		48	Asia/Dubai	2011-11-06
+291115	Quwayd	Quwayd	Quwayd	25.13333	56.05	L	TRB	AE		05				0		488	Asia/Dubai	2011-11-06
+291116	Quţūf	Qutuf	Qatuf,Qaṭūf,Qutuf,Quţūf	23.10971	53.72738	P	PPL	AE		01				0		87	Asia/Dubai	2011-11-06
+291117	Quţūf	Qutuf	Qutuf,Quţūf	23.10794	53.69333	T	DPR	AE		01				0		80	Asia/Dubai	2011-11-06
+291118	Jabal Qutayyib	Jabal Qutayyib	Jabal Qutayyib	25.94139	56.09333	T	HLL	AE		05				0		881	Asia/Dubai	2011-11-06
+291119	Jabal al Qusīy	Jabal al Qusiy	Jabal al Qusiy,Jabal al Qusīy	25.06667	56.31667	T	HLL	AE		06				0		90	Asia/Dubai	2011-11-06
+291120	QushÄsh	Qushash	Qushash,QushÄsh	23.95539	53.62028	T	SAND	AE		01				0		14	Asia/Dubai	2011-11-06
+291121	Sabkhat Qusaywirah	Sabkhat Qusaywirah	Sabkhat Qusaywirah	22.77956	55.04968	H	SBKH	AE		01				0		76	Asia/Dubai	2011-11-06
+291122	Qusaywirah	Qusaywirah	Geseiwra,Jiseiwrah,Qusaywirah	22.81918	54.79949	L	LCTY	AE		01				0		117	Asia/Dubai	2011-11-06
+291124	QuÅŸaydÄt	Qusaydat	Qusaidat,Qusaydat,QuÅŸaydÄt	25.77461	55.97002	P	PPLX	AE		05				0		21	Asia/Dubai	2011-11-06
+291125	Qurmidah	Qurmidah	Garmada,GarmadÄ,Jarmada,JarmadÄ,Qarmadah,Qurmidah	23.11957	53.82855	P	PPL	AE		01				0		154	Asia/Dubai	2011-11-06
+291126	Qurmidah	Qurmidah	Qarmadah,Qurmidah	23.11234	53.82152	L	OAS	AE		01				0		86	Asia/Dubai	2011-11-06
+291127	Jazīrat Qurmah	Jazirat Qurmah	Jazirat Qurmah,Jazīrat Qurmah	25.76667	55.95	T	ISL	AE		05				0		20	Asia/Dubai	2011-11-06
+291128	Khawr Qurm	Khawr Qurm	Khawr Qurm	25.72056	55.84667	H	LGN	AE		05				0		-9999	Asia/Dubai	2011-11-06
+291129	Qurm	Qurm	Qurm	25.91139	56.06417	P	PPL	AE		05				0		29	Asia/Dubai	2011-11-06
+291130	WÄdÄ« Qurayyah	Wadi Qurayyah	Wadi Qaraiyat,Wadi Qurayyah,WÄdÄ« Qurayyah	25.24214	56.34825	H	WAD	AE		04				0		16	Asia/Dubai	2011-11-06
+291131	Jabal Qurayyah	Jabal Qurayyah	Jabal Qurayyah	25.25531	56.3374	T	HLL	AE		04				0		40	Asia/Dubai	2011-11-06
+291132	Quraytisah	Quraytisah	Quraytisah	23.57867	54.87448	H	WLL	AE		01				0		147	Asia/Dubai	2011-11-06
+291133	Ţawī Qurayn	Tawi Qurayn	Tawi Qurayn,Ţawī Qurayn	23.66667	54.7	H	WLL	AE		01				0		143	Asia/Dubai	2011-11-06
+291134	Sayḩ Qurayḩah	Sayh Qurayhah	Sayh Qurayhah,Sayḩ Qurayḩah	25.41667	55.7	T	TRGD	AE		07				0		77	Asia/Dubai	2011-11-06
+291135	Quraydah	Quraydah	Quraydah	23.13464	53.86398	L	OAS	AE		01				0		167	Asia/Dubai	2011-11-06
+291136	QÅ«r	Qur	Al Qawr,Al Qor,Quor,Qur,QÅ«r	24.91214	56.09566	P	PPL	AE		05				0		340	Asia/Dubai	2011-11-06
+291137	Qunayy	Qunayy	Qunayy	23.16667	54.38333	H	WLL	AE		01				0		151	Asia/Dubai	2011-11-06
+291138	Bid‘ al QumzÄn	Bid` al Qumzan	Bid` al Qumzan,Bid‘ al QumzÄn	23.78619	53.42782	H	WLL	AE		01				0		102	Asia/Dubai	2011-11-06
+291139	Qulaydah	Qulaydah	Qulaydah	25.1968	55.96372	T	RDGE	AE		06				0		213	Asia/Dubai	2011-11-06
+291140	Qufayyid	Qufayyid	Qiff Rajajil,Qiff RajÄjÄ«l,Qufayyid	23.7573	52.87017	T	SAND	AE		01				0		94	Asia/Dubai	2011-11-06
+291141	Sih al Qubua	Sih al Qubua	Sayh al Qubua,Sayḩ al Qubua,Sih al Qubua	24.31519	54.88739	T	DPR	AE		01				0		75	Asia/Dubai	2011-11-06
+291142	Qubbat BÅ« Lisail	Qubbat Bu Lisail	Qubbat Bu Lisail,Qubbat BÅ« Lisail	24.36204	55.64738	L	LCTY	AE		01				0		278	Asia/Dubai	2011-11-06
+291143	WÄdÄ« Qubbah	Wadi Qubbah	Wadi Qubbah,WÄdÄ« Qubbah	25.3444	56.13202	H	WAD	AE		00				0		760	Asia/Dubai	2011-11-06
+291144	Sayḩ QubaysÄt	Sayh Qubaysat	Sayh Qubaysat,Sayḩ QubaysÄt	24.31489	55.05401	T	TRGD	AE		01				0		95	Asia/Dubai	2011-11-06
+291145	GhaḑÄ’ QÅ«bah	Ghada' Qubah	Ghada' Qubah,GhaḑÄ’ QÅ«bah,Sayh as Salab,Sayḩ as Salab	24.26986	55.30432	T	TRGD	AE		01				0		141	Asia/Dubai	2011-11-06
+291146	Qu‘aysah	Qu`aysah	Ge'aisa,Ge’aisa,Je`eisah,Je‘eiṣah,Qu`aysah,Qu‘aysah	22.9941	54.22184	P	PPL	AE		01				0		151	Asia/Dubai	2011-11-06
+291147	Quar Ah Qahlish	Quar Ah Qahlish	Quar Ah Qahlish	25.985	56.08639	P	PPL	AE		05				0		10	Asia/Dubai	2011-11-06
+291148	Jabal Qitab	Jabal Qitab	Jabal Kitas,Jabal Qitab	25.02017	56.2411	T	MT	AE		00				0	1029	1004	Asia/Dubai	2011-11-06
+291149	Khawr QirqishÄn	Khawr Qirqishan	Khawr Qirqishan,Khawr QirqishÄn	24.37884	54.36899	H	BAY	AE		01				0		-9999	Asia/Dubai	2011-11-06
+291150	WÄdÄ« Qimah	Wadi Qimah	Wadi Qamh,Wadi Qima,Wadi Qimah,Wadi Qimh,WÄdÄ« Qamḩ,WÄdÄ« Qimah,WÄdÄ« Qimh,WÄdÄ« QÄ«má	24.79862	56.13736	H	WAD	AE		03				0		306	Asia/Dubai	2011-11-06
+291151	Jabal Qimah	Jabal Qimah	Jabal Qima,Jabal Qimah,Jabal Qīmá	24.79495	56.15749	T	HLL	AE		03				0		609	Asia/Dubai	2011-11-06
+291152	Qimah	Qimah	Qamh,Qamḩ,Qima,Qimah,Qimh,Qīmá	24.78668	56.14143	P	PPL	AE		03				0		322	Asia/Dubai	2011-11-06
+291153	Ra’s al QilÄ‘	Ra's al Qila`	Qala`,Qala‘,Ra's al Qila`,Ras Djaliya,Ras Ijla,Ras Jaliya,Ras Jaliyah,Ras al Jala'a,Ras al Jala’a,Ras al-Qela',Ra’s al QilÄ‘,RÄs al-QelÄ’	24.15507	52.97896	T	PT	AE		01				0		-9999	Asia/Dubai	2011-11-06
+291154	Jabal Qidfa‘	Jabal Qidfa`	Jabal Qidfa`,Jabal Qidfa‘	25.33201	56.32246	T	MT	AE		06				0		473	Asia/Dubai	2011-11-06
+291155	Qidfa‘	Qidfa`	Qidfa,Qidfa`,Qidfa‘,Qidfi`,Qidfi‘	25.30426	56.37034	P	PPL	AE		06				0		45	Asia/Dubai	2011-11-06
+291156	Qayyat al Bawl	Qayyat al Bawl	Qayyat al Bawl	24.27465	54.62206	T	DUNE	AE		01				0		15	Asia/Dubai	2011-11-06
+291157	NaqÄ Qaydah	Naqa Qaydah	Naqa Qaydah,NaqÄ Qaydah	23.36667	55.4	T	DUNE	AE		00				0		174	Asia/Dubai	2011-11-06
+291158	Qawm ‘AnÄ«yÄt	Qawm `Aniyat	Qaum `Aniyat,Qaum ‘AnÄ«yÄt,Qawm `Aniyat,Qawm ‘AnÄ«yÄt	24.11544	54.34195	T	HLL	AE		01				0		21	Asia/Dubai	2011-11-06
+291159	QaţţÄrah	Qattarah	Al-Qatarah,Qatara,Qatarah,Qattara,Qattarah,QatÄrah,QaţţÄrah,Qaá¹­á¹­Ära	24.25993	55.75566	P	PPL	AE		01				0		292	Asia/Dubai	2011-11-06
+291160	Ţawī Qaţam	Tawi Qatam	Tawi Qatam,Ţawī Qaţam	24.0525	54.74417	H	WLL	AE		01				0		74	Asia/Dubai	2011-11-06
+291161	Qaţam	Qatam	Qatam,Qaţam	24.0532	54.73458	T	DUNE	AE		01				0		74	Asia/Dubai	2011-11-06
+291162	Qataiwa	Qataiwa	Qataiwa	23.41667	55.28333	H	WLL	AE		01				0		155	Asia/Dubai	2011-11-06
+291163	Qassīs Ḩayy	Qassis Hayy	Qassis Haiy,Qassis Hayy,Qassīs Haiy,Qassīs Ḩayy	24.1	55.63333	T	DPR	AE	AE	01				0		250	Asia/Dubai	2011-11-06
+291164	Ramlat al Qassīs	Ramlat al Qassis	Ramlat al Qassis,Ramlat al Qassīs	24.08333	55.58333	T	DUNE	AE		01				0		238	Asia/Dubai	2011-11-06
+291165	QaÅŸÅŸÄbÄ«	Qassabi	Gassabi,GassÄbi,Jassabi,JaÅŸÅŸÄbÄ«,Qassabi,QaÅŸÅŸÄbÄ«,Qusabi,QuÅŸÄbÄ«,Umm al Majarib,Umm al MajÄrib	24.21093	54.1017	T	ISL	AE		01				0		11	Asia/Dubai	2011-11-06
+291166	QaÅŸr ÅžÄliḩ	Qasr Salih	Qasr Salih,QaÅŸr ÅžÄliḩ	23.92871	52.78831	T	SAND	AE		01				0		43	Asia/Dubai	2011-11-06
+291167	WÄdÄ« Qasmah	Wadi Qasmah	Wadi Qasma,Wadi Qasmah,WÄdÄ« Qasma,WÄdÄ« Qasmah	24.48968	55.53049	H	WAD	AE		01				0		251	Asia/Dubai	2011-11-06
+291168	Kharīmat Qaşīmah	Kharimat Qasimah	Kharimat Qasimah,Kharmat Qasimah,Kharmat Qaşīmah,Kharīmat Qaşīmah,Qasimah,Qaşimah	24.07524	54.89174	T	TRGD	AE		01				0		90	Asia/Dubai	2011-11-06
+291169	QaÅŸÄ«mah	Qasimah	Qasimah,QaÅŸÄ«mah	24.05154	54.90686	H	WLL	AE		01				0		83	Asia/Dubai	2011-11-06
+291170	Qarn Qashash	Qarn Qashash	Qarn Qashash	24.42261	55.59755	T	HLL	AE		01				0		275	Asia/Dubai	2011-11-06
+291171	Qarqarī	Qarqari	Qarqari,Qarqarī	24.31889	55.61222	T	DPR	AE		01				0		221	Asia/Dubai	2011-11-06
+291172	Qarnayn	Qarnayn	Al Qarnain,Al Qarnayn,Jazirat al Qarnayn,Jazīrat al Qarnayn,Jezirat Qarnain,Karnain Island,Qarnayn,Qarnein	24.93528	52.84972	T	ISL	AE	AE	01				0		-9999	Asia/Dubai	2011-11-06
+291173	Qarḩat Suwayd	Qarhat Suwayd	Qarhat Suwaid,Qarhat Suwayd,Qarḩat Suwayd	24.5	55.68333	V	TREE	AE	AE	01				0		286	Asia/Dubai	2011-11-06
+291174	WÄdÄ« Qarḩah	Wadi Qarhah	Wadi Qarhah,WÄdÄ« Qarḩah	25.36471	55.8084	T	DPR	AE		06				0		80	Asia/Dubai	2011-11-06
+291175	Sayḩ QarÄţīs	Sayh Qaratis	Sayh Qaratis,Sayḩ QarÄţīs	24.86667	55.55	T	DPR	AE		03				0		148	Asia/Dubai	2011-11-06
+291176	Ţawī al Qaran	Tawi al Qaran	Tawi al Qaran,Ţawī al Qaran	25.53317	55.74746	H	WLL	AE		07				0		49	Asia/Dubai	2011-11-06
+291177	QarÄmidah	Qaramidah	Qaramidah,QarÄmidah	23.14517	53.71552	L	OAS	AE		01				0		160	Asia/Dubai	2011-11-06
+291178	Å¢awÄ« al QÄrah	Tawi al Qarah	Alcara,Tawi al Qarah,Å¢awÄ« al QÄrah	25.69146	56.00238	H	WLL	AE		05				0		15	Asia/Dubai	2011-11-06
+291179	Qar‘ah	Qar`ah	Qar'a,Qar`ah,Qara'a,Qara’a,Qar‘ah,Qar’a	24.9	55.03333	T	SAND	AE		03				0		16	Asia/Dubai	2011-11-06
+291180	Å¢awÄ« Qarad	Tawi Qarad	Tawi Garhad,Tawi Qarad,Å¢awÄ« Qarad,Å¢ÄwÄ« Garhad	25.62735	55.75506	H	WLL	AE		07				0		23	Asia/Dubai	2011-11-06
+291181	Khawr Qanţūr	Khawr Qantur	Khawr Qantur,Khawr Qanţūr	24.15843	54.06167	H	CHNM	AE		01				0		-9999	Asia/Dubai	2011-11-06
+291182	Qamshī	Qamshi	Qamshi,Qamshī,Qemshi	24.74458	54.77306	T	HLL	AE		01				0		12	Asia/Dubai	2011-11-06
+291183	QamrÄ’	Qamra'	Qamra',QamrÄ’	23.03861	52.60583	H	WLL	AE		01				0		103	Asia/Dubai	2011-11-06
+291184	QamrÄ’	Qamra'	Qamra',QamrÄ’	23.01254	52.48934	L	LCTY	AE		01				0		85	Asia/Dubai	2011-11-06
+291185	Qamarah	Qamarah	Qamarah	23.71667	53.4	T	HLL	AE		01				0		81	Asia/Dubai	2011-11-06
+291186	Jabal al Qumr ash ShamÄlÄ«	Jabal al Qumr ash Shamali	Jabal al Qamar,Jabal al Qumr ash Shamali,Jabal al Qumr ash ShamÄlÄ«	25.46168	56.04501	T	MT	AE		05				0		561	Asia/Dubai	2011-11-06
+291187	Jabal al Qumr al Janūbī	Jabal al Qumr al Janubi	Jabal al Qamar,Jabal al Qumr al Janubi,Jabal al Qumr al Janūbī	25.43354	56.044	T	MT	AE		05				0		455	Asia/Dubai	2011-11-06
+291188	WÄdÄ« al QildÄ«	Wadi al Qildi	Wadi al Qaliddi,Wadi al Qildi,WÄdÄ« al QaliddÄ«,WÄdÄ« al QildÄ«	25.59763	55.98951	H	WAD	AE		04				0		36	Asia/Dubai	2011-11-06
+291189	WÄdÄ« al QaliddÄ«	Wadi al Qaliddi	Wadi al Qaliddi,WÄdÄ« al QaliddÄ«	25.56667	56.05	H	WAD	AE		04				0		346	Asia/Dubai	2011-11-06
+291190	‘Aqabat al QaliddÄ«	`Aqabat al Qaliddi	Qalidda Pass,Qaliddi Pass,QÄliddi Pass,`Aqabat al Qaliddi,‘Aqabat al QaliddÄ«	25.53387	56.12393	T	PASS	AE		04				0		512	Asia/Dubai	2011-11-06
+291191	Qaiyaisha	Qaiyaisha	Qaiyaisha	25.88333	56.11667	L	TRB	AE		05				0		853	Asia/Dubai	2011-11-06
+291192	Ḩabl QÄ’imah	Habl Qa'imah	Habl Qa'im,Habl Qa'imah,Habl Qa’im,Ḩabl QÄ’imah	24.35345	54.73194	T	SAND	AE		01				0		46	Asia/Dubai	2011-11-06
+291193	QÄ’imah	Qa'imah	Gaimo,Qa'imah,QÄ’imah	24.32635	54.7658	H	WLLS	AE		01				0		53	Asia/Dubai	2011-11-06
+291194	Raml al Qaḩţ	Raml al Qaht	Raml al Qaht,Raml al Qaḩţ	24.73333	55.28333	T	SAND	AE		03				0		115	Asia/Dubai	2011-11-06
+291195	Ţawī Qafan	Tawi Qafan	Tawi Qafan,Ţawī Qafan	25.29778	55.88472	H	WLL	AE		06				0		118	Asia/Dubai	2011-11-06
+291196	WÄdÄ« Qada‘ah	Wadi Qada`ah	Wadi Ghayl,Wadi Qada`a,Wadi Qada`ah,Wadi Qadda`a,WÄdÄ« Qada‘a,WÄdÄ« Qada‘ah,WÄdÄ« Qadda‘a	25.79083	56.07842	H	WAD	AE		05				0		203	Asia/Dubai	2011-11-06
+291197	Jabal Qada‘ah	Jabal Qada`ah	Jabal Qada`a,Jabal Qada`ah,Jabal Qada‘a,Jabal Qada‘ah	25.77944	56.13973	T	MT	AE		05				0		1374	Asia/Dubai	2011-11-06
+291198	Qada‘ah	Qada`ah	Qada`a,Qada`ah,Qada‘a,Qada‘ah	25.76667	56.08333	V	CULT	AE		05				0		379	Asia/Dubai	2011-11-06
+291199	Qabas	Qabas	Qabas	26.03435	56.12302	P	PPL	AE		05				0		419	Asia/Dubai	2011-11-06
+291200	Qubaythah	Qubaythah	Qabaitha,Qubaythah	24.41472	55.69599	L	LCTY	AE		01				0		318	Asia/Dubai	2011-11-06
+291201	Petty Patches	Petty Patches	Petty Patches	24.48392	52.4424	H	SHOL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+291204	NuwayrÄn	Nuwayran	Nuwayran,NuwayrÄn	24.0781	54.66495	T	DUNE	AE		01				0		56	Asia/Dubai	2011-11-06
+291205	Ţawī Nuşaylī	Tawi Nusayli	Tawi Nusayli,Ţawī Nuşaylī	24.34467	55.22058	H	WLLS	AE		01				0		133	Asia/Dubai	2011-11-06
+291206	Sabkhat Nuşaylī	Sabkhat Nusayli	Sabkhat Nusayli,Sabkhat Nuşaylī	24.32398	55.1741	H	SBKH	AE		01				0		124	Asia/Dubai	2011-11-06
+291207	Qarn NuÅŸaylah	Qarn Nusaylah	Al-Neseilah,Qarn Nusaylah,Qarn NuÅŸaylah	24.46222	54.63361	T	DUNE	AE	AE	01				0		14	Asia/Dubai	2011-11-06
+291208	NuÅŸayb	Nusayb	Nusayb,NuÅŸayb	24.45	55.11667	T	TRGD	AE		01				0		123	Asia/Dubai	2011-11-06
+291209	Nuqayrah	Nuqayrah	Nuqayrah	23.30021	54.09107	P	PPL	AE		01				0		172	Asia/Dubai	2011-11-06
+291210	Nuqaydhah	Nuqaydhah	Nuqaydhah	23.02418	53.56632	T	DPR	AE		01				0		77	Asia/Dubai	2011-11-06
+291211	Ţawī Numayrīyah	Tawi Numayriyah	Nimairiya,Numairiya,Tawi Nimairiyyah,Tawi Numayriyah,Ţawī Numayrīyah	23.75469	54.42187	H	WLL	AE		01				0		104	Asia/Dubai	2011-11-06
+291212	Numayl	Numayl	Nemail,Numayl	23.13487	53.88304	L	OAS	AE		01				0		106	Asia/Dubai	2011-11-06
+291213	Å¢awÄ« an Nukharah	Tawi an Nukharah	Tawi Nakh,Tawi an Nakharah,Tawi an Nukharah,Å¢awÄ« an Nakharah,Å¢awÄ« an Nukharah,ṬÄwÄ« Nakh	24.93857	55.39779	H	WLL	AE		03				0		83	Asia/Dubai	2011-11-06
+291214	Sayḩ an Nukharah	Sayh an Nukharah	Sayh an Nakharah,Sayh an Nukharah,Sayḩ an Nakharah,Sayḩ an Nukharah	24.93333	55.35	T	TRGD	AE	AE	03				0		71	Asia/Dubai	2011-11-06
+291215	Nuhayy	Nuhayy	Nahai,Nuhayy	25.27074	56.36242	P	PPL	AE		06				0		22	Asia/Dubai	2011-11-06
+291216	Ţawī Nubayy	Tawi Nubayy	Tawi Nubayy,Ţawī Nubayy	25.48333	55.71667	H	WLL	AE		07				0		45	Asia/Dubai	2011-11-06
+291217	Ţawī Nubaybigh	Tawi Nubaybigh	Nebeibighat,Tawi Nubaybigh,Tawi Nubaybighah,Ţawī Nubaybigh,Ţawī Nubaybighah	25.26667	55.81667	H	WLL	AE	AE	06				0		125	Asia/Dubai	2011-11-06
+291218	WÄdÄ« NiyÄm	Wadi Niyam	Wadi Niyam,WÄdÄ« NiyÄm	25.08412	55.92606	H	WAD	AE		05				0		185	Asia/Dubai	2011-11-06
+291219	QurÅ«n NiÅŸÄb	Qurun Nisab	Qurun Nisab,QurÅ«n NiÅŸÄb	23.80045	54.33433	T	DUNE	AE		01				0		100	Asia/Dubai	2011-11-06
+291220	NisÄb	Nisab	Nisab,NisÄb	23.79429	54.35987	H	WLL	AE		01				0		90	Asia/Dubai	2011-11-06
+291221	Nibrī	Nibri	Nibri,Nibrī	25.35176	55.84985	S	FT	AE		07				0		75	Asia/Dubai	2011-11-06
+291222	Jabal Nibah	Jabal Nibah	Jabal Nibah	24.76861	56.15111	T	MT	AE		00				0		522	Asia/Dubai	2011-11-06
+291223	WÄdÄ« Nazwá	Wadi Nazwa	Wadi Nazwa,WÄdÄ« Nazwá	25.01163	55.65579	T	TRGD	AE		06				0		176	Asia/Dubai	2011-11-06
+291224	Å¢awÄ« Nazwá	Tawi Nazwa	Nazwa,NazwÄ,Tawi Nazwa,Tawi Nezwa,Tawi Nizwa,Å¢awÄ« Nazwá,Å¢ÄwÄ« Nezwa	25.0325	55.68361	H	WLL	AE	AE	06				0		168	Asia/Dubai	2011-11-06
+291225	Qarn Nazwá	Qarn Nazwa	Qarn Nazwa,Qarn Nazwá,Qarn Nizwa	24.98371	55.66235	T	HLL	AE		00				0		173	Asia/Dubai	2011-11-06
+291226	WÄdÄ« NayÄs	Wadi Nayas	Wadi Nayas,Wadi Nayassa,WÄdÄ« Nayassa,WÄdÄ« NayÄs	25.02082	55.9848	H	WAD	AE		05				0		247	Asia/Dubai	2011-11-06
+291227	NaqÄ an NawÄ	Naqa an Nawa	Naqa Nawi,Naqa an Nawa,NaqÄ NÄwÄ«,NaqÄ an NawÄ	25.14487	55.42354	T	DUNE	AE		03				0		55	Asia/Dubai	2011-11-06
+291228	Sayḩ Naşūrīyah Ghafanī	Sayh Nasuriyah Ghafani	Sayh Nasuriyah Ghafani,Sayh Nisuriyah,Sayḩ Naşūrīyah Ghafanī,Sayḩ Nişūrīyah	24.39117	55.2855	T	TRGD	AE		01				0		155	Asia/Dubai	2011-11-06
+291229	Ţawī Naşūrīyah	Tawi Nasuriyah	Tawi Nasuriyah,Tawi Nisuriyah,Ţawī Naşūrīyah,Ţawī Nişūrīyah	24.32521	55.35583	H	WLL	AE		01				0		146	Asia/Dubai	2011-11-06
+291230	Sayḩ Naşūrīyah	Sayh Nasuriyah	Sayh Nasuriyah,Sayh Nisuriyah,Sayḩ Naşūrīyah,Sayḩ Nişūrīyah	24.36214	55.35931	T	TRGD	AE		01				0		179	Asia/Dubai	2011-11-06
+291231	Naşūrīyah	Nasuriyah	Nasuriyah,Naşūrīyah,Ryah Nisuh	24.4	55.31667	H	WLL	AE	AE	01				0		100	Asia/Dubai	2011-11-06
+291232	Naşūrīyah	Nasuriyah	Nasuriyah,Naşūrīyah,Nisuriyah,Nişūrīyah	24.36667	55.41667	T	DUNE	AE	AE	01				0		184	Asia/Dubai	2011-11-06
+291233	NaÅŸlah	Naslah	Naslah,NaÅŸlah	23.82234	52.6032	T	SAND	AE		01				0		62	Asia/Dubai	2011-11-06
+291234	NaÅŸÄ«b	Nasib	Nasib,NaÅŸÄ«b	24.56667	55.16667	T	DUNE	AE		01				0		92	Asia/Dubai	2011-11-06
+291235	Nashimah	Nashimah	Nashimah	23.09103	53.80754	T	DPR	AE		01				0		81	Asia/Dubai	2011-11-06
+291236	Sayḩ an Nashash	Sayh an Nashash	Sayh an Nashash,Sayḩ an Nashash,Sih al Nashash,Sih an Nashash,Sīḩ an NashÄsh	24.08865	55.71184	T	TRGD	AE		01				0		245	Asia/Dubai	2011-11-06
+291237	Milhala	Milhala	Milhala,Tawi Nasas,Å¢awÄ« Nasas,Å¢awÄ« NaÅŸÄÅŸ	25.02694	55.97	H	WLL	AE		05				0		265	Asia/Dubai	2011-11-06
+291238	Ramlat NasÄs	Ramlat Nasas	Ramlat Nasas,Ramlat NasÄs	24.51593	55.168	T	DUNE	AE		01				0		106	Asia/Dubai	2011-11-06
+291239	Nasas	Nasas	Nasas	24.5	55.18333	T	TRGD	AE		01				0		125	Asia/Dubai	2011-11-06
+291240	Ţawī Naqrah	Tawi Naqrah	Tawi Nagarah,Tawi Naqara,Tawi Naqrah,Ţawī Naqara,Ţawī Naqrah	24.3762	55.53108	H	WLLS	AE		01				0		194	Asia/Dubai	2011-11-06
+291241	Qarn Naqrah	Qarn Naqrah	Qarn Naqrah	24.37348	55.52974	T	DUNE	AE		01				0		235	Asia/Dubai	2011-11-06
+291242	Naqrah	Naqrah	Naqrah	24.37644	55.56032	P	PPL	AE		01				0		247	Asia/Dubai	2011-11-06
+291243	Naqrah	Naqrah	Naqrah	24.38333	55.45	T	DUNE	AE		01				0		204	Asia/Dubai	2011-11-06
+291244	Naqbīyīn	Naqbiyin	Naqbiyin,Naqbīyīn	25.66667	56	L	TRB	AE		00				0		12	Asia/Dubai	2011-11-06
+291245	Naqbiyīn	Naqbiyin	Naqbiyin,Naqbiyīn	25.33333	56.33333	L	TRB	AE		00				0		426	Asia/Dubai	2011-11-06
+291246	WÄdÄ« Naqat	Wadi Naqat	Wadi Naqat,WÄdÄ« Naqat	25.86667	56.1	H	WAD	AE		05				0		205	Asia/Dubai	2011-11-06
+291247	Jaww an NÄqah	Jaww an Naqah	Jaww an Naqah,Jaww an NÄqah	22.96387	54.14931	T	DPR	AE		01				0		95	Asia/Dubai	2011-11-06
+291248	Naqab Sha‘rÄn	Naqab Sha`ran	Naqab Sha`ran,Naqab Sha‘rÄn	24.17121	54.66632	T	SAND	AE		01				0		44	Asia/Dubai	2011-11-06
+291249	WÄdÄ« Naqab	Wadi Naqab	Wadi Naqab,WÄdÄ« Naqab	25.70384	56.0662	H	WAD	AE		05				0		85	Asia/Dubai	2011-11-06
+291250	Namlīyah	Namliyah	Namliyah,Namlīyah	23.05915	53.958	T	DPR	AE		01				0		170	Asia/Dubai	2011-11-06
+291251	Namlīyah	Namliyah	Namliyah,Namlīyah	23.03453	54.02193	T	DPR	AE		01				0		87	Asia/Dubai	2011-11-06
+291252	Namlah	Namlah	Namlah	23.06497	53.97529	H	WLL	AE		01				0		76	Asia/Dubai	2011-11-06
+291253	Namlah	Namlah	Namalah,Namlah	23.01393	53.47494	T	DPR	AE		01				0		172	Asia/Dubai	2011-11-06
+291254	Nakhl Subayyis	Nakhl Subayyis	Nakhl Sibaiyis,Nakhl Sibaylis,Nakhl Subaiyis,Nakhl Subayyis	24.3575	55.20611	V	TREE	AE	AE	01				0		107	Asia/Dubai	2011-11-06
+291255	Nakhl Bin JirwÄn	Nakhl Bin Jirwan	Nakhl Bin Jirwan,Nakhl Bin JirwÄn,Nakhl bin Jerwan	24.18333	55.11667	H	WLLS	AE	AE	01				0		123	Asia/Dubai	2011-11-06
+291256	‘Uqlat an Nakhlah	`Uqlat an Nakhlah	An Nakhlah,Nakhla,`Uglat Nakhlan,`Uqlat al-Nakhlah,`Uqlat an Nakhlah,‘Uglat Nakhlan,‘Uqlat al-Nakhlah,‘Uqlat an Nakhlah	24.31667	51.2	H	WLL	AE		01				0		7	Asia/Dubai	2011-11-06
+291257	Ramlat an Nakhlah	Ramlat an Nakhlah	Ramlat an Nakhlah	24.35861	51.20632	T	DUNE	AE		01				0		10	Asia/Dubai	2011-11-06
+291258	Nakhalai	Nakhalai	Nakhalai	25.09528	55.57653	L	LCTY	AE		03				0		89	Asia/Dubai	2011-11-06
+291259	Jabal Najla‘	Jabal Najla`	Jabal Najla`,Jabal Najla‘	25.43593	55.99124	T	HLL	AE		05				0		208	Asia/Dubai	2011-11-06
+291260	Jabal Najdayn	Jabal Najdayn	Jabal Najdayn,Jabal Naydayn	25.17988	56.17201	T	MT	AE		00				0		880	Asia/Dubai	2011-11-06
+291261	Najdayn	Najdayn	Najdayn	25.16667	56.2	V	CULT	AE		04				0		639	Asia/Dubai	2011-11-06
+291262	NajdÄt	Najdat	Najadat,NajadÄt,Najdat,NajdÄt	24.13333	55.91667	L	TRB	AE	AE	01				0		378	Asia/Dubai	2011-11-06
+291263	Na‘ītah	Na`itah	Jazirat Na`itah,Jazīrat Na‘ītah,Na`itah,Naita,Na‘ītah,Ne'ita,Ne’īta,Ni`eitah,Ni‘eitah	24.29085	51.79455	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+291264	NaqÄ NÄ’if	Naqa Na'if	Naqa Na'if,NaqÄ NÄ’if	23.08742	55.22844	T	DUNE	AE		01				0		171	Asia/Dubai	2011-11-06
+291265	Nahwá	Nahwa	Nahawa,Nahwa,Nahwá	25.26823	56.28002	P	PPL	AE		06				0		213	Asia/Dubai	2011-11-06
+291266	Ţawī Nahshīlah	Tawi Nahshilah	Tawi Nahshilah,Ţawī Nahshīlah	24.40832	55.10681	H	WLL	AE		01				0		103	Asia/Dubai	2011-11-06
+291267	Nahshīlah	Nahshilah	Nahshilah,Nahshīlah	24.4032	55.05936	T	SAND	AE		01				0		100	Asia/Dubai	2011-11-06
+291268	Nahdayn	Nahdayn	Nahdain,Nahdayn,Nahdein	23.68333	52.3	S	OILW	AE	AE	01				0		41	Asia/Dubai	2011-11-06
+291269	Nahdayn	Nahdayn	Nahadain,Nahdayn	23.68725	52.30246	T	HLL	AE		01				0		49	Asia/Dubai	2011-11-06
+291270	Nafīr	Nafir	Nafir,Nafīr,Nefair	23.10393	53.78919	P	PPL	AE		01				0		86	Asia/Dubai	2011-11-06
+291271	Nafīr	Nafir	Nafir,Nafīr	23.10444	53.78667	T	DPR	AE		01				0		86	Asia/Dubai	2011-11-06
+291272	NaqÄ Nadh	Naqa Nadh	Naqa Nadh,NaqÄ Nadh	23.79851	55.49041	T	DUNE	AE		01				0		194	Asia/Dubai	2011-11-06
+291273	Nadd Umm Ḩaşá	Nadd Umm Hasa	Nadd Umm Hasa,Nadd Umm Ḩaşá	25.13954	55.38266	L	LCTY	AE		03				0		20	Asia/Dubai	2011-11-06
+291274	Nadd MÄni‘	Nadd Mani`	Nadd Mani`,Nadd MÄni‘,Ned Bin Tamana,Nidd Mani`,Nidd MÄni‘	25.55306	55.5475	T	PEN	AE		07				0		18	Asia/Dubai	2011-11-06
+291275	Nadd BayḑÄ’	Nadd Bayda'	Nad Beidha,Nadd Bayda',Nadd BayḑÄ’	25.35352	55.43762	L	LCTY	AE		06				0		15	Asia/Dubai	2011-11-06
+291276	Ramlat Nadd	Ramlat Nadd	Ramlat Nadd	23.97641	55.12876	T	DUNE	AE		01				0		143	Asia/Dubai	2011-11-06
+291277	Naban	Naban	Naban	24.23824	54.90422	H	WLL	AE		01				0		77	Asia/Dubai	2011-11-06
+291278	Na‘adhal	Na`adhal	Na`adhal,Na‘adhal	22.98454	53.54606	T	DPR	AE		01				0		46	Asia/Dubai	2011-11-06
+291279	Muzayri‘	Muzayri`	Mezaira'a,Mezaira’a,Mizeir`ah,Mizeir‘ah,Mozayri`,Mozayri‘,Muzayri`,Muzayri‘	23.14355	53.7881	P	PPL	AE		01				10000		181	Asia/Dubai	2011-11-06
+291280	Mughaylat MuzÄri‘	Mughaylat Muzari`	Maghailat Muzaria,Mughaylat Muzari`,Mughaylat MuzÄri‘	24.03333	54.95	H	WLL	AE		01				0		90	Asia/Dubai	2011-11-06
+291281	GhÄfat al Muyayridah	Ghafat al Muyayridah	Ghafat al Muyayridah,GhÄfat al Muyayridah	25.6	56.3	H	WLL	AE		04				0		45	Asia/Dubai	2011-11-06
+291282	Sayḩ Muyaydirah	Sayh Muyaydirah	Sayh Muyaydirah,Sayḩ Muyaydirah	24.68333	55.4	T	TRGD	AE		03				0		155	Asia/Dubai	2011-11-06
+291283	Muyaydirah	Muyaydirah	Muyaydirah	24.7	55.38333	T	SAND	AE		03				0		118	Asia/Dubai	2011-11-06
+291284	NaqÄ Muwayzah	Naqa Muwayzah	Naqa Muwayzah,NaqÄ Muwayzah	24.6	55.31667	T	SAND	AE		01				0		135	Asia/Dubai	2011-11-06
+291285	Ḩişn al Muwayqi‘ī	Hisn al Muwayqi`i	Hisn al Muwayqi`i,Ḩişn al Muwayqi‘ī	24.21667	55.71667	S	FT	AE		01				0		266	Asia/Dubai	2011-11-06
+291286	Muwayliḩ	Muwaylih	Muailah,Muwaylih,Muwayliḩ	24.61506	54.7728	H	WLL	AE		01				0		24	Asia/Dubai	2011-11-06
+291287	WÄdÄ« al Muwayji‘ah	Wadi al Muwayji`ah	Wadi al Muwayji`ah,WÄdÄ« al Muwayji‘ah	24.95	55.4	T	DPR	AE		03				0		85	Asia/Dubai	2011-11-06
+291288	Muwayh Arnab	Muwayh Arnab	Muwayh Arnab	24.27467	55.05049	T	SAND	AE		01				0		123	Asia/Dubai	2011-11-06
+291289	Muwayh al Ju’Äbir	Muwayh al Ju'abir	Muwayh al Ju'abir,Muwayh al Ju’Äbir	23.82211	55.46188	T	DUNE	AE		01				0		181	Asia/Dubai	2011-11-06
+291290	Muwayh al Huḑayb	Muwayh al Hudayb	Muwayh al Hudayb,Muwayh al Huḑayb	24.06076	55.35169	T	SAND	AE		01				0		191	Asia/Dubai	2011-11-06
+291291	Bi’r Muwayfiqah	Bi'r Muwayfiqah	Bi'r Muwayfiqah,Bi’r Muwayfiqah,Muwafiqa,Muwafiqah,MuwÄfiqa,MuwÄfÄ«qah	24.13194	55.68139	H	WLL	AE		01				0		247	Asia/Dubai	2011-11-06
+291292	Jabal Muthrad	Jabal Muthrad	Jabal Muthrad	25.15731	56.28963	T	MT	AE		04				0		387	Asia/Dubai	2011-11-06
+291293	Ţawī Mutayn	Tawi Mutayn	Tawi Mutayn,Ţawī Mutayn	23.2725	53.60472	H	WLL	AE		01				0		141	Asia/Dubai	2011-11-06
+291294	MuÅ£aylÄn	Mutaylan	Mitailan,Mutailan,Mutaylan,MuÅ£aylÄn	23.84996	53.82125	L	AREA	AE		01				0		69	Asia/Dubai	2011-11-06
+291295	Mughayyil Muţawwá	Mughayyil Mutawwa	Mughayyil Mutawwa,Mughayyil Muţawwá	24.06546	54.89046	H	WLL	AE		01				0		107	Asia/Dubai	2011-11-06
+291296	Bid‘ al MuÅ£Äwa‘ah	Bid` al Mutawa`ah	Bid' al-Mataw'a,Bid` al Mutawa`ah,Bid` al Mutawwa`,Bid` al-Mataw`ah,Bid‘ al MuÅ£awwa‘,Bid‘ al MuÅ£Äwa‘ah,Bid‘ al-Maá¹­Äw‘ah,Bid’ al-Mataw’a	23.75972	52.51917	H	WLL	AE		01				0		68	Asia/Dubai	2011-11-06
+291297	WÄdÄ« Mu‘tariḑah	Wadi Mu`taridah	Wadi Mu`taridah,WÄdÄ« Mu‘tariḑah	25.5165	56.00264	H	WAD	AE		00				0		97	Asia/Dubai	2011-11-06
+291298	Mu‘tariḑah	Mu`taridah	Mu`taridah,Mu`taridat,Mu‘tariḑah,Mu‘tariḑat	23.93178	52.41949	T	RKS	AE		01				0		58	Asia/Dubai	2011-11-06
+291299	Mu‘tariḑah	Mu`taridah	Mu`taridah,Mu‘tariḑah	25.50841	56.11046	P	PPL	AE		04				0		350	Asia/Dubai	2011-11-06
+291300	Mu‘tariḑah	Mu`taridah	Mu`taridah,Mu‘tariḑah	24.03558	53.34833	T	HLLS	AE		01				0		18	Asia/Dubai	2011-11-06
+291301	Mu‘tariḑah	Mu`taridah	Mu`taridah,Mu‘tariḑah	23.92444	52.43222	T	DUNE	AE		01				0		38	Asia/Dubai	2011-11-06
+291302	Jabal Mu‘tariḑ	Jabal Mu`tarid	Jabal Ma'atridh,Jabal Ma’atridh,Jabal Mu`tarid,Jabal Mu‘tariḑ	25.45641	55.99319	T	HLL	AE		05				0		233	Asia/Dubai	2011-11-06
+291303	Bid‘ Mussama	Bid` Mussama	Bid` Mussama,Bid‘ Mussama	23.55	53.61667	H	WLL	AE		01				0		128	Asia/Dubai	2011-11-06
+291304	Musaqab	Musaqab	Musaqab,Musqab	25.16778	56.14167	V	GRVP	AE	AE	05				0		363	Asia/Dubai	2011-11-06
+291305	Mushrif	Mushrif	Mushrif	25.21667	55.45	H	WLL	AE		03				0		34	Asia/Dubai	2011-11-06
+291306	Ţawī Mushayrif	Tawi Mushayrif	Mashairif,Mushairif,Tawi Mushayrif,Ţawī Mushayrif	24.08689	54.81192	H	WLL	AE		01				0		76	Asia/Dubai	2011-11-06
+291307	Sabkhat Mushayrif	Sabkhat Mushayrif	Sabkhat Mushayrif	24.15149	54.66116	H	SBKH	AE		01				0		40	Asia/Dubai	2011-11-06
+291308	Mushayrif	Mushayrif	Mushayrif	23.92507	54.31447	T	TRGD	AE		01				0		81	Asia/Dubai	2011-11-06
+291309	Mushayrif	Mushayrif	Mushairif,Mushayrif	24.11525	54.81618	L	LCTY	AE		01				0		84	Asia/Dubai	2011-11-06
+291310	Mushayrif	Mushayrif	Mushayrif	24.11489	54.66871	T	DUNE	AE		01				0		57	Asia/Dubai	2011-11-06
+291311	Ra’s Mushayrib	Ra's Mushayrib	Ra's Mashayrib,Ra's Mushayrib,Ras Mashairif,Ras Masheirib,Ras Masherib,Ras Mushairib,Ras Musheirib,Ra’s Mashayrib,Ra’s Mushayrib,RÄs Mushairib	24.29292	51.74242	T	PT	AE		01				0		-9999	Asia/Dubai	2011-11-06
+291312	Mushayrib	Mushayrib	Mushayrib	24.46667	54.41667	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+291313	MushÄshÄ«	Mushashi	Mashashi,Mushashi,MushÄshÄ«	23.11124	52.5636	S	OILW	AE		01				0		114	Asia/Dubai	2011-11-06
+291314	MushÄsh	Mushash	Imshash,Mushash,MushÄsh	24.55769	51.35768	H	WLL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+291315	MushÄjir	Mushajir	Mushajir,MushÄjir	23.08605	53.99163	L	OAS	AE		01				0		178	Asia/Dubai	2011-11-06
+291316	MushÄjir	Mushajir	Mushajir,MushÄjir	23.07049	53.93374	T	DPR	AE		01				0		73	Asia/Dubai	2011-11-06
+291317	Bid‘ Muşfir	Bid` Musfir	Bid` Musfir,Bid‘ Muşfir,Bir Misfar	24.0619	55.29648	H	WLL	AE		01				0		175	Asia/Dubai	2011-11-06
+291318	Ruqq Musfayr	Ruqq Musfayr	Ruqq Musfayr	24.27875	51.88656	H	RF	AE		01				0		-9999	Asia/Dubai	2011-11-06
+291319	Musayyibah	Musayyibah	Musayyibah	23.82699	55.2768	T	TRGD	AE		01				0		122	Asia/Dubai	2011-11-06
+291320	Ţawī Musannad	Tawi Musannad	Tawi Musannad,Tawi Mussarad,Ţawī Musannad,Ţawī Mussarad	25.25	55.7	H	WLL	AE	AE	06				0		111	Asia/Dubai	2011-11-06
+291321	Sayḩ Musannad	Sayh Musannad	Sayh Musannad,Sayḩ Musannad	25.25104	55.67154	T	TRGD	AE		06				0		71	Asia/Dubai	2011-11-06
+291322	WÄdÄ« MusallÄ	Wadi Musalla	Wadi Musalla,WÄdÄ« MusallÄ	25.51575	56.00944	H	WAD	AE		04				0		142	Asia/Dubai	2011-11-06
+291323	Ḩadd Musafsif	Hadd Musafsif	Hadd Musafsif,Ḩadd Musafsif	24.10083	52.06417	H	SHOL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+291324	Khawr Musá	Khawr Musa	Khawr Musa,Khawr Musá	22.725	53.51671	T	DPR	AE		01				0		62	Asia/Dubai	2011-11-06
+291325	Sayḩ Muruq	Sayh Muruq	Sayh Muruq,Sayḩ Muruq	25.56617	56.0731	T	DPR	AE		04				0		211	Asia/Dubai	2011-11-06
+291326	WÄdÄ« Murtaqam	Wadi Murtaqam	Wadi Murtaqam,Wadi Murtaqau,WÄdÄ« Murtaqam,WÄdÄ« Murtaqau	25.36727	56.25448	H	WAD	AE		04				0		277	Asia/Dubai	2011-11-06
+291327	WÄdÄ« Murrah	Wadi Murrah	Wadi Murrah,WÄdÄ« Murrah	25.19803	56.22673	H	WAD	AE		04				0		321	Asia/Dubai	2011-11-06
+291328	WÄdÄ« Murrah	Wadi Murrah	Wadi Murrah,WÄdÄ« Murrah	24.9	55.43333	T	TRGD	AE		03				0		105	Asia/Dubai	2011-11-06
+291329	Å¢awÄ« Murrah	Tawi Murrah	Al-Murrah,Qa`r Murra,Qa‘r Murra,Tawi Murra,Tawi Murrah,Å¢awÄ« Murrah,Å¢ÄwÄ« Murra	25.13443	55.74992	H	WLL	AE		06				0		117	Asia/Dubai	2011-11-06
+291330	Ţawī Murrah	Tawi Murrah	Tawi Morro,Tawi Motto,Tawi Murra,Tawi Murrah,Ţawī Murrah	24.90846	55.44146	H	WLL	AE		03				0		113	Asia/Dubai	2011-11-06
+291331	Raml Murrah	Raml Murrah	Raml Murrah	24.86266	55.38085	T	SAND	AE		03				0		80	Asia/Dubai	2011-11-06
+291332	Qarn Murrah	Qarn Murrah	Qarat Murra,Qarat Murrah,Qarn Murrah,QÄrat Murra,QÄrat Murrah	25.13349	55.7703	T	DUNE	AE		06				0		150	Asia/Dubai	2011-11-06
+291333	Murrah	Murrah	Murrah	25.03333	55.6	V	TREE	AE		03				0		162	Asia/Dubai	2011-11-06
+291334	Murrah	Murrah	Murrah	25.19903	56.22165	P	PPL	AE		04				0		223	Asia/Dubai	2011-11-06
+291335	Muriya HammÄmah	Muriya Hammamah	Muriya Hammamah,Muriya HammÄmah	24.93333	54.26667	T	RKS	AE		01				0		-9999	Asia/Dubai	2011-11-06
+291336	Ḩaql MurbÄn	Haql Murban	Haql Murban,Mirban,Murban Field,Ḩaql MurbÄn	23.83333	53.75	L	OILF	AE	AE	01				0		76	Asia/Dubai	2011-11-06
+291337	MurbÄn	Murban	Mirban,MirbÄn,Murban,MurbÄn	23.95342	53.69134	T	HLL	AE		01				0		3	Asia/Dubai	2011-11-06
+291338	Sabkhat Murbaḩ	Sabkhat Murbah	Sabkhat Murbah,Sabkhat Murbaḩ	25.25	56.36667	H	SBKH	AE		04				0		-9999	Asia/Dubai	2011-11-06
+291339	Murbaḩ	Murbah	Marbah,Mirba,Mirbih,Mirbiḥ,Murbah,Murbaḩ	25.27623	56.36256	P	PPL	AE		06				0		23	Asia/Dubai	2011-11-06
+291340	Murbaḑ	Murbad	Marbad,Murbad,Murbaḑ	25.3254	56.13311	P	PPL	AE		04				0		487	Asia/Dubai	2011-11-06
+291341	Murayyil	Murayyil	Murayyil	25.11667	55.51667	V	TREE	AE		06				0		82	Asia/Dubai	2011-11-06
+291342	Muraytah	Muraytah	Muraytah	25.40032	56.05844	P	PPL	AE		05				0		240	Asia/Dubai	2011-11-06
+291343	WÄdÄ« Murayshid	Wadi Murayshid	Wadi Murayshid,WÄdÄ« Murayshid	25.1	56.36667	H	WAD	AE		04				0		-9999	Asia/Dubai	2011-11-06
+291344	Jabal Murayshid	Jabal Murayshid	Jabal Murayshid	25.11667	56.33333	T	HLL	AE		04				0		35	Asia/Dubai	2011-11-06
+291345	Barqat Muraymīth	Barqat Muraymith	Barqat Muraymith,Barqat Muraymīth	23.88902	54.47119	T	RKS	AE		01				0		88	Asia/Dubai	2011-11-06
+291346	Muraykh	Muraykh	Maraikh,Muraykh	24	55.41667	H	WLL	AE	AE	01				0		188	Asia/Dubai	2011-11-06
+291347	Ţawī Muray	Tawi Muray	Tawi Muray,Ţawī Muray	23.86667	54.61667	H	WLL	AE		01				0		86	Asia/Dubai	2011-11-06
+291348	Å¢awÄ« MuraqqibÄt	Tawi Muraqqibat	Muraghabat,Muraqabat,Muraqqibat,Tawi Muraqqibat,Å¢awÄ« MuraqqibÄt	25.32611	55.89944	H	WLL	AE	AE	07				0		170	Asia/Dubai	2011-11-06
+291349	Ţawī Muraqqab	Tawi Muraqqab	Tawi Muraqqab,Ţawī Muraqqab	24.82029	55.58435	H	WLL	AE		03				0		182	Asia/Dubai	2011-11-06
+291350	MurÄqabÄt	Muraqabat	Muraqabat,MurÄqabÄt	25.16361	55.34111	H	WLL	AE		03				0		13	Asia/Dubai	2011-11-06
+291351	Murabba‘ah	Murabba`ah	Murabba`ah,Murabba‘ah	25.88333	56.03333	S	TOWR	AE		05				0		1	Asia/Dubai	2011-11-06
+291352	Ra’s Muqayshiţ	Ra's Muqayshit	Mughhaishat,Ra's Muqayshit,Ra's al Ibrah,Ra’s Muqayshiţ,Ra’s al Ibrah	24.16778	53.6236	T	PT	AE		01				0		-9999	Asia/Dubai	2011-11-06
+291353	Muqayshiţ	Muqayshit	Megaishit,Megeshit,Megeshiţ,Muqayshit,Muqayshiţ	24.18908	53.75226	T	ISLX	AE		01				0		10	Asia/Dubai	2011-11-06
+291354	Muqaţţarah	Muqattarah	Miqattarah,Miqaá¹­á¹­arah,Mugatara,Mugattara,Mugaţţara,Muqatirah,Muqattarah,Muqaţţarah,MuqÄtirah	24.2694	54.51333	T	HLL	AE		01				0		10	Asia/Dubai	2011-11-06
+291355	Muqala	Muqala	Muqala	23.53333	54.43333	H	WLL	AE		01				0		131	Asia/Dubai	2011-11-06
+291356	Muntahá	Muntaha	Mntaha,Muntaha,Muntahá	23.84263	55.41381	T	DPR	AE		01				0		187	Asia/Dubai	2011-11-06
+291357	Munfatrah	Munfatrah	Mufatra,Munfatrah	23.96667	53.03333	T	HLL	AE	AE	01				0		39	Asia/Dubai	2011-11-06
+291358	Mundafinah	Mundafinah	Mondafanah,Mondafinah,Mundafinah,Mundafnah	24.11667	55.8	P	PPL	AE	AE	01				0		320	Asia/Dubai	2011-11-06
+291359	Khawr ManÄ’if	Khawr Mana'if	Khawr Mana'if,Khawr ManÄ’if,Khawr Munayyif,Khor Manayef,Khor ManÄyef	24.14456	52.91494	H	COVE	AE		01				0		-9999	Asia/Dubai	2011-11-06
+291360	Munayyif	Munayyif	Munayyif	24.07278	52.94833	L	LCTY	AE		01				0		22	Asia/Dubai	2011-11-06
+291361	Munayyif	Munayyif	Munaiyif,Munayyif	24.09324	52.93446	T	HLL	AE		01				0		51	Asia/Dubai	2011-11-06
+291362	WÄdÄ« Munay‘ī	Wadi Munay`i	Wadi Manai,Wadi Munay`,Wadi Munay`i,WÄdÄ« Munay‘,WÄdÄ« Munay‘ī	24.88227	56.20944	H	WAD	AE		05				0		142	Asia/Dubai	2011-11-06
+291363	Munay‘ī	Munay`i	Manai,Manai'i,Manai’i,Munay`i,Munay‘ī	24.95135	56.14997	P	PPL	AE		05				0		370	Asia/Dubai	2011-11-06
+291364	Jabal Mulfīrah	Jabal Mulfirah	Jabal Mulfirah,Jabal Mulfīrah	25.15716	56.31601	T	HLL	AE		04				0		229	Asia/Dubai	2011-11-06
+291365	Mulaysah	Mulaysah	Mulaysah	23.91634	53.03505	H	WLL	AE		01				0		63	Asia/Dubai	2011-11-06
+291366	Mulaysah	Mulaysah	Mulaysah	23.9	53.05	H	WLL	AE		01				0		51	Asia/Dubai	2011-11-06
+291367	Mulayhim	Mulayhim	Malayham,Mulayhim	22.90126	53.43345	T	DPR	AE		01				0		54	Asia/Dubai	2011-11-06
+291368	WÄdÄ« Mulayḩah	Wadi Mulayhah	Wadi Mulayhah,WÄdÄ« Mulayḩah	25.73333	56.01667	H	WAD	AE		05				0		28	Asia/Dubai	2011-11-06
+291369	WÄdÄ« Mulayḩah	Wadi Mulayhah	Wadi Mulayhah,WÄdÄ« Mulayḩah	25.23504	55.90669	H	WAD	AE		06				0		121	Asia/Dubai	2011-11-06
+291370	Ţawī Mulayḩah	Tawi Mulayhah	Tawi Mulaiha,Tawi Mulayhah,Ţawī Mulaiha,Ţawī Mulayḩah	25.02722	55.795	H	WLL	AE	AE	06				0		161	Asia/Dubai	2011-11-06
+291371	Jabal Mulayḩah	Jabal Mulayhah	Jabal Milaiha,Jabal Mileihah,Jabal Mileiḥah,Jabal Mulayhah,Jabal Mulayḩah,Jibal Mulayhah,JibÄl Mulayḩah,Mulaiha	25.14818	55.83716	T	HLL	AE		06				0		362	Asia/Dubai	2011-11-06
+291372	Mulayḩah	Mulayhah	Mileihah,Mileiḥah,Miliaha,Mulayhah,Mulayḩah	25.16667	55.8	H	WLLS	AE	AE	06				0		158	Asia/Dubai	2011-11-06
+291373	Qarn Mulayḩ	Qarn Mulayh	Qarn Mileih,Qarn Mulayh,Qarn Mulayḩ	25.02286	55.72966	T	HLL	AE		06				0	274	236	Asia/Dubai	2011-11-06
+291374	Mukhtaraqah	Mukhtaraqah	Mahtarraqah,Muhtarqah,Mukhtaraja,Mukhtaraqah	25.54092	56.13066	P	PPL	AE		04				0		272	Asia/Dubai	2011-11-06
+291375	Mukhayūs	Mukhayus	Mukhayus,Mukhayūs	25.73222	55.95389	H	WLL	AE		05				0		47	Asia/Dubai	2011-11-06
+291376	MÅ«jib	Mujib	Mujib,MÅ«jib	23.11667	53.68333	P	PPL	AE		01				0		84	Asia/Dubai	2011-11-06
+291377	Kharmat al Muhayn	Kharmat al Muhayn	Kharimat al Mahain,Kharmat al Mahaim,Kharmat al Muhayn	23.01796	55.10773	T	DPR	AE		01				0		109	Asia/Dubai	2011-11-06
+291378	WÄdÄ« MuhaylÄ«	Wadi Muhayli	Wadi Muhayli,WÄdÄ« MuhaylÄ«	25.7044	56.06564	H	WAD	AE		05				0		85	Asia/Dubai	2011-11-06
+291379	Ţawī Muḩayjir	Tawi Muhayjir	Tawi Muhayjir,Ţawī Muḩayjir	24.5511	55.76381	H	WLL	AE		01				0		319	Asia/Dubai	2011-11-06
+291380	Jabal Muḩayjir	Jabal Muhayjir	Jabal Muhayjir,Jabal Muḩayjir,Qarn Muhaiyir	24.53819	55.73512	T	HLL	AE		01				0		293	Asia/Dubai	2011-11-06
+291381	Muhammalīyah	Muhammaliyah	Al Mahammaliyah,Jazirat Muhammaliyah,Jazīrat Muḩammalīyah,Mahamaliya,Mehammaliyya,Mehammaliyyah,Muhammaliya,Muhammaliyah,Muhammalīyah	24.11616	51.89603	T	ISL	AE		01				0		-9999	Asia/Dubai	2011-11-06
+291382	Ţawī Muḩammad	Tawi Muhammad	Tawi Muhammad,Ţawī Muḩammad	24.94636	55.75046	H	WLL	AE		06				0		183	Asia/Dubai	2011-11-06
+291383	Bid‘ Muḩammad	Bid` Muhammad	Bid` Muhammad,Bid‘ Muḩammad	24.25	55.1	H	WLL	AE		01				0		125	Asia/Dubai	2011-11-06
+291384	Qullat MuḩÄfiz̧	Qullat Muhafiz	Qal`eh Mahafidh,Qallah Mahafidh,Qal‘eh MahÄfidh,Qullat Mahafiz,Qullat MaḩÄfiz̧,Qullat Muhafiz,Qullat MuḩÄfiz̧	25.15848	55.93008	T	PLN	AE		06				0		164	Asia/Dubai	2011-11-06
+291385	Mughīlah	Mughilah	Mughila,Mughilah,Mughīlah	23.78842	55.18178	T	DPR	AE		01				0		113	Asia/Dubai	2011-11-06
+291386	Mughayyin	Mughayyin	Mughaiyin,Mughayyin	23.89196	52.78213	T	SAND	AE		01				0		69	Asia/Dubai	2011-11-06
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dataimport/test/data/people.csv	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,3 @@
+# uri,name,knows
+http://www.example.org/alice,Alice,
+http://www.example.org/bob,Bob,http://www.example.org/alice
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dataimport/test/data/schema.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,29 @@
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+
+from yams.buildobjs import EntityType, String, SubjectRelation
+
+from cubicweb.schema import RQLConstraint
+
+
+class Personne(EntityType):
+    nom = String(required=True)
+    prenom = String()
+    enfant = SubjectRelation('Personne', inlined=True, cardinality='?*')
+    connait = SubjectRelation('Personne', symmetric=True,
+                              constraints=[RQLConstraint('NOT S identity O')])
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dataimport/test/data/timeZones.txt	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,410 @@
+Africa/Abidjan	0.0	0.0	0.0
+Africa/Accra	0.0	0.0	0.0
+Africa/Addis_Ababa	3.0	3.0	3.0
+Africa/Algiers	1.0	1.0	1.0
+Africa/Asmara	3.0	3.0	3.0
+Africa/Bamako	0.0	0.0	0.0
+Africa/Bangui	1.0	1.0	1.0
+Africa/Banjul	0.0	0.0	0.0
+Africa/Bissau	0.0	0.0	0.0
+Africa/Blantyre	2.0	2.0	2.0
+Africa/Brazzaville	1.0	1.0	1.0
+Africa/Bujumbura	2.0	2.0	2.0
+Africa/Cairo	2.0	2.0	2.0
+Africa/Casablanca	0.0	1.0	0.0
+Africa/Ceuta	1.0	2.0	1.0
+Africa/Conakry	0.0	0.0	0.0
+Africa/Dakar	0.0	0.0	0.0
+Africa/Dar_es_Salaam	3.0	3.0	3.0
+Africa/Djibouti	3.0	3.0	3.0
+Africa/Douala	1.0	1.0	1.0
+Africa/El_Aaiun	0.0	0.0	0.0
+Africa/Freetown	0.0	0.0	0.0
+Africa/Gaborone	2.0	2.0	2.0
+Africa/Harare	2.0	2.0	2.0
+Africa/Johannesburg	2.0	2.0	2.0
+Africa/Kampala	3.0	3.0	3.0
+Africa/Khartoum	3.0	3.0	3.0
+Africa/Kigali	2.0	2.0	2.0
+Africa/Kinshasa	1.0	1.0	1.0
+Africa/Lagos	1.0	1.0	1.0
+Africa/Libreville	1.0	1.0	1.0
+Africa/Lome	0.0	0.0	0.0
+Africa/Luanda	1.0	1.0	1.0
+Africa/Lubumbashi	2.0	2.0	2.0
+Africa/Lusaka	2.0	2.0	2.0
+Africa/Malabo	1.0	1.0	1.0
+Africa/Maputo	2.0	2.0	2.0
+Africa/Maseru	2.0	2.0	2.0
+Africa/Mbabane	2.0	2.0	2.0
+Africa/Mogadishu	3.0	3.0	3.0
+Africa/Monrovia	0.0	0.0	0.0
+Africa/Nairobi	3.0	3.0	3.0
+Africa/Ndjamena	1.0	1.0	1.0
+Africa/Niamey	1.0	1.0	1.0
+Africa/Nouakchott	0.0	0.0	0.0
+Africa/Ouagadougou	0.0	0.0	0.0
+Africa/Porto-Novo	1.0	1.0	1.0
+Africa/Sao_Tome	0.0	0.0	0.0
+Africa/Tripoli	2.0	2.0	2.0
+Africa/Tunis	1.0	1.0	1.0
+Africa/Windhoek	2.0	1.0	1.0
+America/Adak	-10.0	-9.0	-10.0
+America/Anchorage	-9.0	-8.0	-9.0
+America/Anguilla	-4.0	-4.0	-4.0
+America/Antigua	-4.0	-4.0	-4.0
+America/Araguaina	-3.0	-3.0	-3.0
+America/Argentina/Buenos_Aires	-3.0	-3.0	-3.0
+America/Argentina/Catamarca	-3.0	-3.0	-3.0
+America/Argentina/Cordoba	-3.0	-3.0	-3.0
+America/Argentina/Jujuy	-3.0	-3.0	-3.0
+America/Argentina/La_Rioja	-3.0	-3.0	-3.0
+America/Argentina/Mendoza	-3.0	-3.0	-3.0
+America/Argentina/Rio_Gallegos	-3.0	-3.0	-3.0
+America/Argentina/Salta	-3.0	-3.0	-3.0
+America/Argentina/San_Juan	-3.0	-3.0	-3.0
+America/Argentina/San_Luis	-3.0	-3.0	-4.0
+America/Argentina/Tucuman	-3.0	-3.0	-3.0
+America/Argentina/Ushuaia	-3.0	-3.0	-3.0
+America/Aruba	-4.0	-4.0	-4.0
+America/Asuncion	-3.0	-4.0	-4.0
+America/Atikokan	-5.0	-5.0	-5.0
+America/Bahia	-3.0	-3.0	-3.0
+America/Bahia_Banderas	-6.0	-5.0	-6.0
+America/Barbados	-4.0	-4.0	-4.0
+America/Belem	-3.0	-3.0	-3.0
+America/Belize	-6.0	-6.0	-6.0
+America/Blanc-Sablon	-4.0	-4.0	-4.0
+America/Boa_Vista	-4.0	-4.0	-4.0
+America/Bogota	-5.0	-5.0	-5.0
+America/Boise	-7.0	-6.0	-7.0
+America/Cambridge_Bay	-7.0	-6.0	-7.0
+America/Campo_Grande	-3.0	-4.0	-4.0
+America/Cancun	-6.0	-5.0	-6.0
+America/Caracas	-4.5	-4.5	-4.5
+America/Cayenne	-3.0	-3.0	-3.0
+America/Cayman	-5.0	-5.0	-5.0
+America/Chicago	-6.0	-5.0	-6.0
+America/Chihuahua	-7.0	-6.0	-7.0
+America/Costa_Rica	-6.0	-6.0	-6.0
+America/Cuiaba	-3.0	-4.0	-4.0
+America/Curacao	-4.0	-4.0	-4.0
+America/Danmarkshavn	0.0	0.0	0.0
+America/Dawson	-8.0	-7.0	-8.0
+America/Dawson_Creek	-7.0	-7.0	-7.0
+America/Denver	-7.0	-6.0	-7.0
+America/Detroit	-5.0	-4.0	-5.0
+America/Dominica	-4.0	-4.0	-4.0
+America/Edmonton	-7.0	-6.0	-7.0
+America/Eirunepe	-4.0	-4.0	-4.0
+America/El_Salvador	-6.0	-6.0	-6.0
+America/Fortaleza	-3.0	-3.0	-3.0
+America/Glace_Bay	-4.0	-3.0	-4.0
+America/Godthab	-3.0	-2.0	-3.0
+America/Goose_Bay	-4.0	-3.0	-4.0
+America/Grand_Turk	-5.0	-4.0	-5.0
+America/Grenada	-4.0	-4.0	-4.0
+America/Guadeloupe	-4.0	-4.0	-4.0
+America/Guatemala	-6.0	-6.0	-6.0
+America/Guayaquil	-5.0	-5.0	-5.0
+America/Guyana	-4.0	-4.0	-4.0
+America/Halifax	-4.0	-3.0	-4.0
+America/Havana	-5.0	-4.0	-5.0
+America/Hermosillo	-7.0	-7.0	-7.0
+America/Indiana/Indianapolis	-5.0	-4.0	-5.0
+America/Indiana/Knox	-6.0	-5.0	-6.0
+America/Indiana/Marengo	-5.0	-4.0	-5.0
+America/Indiana/Petersburg	-5.0	-4.0	-5.0
+America/Indiana/Tell_City	-6.0	-5.0	-6.0
+America/Indiana/Vevay	-5.0	-4.0	-5.0
+America/Indiana/Vincennes	-5.0	-4.0	-5.0
+America/Indiana/Winamac	-5.0	-4.0	-5.0
+America/Inuvik	-7.0	-6.0	-7.0
+America/Iqaluit	-5.0	-4.0	-5.0
+America/Jamaica	-5.0	-5.0	-5.0
+America/Juneau	-9.0	-8.0	-9.0
+America/Kentucky/Louisville	-5.0	-4.0	-5.0
+America/Kentucky/Monticello	-5.0	-4.0	-5.0
+America/La_Paz	-4.0	-4.0	-4.0
+America/Lima	-5.0	-5.0	-5.0
+America/Los_Angeles	-8.0	-7.0	-8.0
+America/Maceio	-3.0	-3.0	-3.0
+America/Managua	-6.0	-6.0	-6.0
+America/Manaus	-4.0	-4.0	-4.0
+America/Marigot	-4.0	-4.0	-4.0
+America/Martinique	-4.0	-4.0	-4.0
+America/Matamoros	-6.0	-5.0	-6.0
+America/Mazatlan	-7.0	-6.0	-7.0
+America/Menominee	-6.0	-5.0	-6.0
+America/Merida	-6.0	-5.0	-6.0
+America/Metlakatla	-8.0	-8.0	-8.0
+America/Mexico_City	-6.0	-5.0	-6.0
+America/Miquelon	-3.0	-2.0	-3.0
+America/Moncton	-4.0	-3.0	-4.0
+America/Monterrey	-6.0	-5.0	-6.0
+America/Montevideo	-2.0	-3.0	-3.0
+America/Montreal	-5.0	-4.0	-5.0
+America/Montserrat	-4.0	-4.0	-4.0
+America/Nassau	-5.0	-4.0	-5.0
+America/New_York	-5.0	-4.0	-5.0
+America/Nipigon	-5.0	-4.0	-5.0
+America/Nome	-9.0	-8.0	-9.0
+America/Noronha	-2.0	-2.0	-2.0
+America/North_Dakota/Beulah	-6.0	-5.0	-6.0
+America/North_Dakota/Center	-6.0	-5.0	-6.0
+America/North_Dakota/New_Salem	-6.0	-5.0	-6.0
+America/Ojinaga	-7.0	-6.0	-7.0
+America/Panama	-5.0	-5.0	-5.0
+America/Pangnirtung	-5.0	-4.0	-5.0
+America/Paramaribo	-3.0	-3.0	-3.0
+America/Phoenix	-7.0	-7.0	-7.0
+America/Port-au-Prince	-5.0	-5.0	-5.0
+America/Port_of_Spain	-4.0	-4.0	-4.0
+America/Porto_Velho	-4.0	-4.0	-4.0
+America/Puerto_Rico	-4.0	-4.0	-4.0
+America/Rainy_River	-6.0	-5.0	-6.0
+America/Rankin_Inlet	-6.0	-5.0	-6.0
+America/Recife	-3.0	-3.0	-3.0
+America/Regina	-6.0	-6.0	-6.0
+America/Resolute	-6.0	-5.0	-6.0
+America/Rio_Branco	-4.0	-4.0	-4.0
+America/Santa_Isabel	-8.0	-7.0	-8.0
+America/Santarem	-3.0	-3.0	-3.0
+America/Santiago	-3.0	-4.0	-4.0
+America/Santo_Domingo	-4.0	-4.0	-4.0
+America/Sao_Paulo	-2.0	-3.0	-3.0
+America/Scoresbysund	-1.0	0.0	-1.0
+America/Shiprock	-7.0	-6.0	-7.0
+America/Sitka	-9.0	-8.0	-9.0
+America/St_Barthelemy	-4.0	-4.0	-4.0
+America/St_Johns	-3.5	-2.5	-3.5
+America/St_Kitts	-4.0	-4.0	-4.0
+America/St_Lucia	-4.0	-4.0	-4.0
+America/St_Thomas	-4.0	-4.0	-4.0
+America/St_Vincent	-4.0	-4.0	-4.0
+America/Swift_Current	-6.0	-6.0	-6.0
+America/Tegucigalpa	-6.0	-6.0	-6.0
+America/Thule	-4.0	-3.0	-4.0
+America/Thunder_Bay	-5.0	-4.0	-5.0
+America/Tijuana	-8.0	-7.0	-8.0
+America/Toronto	-5.0	-4.0	-5.0
+America/Tortola	-4.0	-4.0	-4.0
+America/Vancouver	-8.0	-7.0	-8.0
+America/Whitehorse	-8.0	-7.0	-8.0
+America/Winnipeg	-6.0	-5.0	-6.0
+America/Yakutat	-9.0	-8.0	-9.0
+America/Yellowknife	-7.0	-6.0	-7.0
+Antarctica/Casey	8.0	8.0	8.0
+Antarctica/Davis	7.0	7.0	7.0
+Antarctica/DumontDUrville	10.0	10.0	10.0
+Antarctica/Macquarie	11.0	11.0	11.0
+Antarctica/Mawson	5.0	5.0	5.0
+Antarctica/McMurdo	13.0	12.0	12.0
+Antarctica/Palmer	-3.0	-4.0	-4.0
+Antarctica/Rothera	-3.0	-3.0	-3.0
+Antarctica/South_Pole	13.0	12.0	12.0
+Antarctica/Syowa	3.0	3.0	3.0
+Antarctica/Vostok	6.0	6.0	6.0
+Arctic/Longyearbyen	1.0	2.0	1.0
+Asia/Aden	3.0	3.0	3.0
+Asia/Almaty	6.0	6.0	6.0
+Asia/Amman	2.0	3.0	2.0
+Asia/Anadyr	11.0	12.0	12.0
+Asia/Aqtau	5.0	5.0	5.0
+Asia/Aqtobe	5.0	5.0	5.0
+Asia/Ashgabat	5.0	5.0	5.0
+Asia/Baghdad	3.0	3.0	3.0
+Asia/Bahrain	3.0	3.0	3.0
+Asia/Baku	4.0	5.0	4.0
+Asia/Bangkok	7.0	7.0	7.0
+Asia/Beirut	2.0	3.0	2.0
+Asia/Bishkek	6.0	6.0	6.0
+Asia/Brunei	8.0	8.0	8.0
+Asia/Choibalsan	8.0	8.0	8.0
+Asia/Chongqing	8.0	8.0	8.0
+Asia/Colombo	5.5	5.5	5.5
+Asia/Damascus	2.0	3.0	2.0
+Asia/Dhaka	6.0	6.0	6.0
+Asia/Dili	9.0	9.0	9.0
+Asia/Dubai	4.0	4.0	4.0
+Asia/Dushanbe	5.0	5.0	5.0
+Asia/Gaza	2.0	3.0	2.0
+Asia/Harbin	8.0	8.0	8.0
+Asia/Ho_Chi_Minh	7.0	7.0	7.0
+Asia/Hong_Kong	8.0	8.0	8.0
+Asia/Hovd	7.0	7.0	7.0
+Asia/Irkutsk	8.0	9.0	9.0
+Asia/Jakarta	7.0	7.0	7.0
+Asia/Jayapura	9.0	9.0	9.0
+Asia/Jerusalem	2.0	3.0	2.0
+Asia/Kabul	4.5	4.5	4.5
+Asia/Kamchatka	11.0	12.0	12.0
+Asia/Karachi	5.0	5.0	5.0
+Asia/Kashgar	8.0	8.0	8.0
+Asia/Kathmandu	5.75	5.75	5.75
+Asia/Kolkata	5.5	5.5	5.5
+Asia/Krasnoyarsk	7.0	8.0	8.0
+Asia/Kuala_Lumpur	8.0	8.0	8.0
+Asia/Kuching	8.0	8.0	8.0
+Asia/Kuwait	3.0	3.0	3.0
+Asia/Macau	8.0	8.0	8.0
+Asia/Magadan	11.0	12.0	12.0
+Asia/Makassar	8.0	8.0	8.0
+Asia/Manila	8.0	8.0	8.0
+Asia/Muscat	4.0	4.0	4.0
+Asia/Nicosia	2.0	3.0	2.0
+Asia/Novokuznetsk	6.0	7.0	7.0
+Asia/Novosibirsk	6.0	7.0	7.0
+Asia/Omsk	6.0	7.0	7.0
+Asia/Oral	5.0	5.0	5.0
+Asia/Phnom_Penh	7.0	7.0	7.0
+Asia/Pontianak	7.0	7.0	7.0
+Asia/Pyongyang	9.0	9.0	9.0
+Asia/Qatar	3.0	3.0	3.0
+Asia/Qyzylorda	6.0	6.0	6.0
+Asia/Rangoon	6.5	6.5	6.5
+Asia/Riyadh	3.0	3.0	3.0
+Asia/Sakhalin	10.0	11.0	11.0
+Asia/Samarkand	5.0	5.0	5.0
+Asia/Seoul	9.0	9.0	9.0
+Asia/Shanghai	8.0	8.0	8.0
+Asia/Singapore	8.0	8.0	8.0
+Asia/Taipei	8.0	8.0	8.0
+Asia/Tashkent	5.0	5.0	5.0
+Asia/Tbilisi	4.0	4.0	4.0
+Asia/Tehran	3.5	4.5	3.5
+Asia/Thimphu	6.0	6.0	6.0
+Asia/Tokyo	9.0	9.0	9.0
+Asia/Ulaanbaatar	8.0	8.0	8.0
+Asia/Urumqi	8.0	8.0	8.0
+Asia/Vientiane	7.0	7.0	7.0
+Asia/Vladivostok	10.0	11.0	11.0
+Asia/Yakutsk	9.0	10.0	10.0
+Asia/Yekaterinburg	5.0	6.0	6.0
+Asia/Yerevan	4.0	5.0	4.0
+Atlantic/Azores	-1.0	0.0	-1.0
+Atlantic/Bermuda	-4.0	-3.0	-4.0
+Atlantic/Canary	0.0	1.0	0.0
+Atlantic/Cape_Verde	-1.0	-1.0	-1.0
+Atlantic/Faroe	0.0	1.0	0.0
+Atlantic/Madeira	0.0	1.0	0.0
+Atlantic/Reykjavik	0.0	0.0	0.0
+Atlantic/South_Georgia	-2.0	-2.0	-2.0
+Atlantic/St_Helena	0.0	0.0	0.0
+Atlantic/Stanley	-3.0	-3.0	-4.0
+Australia/Adelaide	10.5	9.5	9.5
+Australia/Brisbane	10.0	10.0	10.0
+Australia/Broken_Hill	10.5	9.5	9.5
+Australia/Currie	11.0	10.0	10.0
+Australia/Darwin	9.5	9.5	9.5
+Australia/Eucla	8.75	8.75	8.75
+Australia/Hobart	11.0	10.0	10.0
+Australia/Lindeman	10.0	10.0	10.0
+Australia/Lord_Howe	11.0	10.5	10.5
+Australia/Melbourne	11.0	10.0	10.0
+Australia/Perth	8.0	8.0	8.0
+Australia/Sydney	11.0	10.0	10.0
+Europe/Amsterdam	1.0	2.0	1.0
+Europe/Andorra	1.0	2.0	1.0
+Europe/Athens	2.0	3.0	2.0
+Europe/Belgrade	1.0	2.0	1.0
+Europe/Berlin	1.0	2.0	1.0
+Europe/Bratislava	1.0	2.0	1.0
+Europe/Brussels	1.0	2.0	1.0
+Europe/Bucharest	2.0	3.0	2.0
+Europe/Budapest	1.0	2.0	1.0
+Europe/Chisinau	2.0	3.0	2.0
+Europe/Copenhagen	1.0	2.0	1.0
+Europe/Dublin	0.0	1.0	0.0
+Europe/Gibraltar	1.0	2.0	1.0
+Europe/Guernsey	0.0	1.0	0.0
+Europe/Helsinki	2.0	3.0	2.0
+Europe/Isle_of_Man	0.0	1.0	0.0
+Europe/Istanbul	2.0	3.0	2.0
+Europe/Jersey	0.0	1.0	0.0
+Europe/Kaliningrad	2.0	3.0	3.0
+Europe/Kiev	2.0	3.0	3.0
+Europe/Lisbon	0.0	1.0	0.0
+Europe/Ljubljana	1.0	2.0	1.0
+Europe/London	0.0	1.0	0.0
+Europe/Luxembourg	1.0	2.0	1.0
+Europe/Madrid	1.0	2.0	1.0
+Europe/Malta	1.0	2.0	1.0
+Europe/Mariehamn	2.0	3.0	2.0
+Europe/Minsk	2.0	3.0	3.0
+Europe/Monaco	1.0	2.0	1.0
+Europe/Moscow	3.0	4.0	4.0
+Europe/Oslo	1.0	2.0	1.0
+Europe/Paris	1.0	2.0	1.0
+Europe/Podgorica	1.0	2.0	1.0
+Europe/Prague	1.0	2.0	1.0
+Europe/Riga	2.0	3.0	2.0
+Europe/Rome	1.0	2.0	1.0
+Europe/Samara	3.0	4.0	4.0
+Europe/San_Marino	1.0	2.0	1.0
+Europe/Sarajevo	1.0	2.0	1.0
+Europe/Simferopol	2.0	3.0	3.0
+Europe/Skopje	1.0	2.0	1.0
+Europe/Sofia	2.0	3.0	2.0
+Europe/Stockholm	1.0	2.0	1.0
+Europe/Tallinn	2.0	3.0	2.0
+Europe/Tirane	1.0	2.0	1.0
+Europe/Uzhgorod	2.0	3.0	3.0
+Europe/Vaduz	1.0	2.0	1.0
+Europe/Vatican	1.0	2.0	1.0
+Europe/Vienna	1.0	2.0	1.0
+Europe/Vilnius	2.0	3.0	2.0
+Europe/Volgograd	3.0	4.0	4.0
+Europe/Warsaw	1.0	2.0	1.0
+Europe/Zagreb	1.0	2.0	1.0
+Europe/Zaporozhye	2.0	3.0	3.0
+Europe/Zurich	1.0	2.0	1.0
+Indian/Antananarivo	3.0	3.0	3.0
+Indian/Chagos	6.0	6.0	6.0
+Indian/Christmas	7.0	7.0	7.0
+Indian/Cocos	6.5	6.5	6.5
+Indian/Comoro	3.0	3.0	3.0
+Indian/Kerguelen	5.0	5.0	5.0
+Indian/Mahe	4.0	4.0	4.0
+Indian/Maldives	5.0	5.0	5.0
+Indian/Mauritius	4.0	4.0	4.0
+Indian/Mayotte	3.0	3.0	3.0
+Indian/Reunion	4.0	4.0	4.0
+Pacific/Apia	-10.0	-11.0	-11.0
+Pacific/Auckland	13.0	12.0	12.0
+Pacific/Chatham	13.75	12.75	12.75
+Pacific/Chuuk	10.0	10.0	10.0
+Pacific/Easter	-5.0	-6.0	-6.0
+Pacific/Efate	11.0	11.0	11.0
+Pacific/Enderbury	13.0	13.0	13.0
+Pacific/Fakaofo	-10.0	-10.0	-10.0
+Pacific/Fiji	13.0	12.0	12.0
+Pacific/Funafuti	12.0	12.0	12.0
+Pacific/Galapagos	-6.0	-6.0	-6.0
+Pacific/Gambier	-9.0	-9.0	-9.0
+Pacific/Guadalcanal	11.0	11.0	11.0
+Pacific/Guam	10.0	10.0	10.0
+Pacific/Honolulu	-10.0	-10.0	-10.0
+Pacific/Johnston	-10.0	-10.0	-10.0
+Pacific/Kiritimati	14.0	14.0	14.0
+Pacific/Kosrae	11.0	11.0	11.0
+Pacific/Kwajalein	12.0	12.0	12.0
+Pacific/Majuro	12.0	12.0	12.0
+Pacific/Marquesas	-9.5	-9.5	-9.5
+Pacific/Midway	-11.0	-11.0	-11.0
+Pacific/Nauru	12.0	12.0	12.0
+Pacific/Niue	-11.0	-11.0	-11.0
+Pacific/Norfolk	11.5	11.5	11.5
+Pacific/Noumea	11.0	11.0	11.0
+Pacific/Pago_Pago	-11.0	-11.0	-11.0
+Pacific/Palau	9.0	9.0	9.0
+Pacific/Pitcairn	-8.0	-8.0	-8.0
+Pacific/Pohnpei	11.0	11.0	11.0
+Pacific/Port_Moresby	10.0	10.0	10.0
+Pacific/Rarotonga	-10.0	-10.0	-10.0
+Pacific/Saipan	10.0	10.0	10.0
+Pacific/Tahiti	-10.0	-10.0	-10.0
+Pacific/Tarawa	12.0	12.0	12.0
+Pacific/Tongatapu	13.0	13.0	13.0
+Pacific/Wake	12.0	12.0	12.0
+Pacific/Wallis	12.0	12.0	12.0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dataimport/test/test_csv.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,72 @@
+# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+"""unittest for cubicweb.dataimport.csv"""
+
+from StringIO import StringIO
+
+from logilab.common.testlib import TestCase, unittest_main
+
+from cubicweb.dataimport import csv
+
+
+class UcsvreaderTC(TestCase):
+
+    def test_empty_lines_skipped(self):
+        stream = StringIO('''a,b,c,d,
+1,2,3,4,
+,,,,
+,,,,
+''')
+        self.assertEqual([[u'a', u'b', u'c', u'd', u''],
+                          [u'1', u'2', u'3', u'4', u''],
+                          ],
+                         list(csv.ucsvreader(stream)))
+        stream.seek(0)
+        self.assertEqual([[u'a', u'b', u'c', u'd', u''],
+                          [u'1', u'2', u'3', u'4', u''],
+                          [u'', u'', u'', u'', u''],
+                          [u'', u'', u'', u'', u'']
+                          ],
+                         list(csv.ucsvreader(stream, skip_empty=False)))
+
+    def test_skip_first(self):
+        stream = StringIO('a,b,c,d,\n1,2,3,4,\n')
+        reader = csv.ucsvreader(stream, skipfirst=True, ignore_errors=True)
+        self.assertEqual(list(reader),
+                         [[u'1', u'2', u'3', u'4', u'']])
+
+        stream.seek(0)
+        reader = csv.ucsvreader(stream, skipfirst=True, ignore_errors=False)
+        self.assertEqual(list(reader),
+                         [[u'1', u'2', u'3', u'4', u'']])
+
+        stream.seek(0)
+        reader = csv.ucsvreader(stream, skipfirst=False, ignore_errors=True)
+        self.assertEqual(list(reader),
+                         [[u'a', u'b', u'c', u'd', u''],
+                          [u'1', u'2', u'3', u'4', u'']])
+
+        stream.seek(0)
+        reader = csv.ucsvreader(stream, skipfirst=False, ignore_errors=False)
+        self.assertEqual(list(reader),
+                         [[u'a', u'b', u'c', u'd', u''],
+                          [u'1', u'2', u'3', u'4', u'']])
+
+
+if __name__ == '__main__':
+    unittest_main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dataimport/test/test_pgstore.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,93 @@
+# coding: utf-8
+# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+"""unittest for cubicweb.dataimport.pgstore"""
+
+import datetime as DT
+
+from logilab.common.testlib import TestCase, unittest_main
+
+from cubicweb.dataimport import pgstore
+
+
+class CreateCopyFromBufferTC(TestCase):
+
+    # test converters
+
+    def test_convert_none(self):
+        cnvt = pgstore._copyfrom_buffer_convert_None
+        self.assertEqual('NULL', cnvt(None))
+
+    def test_convert_number(self):
+        cnvt = pgstore._copyfrom_buffer_convert_number
+        self.assertEqual('42', cnvt(42))
+        self.assertEqual('42', cnvt(42L))
+        self.assertEqual('42.42', cnvt(42.42))
+
+    def test_convert_string(self):
+        cnvt = pgstore._copyfrom_buffer_convert_string
+        # simple
+        self.assertEqual('babar', cnvt('babar'))
+        # unicode
+        self.assertEqual('\xc3\xa9l\xc3\xa9phant', cnvt(u'éléphant'))
+        self.assertEqual('\xe9l\xe9phant', cnvt(u'éléphant', encoding='latin1'))
+        # escaping
+        self.assertEqual('babar\\tceleste\\n', cnvt('babar\tceleste\n'))
+        self.assertEqual(r'C:\\new\tC:\\test', cnvt('C:\\new\tC:\\test'))
+
+    def test_convert_date(self):
+        cnvt = pgstore._copyfrom_buffer_convert_date
+        self.assertEqual('0666-01-13', cnvt(DT.date(666, 1, 13)))
+
+    def test_convert_time(self):
+        cnvt = pgstore._copyfrom_buffer_convert_time
+        self.assertEqual('06:06:06.000100', cnvt(DT.time(6, 6, 6, 100)))
+
+    def test_convert_datetime(self):
+        cnvt = pgstore._copyfrom_buffer_convert_datetime
+        self.assertEqual('0666-06-13 06:06:06.000000', cnvt(DT.datetime(666, 6, 13, 6, 6, 6)))
+
+    # test buffer
+    def test_create_copyfrom_buffer_tuple(self):
+        data = ((42, 42L, 42.42, u'éléphant', DT.date(666, 1, 13), DT.time(6, 6, 6),
+                 DT.datetime(666, 6, 13, 6, 6, 6)),
+                (6, 6L, 6.6, u'babar', DT.date(2014, 1, 14), DT.time(4, 2, 1),
+                 DT.datetime(2014, 1, 1, 0, 0, 0)))
+        results = pgstore._create_copyfrom_buffer(data)
+        # all columns
+        expected = '''42\t42\t42.42\téléphant\t0666-01-13\t06:06:06.000000\t0666-06-13 06:06:06.000000
+6\t6\t6.6\tbabar\t2014-01-14\t04:02:01.000000\t2014-01-01 00:00:00.000000'''
+        self.assertMultiLineEqual(expected, results.getvalue())
+        # selected columns
+        results = pgstore._create_copyfrom_buffer(data, columns=(1, 3, 6))
+        expected = '''42\téléphant\t0666-06-13 06:06:06.000000
+6\tbabar\t2014-01-01 00:00:00.000000'''
+        self.assertMultiLineEqual(expected, results.getvalue())
+
+    def test_create_copyfrom_buffer_dict(self):
+        data = (dict(integer=42, double=42.42, text=u'éléphant',
+                     date=DT.datetime(666, 6, 13, 6, 6, 6)),
+                dict(integer=6, double=6.6, text=u'babar',
+                     date=DT.datetime(2014, 1, 1, 0, 0, 0)))
+        results = pgstore._create_copyfrom_buffer(data, ('integer', 'text'))
+        expected = '''42\téléphant\n6\tbabar'''
+        self.assertMultiLineEqual(expected, results.getvalue())
+
+
+if __name__ == '__main__':
+    unittest_main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dataimport/test/test_sqlgenstore.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,124 @@
+# -*- coding: utf-8 -*-
+# copyright 2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr -- mailto:contact@logilab.fr
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+"""SQL object store test case"""
+
+import itertools
+
+from cubicweb.dataimport import ucsvreader
+from cubicweb.devtools import testlib, PostgresApptestConfiguration
+from cubicweb.devtools import startpgcluster, stoppgcluster
+from cubicweb.dataimport.pgstore import SQLGenObjectStore
+
+
+def setUpModule():
+    startpgcluster(__file__)
+
+
+def tearDownModule(*args):
+    stoppgcluster(__file__)
+
+
+class SQLGenImportSimpleTC(testlib.CubicWebTC):
+    configcls = PostgresApptestConfiguration
+    appid = 'data-massimport'
+
+    def cast(self, _type, value):
+        try:
+            return _type(value)
+        except ValueError:
+            return None
+
+    def push_geonames_data(self, dumpname, store):
+        # Push timezones
+        cnx = store._cnx
+        for code, gmt, dst, raw_offset in ucsvreader(open(self.datapath('timeZones.txt'), 'rb'),
+                                                     delimiter='\t'):
+            cnx.create_entity('TimeZone', code=code, gmt=float(gmt),
+                                    dst=float(dst), raw_offset=float(raw_offset))
+        timezone_code = dict(cnx.execute('Any C, X WHERE X is TimeZone, X code C'))
+        cnx.commit()
+        # Push data
+        for ind, infos in enumerate(ucsvreader(open(dumpname, 'rb'),
+                                               delimiter='\t',
+                                               ignore_errors=True)):
+            if ind > 99:
+                break
+            latitude = self.cast(float, infos[4])
+            longitude = self.cast(float, infos[5])
+            population = self.cast(int, infos[14])
+            elevation = self.cast(int, infos[15])
+            gtopo = self.cast(int, infos[16])
+            feature_class = infos[6]
+            if len(infos[6]) != 1:
+                feature_class = None
+            entity = {'name': infos[1],
+                      'asciiname': infos[2],
+                      'alternatenames': infos[3],
+                      'latitude': latitude, 'longitude': longitude,
+                      'feature_class': feature_class,
+                      'alternate_country_code':infos[9],
+                      'admin_code_3': infos[12],
+                      'admin_code_4': infos[13],
+                      'population': population, 'elevation': elevation,
+                      'gtopo30': gtopo, 'timezone': timezone_code.get(infos[17]),
+                      'cwuri':  u'http://sws.geonames.org/%s/' % int(infos[0]),
+                      'geonameid': int(infos[0]),
+                      }
+            store.prepare_insert_entity('Location', **entity)
+
+    def test_autoflush_metadata(self):
+        with self.admin_access.repo_cnx() as cnx:
+            crs = cnx.system_sql('SELECT * FROM entities WHERE type=%(t)s',
+                                 {'t': 'Location'})
+            self.assertEqual(len(crs.fetchall()), 0)
+            store = SQLGenObjectStore(cnx)
+            store.prepare_insert_entity('Location', name=u'toto')
+            store.flush()
+            store.commit()
+            cnx.commit()
+        with self.admin_access.repo_cnx() as cnx:
+            crs = cnx.system_sql('SELECT * FROM entities WHERE type=%(t)s',
+                                 {'t': 'Location'})
+            self.assertEqual(len(crs.fetchall()), 1)
+
+    def test_sqlgenstore_etype_metadata(self):
+        with self.admin_access.repo_cnx() as cnx:
+            store = SQLGenObjectStore(cnx)
+            timezone_eid = store.prepare_insert_entity('TimeZone')
+            store.prepare_insert_entity('Location', timezone=timezone_eid)
+            store.flush()
+            store.commit()
+            eid, etname = cnx.execute('Any X, TN WHERE X timezone TZ, X is T, '
+                                      'T name TN')[0]
+            self.assertEqual(cnx.entity_from_eid(eid).cw_etype, etname)
+
+    def test_simple_insert(self):
+        with self.admin_access.repo_cnx() as cnx:
+            store = SQLGenObjectStore(cnx)
+            self.push_geonames_data(self.datapath('geonames.csv'), store)
+            store.flush()
+            store.commit()
+        with self.admin_access.repo_cnx() as cnx:
+            rset = cnx.execute('Any X WHERE X is Location')
+            self.assertEqual(len(rset), 100)
+            rset = cnx.execute('Any X WHERE X is Location, X timezone T')
+            self.assertEqual(len(rset), 100)
+
+
+if __name__ == '__main__':
+    from logilab.common.testlib import unittest_main
+    unittest_main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dataimport/test/test_stores.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,88 @@
+# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+"""unittest for cubicweb.dataimport.stores"""
+
+import datetime as DT
+
+from cubicweb.dataimport import stores
+from cubicweb.devtools.testlib import CubicWebTC
+
+
+class RQLObjectStoreTC(CubicWebTC):
+
+    def test_all(self):
+        with self.admin_access.repo_cnx() as cnx:
+            store = stores.RQLObjectStore(cnx)
+            # Check data insertion
+            group_eid = store.prepare_insert_entity('CWGroup', name=u'grp')
+            user_eid = store.prepare_insert_entity('CWUser', login=u'lgn',
+                                                   upassword=u'pwd')
+            store.prepare_insert_relation(user_eid, 'in_group', group_eid)
+            cnx.commit()
+            users = cnx.execute('CWUser X WHERE X login "lgn"')
+            self.assertEqual(1, len(users))
+            self.assertEqual(user_eid, users.one().eid)
+            groups = cnx.execute('CWGroup X WHERE U in_group X, U login "lgn"')
+            self.assertEqual(1, len(users))
+            self.assertEqual(group_eid, groups.one().eid)
+            # Check data update
+            self.set_description('Check data update')
+            store.prepare_update_entity('CWGroup', group_eid, name=u'new_grp')
+            cnx.commit()
+            group = cnx.execute('CWGroup X WHERE X name "grp"')
+            self.assertEqual(len(group), 0)
+            group = cnx.execute('CWGroup X WHERE X name "new_grp"')
+            self.assertEqual, len(group), 1
+            # Check data update with wrong type
+            with self.assertRaises(AssertionError):
+                store.prepare_update_entity('CWUser', group_eid, name=u'new_user')
+            cnx.commit()
+            group = cnx.execute('CWGroup X WHERE X name "new_user"')
+            self.assertEqual(len(group), 0)
+            group = cnx.execute('CWGroup X WHERE X name "new_grp"')
+            self.assertEqual(len(group), 1)
+
+
+class MetaGeneratorTC(CubicWebTC):
+
+    def test_dont_generate_relation_to_internal_manager(self):
+        with self.admin_access.repo_cnx() as cnx:
+            metagen = stores.MetaGenerator(cnx)
+            self.assertIn('created_by', metagen.etype_rels)
+            self.assertIn('owned_by', metagen.etype_rels)
+        with self.repo.internal_cnx() as cnx:
+            metagen = stores.MetaGenerator(cnx)
+            self.assertNotIn('created_by', metagen.etype_rels)
+            self.assertNotIn('owned_by', metagen.etype_rels)
+
+    def test_dont_generate_specified_values(self):
+        with self.admin_access.repo_cnx() as cnx:
+            metagen = stores.MetaGenerator(cnx)
+            # hijack gen_modification_date to ensure we don't go through it
+            metagen.gen_modification_date = None
+            md = DT.datetime.now() - DT.timedelta(days=1)
+            entity, rels = metagen.base_etype_dicts('CWUser')
+            entity.cw_edited.update(dict(modification_date=md))
+            with cnx.ensure_cnx_set:
+                metagen.init_entity(entity)
+            self.assertEqual(entity.cw_edited['modification_date'], md)
+
+
+if __name__ == '__main__':
+    from logilab.common.testlib import unittest_main
+    unittest_main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dataimport/test/unittest_importer.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,190 @@
+# -*- coding: utf-8 -*-
+# copyright 2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr -- mailto:contact@logilab.fr
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+"""Tests for cubicweb.dataimport.importer"""
+
+from collections import defaultdict
+
+from logilab.common.testlib import TestCase, unittest_main
+
+from cubicweb import ValidationError
+from cubicweb.devtools.testlib import CubicWebTC
+from cubicweb.dataimport import RQLObjectStore, ucsvreader
+from cubicweb.dataimport.importer import (ExtEntity, ExtEntitiesImporter, SimpleImportLog,
+                                          RelationMapping, use_extid_as_cwuri)
+
+
+class RelationMappingTC(CubicWebTC):
+
+    def test_nosource(self):
+        with self.admin_access.repo_cnx() as cnx:
+            alice_eid = cnx.create_entity('Personne', nom=u'alice').eid
+            bob_eid = cnx.create_entity('Personne', nom=u'bob', connait=alice_eid).eid
+            cnx.commit()
+            mapping = RelationMapping(cnx)
+            self.assertEqual(mapping['connait'],
+                             set([(bob_eid, alice_eid), (alice_eid, bob_eid)]))
+
+    def test_with_source(self):
+        with self.admin_access.repo_cnx() as cnx:
+            alice_eid = cnx.create_entity('Personne', nom=u'alice').eid
+            bob_eid = cnx.create_entity('Personne', nom=u'bob', connait=alice_eid).eid
+            cnx.commit()
+            mapping = RelationMapping(cnx, cnx.find('CWSource', name=u'system').one())
+            self.assertEqual(mapping['connait'],
+                             set([(bob_eid, alice_eid), (alice_eid, bob_eid)]))
+
+
+class ExtEntitiesImporterTC(CubicWebTC):
+
+    def importer(self, cnx):
+        store = RQLObjectStore(cnx)
+        return ExtEntitiesImporter(self.schema, store, raise_on_error=True)
+
+    def test_simple_import(self):
+        with self.admin_access.repo_cnx() as cnx:
+            importer = self.importer(cnx)
+            personne = ExtEntity('Personne', 1, {'nom': set([u'de la lune']),
+                                                 'prenom': set([u'Jean'])})
+            importer.import_entities([personne])
+            cnx.commit()
+            rset = cnx.execute('Any X WHERE X is Personne')
+            entity = rset.get_entity(0, 0)
+            self.assertEqual(entity.nom, u'de la lune')
+            self.assertEqual(entity.prenom, u'Jean')
+
+    def test_import_missing_required_attribute(self):
+        """Check import of ext entity with missing required attribute"""
+        with self.admin_access.repo_cnx() as cnx:
+            importer = self.importer(cnx)
+            tag = ExtEntity('Personne', 2, {'prenom': set([u'Jean'])})
+            self.assertRaises(ValidationError, importer.import_entities, [tag])
+
+    def test_import_inlined_relation(self):
+        """Check import of ext entities with inlined relation"""
+        with self.admin_access.repo_cnx() as cnx:
+            importer = self.importer(cnx)
+            richelieu = ExtEntity('Personne', 3, {'nom': set([u'Richelieu']),
+                                                  'enfant': set([4])})
+            athos = ExtEntity('Personne', 4, {'nom': set([u'Athos'])})
+            importer.import_entities([athos, richelieu])
+            cnx.commit()
+            rset = cnx.execute('Any X WHERE X is Personne, X nom "Richelieu"')
+            entity = rset.get_entity(0, 0)
+            self.assertEqual(entity.enfant[0].nom, 'Athos')
+
+    def test_import_non_inlined_relation(self):
+        """Check import of ext entities with non inlined relation"""
+        with self.admin_access.repo_cnx() as cnx:
+            importer = self.importer(cnx)
+            richelieu = ExtEntity('Personne', 5, {'nom': set([u'Richelieu']),
+                                                  'connait': set([6])})
+            athos = ExtEntity('Personne', 6, {'nom': set([u'Athos'])})
+            importer.import_entities([athos, richelieu])
+            cnx.commit()
+            rset = cnx.execute('Any X WHERE X is Personne, X nom "Richelieu"')
+            entity = rset.get_entity(0, 0)
+            self.assertEqual(entity.connait[0].nom, 'Athos')
+            rset = cnx.execute('Any X WHERE X is Personne, X nom "Athos"')
+            entity = rset.get_entity(0, 0)
+            self.assertEqual(entity.connait[0].nom, 'Richelieu')
+
+    def test_import_missing_inlined_relation(self):
+        """Check import of ext entity with missing inlined relation"""
+        with self.admin_access.repo_cnx() as cnx:
+            importer = self.importer(cnx)
+            richelieu = ExtEntity('Personne', 7,
+                                  {'nom': set([u'Richelieu']), 'enfant': set([8])})
+            self.assertRaises(Exception, importer.import_entities, [richelieu])
+            cnx.commit()
+            rset = cnx.execute('Any X WHERE X is Personne, X nom "Richelieu"')
+            self.assertEqual(len(rset), 0)
+
+    def test_import_missing_non_inlined_relation(self):
+        """Check import of ext entity with missing non-inlined relation"""
+        with self.admin_access.repo_cnx() as cnx:
+            importer = self.importer(cnx)
+            richelieu = ExtEntity('Personne', 9,
+                                  {'nom': set([u'Richelieu']), 'connait': set([10])})
+            self.assertRaises(Exception, importer.import_entities, [richelieu])
+            cnx.commit()
+            rset = cnx.execute('Any X WHERE X is Personne, X nom "Richelieu"')
+            entity = rset.get_entity(0, 0)
+            self.assertEqual(entity.nom, u'Richelieu')
+            self.assertEqual(len(entity.connait), 0)
+
+    def test_update(self):
+        """Check update of ext entity"""
+        with self.admin_access.repo_cnx() as cnx:
+            importer = self.importer(cnx)
+            # First import
+            richelieu = ExtEntity('Personne', 11,
+                                  {'nom': {u'Richelieu Diacre'}})
+            importer.import_entities([richelieu])
+            cnx.commit()
+            rset = cnx.execute('Any X WHERE X is Personne')
+            entity = rset.get_entity(0, 0)
+            self.assertEqual(entity.nom, u'Richelieu Diacre')
+            # Second import
+            richelieu = ExtEntity('Personne', 11,
+                                  {'nom': {u'Richelieu Cardinal'}})
+            importer.import_entities([richelieu])
+            cnx.commit()
+            rset = cnx.execute('Any X WHERE X is Personne')
+            self.assertEqual(len(rset), 1)
+            entity = rset.get_entity(0, 0)
+            self.assertEqual(entity.nom, u'Richelieu Cardinal')
+
+
+class UseExtidAsCwuriTC(TestCase):
+
+    def test(self):
+        personne = ExtEntity('Personne', 1, {'nom': set([u'de la lune']),
+                                             'prenom': set([u'Jean'])})
+        mapping = {}
+        set_cwuri = use_extid_as_cwuri(mapping)
+        list(set_cwuri((personne,)))
+        self.assertIn('cwuri', personne.values)
+        self.assertEqual(personne.values['cwuri'], set(['1']))
+        mapping[1] = 'whatever'
+        personne.values.pop('cwuri')
+        list(set_cwuri((personne,)))
+        self.assertNotIn('cwuri', personne.values)
+
+
+def extentities_from_csv(fpath):
+    """Yield ExtEntity read from `fpath` CSV file."""
+    with open(fpath) as f:
+        for uri, name, knows in ucsvreader(f, skipfirst=True, skip_empty=False):
+            yield ExtEntity('Personne', uri,
+                            {'nom': set([name]), 'connait': set([knows])})
+
+
+class DataimportFunctionalTC(CubicWebTC):
+
+    def test_csv(self):
+        extenties = extentities_from_csv(self.datapath('people.csv'))
+        with self.admin_access.repo_cnx() as cnx:
+            store = RQLObjectStore(cnx)
+            importer = ExtEntitiesImporter(self.schema, store)
+            importer.import_entities(extenties)
+            cnx.commit()
+            rset = cnx.execute('String N WHERE X nom N, X connait Y, Y nom "Alice"')
+            self.assertEqual(rset[0][0], u'Bob')
+
+
+if __name__ == '__main__':
+    unittest_main()
--- a/dbapi.py	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,836 +0,0 @@
-# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of CubicWeb.
-#
-# CubicWeb is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""DB-API 2.0 compliant module
-
-Take a look at http://www.python.org/peps/pep-0249.html
-
-(most parts of this document are reported here in docstrings)
-"""
-
-__docformat__ = "restructuredtext en"
-
-from threading import currentThread
-from logging import getLogger
-from time import time, clock
-from itertools import count
-from warnings import warn
-from os.path import join
-from uuid import uuid4
-from urlparse import  urlparse
-
-from logilab.common.logging_ext import set_log_methods
-from logilab.common.decorators import monkeypatch, cachedproperty
-from logilab.common.deprecation import deprecated
-
-from cubicweb import (ETYPE_NAME_MAP, AuthenticationError, ProgrammingError,
-                      cwvreg, cwconfig)
-from cubicweb.repoapi import get_repository
-from cubicweb.req import RequestSessionBase
-
-
-_MARKER = object()
-
-def _fake_property_value(self, name):
-    try:
-        return super(DBAPIRequest, self).property_value(name)
-    except KeyError:
-        return ''
-
-def fake(*args, **kwargs):
-    return None
-
-def multiple_connections_fix():
-    """some monkey patching necessary when an application has to deal with
-    several connections to different repositories. It tries to hide buggy class
-    attributes since classes are not designed to be shared among multiple
-    registries.
-    """
-    defaultcls = cwvreg.CWRegistryStore.REGISTRY_FACTORY[None]
-
-    etypescls = cwvreg.CWRegistryStore.REGISTRY_FACTORY['etypes']
-    orig_etype_class = etypescls.orig_etype_class = etypescls.etype_class
-    @monkeypatch(defaultcls)
-    def etype_class(self, etype):
-        """return an entity class for the given entity type.
-        Try to find out a specific class for this kind of entity or
-        default to a dump of the class registered for 'Any'
-        """
-        usercls = orig_etype_class(self, etype)
-        if etype == 'Any':
-            return usercls
-        usercls.e_schema = self.schema.eschema(etype)
-        return usercls
-
-def multiple_connections_unfix():
-    etypescls = cwvreg.CWRegistryStore.REGISTRY_FACTORY['etypes']
-    etypescls.etype_class = etypescls.orig_etype_class
-
-
-class ConnectionProperties(object):
-    def __init__(self, cnxtype=None, close=True, log=False):
-        if cnxtype is not None:
-            warn('[3.16] cnxtype argument is deprecated', DeprecationWarning,
-                 stacklevel=2)
-        self.cnxtype = cnxtype
-        self.log_queries = log
-        self.close_on_del = close
-
-
-@deprecated('[3.19] the dbapi is deprecated. Have a look at the new repoapi.')
-def _repo_connect(repo, login, **kwargs):
-    """Constructor to create a new connection to the given CubicWeb repository.
-
-    Returns a Connection instance.
-
-    Raises AuthenticationError if authentication failed
-    """
-    cnxid = repo.connect(unicode(login), **kwargs)
-    cnx = Connection(repo, cnxid, kwargs.get('cnxprops'))
-    if cnx.is_repo_in_memory:
-        cnx.vreg = repo.vreg
-    return cnx
-
-def connect(database, login=None,
-            cnxprops=None, setvreg=True, mulcnx=True, initlog=True, **kwargs):
-    """Constructor for creating a connection to the CubicWeb repository.
-    Returns a :class:`Connection` object.
-
-    Typical usage::
-
-      cnx = connect('myinstance', login='me', password='toto')
-
-    `database` may be:
-
-    * a simple instance id for in-memory connection
-
-    * a uri like scheme://host:port/instanceid where scheme may be one of
-      'pyro', 'inmemory' or 'zmqpickle'
-
-      * if scheme is 'pyro', <host:port> determine the name server address. If
-        not specified (e.g. 'pyro:///instanceid'), it will be detected through a
-        broadcast query. The instance id is the name of the instance in the name
-        server and may be prefixed by a group (e.g.
-        'pyro:///:cubicweb.instanceid')
-
-      * if scheme is handled by ZMQ (eg 'tcp'), you should not specify an
-        instance id
-
-    Other arguments:
-
-    :login:
-      the user login to use to authenticate.
-
-    :cnxprops:
-      a :class:`ConnectionProperties` instance, allowing to specify
-      the connection method (eg in memory or pyro). A Pyro connection will be
-      established if you don't specify that argument.
-
-    :setvreg:
-      flag telling if a registry should be initialized for the connection.
-      Don't change this unless you know what you're doing.
-
-    :mulcnx:
-      Will disappear at some point. Try to deal with connections to differents
-      instances in the same process unless specified otherwise by setting this
-      flag to False. Don't change this unless you know what you're doing.
-
-    :initlog:
-      flag telling if logging should be initialized. You usually don't want
-      logging initialization when establishing the connection from a process
-      where it's already initialized.
-
-    :kwargs:
-      there goes authentication tokens. You usually have to specify a password
-      for the given user, using a named 'password' argument.
-    """
-    if not urlparse(database).scheme:
-        warn('[3.16] give an qualified URI as database instead of using '
-             'host/cnxprops to specify the connection method',
-             DeprecationWarning, stacklevel=2)
-        if cnxprops and cnxprops.cnxtype == 'zmq':
-            database = kwargs.pop('host')
-        elif cnxprops and cnxprops.cnxtype == 'inmemory':
-            database = 'inmemory://' + database
-        else:
-            host = kwargs.pop('host', None)
-            if host is None:
-                host = ''
-            group = kwargs.pop('group', None)
-            if group is None:
-                group = 'cubicweb'
-            database = 'pyro://%s/%s.%s' % (host, group, database)
-    puri = urlparse(database)
-    method = puri.scheme.lower()
-    if method == 'inmemory':
-        config = cwconfig.instance_configuration(puri.netloc)
-    else:
-        config = cwconfig.CubicWebNoAppConfiguration()
-    repo = get_repository(database, config=config)
-    if method == 'inmemory':
-        vreg = repo.vreg
-    elif setvreg:
-        if mulcnx:
-            multiple_connections_fix()
-        vreg = cwvreg.CWRegistryStore(config, initlog=initlog)
-        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, login, cnxprops=cnxprops, **kwargs)
-    cnx.vreg = vreg
-    return cnx
-
-def in_memory_repo(config):
-    """Return and in_memory Repository object from a config (or vreg)"""
-    if isinstance(config, cwvreg.CWRegistryStore):
-        vreg = config
-        config = None
-    else:
-        vreg = None
-    # get local access to the repository
-    return get_repository('inmemory://', config=config, vreg=vreg)
-
-def in_memory_repo_cnx(config, login, **kwargs):
-    """useful method for testing and scripting to get a dbapi.Connection
-    object connected to an in-memory repository instance
-    """
-    # connection to the CubicWeb repository
-    repo = in_memory_repo(config)
-    return repo, _repo_connect(repo, login, **kwargs)
-
-# XXX web only method, move to webconfig?
-def anonymous_session(vreg):
-    """return a new anonymous session
-
-    raises an AuthenticationError if anonymous usage is not allowed
-    """
-    anoninfo = vreg.config.anonymous_user()
-    if anoninfo[0] is None: # no anonymous user
-        raise AuthenticationError('anonymous access is not authorized')
-    anon_login, anon_password = anoninfo
-    # use vreg's repository cache
-    repo = vreg.config.repository(vreg)
-    anon_cnx = _repo_connect(repo, anon_login, password=anon_password)
-    anon_cnx.vreg = vreg
-    return DBAPISession(anon_cnx, anon_login)
-
-
-class _NeedAuthAccessMock(object):
-    def __getattribute__(self, attr):
-        raise AuthenticationError()
-    def __nonzero__(self):
-        return False
-
-class DBAPISession(object):
-    def __init__(self, cnx, login=None):
-        self.cnx = cnx
-        self.data = {}
-        self.login = login
-        # dbapi session identifier is the same as the first connection
-        # identifier, but may later differ in case of auto-reconnection as done
-        # by the web authentication manager (in cw.web.views.authentication)
-        if cnx is not None:
-            self.sessionid = cnx.sessionid
-        else:
-            self.sessionid = uuid4().hex
-
-    @property
-    def anonymous_session(self):
-        return not self.cnx or self.cnx.anonymous_connection
-
-    def __repr__(self):
-        return '<DBAPISession %r>' % self.sessionid
-
-
-class DBAPIRequest(RequestSessionBase):
-    #: Request language identifier eg: 'en'
-    lang = None
-
-    def __init__(self, vreg, session=None):
-        super(DBAPIRequest, self).__init__(vreg)
-        #: 'language' => translation_function() mapping
-        try:
-            # no vreg or config which doesn't handle translations
-            self.translations = vreg.config.translations
-        except AttributeError:
-            self.translations = {}
-        #: cache entities built during the request
-        self._eid_cache = {}
-        if session is not None:
-            self.set_session(session)
-        else:
-            # these args are initialized after a connection is
-            # established
-            self.session = DBAPISession(None)
-            self.cnx = self.user = _NeedAuthAccessMock()
-        self.set_default_language(vreg)
-
-    def get_option_value(self, option, foreid=None):
-        if foreid is not None:
-            warn('[3.19] foreid argument is deprecated', DeprecationWarning,
-                 stacklevel=2)
-        return self.cnx.get_option_value(option)
-
-    def set_session(self, session):
-        """method called by the session handler when the user is authenticated
-        or an anonymous connection is open
-        """
-        self.session = session
-        if session.cnx:
-            self.cnx = session.cnx
-            self.execute = session.cnx.cursor(self).execute
-            self.user = self.cnx.user(self)
-            self.set_entity_cache(self.user)
-
-    def execute(self, *args, **kwargs): # pylint: disable=E0202
-        """overriden when session is set. By default raise authentication error
-        so authentication is requested.
-        """
-        raise AuthenticationError()
-
-    def set_default_language(self, vreg):
-        try:
-            lang = vreg.property_value('ui.language')
-        except Exception: # property may not be registered
-            lang = 'en'
-        try:
-            self.set_language(lang)
-        except KeyError:
-            # this occurs usually during test execution
-            self._ = self.__ = unicode
-            self.pgettext = lambda x, y: unicode(y)
-
-    # server-side service call #################################################
-
-    def call_service(self, regid, **kwargs):
-        return self.cnx.call_service(regid, **kwargs)
-
-    # entities cache management ###############################################
-
-    def entity_cache(self, eid):
-        return self._eid_cache[eid]
-
-    def set_entity_cache(self, entity):
-        self._eid_cache[entity.eid] = entity
-
-    def cached_entities(self):
-        return self._eid_cache.values()
-
-    def drop_entity_cache(self, eid=None):
-        if eid is None:
-            self._eid_cache = {}
-        else:
-            del self._eid_cache[eid]
-
-    # low level session data management #######################################
-
-    @deprecated('[3.19] use session or transaction data')
-    def get_shared_data(self, key, default=None, pop=False, txdata=False):
-        """see :meth:`Connection.get_shared_data`"""
-        return self.cnx.get_shared_data(key, default, pop, txdata)
-
-    @deprecated('[3.19] use session or transaction data')
-    def set_shared_data(self, key, value, txdata=False, querydata=None):
-        """see :meth:`Connection.set_shared_data`"""
-        if querydata is not None:
-            txdata = querydata
-            warn('[3.10] querydata argument has been renamed to txdata',
-                 DeprecationWarning, stacklevel=2)
-        return self.cnx.set_shared_data(key, value, txdata)
-
-    # server session compat layer #############################################
-
-    def entity_metas(self, eid):
-        """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
-        return self.cnx.entity_metas(eid)
-
-    def source_defs(self):
-        """return the definition of sources used by the repository."""
-        return self.cnx.source_defs()
-
-    @deprecated('[3.19] use .entity_metas(eid) instead')
-    def describe(self, eid, asdict=False):
-        """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
-        return self.cnx.describe(eid, asdict)
-
-    # these are overridden by set_log_methods below
-    # only defining here to prevent pylint from complaining
-    info = warning = error = critical = exception = debug = lambda msg,*a,**kw: None
-
-set_log_methods(DBAPIRequest, getLogger('cubicweb.dbapi'))
-
-
-
-# cursor / connection objects ##################################################
-
-class Cursor(object):
-    """These objects represent a database cursor, which is used to manage the
-    context of a fetch operation. Cursors created from the same connection are
-    not isolated, i.e., any changes done to the database by a cursor are
-    immediately visible by the other cursors. Cursors created from different
-    connections are isolated.
-    """
-
-    def __init__(self, connection, repo, req=None):
-        """This read-only attribute return a reference to the Connection
-        object on which the cursor was created.
-        """
-        self.connection = connection
-        """optionnal issuing request instance"""
-        self.req = req
-        self._repo = repo
-        self._sessid = connection.sessionid
-
-    def close(self):
-        """no effect"""
-        pass
-
-    def _txid(self):
-        return self.connection._txid(self)
-
-    def execute(self, rql, args=None, build_descr=True):
-        """execute a rql query, return resulting rows and their description in
-        a :class:`~cubicweb.rset.ResultSet` object
-
-        * `rql` should be a Unicode string or a plain ASCII string, containing
-          the rql query
-
-        * `args` the optional args dictionary associated to the query, with key
-          matching named substitution in `rql`
-
-        * `build_descr` is a boolean flag indicating if the description should
-          be built on select queries (if false, the description will be en empty
-          list)
-
-        on INSERT queries, there will be one row for each inserted entity,
-        containing its eid
-
-        on SET queries, XXX describe
-
-        DELETE queries returns no result.
-
-        .. Note::
-          to maximize the rql parsing/analyzing cache performance, you should
-          always use substitute arguments in queries, i.e. avoid query such as::
-
-            execute('Any X WHERE X eid 123')
-
-          use::
-
-            execute('Any X WHERE X eid %(x)s', {'x': 123})
-        """
-        rset = self._repo.execute(self._sessid, rql, args,
-                                  build_descr=build_descr, **self._txid())
-        rset.req = self.req
-        return rset
-
-
-class LogCursor(Cursor):
-    """override the standard cursor to log executed queries"""
-
-    def execute(self, operation, parameters=None, build_descr=True):
-        """override the standard cursor to log executed queries"""
-        tstart, cstart = time(), clock()
-        rset = Cursor.execute(self, operation, parameters, build_descr=build_descr)
-        self.connection.executed_queries.append((operation, parameters,
-                                                 time() - tstart, clock() - cstart))
-        return rset
-
-def check_not_closed(func):
-    def decorator(self, *args, **kwargs):
-        if self._closed is not None:
-            raise ProgrammingError('Closed connection %s' % self.sessionid)
-        return func(self, *args, **kwargs)
-    return decorator
-
-class Connection(object):
-    """DB-API 2.0 compatible Connection object for CubicWeb
-    """
-    # make exceptions available through the connection object
-    ProgrammingError = ProgrammingError
-    # attributes that may be overriden per connection instance
-    cursor_class = Cursor
-    vreg = None
-    _closed = None
-
-    def __init__(self, repo, cnxid, cnxprops=None):
-        self._repo = repo
-        self.sessionid = cnxid
-        self._close_on_del = getattr(cnxprops, 'close_on_del', True)
-        self._web_request = False
-        if cnxprops and cnxprops.log_queries:
-            self.executed_queries = []
-            self.cursor_class = LogCursor
-
-    @property
-    def is_repo_in_memory(self):
-        """return True if this is a local, aka in-memory, connection to the
-        repository
-        """
-        try:
-            from cubicweb.server.repository import Repository
-        except ImportError:
-            # code not available, no way
-            return False
-        return isinstance(self._repo, Repository)
-
-    @property # could be a cached property but we want to prevent assigment to
-              # catch potential programming error.
-    def anonymous_connection(self):
-        login = self._repo.user_info(self.sessionid)[1]
-        anon_login = self.vreg.config.get('anonymous-user')
-        return login == anon_login
-
-    def __repr__(self):
-        if self.anonymous_connection:
-            return '<Connection %s (anonymous)>' % self.sessionid
-        return '<Connection %s>' % self.sessionid
-
-    def __enter__(self):
-        return self.cursor()
-
-    def __exit__(self, exc_type, exc_val, exc_tb):
-        if exc_type is None:
-            self.commit()
-        else:
-            self.rollback()
-            return False #propagate the exception
-
-    def __del__(self):
-        """close the remote connection if necessary"""
-        if self._closed is None and self._close_on_del:
-            try:
-                self.close()
-            except Exception:
-                pass
-
-    # server-side service call #################################################
-
-    @check_not_closed
-    def call_service(self, regid, **kwargs):
-        return self._repo.call_service(self.sessionid, regid, **kwargs)
-
-    # connection initialization methods ########################################
-
-    def load_appobjects(self, cubes=_MARKER, subpath=None, expand=True):
-        config = self.vreg.config
-        if cubes is _MARKER:
-            cubes = self._repo.get_cubes()
-        elif cubes is None:
-            cubes = ()
-        else:
-            if not isinstance(cubes, (list, tuple)):
-                cubes = (cubes,)
-            if expand:
-                cubes = config.expand_cubes(cubes)
-        if subpath is None:
-            subpath = esubpath = ('entities', 'views')
-        else:
-            esubpath = subpath
-        if 'views' in subpath:
-            esubpath = list(subpath)
-            esubpath.remove('views')
-            esubpath.append(join('web', 'views'))
-        # first load available configs, necessary for proper persistent
-        # properties initialization
-        config.load_available_configs()
-        # then init cubes
-        config.init_cubes(cubes)
-        # then load appobjects into the registry
-        vpath = config.build_appobjects_path(reversed(config.cubes_path()),
-                                             evobjpath=esubpath,
-                                             tvobjpath=subpath)
-        self.vreg.register_objects(vpath)
-
-    def use_web_compatible_requests(self, baseurl, sitetitle=None):
-        """monkey patch DBAPIRequest to fake a cw.web.request, so you should
-        able to call html views using rset from a simple dbapi connection.
-
-        You should call `load_appobjects` at some point to register those views.
-        """
-        DBAPIRequest.property_value = _fake_property_value
-        DBAPIRequest.next_tabindex = count().next
-        DBAPIRequest.relative_path = fake
-        DBAPIRequest.url = fake
-        DBAPIRequest.get_page_data = fake
-        DBAPIRequest.set_page_data = fake
-        # XXX could ask the repo for it's base-url configuration
-        self.vreg.config.set_option('base-url', baseurl)
-        self.vreg.config.uiprops = {}
-        self.vreg.config.datadir_url = baseurl + '/data'
-        # XXX why is this needed? if really needed, could be fetched by a query
-        if sitetitle is not None:
-            self.vreg['propertydefs']['ui.site-title'] = {'default': sitetitle}
-        self._web_request = True
-
-    def request(self):
-        if self._web_request:
-            from cubicweb.web.request import DBAPICubicWebRequestBase
-            req = DBAPICubicWebRequestBase(self.vreg, False)
-            req.get_header = lambda x, default=None: default
-            req.set_session = lambda session: DBAPIRequest.set_session(
-                req, session)
-            req.relative_path = lambda includeparams=True: ''
-        else:
-            req = DBAPIRequest(self.vreg)
-        req.set_session(DBAPISession(self))
-        return req
-
-    @check_not_closed
-    def user(self, req=None, props=None):
-        """return the User object associated to this connection"""
-        # cnx validity is checked by the call to .user_info
-        eid, login, groups, properties = self._repo.user_info(self.sessionid,
-                                                              props)
-        if req is None:
-            req = self.request()
-        rset = req.eid_rset(eid, 'CWUser')
-        if self.vreg is not None and 'etypes' in self.vreg:
-            user = self.vreg['etypes'].etype_class('CWUser')(
-                req, rset, row=0, groups=groups, properties=properties)
-        else:
-            from cubicweb.entity import Entity
-            user = Entity(req, rset, row=0)
-        user.cw_attr_cache['login'] = login # cache login
-        return user
-
-    @check_not_closed
-    def check(self):
-        """raise `BadConnectionId` if the connection is no more valid, else
-        return its latest activity timestamp.
-        """
-        return self._repo.check_session(self.sessionid)
-
-    def _txid(self, cursor=None): # pylint: disable=E0202
-        # XXX could now handle various isolation level!
-        # return a dict as bw compat trick
-        return {'txid': currentThread().getName()}
-
-    # session data methods #####################################################
-
-    @check_not_closed
-    def get_shared_data(self, key, default=None, pop=False, txdata=False):
-        """return value associated to key in the session's data dictionary or
-        session's transaction's data if `txdata` is true.
-
-        If pop is True, value will be removed from the dictionary.
-
-        If key isn't defined in the dictionary, value specified by the
-        `default` argument will be returned.
-        """
-        return self._repo.get_shared_data(self.sessionid, key, default, pop, txdata)
-
-    @check_not_closed
-    def set_shared_data(self, key, value, txdata=False):
-        """set value associated to `key` in shared data
-
-        if `txdata` is true, the value will be added to the repository
-        session's query data which are cleared on commit/rollback of the current
-        transaction.
-        """
-        return self._repo.set_shared_data(self.sessionid, key, value, txdata)
-
-    # meta-data accessors ######################################################
-
-    @check_not_closed
-    def source_defs(self):
-        """Return the definition of sources used by the repository."""
-        return self._repo.source_defs()
-
-    @check_not_closed
-    def get_schema(self):
-        """Return the schema currently used by the repository."""
-        return self._repo.get_schema()
-
-    @check_not_closed
-    def get_option_value(self, option, foreid=None):
-        """Return the value for `option` in the configuration.
-
-        `foreid` argument is deprecated and now useless (as of 3.19).
-        """
-        if foreid is not None:
-            warn('[3.19] foreid argument is deprecated', DeprecationWarning,
-                 stacklevel=2)
-        return self._repo.get_option_value(option)
-
-
-    @check_not_closed
-    def entity_metas(self, eid):
-        """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
-        try:
-            return self._repo.entity_metas(self.sessionid, eid, **self._txid())
-        except AttributeError:
-            # talking to pre 3.19 repository
-            metas = self._repo.describe(self.sessionid, eid, **self._txid())
-            if len(metas) == 3: # even older backward compat
-                metas = list(metas)
-                metas.append(metas[1])
-            return dict(zip(('type', 'source', 'extid', 'asource'), metas))
-
-
-    @deprecated('[3.19] use .entity_metas(eid) instead')
-    @check_not_closed
-    def describe(self, eid, asdict=False):
-        try:
-            metas = self._repo.entity_metas(self.sessionid, eid, **self._txid())
-        except AttributeError:
-            metas = self._repo.describe(self.sessionid, eid, **self._txid())
-            # talking to pre 3.19 repository
-            if len(metas) == 3: # even older backward compat
-                metas = list(metas)
-                metas.append(metas[1])
-            if asdict:
-                return dict(zip(('type', 'source', 'extid', 'asource'), metas))
-            return metas[:-1]
-        if asdict:
-            metas['asource'] = meta['source'] # XXX pre 3.19 client compat
-            return metas
-        return metas['type'], metas['source'], metas['extid']
-
-
-    # db-api like interface ####################################################
-
-    @check_not_closed
-    def commit(self):
-        """Commit pending transaction for this connection to the repository.
-
-        may raises `Unauthorized` or `ValidationError` if we attempted to do
-        something we're not allowed to for security or integrity reason.
-
-        If the transaction is undoable, a transaction id will be returned.
-        """
-        return self._repo.commit(self.sessionid, **self._txid())
-
-    @check_not_closed
-    def rollback(self):
-        """This method is optional since not all databases provide transaction
-        support.
-
-        In case a database does provide transactions this method causes the the
-        database to roll back to the start of any pending transaction.  Closing
-        a connection without committing the changes first will cause an implicit
-        rollback to be performed.
-        """
-        self._repo.rollback(self.sessionid, **self._txid())
-
-    @check_not_closed
-    def cursor(self, req=None):
-        """Return a new Cursor Object using the connection.
-
-        On pyro connection, you should get cursor after calling if
-        load_appobjects method if desired (which you should call if you intend
-        to use ORM abilities).
-        """
-        if req is None:
-            req = self.request()
-        return self.cursor_class(self, self._repo, req=req)
-
-    @check_not_closed
-    def close(self):
-        """Close the connection now (rather than whenever __del__ is called).
-
-        The connection will be unusable from this point forward; an Error (or
-        subclass) exception will be raised if any operation is attempted with
-        the connection. The same applies to all cursor objects trying to use the
-        connection.  Note that closing a connection without committing the
-        changes first will cause an implicit rollback to be performed.
-        """
-        self._repo.close(self.sessionid, **self._txid())
-        del self._repo # necessary for proper garbage collection
-        self._closed = 1
-
-    # undo support ############################################################
-
-    @check_not_closed
-    def undoable_transactions(self, ueid=None, req=None, **actionfilters):
-        """Return a list of undoable transaction objects by the connection's
-        user, ordered by descendant transaction time.
-
-        Managers may filter according to user (eid) who has done the transaction
-        using the `ueid` argument. Others will only see their own transactions.
-
-        Additional filtering capabilities is provided by using the following
-        named arguments:
-
-        * `etype` to get only transactions creating/updating/deleting entities
-          of the given type
-
-        * `eid` to get only transactions applied to entity of the given eid
-
-        * `action` to get only transactions doing the given action (action in
-          'C', 'U', 'D', 'A', 'R'). If `etype`, action can only be 'C', 'U' or
-          'D'.
-
-        * `public`: when additional filtering is provided, their are by default
-          only searched in 'public' actions, unless a `public` argument is given
-          and set to false.
-        """
-        actionfilters.update(self._txid())
-        txinfos = self._repo.undoable_transactions(self.sessionid, ueid,
-                                                   **actionfilters)
-        if req is None:
-            req = self.request()
-        for txinfo in txinfos:
-            txinfo.req = req
-        return txinfos
-
-    @check_not_closed
-    def transaction_info(self, txuuid, req=None):
-        """Return transaction object for the given uid.
-
-        raise `NoSuchTransaction` if not found or if session's user is not
-        allowed (eg not in managers group and the transaction doesn't belong to
-        him).
-        """
-        txinfo = self._repo.transaction_info(self.sessionid, txuuid,
-                                             **self._txid())
-        if req is None:
-            req = self.request()
-        txinfo.req = req
-        return txinfo
-
-    @check_not_closed
-    def transaction_actions(self, txuuid, public=True):
-        """Return an ordered list of action effectued during that transaction.
-
-        If public is true, return only 'public' actions, eg not ones triggered
-        under the cover by hooks, else return all actions.
-
-        raise `NoSuchTransaction` if the transaction is not found or if
-        session's user is not allowed (eg not in managers group and the
-        transaction doesn't belong to him).
-        """
-        return self._repo.transaction_actions(self.sessionid, txuuid, public,
-                                              **self._txid())
-
-    @check_not_closed
-    def undo_transaction(self, txuuid):
-        """Undo the given transaction. Return potential restoration errors.
-
-        raise `NoSuchTransaction` if not found or if session's user is not
-        allowed (eg not in managers group and the transaction doesn't belong to
-        him).
-        """
-        return self._repo.undo_transaction(self.sessionid, txuuid,
-                                           **self._txid())
-
-in_memory_cnx = deprecated('[3.16] use _repo_connect instead)')(_repo_connect)
--- a/debian/changelog	Mon May 09 17:24:03 2016 +0200
+++ b/debian/changelog	Tue Jun 21 07:42:30 2016 +0200
@@ -1,3 +1,51 @@
+cubicweb (3.21.6-1) unstable; urgency=medium
+
+  * new upstream release
+
+ -- Julien Cristau <julien.cristau@logilab.fr>  Tue, 16 Feb 2016 18:53:15 +0100
+
+cubicweb (3.21.5-2) unstable; urgency=medium
+
+  * Fix conflict between cubicweb-server and cubicweb-dev.
+
+ -- Julien Cristau <julien.cristau@logilab.fr>  Thu, 17 Dec 2015 14:56:07 +0100
+
+cubicweb (3.21.5-1) unstable; urgency=medium
+
+  * New upstream release.
+
+ -- Rémi Cardona <remi.cardona@logilab.fr>  Tue, 15 Dec 2015 17:33:05 +0100
+
+cubicweb (3.21.4-1) unstable; urgency=medium
+
+  * New upstream release.
+
+ -- Rémi Cardona <remi.cardona@logilab.fr>  Tue, 15 Dec 2015 11:59:58 +0100
+
+cubicweb (3.21.3-1) unstable; urgency=medium
+
+  * New upstream release.
+
+ -- Rémi Cardona <remi.cardona@logilab.fr>  Wed, 09 Dec 2015 18:29:39 +0100
+
+cubicweb (3.21.2-1) unstable; urgency=medium
+
+  * New upstream release.
+
+ -- Rémi Cardona <remi.cardona@logilab.fr>  Fri, 09 Oct 2015 18:00:39 +0200
+
+cubicweb (3.21.1-1) unstable; urgency=medium
+
+  * new upstream release
+
+ -- Julien Cristau <julien.cristau@logilab.fr>  Tue, 28 Jul 2015 18:05:55 +0200
+
+cubicweb (3.21.0-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Julien Cristau <julien.cristau@logilab.fr>  Fri, 10 Jul 2015 17:04:11 +0200
+
 cubicweb (3.20.15-1) unstable; urgency=medium
 
   * new upstream release 
--- a/debian/control	Mon May 09 17:24:03 2016 +0200
+++ b/debian/control	Tue Jun 21 07:42:30 2016 +0200
@@ -20,7 +20,7 @@
  python-lxml,
 Standards-Version: 3.9.1
 Homepage: http://www.cubicweb.org
-XS-Python-Version: >= 2.6
+X-Python-Version: >= 2.6
 
 Package: cubicweb
 Architecture: all
@@ -58,10 +58,10 @@
  | python-pysqlite2,
  python-passlib
 Recommends:
- pyro (<< 4.0.0),
- cubicweb-documentation (= ${source:Version})
+ cubicweb-documentation (= ${source:Version}),
 Suggests:
- python-zmq
+ python-zmq,
+ python-cwclientlib (>= 0.4.0),
 Description: server part of the CubicWeb framework
  CubicWeb is a semantic web application framework.
  .
@@ -109,7 +109,6 @@
  cubicweb-ctl (= ${source:Version}),
  python-twisted-web
 Recommends:
- pyro (<< 4.0.0),
  cubicweb-documentation (= ${source:Version})
 Description: twisted-based web interface for the CubicWeb framework
  CubicWeb is a semantic web application framework.
@@ -137,6 +136,7 @@
 Breaks:
  cubicweb-inlinedit (<< 1.1.1),
  cubicweb-bootstrap (<< 0.6.6),
+ cubicweb-folder (<< 1.10.0),
 Description: web interface library for the CubicWeb framework
  CubicWeb is a semantic web application framework.
  .
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/cubicweb-common.install	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,4 @@
+usr/lib/python2*/*-packages/cubicweb/entities/
+usr/lib/python2*/*-packages/cubicweb/ext/
+usr/share/cubicweb/cubes/
+usr/lib/python2*/*-packages/cubicweb/*.py
--- a/debian/cubicweb-common.install.in	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-usr/lib/PY_VERSION/*-packages/cubicweb/entities/
-usr/lib/PY_VERSION/*-packages/cubicweb/ext/
-usr/share/cubicweb/cubes/
-usr/lib/PY_VERSION/*-packages/cubicweb/*.py
--- a/debian/cubicweb-ctl.cubicweb.init	Mon May 09 17:24:03 2016 +0200
+++ b/debian/cubicweb-ctl.cubicweb.init	Tue Jun 21 07:42:30 2016 +0200
@@ -4,16 +4,14 @@
 # Provides:          cubicweb
 # Required-Start:    $remote_fs $syslog $local_fs $network
 # Required-Stop:     $remote_fs $syslog $local_fs $network
-# Should-Start:      postgresql pyro-nsd
-# Should-Stop:       postgresql pyro-nsd
+# Should-Start:      postgresql
+# Should-Stop:       postgresql
 # Default-Start:     2 3 4 5
 # Default-Stop:      0 1 6
 # Short-Description: Start cubicweb application at boot time
 ### END INIT INFO
 
 # FIXME Seems to be inadequate here
-# FIXME If related to pyro, try instead:
-# export PYRO_STORAGE="/tmp"
 cd /tmp
 
 # FIXME Work-around about the following lintian error
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/cubicweb-ctl.install	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,3 @@
+usr/bin/cubicweb-ctl usr/bin/
+usr/lib/python2*/*-packages/cubicweb/cwctl.py
+../cubicweb-ctl.bash_completion etc/bash_completion.d/cubicweb-ctl
--- a/debian/cubicweb-ctl.install.in	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-usr/bin/cubicweb-ctl usr/bin/
-usr/lib/PY_VERSION/*-packages/cubicweb/cwctl.py
-../cubicweb-ctl.bash_completion etc/bash_completion.d/cubicweb-ctl
--- a/debian/cubicweb-ctl.postinst	Mon May 09 17:24:03 2016 +0200
+++ b/debian/cubicweb-ctl.postinst	Tue Jun 21 07:42:30 2016 +0200
@@ -10,32 +10,6 @@
     ;;
 esac
 
-if [ "$1" = configure ]; then
-    # XXX bw compat: erudi -> cubicweb migration
-    if [ -e "/etc/erudi.d/" ]; then
-      mv /etc/erudi.d/* /etc/cubicweb.d/ && (
-	  echo 'moved /etc/erudi.d/* to /etc/cubicweb.d/'
-	  sed -i s/ginco/cubicweb/g /etc/*/*.py
-	  sed -i s/erudi/cubicweb/ */*.conf
-	  ) || true # empty dir
-    fi
-    if [ -e "/var/log/erudi/" ]; then
-      mv /var/log/erudi/* /var/log/cubicweb/ && (
-	  echo 'moved /var/log/erudi/* to /var/log/cubicweb/'
-	  ) || true # empty dir
-    fi
-    if [ -e "/var/lib/erudi/backup" ]; then
-      mv /var/lib/erudi/backup/* /var/lib/cubicweb/backup/ && (
-	  echo 'moved /var/lib/erudi/backup/* to /var/lib/cubicweb/backup/'
-	  ) || true # empty dir
-    fi
-    if [ -e "/var/lib/erudi/instances" ]; then
-      mv /var/lib/erudi/instances/* /var/lib/cubicweb/instances/ && (
-	  echo 'moved /var/lib/erudi/instances/* to /var/lib/cubicweb/instances/'
-	  ) || true # empty dir
-    fi
-fi
-
 #DEBHELPER#
 
 exit 0
--- a/debian/cubicweb-ctl.postrm	Mon May 09 17:24:03 2016 +0200
+++ b/debian/cubicweb-ctl.postrm	Tue Jun 21 07:42:30 2016 +0200
@@ -1,8 +1,15 @@
 #!/bin/sh -e
-if [ "$1" = "purge" ] ; then
+
+if [ "$1" = "remove" ]; then
         update-rc.d cubicweb remove >/dev/null
 fi
  
+if [ "$1" = "purge" ] ; then
+        rm -rf /etc/cubicweb.d/
+        rm -rf /var/log/cubicweb/
+        rm -rf /var/lib/cubicweb/
+fi
+
 #DEBHELPER#
  
 exit 0
--- a/debian/cubicweb-ctl.prerm	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-#! /bin/sh -e
- 
-case "$1" in
-    purge)
-	rm -rf /etc/cubicweb.d/
-	rm -rf /var/log/cubicweb/
-	rm -rf /var/lib/cubicweb/
-    ;;
-esac
- 
-#DEBHELPER#
- 
-exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/cubicweb-dev.install	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,11 @@
+usr/lib/python2*/*-packages/cubicweb/devtools/
+usr/lib/python2*/*-packages/cubicweb/skeleton/
+usr/lib/python2*/*-packages/cubicweb/test
+usr/lib/python2*/*-packages/cubicweb/dataimport/test
+usr/lib/python2*/*-packages/cubicweb/entities/test
+usr/lib/python2*/*-packages/cubicweb/ext/test
+usr/lib/python2*/*-packages/cubicweb/server/test
+usr/lib/python2*/*-packages/cubicweb/sobjects/test
+usr/lib/python2*/*-packages/cubicweb/hooks/test
+usr/lib/python2*/*-packages/cubicweb/web/test
+usr/lib/python2*/*-packages/cubicweb/etwist/test
--- a/debian/cubicweb-dev.install.in	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-usr/lib/PY_VERSION/*-packages/cubicweb/devtools/
-usr/lib/PY_VERSION/*-packages/cubicweb/skeleton/
-usr/lib/PY_VERSION/*-packages/cubicweb/test
-usr/lib/PY_VERSION/*-packages/cubicweb/entities/test
-usr/lib/PY_VERSION/*-packages/cubicweb/ext/test
-usr/lib/PY_VERSION/*-packages/cubicweb/server/test
-usr/lib/PY_VERSION/*-packages/cubicweb/sobjects/test
-usr/lib/PY_VERSION/*-packages/cubicweb/hooks/test
-usr/lib/PY_VERSION/*-packages/cubicweb/web/test
-usr/lib/PY_VERSION/*-packages/cubicweb/etwist/test
--- a/debian/cubicweb-doc	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-Document: cubicweb-doc
-Title: CubicWeb documentation
-Author: Logilab
-Abstract: Some base documentation for CubicWeb users and developpers
-Section: Apps/Programming
-
-Format: HTML
-Index: /usr/share/doc/cubicweb-documentation/index.html
-Files: /usr/share/doc/cubicweb-documentation/*.html
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/cubicweb-documentation.doc-base	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,9 @@
+Document: cubicweb-doc
+Title: CubicWeb documentation
+Author: Logilab
+Abstract: Some base documentation for CubicWeb users and developpers
+Section: Apps/Programming
+
+Format: HTML
+Index: /usr/share/doc/cubicweb-documentation/index.html
+Files: /usr/share/doc/cubicweb-documentation/*.html
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/cubicweb-documentation.install	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,2 @@
+../../doc/book usr/share/doc/cubicweb-documentation
+../../doc/_build/html usr/share/doc/cubicweb-documentation
--- a/debian/cubicweb-documentation.install.in	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-../../doc/book usr/share/doc/cubicweb-documentation
-../../doc/html usr/share/doc/cubicweb-documentation
-../../debian/cubicweb-doc usr/share/doc-base/cubicweb-doc
--- a/debian/cubicweb-documentation.postinst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-#! /bin/sh -e
-#
-
-if [ "$1" = configure ]; then
-  if which install-docs >/dev/null 2>&1; then
-    install-docs -i /usr/share/doc-base/cubicweb-doc
-  fi
-fi
-
-
-#DEBHELPER#
-
-exit 0
--- a/debian/cubicweb-documentation.prerm	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-#! /bin/sh -e
-#
-
-if [ "$1" = remove -o "$1" = upgrade ]; then
-  if which install-docs >/dev/null 2>&1; then
-    install-docs -r cubicweb-doc
-  fi
-fi
-
-#DEBHELPER#
-
-exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/cubicweb-server.install	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,6 @@
+usr/lib/python2*/*-packages/cubicweb/dataimport/
+usr/lib/python2*/*-packages/cubicweb/server/
+usr/lib/python2*/*-packages/cubicweb/hooks/
+usr/lib/python2*/*-packages/cubicweb/sobjects/
+usr/lib/python2*/*-packages/cubicweb/schemas/
+usr/share/cubicweb/migration/
--- a/debian/cubicweb-server.install.in	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-usr/lib/PY_VERSION/*-packages/cubicweb/server/
-usr/lib/PY_VERSION/*-packages/cubicweb/hooks/
-usr/lib/PY_VERSION/*-packages/cubicweb/sobjects/
-usr/lib/PY_VERSION/*-packages/cubicweb/schemas/
-usr/share/cubicweb/migration/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/cubicweb-twisted.install	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,1 @@
+usr/lib/python2*/*-packages/cubicweb/etwist/
--- a/debian/cubicweb-twisted.install.in	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-usr/lib/PY_VERSION/*-packages/cubicweb/etwist/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/cubicweb-web.install	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,4 @@
+usr/lib/python2*/*-packages/cubicweb/web
+usr/lib/python2*/*-packages/cubicweb/wsgi
+usr/share/cubicweb/cubes/shared/data
+usr/share/cubicweb/cubes/shared/wdoc
--- a/debian/cubicweb-web.install.in	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-usr/lib/PY_VERSION/*-packages/cubicweb/web
-usr/lib/PY_VERSION/*-packages/cubicweb/wsgi
-usr/share/cubicweb/cubes/shared/data
-usr/share/cubicweb/cubes/shared/wdoc
--- a/debian/pycompat	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-2
--- a/debian/rules	Mon May 09 17:24:03 2016 +0200
+++ b/debian/rules	Tue Jun 21 07:42:30 2016 +0200
@@ -5,8 +5,6 @@
 # Uncomment this to turn on verbose mode.
 #export DH_VERBOSE=1
 
-PY_VERSION:=$(shell pyversions -d)
-
 build: build-stamp
 build-stamp:
 	dh_testdir
@@ -17,7 +15,7 @@
 	# documentation build is now made optional since it can break for old
 	# distributions and we don't want to block a new release of Cubicweb
 	# because of documentation issues.
-	-PYTHONPATH=$${PYTHONPATH:+$${PYTHONPATH}:}$(CURDIR)/debian/pythonpath $(MAKE) -C doc/book/en all
+	-PYTHONPATH=$${PYTHONPATH:+$${PYTHONPATH}:}$(CURDIR)/debian/pythonpath $(MAKE) -C doc all
 	rm -rf debian/pythonpath
 	touch build-stamp
 
@@ -27,10 +25,10 @@
 	rm -rf build
 	#rm -rf debian/cubicweb-*/
 	find . -name "*.pyc" -delete
-	rm -f $(basename $(wildcard debian/*.in))
+	-$(MAKE) -C doc clean
 	dh_clean
 
-install: build $(basename $(wildcard debian/*.in))
+install: build
 	dh_testdir
 	dh_testroot
 	dh_clean
@@ -49,30 +47,29 @@
 	dh_lintian
 
 	# Remove unittests directory (should be available in cubicweb-dev only)
-	rm -rf debian/cubicweb-server/usr/lib/${PY_VERSION}/*-packages/cubicweb/server/test
-	rm -rf debian/cubicweb-server/usr/lib/${PY_VERSION}/*-packages/cubicweb/hooks/test
-	rm -rf debian/cubicweb-server/usr/lib/${PY_VERSION}/*-packages/cubicweb/sobjects/test
-	rm -rf debian/cubicweb-web/usr/lib/${PY_VERSION}/*-packages/cubicweb/web/test
-	rm -rf debian/cubicweb-twisted/usr/lib/${PY_VERSION}/*-packages/cubicweb/etwist/test
-	rm -rf debian/cubicweb-common/usr/lib/${PY_VERSION}/*-packages/cubicweb/ext/test
-	rm -rf debian/cubicweb-common/usr/lib/${PY_VERSION}/*-packages/cubicweb/entities/test
+	rm -rf debian/cubicweb-server/usr/lib/python2*/*-packages/cubicweb/dataimport/test
+	rm -rf debian/cubicweb-server/usr/lib/python2*/*-packages/cubicweb/server/test
+	rm -rf debian/cubicweb-server/usr/lib/python2*/*-packages/cubicweb/hooks/test
+	rm -rf debian/cubicweb-server/usr/lib/python2*/*-packages/cubicweb/sobjects/test
+	rm -rf debian/cubicweb-web/usr/lib/python2*/*-packages/cubicweb/web/test
+	rm -rf debian/cubicweb-twisted/usr/lib/python2*/*-packages/cubicweb/etwist/test
+	rm -rf debian/cubicweb-common/usr/lib/python2*/*-packages/cubicweb/ext/test
+	rm -rf debian/cubicweb-common/usr/lib/python2*/*-packages/cubicweb/entities/test
 
 
-%: %.in
-	sed "s/PY_VERSION/${PY_VERSION}/g" < $< > $@
-
 # Build architecture-independent files here.
 binary-indep: build install
 	dh_testdir
 	dh_testroot -i
 	dh_python2 -i
+	dh_python2 -i /usr/share/cubicweb
 	dh_installinit -i -n --name cubicweb -u"defaults 99"
 	dh_installlogrotate -i
 	dh_installdocs -i -A README
 	dh_installman -i
-	dh_installchangelogs -i
+	dh_installchangelogs -i -Xdoc/changes
 	dh_link -i
-	dh_compress -i -X.py -X.ini -X.xml -X.js -X.rst -X.txt
+	dh_compress -i -X.py -X.ini -X.xml -X.js -X.rst -X.txt -Xchangelog.html
 	dh_fixperms -i
 	dh_installdeb -i
 	dh_gencontrol  -i
--- a/devtools/__init__.py	Mon May 09 17:24:03 2016 +0200
+++ b/devtools/__init__.py	Tue Jun 21 07:42:30 2016 +0200
@@ -26,7 +26,6 @@
 import shutil
 import pickle
 import glob
-import random
 import subprocess
 import warnings
 import tempfile
@@ -93,8 +92,6 @@
 DEFAULT_PSQL_SOURCES = DEFAULT_SOURCES.copy()
 DEFAULT_PSQL_SOURCES['system'] = DEFAULT_SOURCES['system'].copy()
 DEFAULT_PSQL_SOURCES['system']['db-driver'] = 'postgres'
-DEFAULT_PSQL_SOURCES['system']['db-host'] = '/tmp'
-DEFAULT_PSQL_SOURCES['system']['db-port'] = str(random.randrange(5432, 2**16))
 DEFAULT_PSQL_SOURCES['system']['db-user'] = unicode(getpass.getuser())
 DEFAULT_PSQL_SOURCES['system']['db-password'] = None
 
@@ -176,8 +173,8 @@
         return self._apphome
     appdatahome = apphome
 
-    def load_configuration(self):
-        super(TestServerConfiguration, self).load_configuration()
+    def load_configuration(self, **kw):
+        super(TestServerConfiguration, self).load_configuration(**kw)
         # no undo support in tests
         self.global_set_option('undo-enabled', 'n')
 
@@ -237,10 +234,6 @@
     def available_languages(self, *args):
         return self.cw_languages()
 
-    def pyro_enabled(self):
-        # but export PYRO_MULTITHREAD=0 or you get problems with sqlite and
-        # threads
-        return True
 
 # XXX merge with BaseApptestConfiguration ?
 class ApptestConfiguration(BaseApptestConfiguration):
@@ -251,7 +244,7 @@
     skip_db_create_and_restore = False
 
     def __init__(self, appid, apphome=None,
-                 log_threshold=logging.CRITICAL, sourcefile=None):
+                 log_threshold=logging.WARNING, sourcefile=None):
         BaseApptestConfiguration.__init__(self, appid, apphome,
                                           log_threshold=log_threshold)
         self.init_repository = sourcefile is None
@@ -303,6 +296,14 @@
         # pure consistency check
         assert self.system_source['db-driver'] == self.DRIVER
 
+        # some handlers want to store info here, avoid a warning
+        from cubicweb.server.sources.native import NativeSQLSource
+        NativeSQLSource.options += (
+            ('global-db-name',
+             {'type': 'string', 'help': 'for internal use only'
+            }),
+        )
+
     def _ensure_test_backup_db_dir(self):
         """Return path of directory for database backup.
 
@@ -398,9 +399,9 @@
 
     def _new_repo(self, config):
         """Factory method to create a new Repository Instance"""
-        from cubicweb.dbapi import in_memory_repo
+        from cubicweb.repoapi import _get_inmemory_repo
         config._cubes = None
-        repo = in_memory_repo(config)
+        repo = _get_inmemory_repo(config)
         config.repository = lambda x=None: repo
         # extending Repository class
         repo._has_started = False
@@ -499,7 +500,7 @@
             repo = self.get_repo(startup=True)
             cnx = self.get_cnx()
             with cnx:
-                pre_setup_func(cnx._cnx, self.config)
+                pre_setup_func(cnx, self.config)
                 cnx.commit()
         self.backup_database(test_db_id)
 
@@ -534,6 +535,48 @@
 
 ### postgres test database handling ############################################
 
+def startpgcluster(pyfile):
+    """Start a postgresql cluster next to pyfile"""
+    datadir = join(os.path.dirname(pyfile), 'data',
+                   'pgdb-%s' % os.path.splitext(os.path.basename(pyfile))[0])
+    if not exists(datadir):
+        try:
+            subprocess.check_call(['initdb', '-D', datadir, '-E', 'utf-8', '--locale=C'])
+
+        except OSError, err:
+            if err.errno == errno.ENOENT:
+                raise OSError('"initdb" could not be found. '
+                              'You should add the postgresql bin folder to your PATH '
+                              '(/usr/lib/postgresql/9.1/bin for example).')
+            raise
+    datadir = os.path.abspath(datadir)
+    pgport = '5432'
+    env = os.environ.copy()
+    sockdir = tempfile.mkdtemp(prefix='cwpg')
+    DEFAULT_PSQL_SOURCES['system']['db-host'] = sockdir
+    DEFAULT_PSQL_SOURCES['system']['db-port'] = pgport
+    options = '-h "" -k %s -p %s' % (sockdir, pgport)
+    options += ' -c fsync=off -c full_page_writes=off'
+    options += ' -c synchronous_commit=off'
+    try:
+        subprocess.check_call(['pg_ctl', 'start', '-w', '-D', datadir,
+                               '-o', options],
+                              env=env)
+    except OSError, err:
+        if err.errno == errno.ENOENT:
+            raise OSError('"pg_ctl" could not be found. '
+                          'You should add the postgresql bin folder to your PATH '
+                          '(/usr/lib/postgresql/9.1/bin for example).')
+        raise
+
+
+def stoppgcluster(pyfile):
+    """Kill the postgresql cluster running next to pyfile"""
+    datadir = join(os.path.dirname(pyfile), 'data',
+                   'pgdb-%s' % os.path.splitext(os.path.basename(pyfile))[0])
+    subprocess.call(['pg_ctl', 'stop', '-D', datadir, '-m', 'fast'])
+
+
 class PostgresTestDataBaseHandler(TestDataBaseHandler):
     DRIVER = 'postgres'
 
@@ -543,45 +586,11 @@
 
     __CTL = set()
 
-    @classmethod
-    def killall(cls):
-        for datadir in cls.__CTL:
-            subprocess.call(['pg_ctl', 'stop', '-D', datadir, '-m', 'fast'])
-
     def __init__(self, *args, **kwargs):
         super(PostgresTestDataBaseHandler, self).__init__(*args, **kwargs)
-        datadir = realpath(join(self.config.apphome, 'pgdb'))
-        if datadir in self.__CTL:
-            return
-        if not exists(datadir):
-            try:
-                subprocess.check_call(['initdb', '-D', datadir, '-E', 'utf-8', '--locale=C'])
-
-            except OSError, err:
-                if err.errno == errno.ENOENT:
-                    raise OSError('"initdb" could not be found. '
-                                  'You should add the postgresql bin folder to your PATH '
-                                  '(/usr/lib/postgresql/9.1/bin for example).')
-                raise
-        port = self.system_source['db-port']
-        directory = self.system_source['db-host']
-        env = os.environ.copy()
-        env['PGPORT'] = str(port)
-        env['PGHOST'] = str(directory)
-        options = '-h "" -k %s -p %s' % (directory, port)
-        options += ' -c fsync=off -c full_page_writes=off'
-        options += ' -c synchronous_commit=off'
-        try:
-            subprocess.check_call(['pg_ctl', 'start', '-w', '-D', datadir,
-                                   '-o', options],
-                                  env=env)
-        except OSError, err:
-            if err.errno == errno.ENOENT:
-                raise OSError('"pg_ctl" could not be found. '
-                              'You should add the postgresql bin folder to your PATH '
-                              '(/usr/lib/postgresql/9.1/bin for example).')
-            raise
-        self.__CTL.add(datadir)
+        if 'global-db-name' not in self.system_source:
+            self.system_source['global-db-name'] = self.system_source['db-name']
+            self.system_source['db-name'] = self.system_source['db-name'] + str(os.getpid())
 
     @property
     @cached
@@ -590,6 +599,10 @@
         return get_db_helper('postgres')
 
     @property
+    def dbname(self):
+        return self.system_source['global-db-name']
+
+    @property
     def dbcnx(self):
         try:
             return self._cnx
@@ -615,13 +628,18 @@
             return backup_name
         return None
 
+    def has_cache(self, db_id):
+        backup_name = self._backup_name(db_id)
+        return (super(PostgresTestDataBaseHandler, self).has_cache(db_id)
+                and backup_name in self.helper.list_databases(self.cursor))
+
     def init_test_database(self):
         """initialize a fresh postgresql database used for testing purpose"""
         from cubicweb.server import init_repository
         from cubicweb.server.serverctl import system_source_cnx, createdb
         # connect on the dbms system base to create our base
         try:
-            self._drop(self.dbname)
+            self._drop(self.system_source['db-name'])
             createdb(self.helper, self.system_source, self.dbcnx, self.cursor)
             self.dbcnx.commit()
             cnx = system_source_cnx(self.system_source, special_privs='LANGUAGE C',
@@ -699,7 +717,7 @@
         """Actual restore of the current database.
 
         Use the value tostored in db_cache as input """
-        self._drop(self.dbname)
+        self._drop(self.system_source['db-name'])
         createdb(self.helper, self.system_source, self.dbcnx, self.cursor,
                  template=backup_coordinates)
         self.dbcnx.commit()
@@ -771,7 +789,7 @@
         dbfile = self.absolute_dbfile()
         backup_file = self.absolute_backup_file(db_id, 'sqlite')
         shutil.copy(dbfile, backup_file)
-        # Usefull to debug WHO write a database
+        # Useful to debug WHO writes a database
         # backup_stack = self.absolute_backup_file(db_id, '.stack')
         #with open(backup_stack, 'w') as backup_stack_file:
         #    import traceback
@@ -800,7 +818,6 @@
 
 import atexit
 atexit.register(SQLiteTestDataBaseHandler._cleanup_all_tmpdb)
-atexit.register(PostgresTestDataBaseHandler.killall)
 
 
 def install_sqlite_patch(querier):
--- a/devtools/data/qunit.css	Mon May 09 17:24:03 2016 +0200
+++ b/devtools/data/qunit.css	Tue Jun 21 07:42:30 2016 +0200
@@ -1,119 +1,291 @@
+/*!
+ * QUnit 1.18.0
+ * http://qunitjs.com/
+ *
+ * Copyright jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2015-04-03T10:23Z
+ */
 
-ol#qunit-tests {
-	font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
-	margin:0;
-	padding:0;
-	list-style-position:inside;
+/** Font Family and Sizes */
+
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
+	font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
+}
+
+#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
+#qunit-tests { font-size: smaller; }
+
+
+/** Resets */
+
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
+	margin: 0;
+	padding: 0;
+}
+
+
+/** Header */
+
+#qunit-header {
+	padding: 0.5em 0 0.5em 1em;
 
-	font-size: smaller;
+	color: #8699A4;
+	background-color: #0D3349;
+
+	font-size: 1.5em;
+	line-height: 1em;
+	font-weight: 400;
+
+	border-radius: 5px 5px 0 0;
+}
+
+#qunit-header a {
+	text-decoration: none;
+	color: #C2CCD1;
 }
-ol#qunit-tests li{
-	padding:0.4em 0.5em 0.4em 2.5em;
-	border-bottom:1px solid #fff;
-	font-size:small;
-	list-style-position:inside;
+
+#qunit-header a:hover,
+#qunit-header a:focus {
+	color: #FFF;
+}
+
+#qunit-testrunner-toolbar label {
+	display: inline-block;
+	padding: 0 0.5em 0 0.1em;
+}
+
+#qunit-banner {
+	height: 5px;
+}
+
+#qunit-testrunner-toolbar {
+	padding: 0.5em 1em 0.5em 1em;
+	color: #5E740B;
+	background-color: #EEE;
+	overflow: hidden;
 }
-ol#qunit-tests li ol{
-	box-shadow: inset 0px 2px 13px #999;
-	-moz-box-shadow: inset 0px 2px 13px #999;
-	-webkit-box-shadow: inset 0px 2px 13px #999;
-	margin-top:0.5em;
-	margin-left:0;
-	padding:0.5em;
-	background-color:#fff;
-	border-radius:15px;
-	-moz-border-radius: 15px;
-	-webkit-border-radius: 15px;
+
+#qunit-userAgent {
+	padding: 0.5em 1em 0.5em 1em;
+	background-color: #2B81AF;
+	color: #FFF;
+	text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
+}
+
+#qunit-modulefilter-container {
+	float: right;
+	padding: 0.2em;
+}
+
+.qunit-url-config {
+	display: inline-block;
+	padding: 0.1em;
+}
+
+.qunit-filter {
+	display: block;
+	float: right;
+	margin-left: 1em;
+}
+
+/** Tests: Pass/Fail */
+
+#qunit-tests {
+	list-style-position: inside;
+}
+
+#qunit-tests li {
+	padding: 0.4em 1em 0.4em 1em;
+	border-bottom: 1px solid #FFF;
+	list-style-position: inside;
 }
-ol#qunit-tests li li{
-	border-bottom:none;
-	margin:0.5em;
-	background-color:#fff;
-	list-style-position: inside;
-	padding:0.4em 0.5em 0.4em 0.5em;
+
+#qunit-tests > li {
+	display: none;
+}
+
+#qunit-tests li.running,
+#qunit-tests li.pass,
+#qunit-tests li.fail,
+#qunit-tests li.skipped {
+	display: list-item;
+}
+
+#qunit-tests.hidepass li.running,
+#qunit-tests.hidepass li.pass {
+	visibility: hidden;
+	position: absolute;
+	width:   0px;
+	height:  0px;
+	padding: 0;
+	border:  0;
+	margin:  0;
+}
+
+#qunit-tests li strong {
+	cursor: pointer;
+}
+
+#qunit-tests li.skipped strong {
+	cursor: default;
+}
+
+#qunit-tests li a {
+	padding: 0.5em;
+	color: #C2CCD1;
+	text-decoration: none;
 }
 
-ol#qunit-tests li li.pass{
-	border-left:26px solid #C6E746;
-	background-color:#fff;
-	color:#5E740B;
-	}
-ol#qunit-tests li li.fail{
-	border-left:26px solid #EE5757;
-	background-color:#fff;
-	color:#710909;
+#qunit-tests li p a {
+	padding: 0.25em;
+	color: #6B6464;
+}
+#qunit-tests li a:hover,
+#qunit-tests li a:focus {
+	color: #000;
+}
+
+#qunit-tests li .runtime {
+	float: right;
+	font-size: smaller;
 }
-ol#qunit-tests li.pass{
-	background-color:#D2E0E6;
-	color:#528CE0;
+
+.qunit-assert-list {
+	margin-top: 0.5em;
+	padding: 0.5em;
+
+	background-color: #FFF;
+
+	border-radius: 5px;
 }
-ol#qunit-tests li.fail{
-	background-color:#EE5757;
-	color:#000;
+
+.qunit-collapsed {
+	display: none;
+}
+
+#qunit-tests table {
+	border-collapse: collapse;
+	margin-top: 0.2em;
 }
-ol#qunit-tests li strong {
-	cursor:pointer;
+
+#qunit-tests th {
+	text-align: right;
+	vertical-align: top;
+	padding: 0 0.5em 0 0;
+}
+
+#qunit-tests td {
+	vertical-align: top;
+}
+
+#qunit-tests pre {
+	margin: 0;
+	white-space: pre-wrap;
+	word-wrap: break-word;
 }
-h1#qunit-header{
-	background-color:#0d3349;
-	margin:0;
-	padding:0.5em 0 0.5em 1em;
-	color:#fff;
-	font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
-	border-top-right-radius:15px;
-	border-top-left-radius:15px;
-	-moz-border-radius-topright:15px;
-	-moz-border-radius-topleft:15px;
-	-webkit-border-top-right-radius:15px;
-	-webkit-border-top-left-radius:15px;
-	text-shadow: rgba(0, 0, 0, 0.5) 4px 4px 1px;
+
+#qunit-tests del {
+	background-color: #E0F2BE;
+	color: #374E0C;
+	text-decoration: none;
+}
+
+#qunit-tests ins {
+	background-color: #FFCACA;
+	color: #500;
+	text-decoration: none;
 }
-h2#qunit-banner{
-	font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
-	height:5px;
-	margin:0;
-	padding:0;
+
+/*** Test Counts */
+
+#qunit-tests b.counts                       { color: #000; }
+#qunit-tests b.passed                       { color: #5E740B; }
+#qunit-tests b.failed                       { color: #710909; }
+
+#qunit-tests li li {
+	padding: 5px;
+	background-color: #FFF;
+	border-bottom: none;
+	list-style-position: inside;
 }
-h2#qunit-banner.qunit-pass{
-	background-color:#C6E746;
-}
-h2#qunit-banner.qunit-fail, #qunit-testrunner-toolbar {
-	background-color:#EE5757;
+
+/*** Passing Styles */
+
+#qunit-tests li li.pass {
+	color: #3C510C;
+	background-color: #FFF;
+	border-left: 10px solid #C6E746;
 }
-#qunit-testrunner-toolbar {
-	font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
-	padding:0;
-	/*width:80%;*/
-	padding:0em 0 0.5em 2em;
-	font-size: small;
+
+#qunit-tests .pass                          { color: #528CE0; background-color: #D2E0E6; }
+#qunit-tests .pass .test-name               { color: #366097; }
+
+#qunit-tests .pass .test-actual,
+#qunit-tests .pass .test-expected           { color: #999; }
+
+#qunit-banner.qunit-pass                    { background-color: #C6E746; }
+
+/*** Failing Styles */
+
+#qunit-tests li li.fail {
+	color: #710909;
+	background-color: #FFF;
+	border-left: 10px solid #EE5757;
+	white-space: pre;
+}
+
+#qunit-tests > li:last-child {
+	border-radius: 0 0 5px 5px;
 }
-h2#qunit-userAgent {
-	font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
-	background-color:#2b81af;
-	margin:0;
-	padding:0;
-	color:#fff;
-	font-size: small;
-	padding:0.5em 0 0.5em 2.5em;
-	text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
+
+#qunit-tests .fail                          { color: #000; background-color: #EE5757; }
+#qunit-tests .fail .test-name,
+#qunit-tests .fail .module-name             { color: #000; }
+
+#qunit-tests .fail .test-actual             { color: #EE5757; }
+#qunit-tests .fail .test-expected           { color: #008000; }
+
+#qunit-banner.qunit-fail                    { background-color: #EE5757; }
+
+/*** Skipped tests */
+
+#qunit-tests .skipped {
+	background-color: #EBECE9;
+}
+
+#qunit-tests .qunit-skipped-label {
+	background-color: #F4FF77;
+	display: inline-block;
+	font-style: normal;
+	color: #366097;
+	line-height: 1.8em;
+	padding: 0 0.5em;
+	margin: -0.4em 0.4em -0.4em 0;
 }
-p#qunit-testresult{
-	font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
-	margin:0;
-	font-size: small;
-	color:#2b81af;
-	border-bottom-right-radius:15px;
-	border-bottom-left-radius:15px;
-	-moz-border-radius-bottomright:15px;
-	-moz-border-radius-bottomleft:15px;
-	-webkit-border-bottom-right-radius:15px;
-	-webkit-border-bottom-left-radius:15px;
-	background-color:#D2E0E6;
-	padding:0.5em 0.5em 0.5em 2.5em;
+
+/** Result */
+
+#qunit-testresult {
+	padding: 0.5em 1em 0.5em 1em;
+
+	color: #2B81AF;
+	background-color: #D2E0E6;
+
+	border-bottom: 1px solid #FFF;
 }
-strong b.fail{
-	color:#710909;
-	}
-strong b.pass{
-	color:#5E740B;
-	}
+#qunit-testresult .module-name {
+	font-weight: 700;
+}
+
+/** Fixture */
+
+#qunit-fixture {
+	position: absolute;
+	top: -10000px;
+	left: -10000px;
+	width: 1000px;
+	height: 1000px;
+}
--- a/devtools/data/qunit.js	Mon May 09 17:24:03 2016 +0200
+++ b/devtools/data/qunit.js	Tue Jun 21 07:42:30 2016 +0200
@@ -1,643 +1,713 @@
-/*
- * QUnit - A JavaScript Unit Testing Framework
- * 
- * http://docs.jquery.com/QUnit
+/*!
+ * QUnit 1.18.0
+ * http://qunitjs.com/
  *
- * Copyright (c) 2009 John Resig, Jörn Zaefferer
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
+ * Copyright jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2015-04-03T10:23Z
  */
 
-(function(window) {
-
-var QUnit = {
-
-	// Initialize the configuration options
-	init: function() {
-		config = {
-			stats: { all: 0, bad: 0 },
-			moduleStats: { all: 0, bad: 0 },
-			started: +new Date,
-			updateRate: 1000,
-			blocking: false,
-			autorun: false,
-			assertions: [],
-			filters: [],
-			queue: []
-		};
-
-		var tests = id("qunit-tests"),
-			banner = id("qunit-banner"),
-			result = id("qunit-testresult");
-
-		if ( tests ) {
-			tests.innerHTML = "";
-		}
-
-		if ( banner ) {
-			banner.className = "";
-		}
-
-		if ( result ) {
-			result.parentNode.removeChild( result );
+(function( window ) {
+
+var QUnit,
+	config,
+	onErrorFnPrev,
+	loggingCallbacks = {},
+	fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ),
+	toString = Object.prototype.toString,
+	hasOwn = Object.prototype.hasOwnProperty,
+	// Keep a local reference to Date (GH-283)
+	Date = window.Date,
+	now = Date.now || function() {
+		return new Date().getTime();
+	},
+	globalStartCalled = false,
+	runStarted = false,
+	setTimeout = window.setTimeout,
+	clearTimeout = window.clearTimeout,
+	defined = {
+		document: window.document !== undefined,
+		setTimeout: window.setTimeout !== undefined,
+		sessionStorage: (function() {
+			var x = "qunit-test-string";
+			try {
+				sessionStorage.setItem( x, x );
+				sessionStorage.removeItem( x );
+				return true;
+			} catch ( e ) {
+				return false;
+			}
+		}())
+	},
+	/**
+	 * Provides a normalized error string, correcting an issue
+	 * with IE 7 (and prior) where Error.prototype.toString is
+	 * not properly implemented
+	 *
+	 * Based on http://es5.github.com/#x15.11.4.4
+	 *
+	 * @param {String|Error} error
+	 * @return {String} error message
+	 */
+	errorString = function( error ) {
+		var name, message,
+			errorString = error.toString();
+		if ( errorString.substring( 0, 7 ) === "[object" ) {
+			name = error.name ? error.name.toString() : "Error";
+			message = error.message ? error.message.toString() : "";
+			if ( name && message ) {
+				return name + ": " + message;
+			} else if ( name ) {
+				return name;
+			} else if ( message ) {
+				return message;
+			} else {
+				return "Error";
+			}
+		} else {
+			return errorString;
 		}
 	},
-	
-	// call on start of module test to prepend name to all tests
-	module: function(name, testEnvironment) {
-		config.currentModule = name;
-
-		synchronize(function() {
-			if ( config.currentModule ) {
-				QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );
+	/**
+	 * Makes a clone of an object using only Array or Object as base,
+	 * and copies over the own enumerable properties.
+	 *
+	 * @param {Object} obj
+	 * @return {Object} New object with only the own properties (recursively).
+	 */
+	objectValues = function( obj ) {
+		var key, val,
+			vals = QUnit.is( "array", obj ) ? [] : {};
+		for ( key in obj ) {
+			if ( hasOwn.call( obj, key ) ) {
+				val = obj[ key ];
+				vals[ key ] = val === Object( val ) ? objectValues( val ) : val;
 			}
-
-			config.currentModule = name;
-			config.moduleTestEnvironment = testEnvironment;
-			config.moduleStats = { all: 0, bad: 0 };
-
-			QUnit.moduleStart( name, testEnvironment );
-		});
+		}
+		return vals;
+	};
+
+QUnit = {};
+
+/**
+ * Config object: Maintain internal state
+ * Later exposed as QUnit.config
+ * `config` initialized at top of scope
+ */
+config = {
+	// The queue of tests to run
+	queue: [],
+
+	// block until document ready
+	blocking: true,
+
+	// by default, run previously failed tests first
+	// very useful in combination with "Hide passed tests" checked
+	reorder: true,
+
+	// by default, modify document.title when suite is done
+	altertitle: true,
+
+	// by default, scroll to top of the page when suite is done
+	scrolltop: true,
+
+	// when enabled, all tests must call expect()
+	requireExpects: false,
+
+	// depth up-to which object will be dumped
+	maxDepth: 5,
+
+	// add checkboxes that are persisted in the query-string
+	// when enabled, the id is set to `true` as a `QUnit.config` property
+	urlConfig: [
+		{
+			id: "hidepassed",
+			label: "Hide passed tests",
+			tooltip: "Only show tests and assertions that fail. Stored as query-strings."
+		},
+		{
+			id: "noglobals",
+			label: "Check for Globals",
+			tooltip: "Enabling this will test if any test introduces new properties on the " +
+				"`window` object. Stored as query-strings."
+		},
+		{
+			id: "notrycatch",
+			label: "No try-catch",
+			tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " +
+				"exceptions in IE reasonable. Stored as query-strings."
+		}
+	],
+
+	// Set of all modules.
+	modules: [],
+
+	// The first unnamed module
+	currentModule: {
+		name: "",
+		tests: []
 	},
 
-	asyncTest: function(testName, expected, callback) {
+	callbacks: {}
+};
+
+// Push a loose unnamed module to the modules collection
+config.modules.push( config.currentModule );
+
+// Initialize more QUnit.config and QUnit.urlParams
+(function() {
+	var i, current,
+		location = window.location || { search: "", protocol: "file:" },
+		params = location.search.slice( 1 ).split( "&" ),
+		length = params.length,
+		urlParams = {};
+
+	if ( params[ 0 ] ) {
+		for ( i = 0; i < length; i++ ) {
+			current = params[ i ].split( "=" );
+			current[ 0 ] = decodeURIComponent( current[ 0 ] );
+
+			// allow just a key to turn on a flag, e.g., test.html?noglobals
+			current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
+			if ( urlParams[ current[ 0 ] ] ) {
+				urlParams[ current[ 0 ] ] = [].concat( urlParams[ current[ 0 ] ], current[ 1 ] );
+			} else {
+				urlParams[ current[ 0 ] ] = current[ 1 ];
+			}
+		}
+	}
+
+	if ( urlParams.filter === true ) {
+		delete urlParams.filter;
+	}
+
+	QUnit.urlParams = urlParams;
+
+	// String search anywhere in moduleName+testName
+	config.filter = urlParams.filter;
+
+	if ( urlParams.maxDepth ) {
+		config.maxDepth = parseInt( urlParams.maxDepth, 10 ) === -1 ?
+			Number.POSITIVE_INFINITY :
+			urlParams.maxDepth;
+	}
+
+	config.testId = [];
+	if ( urlParams.testId ) {
+
+		// Ensure that urlParams.testId is an array
+		urlParams.testId = decodeURIComponent( urlParams.testId ).split( "," );
+		for ( i = 0; i < urlParams.testId.length; i++ ) {
+			config.testId.push( urlParams.testId[ i ] );
+		}
+	}
+
+	// Figure out if we're running the tests from a server or not
+	QUnit.isLocal = location.protocol === "file:";
+
+	// Expose the current QUnit version
+	QUnit.version = "1.18.0";
+}());
+
+// Root QUnit object.
+// `QUnit` initialized at top of scope
+extend( QUnit, {
+
+	// call on start of module test to prepend name to all tests
+	module: function( name, testEnvironment ) {
+		var currentModule = {
+			name: name,
+			testEnvironment: testEnvironment,
+			tests: []
+		};
+
+		// DEPRECATED: handles setup/teardown functions,
+		// beforeEach and afterEach should be used instead
+		if ( testEnvironment && testEnvironment.setup ) {
+			testEnvironment.beforeEach = testEnvironment.setup;
+			delete testEnvironment.setup;
+		}
+		if ( testEnvironment && testEnvironment.teardown ) {
+			testEnvironment.afterEach = testEnvironment.teardown;
+			delete testEnvironment.teardown;
+		}
+
+		config.modules.push( currentModule );
+		config.currentModule = currentModule;
+	},
+
+	// DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0.
+	asyncTest: function( testName, expected, callback ) {
 		if ( arguments.length === 2 ) {
 			callback = expected;
-			expected = 0;
+			expected = null;
 		}
 
-		QUnit.test(testName, expected, callback, true);
+		QUnit.test( testName, expected, callback, true );
 	},
-	
-	test: function(testName, expected, callback, async) {
-		var name = testName, testEnvironment, testEnvironmentArg;
+
+	test: function( testName, expected, callback, async ) {
+		var test;
 
 		if ( arguments.length === 2 ) {
 			callback = expected;
 			expected = null;
 		}
-		// is 2nd argument a testEnvironment?
-		if ( expected && typeof expected === 'object') {
-			testEnvironmentArg =  expected;
-			expected = null;
-		}
-
-		if ( config.currentModule ) {
-			name = config.currentModule + " module: " + name;
-		}
-
-		if ( !validTest(name) ) {
-			return;
-		}
-
-		synchronize(function() {
-			QUnit.testStart( testName );
-
-			testEnvironment = extend({
-				setup: function() {},
-				teardown: function() {}
-			}, config.moduleTestEnvironment);
-			if (testEnvironmentArg) {
-				extend(testEnvironment,testEnvironmentArg);
-			}
-
-			// allow utility functions to access the current test environment
-			QUnit.current_testEnvironment = testEnvironment;
-			
-			config.assertions = [];
-			config.expected = expected;
-
-			try {
-				if ( !config.pollution ) {
-					saveGlobal();
-				}
-
-				testEnvironment.setup.call(testEnvironment);
-			} catch(e) {
-				QUnit.ok( false, "Setup failed on " + name + ": " + e.message );
-			}
-
-			if ( async ) {
-				QUnit.stop();
-			}
-
-			try {
-				callback.call(testEnvironment);
-			} catch(e) {
-				fail("Test " + name + " died, exception and test follows", e, callback);
-				QUnit.ok( false, "Died on test #" + (config.assertions.length + 1) + ": " + e.message );
-				// else next test will carry the responsibility
-				saveGlobal();
-
-				// Restart the tests if they're blocking
-				if ( config.blocking ) {
-					start();
-				}
-			}
+
+		test = new Test({
+			testName: testName,
+			expected: expected,
+			async: async,
+			callback: callback
+		});
+
+		test.queue();
+	},
+
+	skip: function( testName ) {
+		var test = new Test({
+			testName: testName,
+			skip: true
 		});
 
-		synchronize(function() {
-			try {
-				checkPollution();
-				testEnvironment.teardown.call(testEnvironment);
-			} catch(e) {
-				QUnit.ok( false, "Teardown failed on " + name + ": " + e.message );
-			}
-
-			try {
-				QUnit.reset();
-			} catch(e) {
-				fail("reset() failed, following Test " + name + ", exception and reset fn follows", e, reset);
-			}
-
-			if ( config.expected && config.expected != config.assertions.length ) {
-				QUnit.ok( false, "Expected " + config.expected + " assertions, but " + config.assertions.length + " were run" );
+		test.queue();
+	},
+
+	// DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0.
+	// In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior.
+	start: function( count ) {
+		var globalStartAlreadyCalled = globalStartCalled;
+
+		if ( !config.current ) {
+			globalStartCalled = true;
+
+			if ( runStarted ) {
+				throw new Error( "Called start() outside of a test context while already started" );
+			} else if ( globalStartAlreadyCalled || count > 1 ) {
+				throw new Error( "Called start() outside of a test context too many times" );
+			} else if ( config.autostart ) {
+				throw new Error( "Called start() outside of a test context when " +
+					"QUnit.config.autostart was true" );
+			} else if ( !config.pageLoaded ) {
+
+				// The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it
+				config.autostart = true;
+				return;
 			}
-
-			var good = 0, bad = 0,
-				tests = id("qunit-tests");
-
-			config.stats.all += config.assertions.length;
-			config.moduleStats.all += config.assertions.length;
-
-			if ( tests ) {
-				var ol  = document.createElement("ol");
-				ol.style.display = "none";
-
-				for ( var i = 0; i < config.assertions.length; i++ ) {
-					var assertion = config.assertions[i];
-
-					var li = document.createElement("li");
-					li.className = assertion.result ? "pass" : "fail";
-					li.appendChild(document.createTextNode(assertion.message || "(no message)"));
-					ol.appendChild( li );
-
-					if ( assertion.result ) {
-						good++;
-					} else {
-						bad++;
-						config.stats.bad++;
-						config.moduleStats.bad++;
-					}
-				}
-
-				var b = document.createElement("strong");
-				b.innerHTML = name + " <b style='color:black;'>(<b class='fail'>" + bad + "</b>, <b class='pass'>" + good + "</b>, " + config.assertions.length + ")</b>";
-				
-				addEvent(b, "click", function() {
-					var next = b.nextSibling, display = next.style.display;
-					next.style.display = display === "none" ? "block" : "none";
-				});
-				
-				addEvent(b, "dblclick", function(e) {
-					var target = e && e.target ? e.target : window.event.srcElement;
-					if ( target.nodeName.toLowerCase() === "strong" ) {
-						var text = "", node = target.firstChild;
-
-						while ( node.nodeType === 3 ) {
-							text += node.nodeValue;
-							node = node.nextSibling;
-						}
-
-						text = text.replace(/(^\s*|\s*$)/g, "");
-
-						if ( window.location ) {
-							window.location.href = window.location.href.match(/^(.+?)(\?.*)?$/)[1] + "?" + encodeURIComponent(text);
-						}
-					}
-				});
-
-				var li = document.createElement("li");
-				li.className = bad ? "fail" : "pass";
-				li.appendChild( b );
-				li.appendChild( ol );
-				tests.appendChild( li );
-
-				if ( bad ) {
-					var toolbar = id("qunit-testrunner-toolbar");
-					if ( toolbar ) {
-						toolbar.style.display = "block";
-						id("qunit-filter-pass").disabled = null;
-						id("qunit-filter-missing").disabled = null;
-					}
-				}
-
-			} else {
-				for ( var i = 0; i < config.assertions.length; i++ ) {
-					if ( !config.assertions[i].result ) {
-						bad++;
-						config.stats.bad++;
-						config.moduleStats.bad++;
-					}
-				}
+		} else {
+
+			// If a test is running, adjust its semaphore
+			config.current.semaphore -= count || 1;
+
+			// Don't start until equal number of stop-calls
+			if ( config.current.semaphore > 0 ) {
+				return;
 			}
 
-			QUnit.testDone( testName, bad, config.assertions.length );
-
-			if ( !window.setTimeout && !config.queue.length ) {
-				done();
+			// throw an Error if start is called more often than stop
+			if ( config.current.semaphore < 0 ) {
+				config.current.semaphore = 0;
+
+				QUnit.pushFailure(
+					"Called start() while already started (test's semaphore was 0 already)",
+					sourceFromStacktrace( 2 )
+				);
+				return;
 			}
-		});
-
-		if ( window.setTimeout && !config.doneTimer ) {
-			config.doneTimer = window.setTimeout(function(){
-				if ( !config.queue.length ) {
-					done();
-				} else {
-					synchronize( done );
-				}
-			}, 13);
 		}
-	},
-	
-	/**
-	 * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
-	 */
-	expect: function(asserts) {
-		config.expected = asserts;
+
+		resumeProcessing();
 	},
 
-	/**
-	 * Asserts true.
-	 * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
-	 */
-	ok: function(a, msg) {
-		QUnit.log(a, msg);
-
-		config.assertions.push({
-			result: !!a,
-			message: msg
-		});
-	},
-
-	/**
-	 * Checks that the first two arguments are equal, with an optional message.
-	 * Prints out both actual and expected values.
-	 *
-	 * Prefered to ok( actual == expected, message )
-	 *
-	 * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );
-	 *
-	 * @param Object actual
-	 * @param Object expected
-	 * @param String message (optional)
-	 */
-	equal: function(actual, expected, message) {
-		push(expected == actual, actual, expected, message);
-	},
-
-	notEqual: function(actual, expected, message) {
-		push(expected != actual, actual, expected, message);
-	},
-	
-	deepEqual: function(a, b, message) {
-		push(QUnit.equiv(a, b), a, b, message);
-	},
-
-	notDeepEqual: function(a, b, message) {
-		push(!QUnit.equiv(a, b), a, b, message);
-	},
-
-	strictEqual: function(actual, expected, message) {
-		push(expected === actual, actual, expected, message);
-	},
-
-	notStrictEqual: function(actual, expected, message) {
-		push(expected !== actual, actual, expected, message);
+	// DEPRECATED: QUnit.stop() will be removed in QUnit 2.0.
+	stop: function( count ) {
+
+		// If there isn't a test running, don't allow QUnit.stop() to be called
+		if ( !config.current ) {
+			throw new Error( "Called stop() outside of a test context" );
+		}
+
+		// If a test is running, adjust its semaphore
+		config.current.semaphore += count || 1;
+
+		pauseProcessing();
 	},
-	
-	start: function() {
-		// A slight delay, to avoid any current callbacks
-		if ( window.setTimeout ) {
-			window.setTimeout(function() {
-				if ( config.timeout ) {
-					clearTimeout(config.timeout);
-				}
-
-				config.blocking = false;
-				process();
-			}, 13);
-		} else {
-			config.blocking = false;
-			process();
-		}
-	},
-	
-	stop: function(timeout) {
-		config.blocking = true;
-
-		if ( timeout && window.setTimeout ) {
-			config.timeout = window.setTimeout(function() {
-				QUnit.ok( false, "Test timed out" );
-				QUnit.start();
-			}, timeout);
-		}
-	},
-	
-	/**
-	 * Resets the test setup. Useful for tests that modify the DOM.
-	 */
-	reset: function() {
-		if ( window.jQuery ) {
-			jQuery("#main").html( config.fixture );
-			jQuery.event.global = {};
-			jQuery.ajaxSettings = extend({}, config.ajaxSettings);
-		}
-	},
-	
-	/**
-	 * Trigger an event on an element.
-	 *
-	 * @example triggerEvent( document.body, "click" );
-	 *
-	 * @param DOMElement elem
-	 * @param String type
-	 */
-	triggerEvent: function( elem, type, event ) {
-		if ( document.createEvent ) {
-			event = document.createEvent("MouseEvents");
-			event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
-				0, 0, 0, 0, 0, false, false, false, false, 0, null);
-			elem.dispatchEvent( event );
-
-		} else if ( elem.fireEvent ) {
-			elem.fireEvent("on"+type);
-		}
-	},
-	
+
+	config: config,
+
 	// Safe object type checking
 	is: function( type, obj ) {
-		return Object.prototype.toString.call( obj ) === "[object "+ type +"]";
+		return QUnit.objectType( obj ) === type;
 	},
-	
-	// Logging callbacks
-	done: function(failures, total) {},
-	log: function(result, message) {},
-	testStart: function(name) {},
-	testDone: function(name, failures, total) {},
-	moduleStart: function(name, testEnvironment) {},
-	moduleDone: function(name, failures, total) {}
-};
-
-// Backwards compatibility, deprecated
-QUnit.equals = QUnit.equal;
-QUnit.same = QUnit.deepEqual;
-
-// Maintain internal state
-var config = {
-	// The queue of tests to run
-	queue: [],
-
-	// block until document ready
-	blocking: true
-};
-
-// Load paramaters
-(function() {
-	var location = window.location || { search: "", protocol: "file:" },
-		GETParams = location.search.slice(1).split('&');
-
-	for ( var i = 0; i < GETParams.length; i++ ) {
-		GETParams[i] = decodeURIComponent( GETParams[i] );
-		if ( GETParams[i] === "noglobals" ) {
-			GETParams.splice( i, 1 );
-			i--;
-			config.noglobals = true;
-		} else if ( GETParams[i].search('=') > -1 ) {
-			GETParams.splice( i, 1 );
-			i--;
+
+	objectType: function( obj ) {
+		if ( typeof obj === "undefined" ) {
+			return "undefined";
+		}
+
+		// Consider: typeof null === object
+		if ( obj === null ) {
+			return "null";
+		}
+
+		var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ),
+			type = match && match[ 1 ] || "";
+
+		switch ( type ) {
+			case "Number":
+				if ( isNaN( obj ) ) {
+					return "nan";
+				}
+				return "number";
+			case "String":
+			case "Boolean":
+			case "Array":
+			case "Date":
+			case "RegExp":
+			case "Function":
+				return type.toLowerCase();
+		}
+		if ( typeof obj === "object" ) {
+			return "object";
+		}
+		return undefined;
+	},
+
+	extend: extend,
+
+	load: function() {
+		config.pageLoaded = true;
+
+		// Initialize the configuration options
+		extend( config, {
+			stats: { all: 0, bad: 0 },
+			moduleStats: { all: 0, bad: 0 },
+			started: 0,
+			updateRate: 1000,
+			autostart: true,
+			filter: ""
+		}, true );
+
+		config.blocking = false;
+
+		if ( config.autostart ) {
+			resumeProcessing();
 		}
 	}
-	
-	// restrict modules/tests by get parameters
-	config.filters = GETParams;
-	
-	// Figure out if we're running the tests from a server or not
-	QUnit.isLocal = !!(location.protocol === 'file:');
-})();
-
-// Expose the API as global variables, unless an 'exports'
-// object exists, in that case we assume we're in CommonJS
-if ( typeof exports === "undefined" || typeof require === "undefined" ) {
-	extend(window, QUnit);
-	window.QUnit = QUnit;
-} else {
-	extend(exports, QUnit);
-	exports.QUnit = QUnit;
-}
-
-if ( typeof document === "undefined" || document.readyState === "complete" ) {
-	config.autorun = true;
-}
-
-addEvent(window, "load", function() {
-	// Initialize the config, saving the execution queue
-	var oldconfig = extend({}, config);
-	QUnit.init();
-	extend(config, oldconfig);
-
-	config.blocking = false;
-
-	var userAgent = id("qunit-userAgent");
-	if ( userAgent ) {
-		userAgent.innerHTML = navigator.userAgent;
+});
+
+// Register logging callbacks
+(function() {
+	var i, l, key,
+		callbacks = [ "begin", "done", "log", "testStart", "testDone",
+			"moduleStart", "moduleDone" ];
+
+	function registerLoggingCallback( key ) {
+		var loggingCallback = function( callback ) {
+			if ( QUnit.objectType( callback ) !== "function" ) {
+				throw new Error(
+					"QUnit logging methods require a callback function as their first parameters."
+				);
+			}
+
+			config.callbacks[ key ].push( callback );
+		};
+
+		// DEPRECATED: This will be removed on QUnit 2.0.0+
+		// Stores the registered functions allowing restoring
+		// at verifyLoggingCallbacks() if modified
+		loggingCallbacks[ key ] = loggingCallback;
+
+		return loggingCallback;
 	}
-	
-	var toolbar = id("qunit-testrunner-toolbar");
-	if ( toolbar ) {
-		toolbar.style.display = "none";
-		
-		var filter = document.createElement("input");
-		filter.type = "checkbox";
-		filter.id = "qunit-filter-pass";
-		filter.disabled = true;
-		addEvent( filter, "click", function() {
-			var li = document.getElementsByTagName("li");
-			for ( var i = 0; i < li.length; i++ ) {
-				if ( li[i].className.indexOf("pass") > -1 ) {
-					li[i].style.display = filter.checked ? "none" : "";
-				}
+
+	for ( i = 0, l = callbacks.length; i < l; i++ ) {
+		key = callbacks[ i ];
+
+		// Initialize key collection of logging callback
+		if ( QUnit.objectType( config.callbacks[ key ] ) === "undefined" ) {
+			config.callbacks[ key ] = [];
+		}
+
+		QUnit[ key ] = registerLoggingCallback( key );
+	}
+})();
+
+// `onErrorFnPrev` initialized at top of scope
+// Preserve other handlers
+onErrorFnPrev = window.onerror;
+
+// Cover uncaught exceptions
+// Returning true will suppress the default browser handler,
+// returning false will let it run.
+window.onerror = function( error, filePath, linerNr ) {
+	var ret = false;
+	if ( onErrorFnPrev ) {
+		ret = onErrorFnPrev( error, filePath, linerNr );
+	}
+
+	// Treat return value as window.onerror itself does,
+	// Only do our handling if not suppressed.
+	if ( ret !== true ) {
+		if ( QUnit.config.current ) {
+			if ( QUnit.config.current.ignoreGlobalErrors ) {
+				return true;
 			}
-		});
-		toolbar.appendChild( filter );
-
-		var label = document.createElement("label");
-		label.setAttribute("for", "qunit-filter-pass");
-		label.innerHTML = "Hide passed tests";
-		toolbar.appendChild( label );
-
-		var missing = document.createElement("input");
-		missing.type = "checkbox";
-		missing.id = "qunit-filter-missing";
-		missing.disabled = true;
-		addEvent( missing, "click", function() {
-			var li = document.getElementsByTagName("li");
-			for ( var i = 0; i < li.length; i++ ) {
-				if ( li[i].className.indexOf("fail") > -1 && li[i].innerHTML.indexOf('missing test - untested code is broken code') > - 1 ) {
-					li[i].parentNode.parentNode.style.display = missing.checked ? "none" : "block";
-				}
-			}
-		});
-		toolbar.appendChild( missing );
-
-		label = document.createElement("label");
-		label.setAttribute("for", "qunit-filter-missing");
-		label.innerHTML = "Hide missing tests (untested code is broken code)";
-		toolbar.appendChild( label );
+			QUnit.pushFailure( error, filePath + ":" + linerNr );
+		} else {
+			QUnit.test( "global failure", extend(function() {
+				QUnit.pushFailure( error, filePath + ":" + linerNr );
+			}, { validTest: true } ) );
+		}
+		return false;
 	}
 
-	var main = id('main');
-	if ( main ) {
-		config.fixture = main.innerHTML;
-	}
-
-	if ( window.jQuery ) {
-		config.ajaxSettings = window.jQuery.ajaxSettings;
-	}
-
-	QUnit.start();
-});
+	return ret;
+};
 
 function done() {
-	if ( config.doneTimer && window.clearTimeout ) {
-		window.clearTimeout( config.doneTimer );
-		config.doneTimer = null;
-	}
-
-	if ( config.queue.length ) {
-		config.doneTimer = window.setTimeout(function(){
-			if ( !config.queue.length ) {
-				done();
-			} else {
-				synchronize( done );
-			}
-		}, 13);
-
-		return;
-	}
+	var runtime, passed;
 
 	config.autorun = true;
 
 	// Log the last module results
-	if ( config.currentModule ) {
-		QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );
-	}
-
-	var banner = id("qunit-banner"),
-		tests = id("qunit-tests"),
-		html = ['Tests completed in ',
-		+new Date - config.started, ' milliseconds.<br/>',
-		'<span class="passed">', config.stats.all - config.stats.bad, '</span> tests of <span class="total">', config.stats.all, '</span> passed, <span class="failed">', config.stats.bad,'</span> failed.'].join('');
-
-	if ( banner ) {
-		banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");
-	}
-
-	if ( tests ) {	
-		var result = id("qunit-testresult");
-
-		if ( !result ) {
-			result = document.createElement("p");
-			result.id = "qunit-testresult";
-			result.className = "result";
-			tests.parentNode.insertBefore( result, tests.nextSibling );
-		}
-
-		result.innerHTML = html;
+	if ( config.previousModule ) {
+		runLoggingCallbacks( "moduleDone", {
+			name: config.previousModule.name,
+			tests: config.previousModule.tests,
+			failed: config.moduleStats.bad,
+			passed: config.moduleStats.all - config.moduleStats.bad,
+			total: config.moduleStats.all,
+			runtime: now() - config.moduleStats.started
+		});
 	}
-
-	QUnit.done( config.stats.bad, config.stats.all );
+	delete config.previousModule;
+
+	runtime = now() - config.started;
+	passed = config.stats.all - config.stats.bad;
+
+	runLoggingCallbacks( "done", {
+		failed: config.stats.bad,
+		passed: passed,
+		total: config.stats.all,
+		runtime: runtime
+	});
 }
 
-function validTest( name ) {
-	var i = config.filters.length,
-		run = false;
-
-	if ( !i ) {
-		return true;
+// Doesn't support IE6 to IE9, it will return undefined on these browsers
+// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
+function extractStacktrace( e, offset ) {
+	offset = offset === undefined ? 4 : offset;
+
+	var stack, include, i;
+
+	if ( e.stack ) {
+		stack = e.stack.split( "\n" );
+		if ( /^error$/i.test( stack[ 0 ] ) ) {
+			stack.shift();
+		}
+		if ( fileName ) {
+			include = [];
+			for ( i = offset; i < stack.length; i++ ) {
+				if ( stack[ i ].indexOf( fileName ) !== -1 ) {
+					break;
+				}
+				include.push( stack[ i ] );
+			}
+			if ( include.length ) {
+				return include.join( "\n" );
+			}
+		}
+		return stack[ offset ];
+
+	// Support: Safari <=6 only
+	} else if ( e.sourceURL ) {
+
+		// exclude useless self-reference for generated Error objects
+		if ( /qunit.js$/.test( e.sourceURL ) ) {
+			return;
+		}
+
+		// for actual exceptions, this is useful
+		return e.sourceURL + ":" + e.line;
 	}
-	
-	while ( i-- ) {
-		var filter = config.filters[i],
-			not = filter.charAt(0) == '!';
-
-		if ( not ) {
-			filter = filter.slice(1);
-		}
-
-		if ( name.indexOf(filter) !== -1 ) {
-			return !not;
-		}
-
-		if ( not ) {
-			run = true;
+}
+
+function sourceFromStacktrace( offset ) {
+	var error = new Error();
+
+	// Support: Safari <=7 only, IE <=10 - 11 only
+	// Not all browsers generate the `stack` property for `new Error()`, see also #636
+	if ( !error.stack ) {
+		try {
+			throw error;
+		} catch ( err ) {
+			error = err;
 		}
 	}
 
-	return run;
+	return extractStacktrace( error, offset );
 }
 
-function push(result, actual, expected, message) {
-	message = message || (result ? "okay" : "failed");
-	QUnit.ok( result, result ? message + ": " + QUnit.jsDump.parse(expected) : message + ", expected: " + QUnit.jsDump.parse(expected) + " result: " + QUnit.jsDump.parse(actual) );
-}
-
-function synchronize( callback ) {
+function synchronize( callback, last ) {
+	if ( QUnit.objectType( callback ) === "array" ) {
+		while ( callback.length ) {
+			synchronize( callback.shift() );
+		}
+		return;
+	}
 	config.queue.push( callback );
 
 	if ( config.autorun && !config.blocking ) {
-		process();
+		process( last );
 	}
 }
 
-function process() {
-	var start = (new Date()).getTime();
+function process( last ) {
+	function next() {
+		process( last );
+	}
+	var start = now();
+	config.depth = ( config.depth || 0 ) + 1;
 
 	while ( config.queue.length && !config.blocking ) {
-		if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) {
+		if ( !defined.setTimeout || config.updateRate <= 0 ||
+				( ( now() - start ) < config.updateRate ) ) {
+			if ( config.current ) {
+
+				// Reset async tracking for each phase of the Test lifecycle
+				config.current.usedAsync = false;
+			}
 			config.queue.shift()();
-
 		} else {
-			setTimeout( process, 13 );
+			setTimeout( next, 13 );
 			break;
 		}
 	}
+	config.depth--;
+	if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
+		done();
+	}
+}
+
+function begin() {
+	var i, l,
+		modulesLog = [];
+
+	// If the test run hasn't officially begun yet
+	if ( !config.started ) {
+
+		// Record the time of the test run's beginning
+		config.started = now();
+
+		verifyLoggingCallbacks();
+
+		// Delete the loose unnamed module if unused.
+		if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) {
+			config.modules.shift();
+		}
+
+		// Avoid unnecessary information by not logging modules' test environments
+		for ( i = 0, l = config.modules.length; i < l; i++ ) {
+			modulesLog.push({
+				name: config.modules[ i ].name,
+				tests: config.modules[ i ].tests
+			});
+		}
+
+		// The test run is officially beginning now
+		runLoggingCallbacks( "begin", {
+			totalTests: Test.count,
+			modules: modulesLog
+		});
+	}
+
+	config.blocking = false;
+	process( true );
+}
+
+function resumeProcessing() {
+	runStarted = true;
+
+	// A slight delay to allow this iteration of the event loop to finish (more assertions, etc.)
+	if ( defined.setTimeout ) {
+		setTimeout(function() {
+			if ( config.current && config.current.semaphore > 0 ) {
+				return;
+			}
+			if ( config.timeout ) {
+				clearTimeout( config.timeout );
+			}
+
+			begin();
+		}, 13 );
+	} else {
+		begin();
+	}
+}
+
+function pauseProcessing() {
+	config.blocking = true;
+
+	if ( config.testTimeout && defined.setTimeout ) {
+		clearTimeout( config.timeout );
+		config.timeout = setTimeout(function() {
+			if ( config.current ) {
+				config.current.semaphore = 0;
+				QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) );
+			} else {
+				throw new Error( "Test timed out" );
+			}
+			resumeProcessing();
+		}, config.testTimeout );
+	}
 }
 
 function saveGlobal() {
 	config.pollution = [];
-	
+
 	if ( config.noglobals ) {
 		for ( var key in window ) {
-			config.pollution.push( key );
+			if ( hasOwn.call( window, key ) ) {
+				// in Opera sometimes DOM element ids show up here, ignore them
+				if ( /^qunit-test-output/.test( key ) ) {
+					continue;
+				}
+				config.pollution.push( key );
+			}
 		}
 	}
 }
 
-function checkPollution( name ) {
-	var old = config.pollution;
+function checkPollution() {
+	var newGlobals,
+		deletedGlobals,
+		old = config.pollution;
+
 	saveGlobal();
-	
-	var newGlobals = diff( old, config.pollution );
+
+	newGlobals = diff( config.pollution, old );
 	if ( newGlobals.length > 0 ) {
-		ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
-		config.expected++;
+		QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) );
 	}
 
-	var deletedGlobals = diff( config.pollution, old );
+	deletedGlobals = diff( old, config.pollution );
 	if ( deletedGlobals.length > 0 ) {
-		ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
-		config.expected++;
+		QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) );
 	}
 }
 
 // returns a new Array with the elements that are in a but not in b
 function diff( a, b ) {
-	var result = a.slice();
-	for ( var i = 0; i < result.length; i++ ) {
-		for ( var j = 0; j < b.length; j++ ) {
-			if ( result[i] === b[j] ) {
-				result.splice(i, 1);
+	var i, j,
+		result = a.slice();
+
+	for ( i = 0; i < result.length; i++ ) {
+		for ( j = 0; j < b.length; j++ ) {
+			if ( result[ i ] === b[ j ] ) {
+				result.splice( i, 1 );
 				i--;
 				break;
 			}
@@ -646,424 +716,3113 @@
 	return result;
 }
 
-function fail(message, exception, callback) {
-	if ( typeof console !== "undefined" && console.error && console.warn ) {
-		console.error(message);
-		console.error(exception);
-		console.warn(callback.toString());
-
-	} else if ( window.opera && opera.postError ) {
-		opera.postError(message, exception, callback.toString);
-	}
-}
-
-function extend(a, b) {
+function extend( a, b, undefOnly ) {
 	for ( var prop in b ) {
-		a[prop] = b[prop];
+		if ( hasOwn.call( b, prop ) ) {
+
+			// Avoid "Member not found" error in IE8 caused by messing with window.constructor
+			if ( !( prop === "constructor" && a === window ) ) {
+				if ( b[ prop ] === undefined ) {
+					delete a[ prop ];
+				} else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) {
+					a[ prop ] = b[ prop ];
+				}
+			}
+		}
 	}
 
 	return a;
 }
 
-function addEvent(elem, type, fn) {
-	if ( elem.addEventListener ) {
-		elem.addEventListener( type, fn, false );
-	} else if ( elem.attachEvent ) {
-		elem.attachEvent( "on" + type, fn );
+function runLoggingCallbacks( key, args ) {
+	var i, l, callbacks;
+
+	callbacks = config.callbacks[ key ];
+	for ( i = 0, l = callbacks.length; i < l; i++ ) {
+		callbacks[ i ]( args );
+	}
+}
+
+// DEPRECATED: This will be removed on 2.0.0+
+// This function verifies if the loggingCallbacks were modified by the user
+// If so, it will restore it, assign the given callback and print a console warning
+function verifyLoggingCallbacks() {
+	var loggingCallback, userCallback;
+
+	for ( loggingCallback in loggingCallbacks ) {
+		if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) {
+
+			userCallback = QUnit[ loggingCallback ];
+
+			// Restore the callback function
+			QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ];
+
+			// Assign the deprecated given callback
+			QUnit[ loggingCallback ]( userCallback );
+
+			if ( window.console && window.console.warn ) {
+				window.console.warn(
+					"QUnit." + loggingCallback + " was replaced with a new value.\n" +
+					"Please, check out the documentation on how to apply logging callbacks.\n" +
+					"Reference: http://api.qunitjs.com/category/callbacks/"
+				);
+			}
+		}
+	}
+}
+
+// from jquery.js
+function inArray( elem, array ) {
+	if ( array.indexOf ) {
+		return array.indexOf( elem );
+	}
+
+	for ( var i = 0, length = array.length; i < length; i++ ) {
+		if ( array[ i ] === elem ) {
+			return i;
+		}
+	}
+
+	return -1;
+}
+
+function Test( settings ) {
+	var i, l;
+
+	++Test.count;
+
+	extend( this, settings );
+	this.assertions = [];
+	this.semaphore = 0;
+	this.usedAsync = false;
+	this.module = config.currentModule;
+	this.stack = sourceFromStacktrace( 3 );
+
+	// Register unique strings
+	for ( i = 0, l = this.module.tests; i < l.length; i++ ) {
+		if ( this.module.tests[ i ].name === this.testName ) {
+			this.testName += " ";
+		}
+	}
+
+	this.testId = generateHash( this.module.name, this.testName );
+
+	this.module.tests.push({
+		name: this.testName,
+		testId: this.testId
+	});
+
+	if ( settings.skip ) {
+
+		// Skipped tests will fully ignore any sent callback
+		this.callback = function() {};
+		this.async = false;
+		this.expected = 0;
 	} else {
-		fn();
+		this.assert = new Assert( this );
 	}
 }
 
-function id(name) {
-	return !!(typeof document !== "undefined" && document && document.getElementById) &&
-		document.getElementById( name );
+Test.count = 0;
+
+Test.prototype = {
+	before: function() {
+		if (
+
+			// Emit moduleStart when we're switching from one module to another
+			this.module !== config.previousModule ||
+
+				// They could be equal (both undefined) but if the previousModule property doesn't
+				// yet exist it means this is the first test in a suite that isn't wrapped in a
+				// module, in which case we'll just emit a moduleStart event for 'undefined'.
+				// Without this, reporters can get testStart before moduleStart  which is a problem.
+				!hasOwn.call( config, "previousModule" )
+		) {
+			if ( hasOwn.call( config, "previousModule" ) ) {
+				runLoggingCallbacks( "moduleDone", {
+					name: config.previousModule.name,
+					tests: config.previousModule.tests,
+					failed: config.moduleStats.bad,
+					passed: config.moduleStats.all - config.moduleStats.bad,
+					total: config.moduleStats.all,
+					runtime: now() - config.moduleStats.started
+				});
+			}
+			config.previousModule = this.module;
+			config.moduleStats = { all: 0, bad: 0, started: now() };
+			runLoggingCallbacks( "moduleStart", {
+				name: this.module.name,
+				tests: this.module.tests
+			});
+		}
+
+		config.current = this;
+
+		this.testEnvironment = extend( {}, this.module.testEnvironment );
+		delete this.testEnvironment.beforeEach;
+		delete this.testEnvironment.afterEach;
+
+		this.started = now();
+		runLoggingCallbacks( "testStart", {
+			name: this.testName,
+			module: this.module.name,
+			testId: this.testId
+		});
+
+		if ( !config.pollution ) {
+			saveGlobal();
+		}
+	},
+
+	run: function() {
+		var promise;
+
+		config.current = this;
+
+		if ( this.async ) {
+			QUnit.stop();
+		}
+
+		this.callbackStarted = now();
+
+		if ( config.notrycatch ) {
+			promise = this.callback.call( this.testEnvironment, this.assert );
+			this.resolvePromise( promise );
+			return;
+		}
+
+		try {
+			promise = this.callback.call( this.testEnvironment, this.assert );
+			this.resolvePromise( promise );
+		} catch ( e ) {
+			this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " +
+				this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
+
+			// else next test will carry the responsibility
+			saveGlobal();
+
+			// Restart the tests if they're blocking
+			if ( config.blocking ) {
+				QUnit.start();
+			}
+		}
+	},
+
+	after: function() {
+		checkPollution();
+	},
+
+	queueHook: function( hook, hookName ) {
+		var promise,
+			test = this;
+		return function runHook() {
+			config.current = test;
+			if ( config.notrycatch ) {
+				promise = hook.call( test.testEnvironment, test.assert );
+				test.resolvePromise( promise, hookName );
+				return;
+			}
+			try {
+				promise = hook.call( test.testEnvironment, test.assert );
+				test.resolvePromise( promise, hookName );
+			} catch ( error ) {
+				test.pushFailure( hookName + " failed on " + test.testName + ": " +
+					( error.message || error ), extractStacktrace( error, 0 ) );
+			}
+		};
+	},
+
+	// Currently only used for module level hooks, can be used to add global level ones
+	hooks: function( handler ) {
+		var hooks = [];
+
+		// Hooks are ignored on skipped tests
+		if ( this.skip ) {
+			return hooks;
+		}
+
+		if ( this.module.testEnvironment &&
+				QUnit.objectType( this.module.testEnvironment[ handler ] ) === "function" ) {
+			hooks.push( this.queueHook( this.module.testEnvironment[ handler ], handler ) );
+		}
+
+		return hooks;
+	},
+
+	finish: function() {
+		config.current = this;
+		if ( config.requireExpects && this.expected === null ) {
+			this.pushFailure( "Expected number of assertions to be defined, but expect() was " +
+				"not called.", this.stack );
+		} else if ( this.expected !== null && this.expected !== this.assertions.length ) {
+			this.pushFailure( "Expected " + this.expected + " assertions, but " +
+				this.assertions.length + " were run", this.stack );
+		} else if ( this.expected === null && !this.assertions.length ) {
+			this.pushFailure( "Expected at least one assertion, but none were run - call " +
+				"expect(0) to accept zero assertions.", this.stack );
+		}
+
+		var i,
+			bad = 0;
+
+		this.runtime = now() - this.started;
+		config.stats.all += this.assertions.length;
+		config.moduleStats.all += this.assertions.length;
+
+		for ( i = 0; i < this.assertions.length; i++ ) {
+			if ( !this.assertions[ i ].result ) {
+				bad++;
+				config.stats.bad++;
+				config.moduleStats.bad++;
+			}
+		}
+
+		runLoggingCallbacks( "testDone", {
+			name: this.testName,
+			module: this.module.name,
+			skipped: !!this.skip,
+			failed: bad,
+			passed: this.assertions.length - bad,
+			total: this.assertions.length,
+			runtime: this.runtime,
+
+			// HTML Reporter use
+			assertions: this.assertions,
+			testId: this.testId,
+
+			// DEPRECATED: this property will be removed in 2.0.0, use runtime instead
+			duration: this.runtime
+		});
+
+		// QUnit.reset() is deprecated and will be replaced for a new
+		// fixture reset function on QUnit 2.0/2.1.
+		// It's still called here for backwards compatibility handling
+		QUnit.reset();
+
+		config.current = undefined;
+	},
+
+	queue: function() {
+		var bad,
+			test = this;
+
+		if ( !this.valid() ) {
+			return;
+		}
+
+		function run() {
+
+			// each of these can by async
+			synchronize([
+				function() {
+					test.before();
+				},
+
+				test.hooks( "beforeEach" ),
+
+				function() {
+					test.run();
+				},
+
+				test.hooks( "afterEach" ).reverse(),
+
+				function() {
+					test.after();
+				},
+				function() {
+					test.finish();
+				}
+			]);
+		}
+
+		// `bad` initialized at top of scope
+		// defer when previous test run passed, if storage is available
+		bad = QUnit.config.reorder && defined.sessionStorage &&
+				+sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName );
+
+		if ( bad ) {
+			run();
+		} else {
+			synchronize( run, true );
+		}
+	},
+
+	push: function( result, actual, expected, message ) {
+		var source,
+			details = {
+				module: this.module.name,
+				name: this.testName,
+				result: result,
+				message: message,
+				actual: actual,
+				expected: expected,
+				testId: this.testId,
+				runtime: now() - this.started
+			};
+
+		if ( !result ) {
+			source = sourceFromStacktrace();
+
+			if ( source ) {
+				details.source = source;
+			}
+		}
+
+		runLoggingCallbacks( "log", details );
+
+		this.assertions.push({
+			result: !!result,
+			message: message
+		});
+	},
+
+	pushFailure: function( message, source, actual ) {
+		if ( !this instanceof Test ) {
+			throw new Error( "pushFailure() assertion outside test context, was " +
+				sourceFromStacktrace( 2 ) );
+		}
+
+		var details = {
+				module: this.module.name,
+				name: this.testName,
+				result: false,
+				message: message || "error",
+				actual: actual || null,
+				testId: this.testId,
+				runtime: now() - this.started
+			};
+
+		if ( source ) {
+			details.source = source;
+		}
+
+		runLoggingCallbacks( "log", details );
+
+		this.assertions.push({
+			result: false,
+			message: message
+		});
+	},
+
+	resolvePromise: function( promise, phase ) {
+		var then, message,
+			test = this;
+		if ( promise != null ) {
+			then = promise.then;
+			if ( QUnit.objectType( then ) === "function" ) {
+				QUnit.stop();
+				then.call(
+					promise,
+					QUnit.start,
+					function( error ) {
+						message = "Promise rejected " +
+							( !phase ? "during" : phase.replace( /Each$/, "" ) ) +
+							" " + test.testName + ": " + ( error.message || error );
+						test.pushFailure( message, extractStacktrace( error, 0 ) );
+
+						// else next test will carry the responsibility
+						saveGlobal();
+
+						// Unblock
+						QUnit.start();
+					}
+				);
+			}
+		}
+	},
+
+	valid: function() {
+		var include,
+			filter = config.filter && config.filter.toLowerCase(),
+			module = QUnit.urlParams.module && QUnit.urlParams.module.toLowerCase(),
+			fullName = ( this.module.name + ": " + this.testName ).toLowerCase();
+
+		// Internally-generated tests are always valid
+		if ( this.callback && this.callback.validTest ) {
+			return true;
+		}
+
+		if ( config.testId.length > 0 && inArray( this.testId, config.testId ) < 0 ) {
+			return false;
+		}
+
+		if ( module && ( !this.module.name || this.module.name.toLowerCase() !== module ) ) {
+			return false;
+		}
+
+		if ( !filter ) {
+			return true;
+		}
+
+		include = filter.charAt( 0 ) !== "!";
+		if ( !include ) {
+			filter = filter.slice( 1 );
+		}
+
+		// If the filter matches, we need to honour include
+		if ( fullName.indexOf( filter ) !== -1 ) {
+			return include;
+		}
+
+		// Otherwise, do the opposite
+		return !include;
+	}
+
+};
+
+// Resets the test setup. Useful for tests that modify the DOM.
+/*
+DEPRECATED: Use multiple tests instead of resetting inside a test.
+Use testStart or testDone for custom cleanup.
+This method will throw an error in 2.0, and will be removed in 2.1
+*/
+QUnit.reset = function() {
+
+	// Return on non-browser environments
+	// This is necessary to not break on node tests
+	if ( typeof window === "undefined" ) {
+		return;
+	}
+
+	var fixture = defined.document && document.getElementById &&
+			document.getElementById( "qunit-fixture" );
+
+	if ( fixture ) {
+		fixture.innerHTML = config.fixture;
+	}
+};
+
+QUnit.pushFailure = function() {
+	if ( !QUnit.config.current ) {
+		throw new Error( "pushFailure() assertion outside test context, in " +
+			sourceFromStacktrace( 2 ) );
+	}
+
+	// Gets current test obj
+	var currentTest = QUnit.config.current;
+
+	return currentTest.pushFailure.apply( currentTest, arguments );
+};
+
+// Based on Java's String.hashCode, a simple but not
+// rigorously collision resistant hashing function
+function generateHash( module, testName ) {
+	var hex,
+		i = 0,
+		hash = 0,
+		str = module + "\x1C" + testName,
+		len = str.length;
+
+	for ( ; i < len; i++ ) {
+		hash  = ( ( hash << 5 ) - hash ) + str.charCodeAt( i );
+		hash |= 0;
+	}
+
+	// Convert the possibly negative integer hash code into an 8 character hex string, which isn't
+	// strictly necessary but increases user understanding that the id is a SHA-like hash
+	hex = ( 0x100000000 + hash ).toString( 16 );
+	if ( hex.length < 8 ) {
+		hex = "0000000" + hex;
+	}
+
+	return hex.slice( -8 );
 }
 
+function Assert( testContext ) {
+	this.test = testContext;
+}
+
+// Assert helpers
+QUnit.assert = Assert.prototype = {
+
+	// Specify the number of expected assertions to guarantee that failed test
+	// (no assertions are run at all) don't slip through.
+	expect: function( asserts ) {
+		if ( arguments.length === 1 ) {
+			this.test.expected = asserts;
+		} else {
+			return this.test.expected;
+		}
+	},
+
+	// Increment this Test's semaphore counter, then return a single-use function that
+	// decrements that counter a maximum of once.
+	async: function() {
+		var test = this.test,
+			popped = false;
+
+		test.semaphore += 1;
+		test.usedAsync = true;
+		pauseProcessing();
+
+		return function done() {
+			if ( !popped ) {
+				test.semaphore -= 1;
+				popped = true;
+				resumeProcessing();
+			} else {
+				test.pushFailure( "Called the callback returned from `assert.async` more than once",
+					sourceFromStacktrace( 2 ) );
+			}
+		};
+	},
+
+	// Exports test.push() to the user API
+	push: function( /* result, actual, expected, message */ ) {
+		var assert = this,
+			currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current;
+
+		// Backwards compatibility fix.
+		// Allows the direct use of global exported assertions and QUnit.assert.*
+		// Although, it's use is not recommended as it can leak assertions
+		// to other tests from async tests, because we only get a reference to the current test,
+		// not exactly the test where assertion were intended to be called.
+		if ( !currentTest ) {
+			throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) );
+		}
+
+		if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) {
+			currentTest.pushFailure( "Assertion after the final `assert.async` was resolved",
+				sourceFromStacktrace( 2 ) );
+
+			// Allow this assertion to continue running anyway...
+		}
+
+		if ( !( assert instanceof Assert ) ) {
+			assert = currentTest.assert;
+		}
+		return assert.test.push.apply( assert.test, arguments );
+	},
+
+	ok: function( result, message ) {
+		message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " +
+			QUnit.dump.parse( result ) );
+		this.push( !!result, result, true, message );
+	},
+
+	notOk: function( result, message ) {
+		message = message || ( !result ? "okay" : "failed, expected argument to be falsy, was: " +
+			QUnit.dump.parse( result ) );
+		this.push( !result, result, false, message );
+	},
+
+	equal: function( actual, expected, message ) {
+		/*jshint eqeqeq:false */
+		this.push( expected == actual, actual, expected, message );
+	},
+
+	notEqual: function( actual, expected, message ) {
+		/*jshint eqeqeq:false */
+		this.push( expected != actual, actual, expected, message );
+	},
+
+	propEqual: function( actual, expected, message ) {
+		actual = objectValues( actual );
+		expected = objectValues( expected );
+		this.push( QUnit.equiv( actual, expected ), actual, expected, message );
+	},
+
+	notPropEqual: function( actual, expected, message ) {
+		actual = objectValues( actual );
+		expected = objectValues( expected );
+		this.push( !QUnit.equiv( actual, expected ), actual, expected, message );
+	},
+
+	deepEqual: function( actual, expected, message ) {
+		this.push( QUnit.equiv( actual, expected ), actual, expected, message );
+	},
+
+	notDeepEqual: function( actual, expected, message ) {
+		this.push( !QUnit.equiv( actual, expected ), actual, expected, message );
+	},
+
+	strictEqual: function( actual, expected, message ) {
+		this.push( expected === actual, actual, expected, message );
+	},
+
+	notStrictEqual: function( actual, expected, message ) {
+		this.push( expected !== actual, actual, expected, message );
+	},
+
+	"throws": function( block, expected, message ) {
+		var actual, expectedType,
+			expectedOutput = expected,
+			ok = false,
+			currentTest = ( this instanceof Assert && this.test ) || QUnit.config.current;
+
+		// 'expected' is optional unless doing string comparison
+		if ( message == null && typeof expected === "string" ) {
+			message = expected;
+			expected = null;
+		}
+
+		currentTest.ignoreGlobalErrors = true;
+		try {
+			block.call( currentTest.testEnvironment );
+		} catch (e) {
+			actual = e;
+		}
+		currentTest.ignoreGlobalErrors = false;
+
+		if ( actual ) {
+			expectedType = QUnit.objectType( expected );
+
+			// we don't want to validate thrown error
+			if ( !expected ) {
+				ok = true;
+				expectedOutput = null;
+
+			// expected is a regexp
+			} else if ( expectedType === "regexp" ) {
+				ok = expected.test( errorString( actual ) );
+
+			// expected is a string
+			} else if ( expectedType === "string" ) {
+				ok = expected === errorString( actual );
+
+			// expected is a constructor, maybe an Error constructor
+			} else if ( expectedType === "function" && actual instanceof expected ) {
+				ok = true;
+
+			// expected is an Error object
+			} else if ( expectedType === "object" ) {
+				ok = actual instanceof expected.constructor &&
+					actual.name === expected.name &&
+					actual.message === expected.message;
+
+			// expected is a validation function which returns true if validation passed
+			} else if ( expectedType === "function" && expected.call( {}, actual ) === true ) {
+				expectedOutput = null;
+				ok = true;
+			}
+		}
+
+		currentTest.assert.push( ok, actual, expectedOutput, message );
+	}
+};
+
+// Provide an alternative to assert.throws(), for enviroments that consider throws a reserved word
+// Known to us are: Closure Compiler, Narwhal
+(function() {
+	/*jshint sub:true */
+	Assert.prototype.raises = Assert.prototype[ "throws" ];
+}());
+
 // Test for equality any JavaScript type.
-// Discussions and reference: http://philrathe.com/articles/equiv
-// Test suites: http://philrathe.com/tests/equiv
 // Author: Philippe Rathé <prathe@gmail.com>
-QUnit.equiv = function () {
-
-    var innerEquiv; // the real equiv function
-    var callers = []; // stack to decide between skip/abort functions
-    var parents = []; // stack to avoiding loops from circular referencing
-
-
-    // Determine what is o.
-    function hoozit(o) {
-        if (QUnit.is("String", o)) {
-            return "string";
-            
-        } else if (QUnit.is("Boolean", o)) {
-            return "boolean";
-
-        } else if (QUnit.is("Number", o)) {
-
-            if (isNaN(o)) {
-                return "nan";
+QUnit.equiv = (function() {
+
+	// Call the o related callback with the given arguments.
+	function bindCallbacks( o, callbacks, args ) {
+		var prop = QUnit.objectType( o );
+		if ( prop ) {
+			if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
+				return callbacks[ prop ].apply( callbacks, args );
+			} else {
+				return callbacks[ prop ]; // or undefined
+			}
+		}
+	}
+
+	// the real equiv function
+	var innerEquiv,
+
+		// stack to decide between skip/abort functions
+		callers = [],
+
+		// stack to avoiding loops from circular referencing
+		parents = [],
+		parentsB = [],
+
+		getProto = Object.getPrototypeOf || function( obj ) {
+			/* jshint camelcase: false, proto: true */
+			return obj.__proto__;
+		},
+		callbacks = (function() {
+
+			// for string, boolean, number and null
+			function useStrictEquality( b, a ) {
+
+				/*jshint eqeqeq:false */
+				if ( b instanceof a.constructor || a instanceof b.constructor ) {
+
+					// to catch short annotation VS 'new' annotation of a
+					// declaration
+					// e.g. var i = 1;
+					// var j = new Number(1);
+					return a == b;
+				} else {
+					return a === b;
+				}
+			}
+
+			return {
+				"string": useStrictEquality,
+				"boolean": useStrictEquality,
+				"number": useStrictEquality,
+				"null": useStrictEquality,
+				"undefined": useStrictEquality,
+
+				"nan": function( b ) {
+					return isNaN( b );
+				},
+
+				"date": function( b, a ) {
+					return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
+				},
+
+				"regexp": function( b, a ) {
+					return QUnit.objectType( b ) === "regexp" &&
+
+						// the regex itself
+						a.source === b.source &&
+
+						// and its modifiers
+						a.global === b.global &&
+
+						// (gmi) ...
+						a.ignoreCase === b.ignoreCase &&
+						a.multiline === b.multiline &&
+						a.sticky === b.sticky;
+				},
+
+				// - skip when the property is a method of an instance (OOP)
+				// - abort otherwise,
+				// initial === would have catch identical references anyway
+				"function": function() {
+					var caller = callers[ callers.length - 1 ];
+					return caller !== Object && typeof caller !== "undefined";
+				},
+
+				"array": function( b, a ) {
+					var i, j, len, loop, aCircular, bCircular;
+
+					// b could be an object literal here
+					if ( QUnit.objectType( b ) !== "array" ) {
+						return false;
+					}
+
+					len = a.length;
+					if ( len !== b.length ) {
+						// safe and faster
+						return false;
+					}
+
+					// track reference to avoid circular references
+					parents.push( a );
+					parentsB.push( b );
+					for ( i = 0; i < len; i++ ) {
+						loop = false;
+						for ( j = 0; j < parents.length; j++ ) {
+							aCircular = parents[ j ] === a[ i ];
+							bCircular = parentsB[ j ] === b[ i ];
+							if ( aCircular || bCircular ) {
+								if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
+									loop = true;
+								} else {
+									parents.pop();
+									parentsB.pop();
+									return false;
+								}
+							}
+						}
+						if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
+							parents.pop();
+							parentsB.pop();
+							return false;
+						}
+					}
+					parents.pop();
+					parentsB.pop();
+					return true;
+				},
+
+				"object": function( b, a ) {
+
+					/*jshint forin:false */
+					var i, j, loop, aCircular, bCircular,
+						// Default to true
+						eq = true,
+						aProperties = [],
+						bProperties = [];
+
+					// comparing constructors is more strict than using
+					// instanceof
+					if ( a.constructor !== b.constructor ) {
+
+						// Allow objects with no prototype to be equivalent to
+						// objects with Object as their constructor.
+						if ( !( ( getProto( a ) === null && getProto( b ) === Object.prototype ) ||
+							( getProto( b ) === null && getProto( a ) === Object.prototype ) ) ) {
+							return false;
+						}
+					}
+
+					// stack constructor before traversing properties
+					callers.push( a.constructor );
+
+					// track reference to avoid circular references
+					parents.push( a );
+					parentsB.push( b );
+
+					// be strict: don't ensure hasOwnProperty and go deep
+					for ( i in a ) {
+						loop = false;
+						for ( j = 0; j < parents.length; j++ ) {
+							aCircular = parents[ j ] === a[ i ];
+							bCircular = parentsB[ j ] === b[ i ];
+							if ( aCircular || bCircular ) {
+								if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
+									loop = true;
+								} else {
+									eq = false;
+									break;
+								}
+							}
+						}
+						aProperties.push( i );
+						if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
+							eq = false;
+							break;
+						}
+					}
+
+					parents.pop();
+					parentsB.pop();
+					callers.pop(); // unstack, we are done
+
+					for ( i in b ) {
+						bProperties.push( i ); // collect b's properties
+					}
+
+					// Ensures identical properties name
+					return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
+				}
+			};
+		}());
+
+	innerEquiv = function() { // can take multiple arguments
+		var args = [].slice.apply( arguments );
+		if ( args.length < 2 ) {
+			return true; // end transition
+		}
+
+		return ( (function( a, b ) {
+			if ( a === b ) {
+				return true; // catch the most you can
+			} else if ( a === null || b === null || typeof a === "undefined" ||
+					typeof b === "undefined" ||
+					QUnit.objectType( a ) !== QUnit.objectType( b ) ) {
+
+				// don't lose time with error prone cases
+				return false;
+			} else {
+				return bindCallbacks( a, callbacks, [ b, a ] );
+			}
+
+			// apply transition with (1..n) arguments
+		}( args[ 0 ], args[ 1 ] ) ) &&
+			innerEquiv.apply( this, args.splice( 1, args.length - 1 ) ) );
+	};
+
+	return innerEquiv;
+}());
+
+// Based on jsDump by Ariel Flesler
+// http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
+QUnit.dump = (function() {
+	function quote( str ) {
+		return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\"";
+	}
+	function literal( o ) {
+		return o + "";
+	}
+	function join( pre, arr, post ) {
+		var s = dump.separator(),
+			base = dump.indent(),
+			inner = dump.indent( 1 );
+		if ( arr.join ) {
+			arr = arr.join( "," + s + inner );
+		}
+		if ( !arr ) {
+			return pre + post;
+		}
+		return [ pre, inner + arr, base + post ].join( s );
+	}
+	function array( arr, stack ) {
+		var i = arr.length,
+			ret = new Array( i );
+
+		if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
+			return "[object Array]";
+		}
+
+		this.up();
+		while ( i-- ) {
+			ret[ i ] = this.parse( arr[ i ], undefined, stack );
+		}
+		this.down();
+		return join( "[", ret, "]" );
+	}
+
+	var reName = /^function (\w+)/,
+		dump = {
+
+			// objType is used mostly internally, you can fix a (custom) type in advance
+			parse: function( obj, objType, stack ) {
+				stack = stack || [];
+				var res, parser, parserType,
+					inStack = inArray( obj, stack );
+
+				if ( inStack !== -1 ) {
+					return "recursion(" + ( inStack - stack.length ) + ")";
+				}
+
+				objType = objType || this.typeOf( obj  );
+				parser = this.parsers[ objType ];
+				parserType = typeof parser;
+
+				if ( parserType === "function" ) {
+					stack.push( obj );
+					res = parser.call( this, obj, stack );
+					stack.pop();
+					return res;
+				}
+				return ( parserType === "string" ) ? parser : this.parsers.error;
+			},
+			typeOf: function( obj ) {
+				var type;
+				if ( obj === null ) {
+					type = "null";
+				} else if ( typeof obj === "undefined" ) {
+					type = "undefined";
+				} else if ( QUnit.is( "regexp", obj ) ) {
+					type = "regexp";
+				} else if ( QUnit.is( "date", obj ) ) {
+					type = "date";
+				} else if ( QUnit.is( "function", obj ) ) {
+					type = "function";
+				} else if ( obj.setInterval !== undefined &&
+						obj.document !== undefined &&
+						obj.nodeType === undefined ) {
+					type = "window";
+				} else if ( obj.nodeType === 9 ) {
+					type = "document";
+				} else if ( obj.nodeType ) {
+					type = "node";
+				} else if (
+
+					// native arrays
+					toString.call( obj ) === "[object Array]" ||
+
+					// NodeList objects
+					( typeof obj.length === "number" && obj.item !== undefined &&
+					( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null &&
+					obj[ 0 ] === undefined ) ) )
+				) {
+					type = "array";
+				} else if ( obj.constructor === Error.prototype.constructor ) {
+					type = "error";
+				} else {
+					type = typeof obj;
+				}
+				return type;
+			},
+			separator: function() {
+				return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&#160;" : " ";
+			},
+			// extra can be a number, shortcut for increasing-calling-decreasing
+			indent: function( extra ) {
+				if ( !this.multiline ) {
+					return "";
+				}
+				var chr = this.indentChar;
+				if ( this.HTML ) {
+					chr = chr.replace( /\t/g, "   " ).replace( / /g, "&#160;" );
+				}
+				return new Array( this.depth + ( extra || 0 ) ).join( chr );
+			},
+			up: function( a ) {
+				this.depth += a || 1;
+			},
+			down: function( a ) {
+				this.depth -= a || 1;
+			},
+			setParser: function( name, parser ) {
+				this.parsers[ name ] = parser;
+			},
+			// The next 3 are exposed so you can use them
+			quote: quote,
+			literal: literal,
+			join: join,
+			//
+			depth: 1,
+			maxDepth: QUnit.config.maxDepth,
+
+			// This is the list of parsers, to modify them, use dump.setParser
+			parsers: {
+				window: "[Window]",
+				document: "[Document]",
+				error: function( error ) {
+					return "Error(\"" + error.message + "\")";
+				},
+				unknown: "[Unknown]",
+				"null": "null",
+				"undefined": "undefined",
+				"function": function( fn ) {
+					var ret = "function",
+
+						// functions never have name in IE
+						name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ];
+
+					if ( name ) {
+						ret += " " + name;
+					}
+					ret += "( ";
+
+					ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" );
+					return join( ret, dump.parse( fn, "functionCode" ), "}" );
+				},
+				array: array,
+				nodelist: array,
+				"arguments": array,
+				object: function( map, stack ) {
+					var keys, key, val, i, nonEnumerableProperties,
+						ret = [];
+
+					if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
+						return "[object Object]";
+					}
+
+					dump.up();
+					keys = [];
+					for ( key in map ) {
+						keys.push( key );
+					}
+
+					// Some properties are not always enumerable on Error objects.
+					nonEnumerableProperties = [ "message", "name" ];
+					for ( i in nonEnumerableProperties ) {
+						key = nonEnumerableProperties[ i ];
+						if ( key in map && inArray( key, keys ) < 0 ) {
+							keys.push( key );
+						}
+					}
+					keys.sort();
+					for ( i = 0; i < keys.length; i++ ) {
+						key = keys[ i ];
+						val = map[ key ];
+						ret.push( dump.parse( key, "key" ) + ": " +
+							dump.parse( val, undefined, stack ) );
+					}
+					dump.down();
+					return join( "{", ret, "}" );
+				},
+				node: function( node ) {
+					var len, i, val,
+						open = dump.HTML ? "&lt;" : "<",
+						close = dump.HTML ? "&gt;" : ">",
+						tag = node.nodeName.toLowerCase(),
+						ret = open + tag,
+						attrs = node.attributes;
+
+					if ( attrs ) {
+						for ( i = 0, len = attrs.length; i < len; i++ ) {
+							val = attrs[ i ].nodeValue;
+
+							// IE6 includes all attributes in .attributes, even ones not explicitly
+							// set. Those have values like undefined, null, 0, false, "" or
+							// "inherit".
+							if ( val && val !== "inherit" ) {
+								ret += " " + attrs[ i ].nodeName + "=" +
+									dump.parse( val, "attribute" );
+							}
+						}
+					}
+					ret += close;
+
+					// Show content of TextNode or CDATASection
+					if ( node.nodeType === 3 || node.nodeType === 4 ) {
+						ret += node.nodeValue;
+					}
+
+					return ret + open + "/" + tag + close;
+				},
+
+				// function calls it internally, it's the arguments part of the function
+				functionArgs: function( fn ) {
+					var args,
+						l = fn.length;
+
+					if ( !l ) {
+						return "";
+					}
+
+					args = new Array( l );
+					while ( l-- ) {
+
+						// 97 is 'a'
+						args[ l ] = String.fromCharCode( 97 + l );
+					}
+					return " " + args.join( ", " ) + " ";
+				},
+				// object calls it internally, the key part of an item in a map
+				key: quote,
+				// function calls it internally, it's the content of the function
+				functionCode: "[code]",
+				// node calls it internally, it's an html attribute value
+				attribute: quote,
+				string: quote,
+				date: quote,
+				regexp: literal,
+				number: literal,
+				"boolean": literal
+			},
+			// if true, entities are escaped ( <, >, \t, space and \n )
+			HTML: false,
+			// indentation unit
+			indentChar: "  ",
+			// if true, items in a collection, are separated by a \n, else just a space.
+			multiline: true
+		};
+
+	return dump;
+}());
+
+// back compat
+QUnit.jsDump = QUnit.dump;
+
+// For browser, export only select globals
+if ( typeof window !== "undefined" ) {
+
+	// Deprecated
+	// Extend assert methods to QUnit and Global scope through Backwards compatibility
+	(function() {
+		var i,
+			assertions = Assert.prototype;
+
+		function applyCurrent( current ) {
+			return function() {
+				var assert = new Assert( QUnit.config.current );
+				current.apply( assert, arguments );
+			};
+		}
+
+		for ( i in assertions ) {
+			QUnit[ i ] = applyCurrent( assertions[ i ] );
+		}
+	})();
+
+	(function() {
+		var i, l,
+			keys = [
+				"test",
+				"module",
+				"expect",
+				"asyncTest",
+				"start",
+				"stop",
+				"ok",
+				"notOk",
+				"equal",
+				"notEqual",
+				"propEqual",
+				"notPropEqual",
+				"deepEqual",
+				"notDeepEqual",
+				"strictEqual",
+				"notStrictEqual",
+				"throws"
+			];
+
+		for ( i = 0, l = keys.length; i < l; i++ ) {
+			window[ keys[ i ] ] = QUnit[ keys[ i ] ];
+		}
+	})();
+
+	window.QUnit = QUnit;
+}
+
+// For nodejs
+if ( typeof module !== "undefined" && module && module.exports ) {
+	module.exports = QUnit;
+
+	// For consistency with CommonJS environments' exports
+	module.exports.QUnit = QUnit;
+}
+
+// For CommonJS with exports, but without module.exports, like Rhino
+if ( typeof exports !== "undefined" && exports ) {
+	exports.QUnit = QUnit;
+}
+
+if ( typeof define === "function" && define.amd ) {
+	define( function() {
+		return QUnit;
+	} );
+	QUnit.config.autostart = false;
+}
+
+// Get a reference to the global object, like window in browsers
+}( (function() {
+	return this;
+})() ));
+
+/*istanbul ignore next */
+// jscs:disable maximumLineLength
+/*
+ * This file is a modified version of google-diff-match-patch's JavaScript implementation
+ * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js),
+ * modifications are licensed as more fully set forth in LICENSE.txt.
+ *
+ * The original source of google-diff-match-patch is attributable and licensed as follows:
+ *
+ * Copyright 2006 Google Inc.
+ * http://code.google.com/p/google-diff-match-patch/
+ *
+ * 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.
+ *
+ * More Info:
+ *  https://code.google.com/p/google-diff-match-patch/
+ *
+ * Usage: QUnit.diff(expected, actual)
+ *
+ * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) === "the  quick <del>brown </del> fox jump<ins>s</ins><del>ed</del over"
+ */
+QUnit.diff = (function() {
+
+    function DiffMatchPatch() {
+
+        // Defaults.
+        // Redefine these in your program to override the defaults.
+
+        // Number of seconds to map a diff before giving up (0 for infinity).
+        this.DiffTimeout = 1.0;
+        // Cost of an empty edit operation in terms of edit characters.
+        this.DiffEditCost = 4;
+    }
+
+    //  DIFF FUNCTIONS
+
+    /**
+     * The data structure representing a diff is an array of tuples:
+     * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
+     * which means: delete 'Hello', add 'Goodbye' and keep ' world.'
+     */
+    var DIFF_DELETE = -1,
+		DIFF_INSERT = 1,
+		DIFF_EQUAL = 0;
+
+    /**
+     * Find the differences between two texts.  Simplifies the problem by stripping
+     * any common prefix or suffix off the texts before diffing.
+     * @param {string} text1 Old string to be diffed.
+     * @param {string} text2 New string to be diffed.
+     * @param {boolean=} optChecklines Optional speedup flag. If present and false,
+     *     then don't run a line-level diff first to identify the changed areas.
+     *     Defaults to true, which does a faster, slightly less optimal diff.
+     * @param {number} optDeadline Optional time when the diff should be complete
+     *     by.  Used internally for recursive calls.  Users should set DiffTimeout
+     *     instead.
+     * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+     */
+    DiffMatchPatch.prototype.DiffMain = function( text1, text2, optChecklines, optDeadline ) {
+        var deadline, checklines, commonlength,
+			commonprefix, commonsuffix, diffs;
+        // Set a deadline by which time the diff must be complete.
+        if ( typeof optDeadline === "undefined" ) {
+            if ( this.DiffTimeout <= 0 ) {
+                optDeadline = Number.MAX_VALUE;
             } else {
-                return "number";
-            }
-
-        } else if (typeof o === "undefined") {
-            return "undefined";
-
-        // consider: typeof null === object
-        } else if (o === null) {
-            return "null";
-
-        // consider: typeof [] === object
-        } else if (QUnit.is( "Array", o)) {
-            return "array";
-        
-        // consider: typeof new Date() === object
-        } else if (QUnit.is( "Date", o)) {
-            return "date";
-
-        // consider: /./ instanceof Object;
-        //           /./ instanceof RegExp;
-        //          typeof /./ === "function"; // => false in IE and Opera,
-        //                                          true in FF and Safari
-        } else if (QUnit.is( "RegExp", o)) {
-            return "regexp";
-
-        } else if (typeof o === "object") {
-            return "object";
-
-        } else if (QUnit.is( "Function", o)) {
-            return "function";
-        } else {
-            return undefined;
-        }
-    }
-
-    // Call the o related callback with the given arguments.
-    function bindCallbacks(o, callbacks, args) {
-        var prop = hoozit(o);
-        if (prop) {
-            if (hoozit(callbacks[prop]) === "function") {
-                return callbacks[prop].apply(callbacks, args);
-            } else {
-                return callbacks[prop]; // or undefined
+                optDeadline = ( new Date() ).getTime() + this.DiffTimeout * 1000;
             }
         }
-    }
-    
-    var callbacks = function () {
-
-        // for string, boolean, number and null
-        function useStrictEquality(b, a) {
-            if (b instanceof a.constructor || a instanceof b.constructor) {
-                // to catch short annotaion VS 'new' annotation of a declaration
-                // e.g. var i = 1;
-                //      var j = new Number(1);
-                return a == b;
-            } else {
-                return a === b;
+        deadline = optDeadline;
+
+        // Check for null inputs.
+        if ( text1 === null || text2 === null ) {
+            throw new Error( "Null input. (DiffMain)" );
+        }
+
+        // Check for equality (speedup).
+        if ( text1 === text2 ) {
+            if ( text1 ) {
+                return [
+                    [ DIFF_EQUAL, text1 ]
+                ];
+            }
+            return [];
+        }
+
+        if ( typeof optChecklines === "undefined" ) {
+            optChecklines = true;
+        }
+
+        checklines = optChecklines;
+
+        // Trim off common prefix (speedup).
+        commonlength = this.diffCommonPrefix( text1, text2 );
+        commonprefix = text1.substring( 0, commonlength );
+        text1 = text1.substring( commonlength );
+        text2 = text2.substring( commonlength );
+
+        // Trim off common suffix (speedup).
+        /////////
+        commonlength = this.diffCommonSuffix( text1, text2 );
+        commonsuffix = text1.substring( text1.length - commonlength );
+        text1 = text1.substring( 0, text1.length - commonlength );
+        text2 = text2.substring( 0, text2.length - commonlength );
+
+        // Compute the diff on the middle block.
+        diffs = this.diffCompute( text1, text2, checklines, deadline );
+
+        // Restore the prefix and suffix.
+        if ( commonprefix ) {
+            diffs.unshift( [ DIFF_EQUAL, commonprefix ] );
+        }
+        if ( commonsuffix ) {
+            diffs.push( [ DIFF_EQUAL, commonsuffix ] );
+        }
+        this.diffCleanupMerge( diffs );
+        return diffs;
+    };
+
+    /**
+     * Reduce the number of edits by eliminating operationally trivial equalities.
+     * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+     */
+    DiffMatchPatch.prototype.diffCleanupEfficiency = function( diffs ) {
+        var changes, equalities, equalitiesLength, lastequality,
+			pointer, preIns, preDel, postIns, postDel;
+        changes = false;
+        equalities = []; // Stack of indices where equalities are found.
+        equalitiesLength = 0; // Keeping our own length var is faster in JS.
+        /** @type {?string} */
+        lastequality = null;
+        // Always equal to diffs[equalities[equalitiesLength - 1]][1]
+        pointer = 0; // Index of current position.
+        // Is there an insertion operation before the last equality.
+        preIns = false;
+        // Is there a deletion operation before the last equality.
+        preDel = false;
+        // Is there an insertion operation after the last equality.
+        postIns = false;
+        // Is there a deletion operation after the last equality.
+        postDel = false;
+        while ( pointer < diffs.length ) {
+            if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { // Equality found.
+                if ( diffs[ pointer ][ 1 ].length < this.DiffEditCost && ( postIns || postDel ) ) {
+                    // Candidate found.
+                    equalities[ equalitiesLength++ ] = pointer;
+                    preIns = postIns;
+                    preDel = postDel;
+                    lastequality = diffs[ pointer ][ 1 ];
+                } else {
+                    // Not a candidate, and can never become one.
+                    equalitiesLength = 0;
+                    lastequality = null;
+                }
+                postIns = postDel = false;
+            } else { // An insertion or deletion.
+                if ( diffs[ pointer ][ 0 ] === DIFF_DELETE ) {
+                    postDel = true;
+                } else {
+                    postIns = true;
+                }
+                /*
+                 * Five types to be split:
+                 * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
+                 * <ins>A</ins>X<ins>C</ins><del>D</del>
+                 * <ins>A</ins><del>B</del>X<ins>C</ins>
+                 * <ins>A</del>X<ins>C</ins><del>D</del>
+                 * <ins>A</ins><del>B</del>X<del>C</del>
+                 */
+                if ( lastequality && ( ( preIns && preDel && postIns && postDel ) ||
+                        ( ( lastequality.length < this.DiffEditCost / 2 ) &&
+                            ( preIns + preDel + postIns + postDel ) === 3 ) ) ) {
+                    // Duplicate record.
+                    diffs.splice( equalities[equalitiesLength - 1], 0, [ DIFF_DELETE, lastequality ] );
+                    // Change second copy to insert.
+                    diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT;
+                    equalitiesLength--; // Throw away the equality we just deleted;
+                    lastequality = null;
+                    if (preIns && preDel) {
+                        // No changes made which could affect previous entry, keep going.
+                        postIns = postDel = true;
+                        equalitiesLength = 0;
+                    } else {
+                        equalitiesLength--; // Throw away the previous equality.
+                        pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1;
+                        postIns = postDel = false;
+                    }
+                    changes = true;
+                }
+            }
+            pointer++;
+        }
+
+        if ( changes ) {
+            this.diffCleanupMerge( diffs );
+        }
+    };
+
+    /**
+     * Convert a diff array into a pretty HTML report.
+     * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+     * @param {integer} string to be beautified.
+     * @return {string} HTML representation.
+     */
+    DiffMatchPatch.prototype.diffPrettyHtml = function( diffs ) {
+        var op, data, x, html = [];
+        for ( x = 0; x < diffs.length; x++ ) {
+            op = diffs[x][0]; // Operation (insert, delete, equal)
+            data = diffs[x][1]; // Text of change.
+            switch ( op ) {
+                case DIFF_INSERT:
+                    html[x] = "<ins>" + data + "</ins>";
+                    break;
+                case DIFF_DELETE:
+                    html[x] = "<del>" + data + "</del>";
+                    break;
+                case DIFF_EQUAL:
+                    html[x] = "<span>" + data + "</span>";
+                    break;
             }
         }
-
-        return {
-            "string": useStrictEquality,
-            "boolean": useStrictEquality,
-            "number": useStrictEquality,
-            "null": useStrictEquality,
-            "undefined": useStrictEquality,
-
-            "nan": function (b) {
-                return isNaN(b);
-            },
-
-            "date": function (b, a) {
-                return hoozit(b) === "date" && a.valueOf() === b.valueOf();
-            },
-
-            "regexp": function (b, a) {
-                return hoozit(b) === "regexp" &&
-                    a.source === b.source && // the regex itself
-                    a.global === b.global && // and its modifers (gmi) ...
-                    a.ignoreCase === b.ignoreCase &&
-                    a.multiline === b.multiline;
-            },
-
-            // - skip when the property is a method of an instance (OOP)
-            // - abort otherwise,
-            //   initial === would have catch identical references anyway
-            "function": function () {
-                var caller = callers[callers.length - 1];
-                return caller !== Object &&
-                        typeof caller !== "undefined";
-            },
-
-            "array": function (b, a) {
-                var i, j, loop;
-                var len;
-
-                // b could be an object literal here
-                if ( ! (hoozit(b) === "array")) {
-                    return false;
-                }   
-                
-                len = a.length;
-                if (len !== b.length) { // safe and faster
-                    return false;
+        return html.join("");
+    };
+
+    /**
+     * Determine the common prefix of two strings.
+     * @param {string} text1 First string.
+     * @param {string} text2 Second string.
+     * @return {number} The number of characters common to the start of each
+     *     string.
+     */
+    DiffMatchPatch.prototype.diffCommonPrefix = function( text1, text2 ) {
+        var pointermid, pointermax, pointermin, pointerstart;
+        // Quick check for common null cases.
+        if ( !text1 || !text2 || text1.charAt(0) !== text2.charAt(0) ) {
+            return 0;
+        }
+        // Binary search.
+        // Performance analysis: http://neil.fraser.name/news/2007/10/09/
+        pointermin = 0;
+        pointermax = Math.min( text1.length, text2.length );
+        pointermid = pointermax;
+        pointerstart = 0;
+        while ( pointermin < pointermid ) {
+            if ( text1.substring( pointerstart, pointermid ) === text2.substring( pointerstart, pointermid ) ) {
+                pointermin = pointermid;
+                pointerstart = pointermin;
+            } else {
+                pointermax = pointermid;
+            }
+            pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin );
+        }
+        return pointermid;
+    };
+
+    /**
+     * Determine the common suffix of two strings.
+     * @param {string} text1 First string.
+     * @param {string} text2 Second string.
+     * @return {number} The number of characters common to the end of each string.
+     */
+    DiffMatchPatch.prototype.diffCommonSuffix = function( text1, text2 ) {
+        var pointermid, pointermax, pointermin, pointerend;
+        // Quick check for common null cases.
+        if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) {
+            return 0;
+        }
+        // Binary search.
+        // Performance analysis: http://neil.fraser.name/news/2007/10/09/
+        pointermin = 0;
+        pointermax = Math.min(text1.length, text2.length);
+        pointermid = pointermax;
+        pointerend = 0;
+        while ( pointermin < pointermid ) {
+            if (text1.substring( text1.length - pointermid, text1.length - pointerend ) ===
+                text2.substring( text2.length - pointermid, text2.length - pointerend ) ) {
+                pointermin = pointermid;
+                pointerend = pointermin;
+            } else {
+                pointermax = pointermid;
+            }
+            pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin );
+        }
+        return pointermid;
+    };
+
+    /**
+     * Find the differences between two texts.  Assumes that the texts do not
+     * have any common prefix or suffix.
+     * @param {string} text1 Old string to be diffed.
+     * @param {string} text2 New string to be diffed.
+     * @param {boolean} checklines Speedup flag.  If false, then don't run a
+     *     line-level diff first to identify the changed areas.
+     *     If true, then run a faster, slightly less optimal diff.
+     * @param {number} deadline Time when the diff should be complete by.
+     * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+     * @private
+     */
+    DiffMatchPatch.prototype.diffCompute = function( text1, text2, checklines, deadline ) {
+        var diffs, longtext, shorttext, i, hm,
+			text1A, text2A, text1B, text2B,
+			midCommon, diffsA, diffsB;
+
+        if ( !text1 ) {
+            // Just add some text (speedup).
+            return [
+                [ DIFF_INSERT, text2 ]
+            ];
+        }
+
+        if (!text2) {
+            // Just delete some text (speedup).
+            return [
+                [ DIFF_DELETE, text1 ]
+            ];
+        }
+
+        longtext = text1.length > text2.length ? text1 : text2;
+        shorttext = text1.length > text2.length ? text2 : text1;
+        i = longtext.indexOf( shorttext );
+        if ( i !== -1 ) {
+            // Shorter text is inside the longer text (speedup).
+            diffs = [
+                [ DIFF_INSERT, longtext.substring( 0, i ) ],
+                [ DIFF_EQUAL, shorttext ],
+                [ DIFF_INSERT, longtext.substring( i + shorttext.length ) ]
+            ];
+            // Swap insertions for deletions if diff is reversed.
+            if ( text1.length > text2.length ) {
+                diffs[0][0] = diffs[2][0] = DIFF_DELETE;
+            }
+            return diffs;
+        }
+
+        if ( shorttext.length === 1 ) {
+            // Single character string.
+            // After the previous speedup, the character can't be an equality.
+            return [
+                [ DIFF_DELETE, text1 ],
+                [ DIFF_INSERT, text2 ]
+            ];
+        }
+
+        // Check to see if the problem can be split in two.
+        hm = this.diffHalfMatch(text1, text2);
+        if (hm) {
+            // A half-match was found, sort out the return data.
+            text1A = hm[0];
+            text1B = hm[1];
+            text2A = hm[2];
+            text2B = hm[3];
+            midCommon = hm[4];
+            // Send both pairs off for separate processing.
+            diffsA = this.DiffMain(text1A, text2A, checklines, deadline);
+            diffsB = this.DiffMain(text1B, text2B, checklines, deadline);
+            // Merge the results.
+            return diffsA.concat([
+                [ DIFF_EQUAL, midCommon ]
+            ], diffsB);
+        }
+
+        if (checklines && text1.length > 100 && text2.length > 100) {
+            return this.diffLineMode(text1, text2, deadline);
+        }
+
+        return this.diffBisect(text1, text2, deadline);
+    };
+
+    /**
+     * Do the two texts share a substring which is at least half the length of the
+     * longer text?
+     * This speedup can produce non-minimal diffs.
+     * @param {string} text1 First string.
+     * @param {string} text2 Second string.
+     * @return {Array.<string>} Five element Array, containing the prefix of
+     *     text1, the suffix of text1, the prefix of text2, the suffix of
+     *     text2 and the common middle.  Or null if there was no match.
+     * @private
+     */
+    DiffMatchPatch.prototype.diffHalfMatch = function(text1, text2) {
+        var longtext, shorttext, dmp,
+			text1A, text2B, text2A, text1B, midCommon,
+			hm1, hm2, hm;
+        if (this.DiffTimeout <= 0) {
+            // Don't risk returning a non-optimal diff if we have unlimited time.
+            return null;
+        }
+        longtext = text1.length > text2.length ? text1 : text2;
+        shorttext = text1.length > text2.length ? text2 : text1;
+        if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {
+            return null; // Pointless.
+        }
+        dmp = this; // 'this' becomes 'window' in a closure.
+
+        /**
+         * Does a substring of shorttext exist within longtext such that the substring
+         * is at least half the length of longtext?
+         * Closure, but does not reference any external variables.
+         * @param {string} longtext Longer string.
+         * @param {string} shorttext Shorter string.
+         * @param {number} i Start index of quarter length substring within longtext.
+         * @return {Array.<string>} Five element Array, containing the prefix of
+         *     longtext, the suffix of longtext, the prefix of shorttext, the suffix
+         *     of shorttext and the common middle.  Or null if there was no match.
+         * @private
+         */
+        function diffHalfMatchI(longtext, shorttext, i) {
+            var seed, j, bestCommon, prefixLength, suffixLength,
+				bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB;
+            // Start with a 1/4 length substring at position i as a seed.
+            seed = longtext.substring(i, i + Math.floor(longtext.length / 4));
+            j = -1;
+            bestCommon = "";
+            while ((j = shorttext.indexOf(seed, j + 1)) !== -1) {
+                prefixLength = dmp.diffCommonPrefix(longtext.substring(i),
+                    shorttext.substring(j));
+                suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i),
+                    shorttext.substring(0, j));
+                if (bestCommon.length < suffixLength + prefixLength) {
+                    bestCommon = shorttext.substring(j - suffixLength, j) +
+                        shorttext.substring(j, j + prefixLength);
+                    bestLongtextA = longtext.substring(0, i - suffixLength);
+                    bestLongtextB = longtext.substring(i + prefixLength);
+                    bestShorttextA = shorttext.substring(0, j - suffixLength);
+                    bestShorttextB = shorttext.substring(j + prefixLength);
                 }
-                
-                //track reference to avoid circular references
-                parents.push(a);
-                for (i = 0; i < len; i++) {
-                    loop = false;
-                    for(j=0;j<parents.length;j++){
-                        if(parents[j] === a[i]){
-                            loop = true;//dont rewalk array
+            }
+            if (bestCommon.length * 2 >= longtext.length) {
+                return [ bestLongtextA, bestLongtextB,
+                    bestShorttextA, bestShorttextB, bestCommon
+                ];
+            } else {
+                return null;
+            }
+        }
+
+        // First check if the second quarter is the seed for a half-match.
+        hm1 = diffHalfMatchI(longtext, shorttext,
+            Math.ceil(longtext.length / 4));
+        // Check again based on the third quarter.
+        hm2 = diffHalfMatchI(longtext, shorttext,
+            Math.ceil(longtext.length / 2));
+        if (!hm1 && !hm2) {
+            return null;
+        } else if (!hm2) {
+            hm = hm1;
+        } else if (!hm1) {
+            hm = hm2;
+        } else {
+            // Both matched.  Select the longest.
+            hm = hm1[4].length > hm2[4].length ? hm1 : hm2;
+        }
+
+        // A half-match was found, sort out the return data.
+        text1A, text1B, text2A, text2B;
+        if (text1.length > text2.length) {
+            text1A = hm[0];
+            text1B = hm[1];
+            text2A = hm[2];
+            text2B = hm[3];
+        } else {
+            text2A = hm[0];
+            text2B = hm[1];
+            text1A = hm[2];
+            text1B = hm[3];
+        }
+        midCommon = hm[4];
+        return [ text1A, text1B, text2A, text2B, midCommon ];
+    };
+
+    /**
+     * Do a quick line-level diff on both strings, then rediff the parts for
+     * greater accuracy.
+     * This speedup can produce non-minimal diffs.
+     * @param {string} text1 Old string to be diffed.
+     * @param {string} text2 New string to be diffed.
+     * @param {number} deadline Time when the diff should be complete by.
+     * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+     * @private
+     */
+    DiffMatchPatch.prototype.diffLineMode = function(text1, text2, deadline) {
+        var a, diffs, linearray, pointer, countInsert,
+			countDelete, textInsert, textDelete, j;
+        // Scan the text on a line-by-line basis first.
+        a = this.diffLinesToChars(text1, text2);
+        text1 = a.chars1;
+        text2 = a.chars2;
+        linearray = a.lineArray;
+
+        diffs = this.DiffMain(text1, text2, false, deadline);
+
+        // Convert the diff back to original text.
+        this.diffCharsToLines(diffs, linearray);
+        // Eliminate freak matches (e.g. blank lines)
+        this.diffCleanupSemantic(diffs);
+
+        // Rediff any replacement blocks, this time character-by-character.
+        // Add a dummy entry at the end.
+        diffs.push( [ DIFF_EQUAL, "" ] );
+        pointer = 0;
+        countDelete = 0;
+        countInsert = 0;
+        textDelete = "";
+        textInsert = "";
+        while (pointer < diffs.length) {
+            switch ( diffs[pointer][0] ) {
+                case DIFF_INSERT:
+                    countInsert++;
+                    textInsert += diffs[pointer][1];
+                    break;
+                case DIFF_DELETE:
+                    countDelete++;
+                    textDelete += diffs[pointer][1];
+                    break;
+                case DIFF_EQUAL:
+                    // Upon reaching an equality, check for prior redundancies.
+                    if (countDelete >= 1 && countInsert >= 1) {
+                        // Delete the offending records and add the merged ones.
+                        diffs.splice(pointer - countDelete - countInsert,
+                            countDelete + countInsert);
+                        pointer = pointer - countDelete - countInsert;
+                        a = this.DiffMain(textDelete, textInsert, false, deadline);
+                        for (j = a.length - 1; j >= 0; j--) {
+                            diffs.splice( pointer, 0, a[j] );
                         }
+                        pointer = pointer + a.length;
                     }
-                    if (!loop && ! innerEquiv(a[i], b[i])) {
-                        parents.pop();
-                        return false;
-                    }
+                    countInsert = 0;
+                    countDelete = 0;
+                    textDelete = "";
+                    textInsert = "";
+                    break;
+            }
+            pointer++;
+        }
+        diffs.pop(); // Remove the dummy entry at the end.
+
+        return diffs;
+    };
+
+    /**
+     * Find the 'middle snake' of a diff, split the problem in two
+     * and return the recursively constructed diff.
+     * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
+     * @param {string} text1 Old string to be diffed.
+     * @param {string} text2 New string to be diffed.
+     * @param {number} deadline Time at which to bail if not yet complete.
+     * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+     * @private
+     */
+    DiffMatchPatch.prototype.diffBisect = function(text1, text2, deadline) {
+        var text1Length, text2Length, maxD, vOffset, vLength,
+			v1, v2, x, delta, front, k1start, k1end, k2start,
+			k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2;
+        // Cache the text lengths to prevent multiple calls.
+        text1Length = text1.length;
+        text2Length = text2.length;
+        maxD = Math.ceil((text1Length + text2Length) / 2);
+        vOffset = maxD;
+        vLength = 2 * maxD;
+        v1 = new Array(vLength);
+        v2 = new Array(vLength);
+        // Setting all elements to -1 is faster in Chrome & Firefox than mixing
+        // integers and undefined.
+        for (x = 0; x < vLength; x++) {
+            v1[x] = -1;
+            v2[x] = -1;
+        }
+        v1[vOffset + 1] = 0;
+        v2[vOffset + 1] = 0;
+        delta = text1Length - text2Length;
+        // If the total number of characters is odd, then the front path will collide
+        // with the reverse path.
+        front = (delta % 2 !== 0);
+        // Offsets for start and end of k loop.
+        // Prevents mapping of space beyond the grid.
+        k1start = 0;
+        k1end = 0;
+        k2start = 0;
+        k2end = 0;
+        for (d = 0; d < maxD; d++) {
+            // Bail out if deadline is reached.
+            if ((new Date()).getTime() > deadline) {
+                break;
+            }
+
+            // Walk the front path one step.
+            for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {
+                k1Offset = vOffset + k1;
+                if ( k1 === -d || ( k1 !== d && v1[ k1Offset - 1 ] < v1[ k1Offset + 1 ] ) ) {
+                    x1 = v1[k1Offset + 1];
+                } else {
+                    x1 = v1[k1Offset - 1] + 1;
                 }
-                parents.pop();
-                return true;
-            },
-
-            "object": function (b, a) {
-                var i, j, loop;
-                var eq = true; // unless we can proove it
-                var aProperties = [], bProperties = []; // collection of strings
-
-                // comparing constructors is more strict than using instanceof
-                if ( a.constructor !== b.constructor) {
-                    return false;
+                y1 = x1 - k1;
+                while (x1 < text1Length && y1 < text2Length &&
+                    text1.charAt(x1) === text2.charAt(y1)) {
+                    x1++;
+                    y1++;
                 }
-
-                // stack constructor before traversing properties
-                callers.push(a.constructor);
-                //track reference to avoid circular references
-                parents.push(a);
-                
-                for (i in a) { // be strict: don't ensures hasOwnProperty and go deep
-                    loop = false;
-                    for(j=0;j<parents.length;j++){
-                        if(parents[j] === a[i])
-                            loop = true; //don't go down the same path twice
-                    }
-                    aProperties.push(i); // collect a's properties
-
-                    if (!loop && ! innerEquiv(a[i], b[i])) {
-                        eq = false;
-                        break;
+                v1[k1Offset] = x1;
+                if (x1 > text1Length) {
+                    // Ran off the right of the graph.
+                    k1end += 2;
+                } else if (y1 > text2Length) {
+                    // Ran off the bottom of the graph.
+                    k1start += 2;
+                } else if (front) {
+                    k2Offset = vOffset + delta - k1;
+                    if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) {
+                        // Mirror x2 onto top-left coordinate system.
+                        x2 = text1Length - v2[k2Offset];
+                        if (x1 >= x2) {
+                            // Overlap detected.
+                            return this.diffBisectSplit(text1, text2, x1, y1, deadline);
+                        }
                     }
                 }
-
-                callers.pop(); // unstack, we are done
-                parents.pop();
-
-                for (i in b) {
-                    bProperties.push(i); // collect b's properties
+            }
+
+            // Walk the reverse path one step.
+            for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {
+                k2Offset = vOffset + k2;
+                if ( k2 === -d || (k2 !== d && v2[ k2Offset - 1 ] < v2[ k2Offset + 1 ] ) ) {
+                    x2 = v2[k2Offset + 1];
+                } else {
+                    x2 = v2[k2Offset - 1] + 1;
+                }
+                y2 = x2 - k2;
+                while (x2 < text1Length && y2 < text2Length &&
+                    text1.charAt(text1Length - x2 - 1) ===
+                    text2.charAt(text2Length - y2 - 1)) {
+                    x2++;
+                    y2++;
+                }
+                v2[k2Offset] = x2;
+                if (x2 > text1Length) {
+                    // Ran off the left of the graph.
+                    k2end += 2;
+                } else if (y2 > text2Length) {
+                    // Ran off the top of the graph.
+                    k2start += 2;
+                } else if (!front) {
+                    k1Offset = vOffset + delta - k2;
+                    if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) {
+                        x1 = v1[k1Offset];
+                        y1 = vOffset + x1 - k1Offset;
+                        // Mirror x2 onto top-left coordinate system.
+                        x2 = text1Length - x2;
+                        if (x1 >= x2) {
+                            // Overlap detected.
+                            return this.diffBisectSplit(text1, text2, x1, y1, deadline);
+                        }
+                    }
+                }
+            }
+        }
+        // Diff took too long and hit the deadline or
+        // number of diffs equals number of characters, no commonality at all.
+        return [
+            [ DIFF_DELETE, text1 ],
+            [ DIFF_INSERT, text2 ]
+        ];
+    };
+
+    /**
+     * Given the location of the 'middle snake', split the diff in two parts
+     * and recurse.
+     * @param {string} text1 Old string to be diffed.
+     * @param {string} text2 New string to be diffed.
+     * @param {number} x Index of split point in text1.
+     * @param {number} y Index of split point in text2.
+     * @param {number} deadline Time at which to bail if not yet complete.
+     * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+     * @private
+     */
+    DiffMatchPatch.prototype.diffBisectSplit = function( text1, text2, x, y, deadline ) {
+        var text1a, text1b, text2a, text2b, diffs, diffsb;
+        text1a = text1.substring(0, x);
+        text2a = text2.substring(0, y);
+        text1b = text1.substring(x);
+        text2b = text2.substring(y);
+
+        // Compute both diffs serially.
+        diffs = this.DiffMain(text1a, text2a, false, deadline);
+        diffsb = this.DiffMain(text1b, text2b, false, deadline);
+
+        return diffs.concat(diffsb);
+    };
+
+    /**
+     * Reduce the number of edits by eliminating semantically trivial equalities.
+     * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+     */
+    DiffMatchPatch.prototype.diffCleanupSemantic = function(diffs) {
+        var changes, equalities, equalitiesLength, lastequality,
+			pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1,
+			lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2;
+        changes = false;
+        equalities = []; // Stack of indices where equalities are found.
+        equalitiesLength = 0; // Keeping our own length var is faster in JS.
+        /** @type {?string} */
+        lastequality = null;
+        // Always equal to diffs[equalities[equalitiesLength - 1]][1]
+        pointer = 0; // Index of current position.
+        // Number of characters that changed prior to the equality.
+        lengthInsertions1 = 0;
+        lengthDeletions1 = 0;
+        // Number of characters that changed after the equality.
+        lengthInsertions2 = 0;
+        lengthDeletions2 = 0;
+        while (pointer < diffs.length) {
+            if (diffs[pointer][0] === DIFF_EQUAL) { // Equality found.
+                equalities[equalitiesLength++] = pointer;
+                lengthInsertions1 = lengthInsertions2;
+                lengthDeletions1 = lengthDeletions2;
+                lengthInsertions2 = 0;
+                lengthDeletions2 = 0;
+                lastequality = diffs[pointer][1];
+            } else { // An insertion or deletion.
+                if (diffs[pointer][0] === DIFF_INSERT) {
+                    lengthInsertions2 += diffs[pointer][1].length;
+                } else {
+                    lengthDeletions2 += diffs[pointer][1].length;
+                }
+                // Eliminate an equality that is smaller or equal to the edits on both
+                // sides of it.
+                if (lastequality && (lastequality.length <=
+                        Math.max(lengthInsertions1, lengthDeletions1)) &&
+                    (lastequality.length <= Math.max(lengthInsertions2,
+                        lengthDeletions2))) {
+                    // Duplicate record.
+                    diffs.splice( equalities[ equalitiesLength - 1 ], 0, [ DIFF_DELETE, lastequality ] );
+                    // Change second copy to insert.
+                    diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
+                    // Throw away the equality we just deleted.
+                    equalitiesLength--;
+                    // Throw away the previous equality (it needs to be reevaluated).
+                    equalitiesLength--;
+                    pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
+                    lengthInsertions1 = 0; // Reset the counters.
+                    lengthDeletions1 = 0;
+                    lengthInsertions2 = 0;
+                    lengthDeletions2 = 0;
+                    lastequality = null;
+                    changes = true;
                 }
-
-                // Ensures identical properties name
-                return eq && innerEquiv(aProperties.sort(), bProperties.sort());
+            }
+            pointer++;
+        }
+
+        // Normalize the diff.
+        if (changes) {
+            this.diffCleanupMerge(diffs);
+        }
+
+        // Find any overlaps between deletions and insertions.
+        // e.g: <del>abcxxx</del><ins>xxxdef</ins>
+        //   -> <del>abc</del>xxx<ins>def</ins>
+        // e.g: <del>xxxabc</del><ins>defxxx</ins>
+        //   -> <ins>def</ins>xxx<del>abc</del>
+        // Only extract an overlap if it is as big as the edit ahead or behind it.
+        pointer = 1;
+        while (pointer < diffs.length) {
+            if (diffs[pointer - 1][0] === DIFF_DELETE &&
+                diffs[pointer][0] === DIFF_INSERT) {
+                deletion = diffs[pointer - 1][1];
+                insertion = diffs[pointer][1];
+                overlapLength1 = this.diffCommonOverlap(deletion, insertion);
+                overlapLength2 = this.diffCommonOverlap(insertion, deletion);
+                if (overlapLength1 >= overlapLength2) {
+                    if (overlapLength1 >= deletion.length / 2 ||
+                        overlapLength1 >= insertion.length / 2) {
+                        // Overlap found.  Insert an equality and trim the surrounding edits.
+                        diffs.splice( pointer, 0, [ DIFF_EQUAL, insertion.substring( 0, overlapLength1 ) ] );
+                        diffs[pointer - 1][1] =
+                            deletion.substring(0, deletion.length - overlapLength1);
+                        diffs[pointer + 1][1] = insertion.substring(overlapLength1);
+                        pointer++;
+                    }
+                } else {
+                    if (overlapLength2 >= deletion.length / 2 ||
+                        overlapLength2 >= insertion.length / 2) {
+                        // Reverse overlap found.
+                        // Insert an equality and swap and trim the surrounding edits.
+                        diffs.splice( pointer, 0, [ DIFF_EQUAL, deletion.substring( 0, overlapLength2 ) ] );
+                        diffs[pointer - 1][0] = DIFF_INSERT;
+                        diffs[pointer - 1][1] =
+                            insertion.substring(0, insertion.length - overlapLength2);
+                        diffs[pointer + 1][0] = DIFF_DELETE;
+                        diffs[pointer + 1][1] =
+                            deletion.substring(overlapLength2);
+                        pointer++;
+                    }
+                }
+                pointer++;
             }
+            pointer++;
+        }
+    };
+
+    /**
+     * Determine if the suffix of one string is the prefix of another.
+     * @param {string} text1 First string.
+     * @param {string} text2 Second string.
+     * @return {number} The number of characters common to the end of the first
+     *     string and the start of the second string.
+     * @private
+     */
+    DiffMatchPatch.prototype.diffCommonOverlap = function(text1, text2) {
+        var text1Length, text2Length, textLength,
+			best, length, pattern, found;
+        // Cache the text lengths to prevent multiple calls.
+        text1Length = text1.length;
+        text2Length = text2.length;
+        // Eliminate the null case.
+        if (text1Length === 0 || text2Length === 0) {
+            return 0;
+        }
+        // Truncate the longer string.
+        if (text1Length > text2Length) {
+            text1 = text1.substring(text1Length - text2Length);
+        } else if (text1Length < text2Length) {
+            text2 = text2.substring(0, text1Length);
+        }
+        textLength = Math.min(text1Length, text2Length);
+        // Quick check for the worst case.
+        if (text1 === text2) {
+            return textLength;
+        }
+
+        // Start by looking for a single character match
+        // and increase length until no match is found.
+        // Performance analysis: http://neil.fraser.name/news/2010/11/04/
+        best = 0;
+        length = 1;
+        while (true) {
+            pattern = text1.substring(textLength - length);
+            found = text2.indexOf(pattern);
+            if (found === -1) {
+                return best;
+            }
+            length += found;
+            if (found === 0 || text1.substring(textLength - length) ===
+                text2.substring(0, length)) {
+                best = length;
+                length++;
+            }
+        }
+    };
+
+    /**
+     * Split two texts into an array of strings.  Reduce the texts to a string of
+     * hashes where each Unicode character represents one line.
+     * @param {string} text1 First string.
+     * @param {string} text2 Second string.
+     * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}
+     *     An object containing the encoded text1, the encoded text2 and
+     *     the array of unique strings.
+     *     The zeroth element of the array of unique strings is intentionally blank.
+     * @private
+     */
+    DiffMatchPatch.prototype.diffLinesToChars = function(text1, text2) {
+        var lineArray, lineHash, chars1, chars2;
+        lineArray = []; // e.g. lineArray[4] === 'Hello\n'
+        lineHash = {}; // e.g. lineHash['Hello\n'] === 4
+
+        // '\x00' is a valid character, but various debuggers don't like it.
+        // So we'll insert a junk entry to avoid generating a null character.
+        lineArray[0] = "";
+
+        /**
+         * Split a text into an array of strings.  Reduce the texts to a string of
+         * hashes where each Unicode character represents one line.
+         * Modifies linearray and linehash through being a closure.
+         * @param {string} text String to encode.
+         * @return {string} Encoded string.
+         * @private
+         */
+        function diffLinesToCharsMunge(text) {
+            var chars, lineStart, lineEnd, lineArrayLength, line;
+            chars = "";
+            // Walk the text, pulling out a substring for each line.
+            // text.split('\n') would would temporarily double our memory footprint.
+            // Modifying text would create many large strings to garbage collect.
+            lineStart = 0;
+            lineEnd = -1;
+            // Keeping our own length variable is faster than looking it up.
+            lineArrayLength = lineArray.length;
+            while (lineEnd < text.length - 1) {
+                lineEnd = text.indexOf("\n", lineStart);
+                if (lineEnd === -1) {
+                    lineEnd = text.length - 1;
+                }
+                line = text.substring(lineStart, lineEnd + 1);
+                lineStart = lineEnd + 1;
+
+                if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) :
+                    (lineHash[line] !== undefined)) {
+                    chars += String.fromCharCode( lineHash[ line ] );
+                } else {
+                    chars += String.fromCharCode(lineArrayLength);
+                    lineHash[line] = lineArrayLength;
+                    lineArray[lineArrayLength++] = line;
+                }
+            }
+            return chars;
+        }
+
+        chars1 = diffLinesToCharsMunge(text1);
+        chars2 = diffLinesToCharsMunge(text2);
+        return {
+            chars1: chars1,
+            chars2: chars2,
+            lineArray: lineArray
         };
-    }();
-
-    innerEquiv = function () { // can take multiple arguments
-        var args = Array.prototype.slice.apply(arguments);
-        if (args.length < 2) {
-            return true; // end transition
+    };
+
+    /**
+     * Rehydrate the text in a diff from a string of line hashes to real lines of
+     * text.
+     * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+     * @param {!Array.<string>} lineArray Array of unique strings.
+     * @private
+     */
+    DiffMatchPatch.prototype.diffCharsToLines = function( diffs, lineArray ) {
+        var x, chars, text, y;
+        for ( x = 0; x < diffs.length; x++ ) {
+            chars = diffs[x][1];
+            text = [];
+            for ( y = 0; y < chars.length; y++ ) {
+                text[y] = lineArray[chars.charCodeAt(y)];
+            }
+            diffs[x][1] = text.join("");
+        }
+    };
+
+    /**
+     * Reorder and merge like edit sections.  Merge equalities.
+     * Any edit section can move as long as it doesn't cross an equality.
+     * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+     */
+    DiffMatchPatch.prototype.diffCleanupMerge = function(diffs) {
+        var pointer, countDelete, countInsert, textInsert, textDelete,
+			commonlength, changes;
+        diffs.push( [ DIFF_EQUAL, "" ] ); // Add a dummy entry at the end.
+        pointer = 0;
+        countDelete = 0;
+        countInsert = 0;
+        textDelete = "";
+        textInsert = "";
+        commonlength;
+        while (pointer < diffs.length) {
+            switch ( diffs[ pointer ][ 0 ] ) {
+                case DIFF_INSERT:
+                    countInsert++;
+                    textInsert += diffs[pointer][1];
+                    pointer++;
+                    break;
+                case DIFF_DELETE:
+                    countDelete++;
+                    textDelete += diffs[pointer][1];
+                    pointer++;
+                    break;
+                case DIFF_EQUAL:
+                    // Upon reaching an equality, check for prior redundancies.
+                    if (countDelete + countInsert > 1) {
+                        if (countDelete !== 0 && countInsert !== 0) {
+                            // Factor out any common prefixies.
+                            commonlength = this.diffCommonPrefix(textInsert, textDelete);
+                            if (commonlength !== 0) {
+                                if ((pointer - countDelete - countInsert) > 0 &&
+                                    diffs[pointer - countDelete - countInsert - 1][0] ===
+                                    DIFF_EQUAL) {
+                                    diffs[pointer - countDelete - countInsert - 1][1] +=
+                                        textInsert.substring(0, commonlength);
+                                } else {
+                                    diffs.splice( 0, 0, [ DIFF_EQUAL,
+                                        textInsert.substring( 0, commonlength )
+                                     ] );
+                                    pointer++;
+                                }
+                                textInsert = textInsert.substring(commonlength);
+                                textDelete = textDelete.substring(commonlength);
+                            }
+                            // Factor out any common suffixies.
+                            commonlength = this.diffCommonSuffix(textInsert, textDelete);
+                            if (commonlength !== 0) {
+                                diffs[pointer][1] = textInsert.substring(textInsert.length -
+                                    commonlength) + diffs[pointer][1];
+                                textInsert = textInsert.substring(0, textInsert.length -
+                                    commonlength);
+                                textDelete = textDelete.substring(0, textDelete.length -
+                                    commonlength);
+                            }
+                        }
+                        // Delete the offending records and add the merged ones.
+                        if (countDelete === 0) {
+                            diffs.splice( pointer - countInsert,
+                                countDelete + countInsert, [ DIFF_INSERT, textInsert ] );
+                        } else if (countInsert === 0) {
+                            diffs.splice( pointer - countDelete,
+                                countDelete + countInsert, [ DIFF_DELETE, textDelete ] );
+                        } else {
+                            diffs.splice( pointer - countDelete - countInsert,
+                                countDelete + countInsert, [ DIFF_DELETE, textDelete ], [ DIFF_INSERT, textInsert ] );
+                        }
+                        pointer = pointer - countDelete - countInsert +
+                            (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1;
+                    } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) {
+                        // Merge this equality with the previous one.
+                        diffs[pointer - 1][1] += diffs[pointer][1];
+                        diffs.splice(pointer, 1);
+                    } else {
+                        pointer++;
+                    }
+                    countInsert = 0;
+                    countDelete = 0;
+                    textDelete = "";
+                    textInsert = "";
+                    break;
+            }
+        }
+        if (diffs[diffs.length - 1][1] === "") {
+            diffs.pop(); // Remove the dummy entry at the end.
+        }
+
+        // Second pass: look for single edits surrounded on both sides by equalities
+        // which can be shifted sideways to eliminate an equality.
+        // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
+        changes = false;
+        pointer = 1;
+        // Intentionally ignore the first and last element (don't need checking).
+        while (pointer < diffs.length - 1) {
+            if (diffs[pointer - 1][0] === DIFF_EQUAL &&
+                diffs[pointer + 1][0] === DIFF_EQUAL) {
+                // This is a single edit surrounded by equalities.
+                if ( diffs[ pointer ][ 1 ].substring( diffs[ pointer ][ 1 ].length -
+                        diffs[ pointer - 1 ][ 1 ].length ) === diffs[ pointer - 1 ][ 1 ] ) {
+                    // Shift the edit over the previous equality.
+                    diffs[pointer][1] = diffs[pointer - 1][1] +
+                        diffs[pointer][1].substring(0, diffs[pointer][1].length -
+                            diffs[pointer - 1][1].length);
+                    diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];
+                    diffs.splice(pointer - 1, 1);
+                    changes = true;
+                } else if ( diffs[ pointer ][ 1 ].substring( 0, diffs[ pointer + 1 ][ 1 ].length ) ===
+                    diffs[ pointer + 1 ][ 1 ] ) {
+                    // Shift the edit over the next equality.
+                    diffs[pointer - 1][1] += diffs[pointer + 1][1];
+                    diffs[pointer][1] =
+                        diffs[pointer][1].substring(diffs[pointer + 1][1].length) +
+                        diffs[pointer + 1][1];
+                    diffs.splice(pointer + 1, 1);
+                    changes = true;
+                }
+            }
+            pointer++;
         }
-
-        return (function (a, b) {
-            if (a === b) {
-                return true; // catch the most you can
-            } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || hoozit(a) !== hoozit(b)) {
-                return false; // don't lose time with error prone cases
-            } else {
-                return bindCallbacks(a, callbacks, [b, a]);
-            }
-
-        // apply transition with (1..n) arguments
-        })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));
+        // If shifts were made, the diff needs reordering and another shift sweep.
+        if (changes) {
+            this.diffCleanupMerge(diffs);
+        }
+    };
+
+    return function(o, n) {
+		var diff, output, text;
+        diff = new DiffMatchPatch();
+        output = diff.DiffMain(o, n);
+        //console.log(output);
+        diff.diffCleanupEfficiency(output);
+        text = diff.diffPrettyHtml(output);
+
+        return text;
     };
-
-    return innerEquiv;
-
-}();
+}());
+// jscs:enable
+
+(function() {
+
+// Deprecated QUnit.init - Ref #530
+// Re-initialize the configuration options
+QUnit.init = function() {
+	var tests, banner, result, qunit,
+		config = QUnit.config;
+
+	config.stats = { all: 0, bad: 0 };
+	config.moduleStats = { all: 0, bad: 0 };
+	config.started = 0;
+	config.updateRate = 1000;
+	config.blocking = false;
+	config.autostart = true;
+	config.autorun = false;
+	config.filter = "";
+	config.queue = [];
+
+	// Return on non-browser environments
+	// This is necessary to not break on node tests
+	if ( typeof window === "undefined" ) {
+		return;
+	}
+
+	qunit = id( "qunit" );
+	if ( qunit ) {
+		qunit.innerHTML =
+			"<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
+			"<h2 id='qunit-banner'></h2>" +
+			"<div id='qunit-testrunner-toolbar'></div>" +
+			"<h2 id='qunit-userAgent'></h2>" +
+			"<ol id='qunit-tests'></ol>";
+	}
+
+	tests = id( "qunit-tests" );
+	banner = id( "qunit-banner" );
+	result = id( "qunit-testresult" );
+
+	if ( tests ) {
+		tests.innerHTML = "";
+	}
+
+	if ( banner ) {
+		banner.className = "";
+	}
+
+	if ( result ) {
+		result.parentNode.removeChild( result );
+	}
+
+	if ( tests ) {
+		result = document.createElement( "p" );
+		result.id = "qunit-testresult";
+		result.className = "result";
+		tests.parentNode.insertBefore( result, tests );
+		result.innerHTML = "Running...<br />&#160;";
+	}
+};
+
+// Don't load the HTML Reporter on non-Browser environments
+if ( typeof window === "undefined" ) {
+	return;
+}
+
+var config = QUnit.config,
+	hasOwn = Object.prototype.hasOwnProperty,
+	defined = {
+		document: window.document !== undefined,
+		sessionStorage: (function() {
+			var x = "qunit-test-string";
+			try {
+				sessionStorage.setItem( x, x );
+				sessionStorage.removeItem( x );
+				return true;
+			} catch ( e ) {
+				return false;
+			}
+		}())
+	},
+	modulesList = [];
+
+/**
+* Escape text for attribute or text content.
+*/
+function escapeText( s ) {
+	if ( !s ) {
+		return "";
+	}
+	s = s + "";
+
+	// Both single quotes and double quotes (for attributes)
+	return s.replace( /['"<>&]/g, function( s ) {
+		switch ( s ) {
+		case "'":
+			return "&#039;";
+		case "\"":
+			return "&quot;";
+		case "<":
+			return "&lt;";
+		case ">":
+			return "&gt;";
+		case "&":
+			return "&amp;";
+		}
+	});
+}
+
+/**
+ * @param {HTMLElement} elem
+ * @param {string} type
+ * @param {Function} fn
+ */
+function addEvent( elem, type, fn ) {
+	if ( elem.addEventListener ) {
+
+		// Standards-based browsers
+		elem.addEventListener( type, fn, false );
+	} else if ( elem.attachEvent ) {
+
+		// support: IE <9
+		elem.attachEvent( "on" + type, function() {
+			var event = window.event;
+			if ( !event.target ) {
+				event.target = event.srcElement || document;
+			}
+
+			fn.call( elem, event );
+		});
+	}
+}
 
 /**
- * jsDump
- * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
- * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
- * Date: 5/15/2008
- * @projectDescription Advanced and extensible data dumping for Javascript.
- * @version 1.0.0
- * @author Ariel Flesler
- * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
+ * @param {Array|NodeList} elems
+ * @param {string} type
+ * @param {Function} fn
  */
-QUnit.jsDump = (function() {
-	function quote( str ) {
-		return '"' + str.toString().replace(/"/g, '\\"') + '"';
-	};
-	function literal( o ) {
-		return o + '';	
-	};
-	function join( pre, arr, post ) {
-		var s = jsDump.separator(),
-			base = jsDump.indent(),
-			inner = jsDump.indent(1);
-		if ( arr.join )
-			arr = arr.join( ',' + s + inner );
-		if ( !arr )
-			return pre + post;
-		return [ pre, inner + arr, base + post ].join(s);
-	};
-	function array( arr ) {
-		var i = arr.length,	ret = Array(i);					
-		this.up();
-		while ( i-- )
-			ret[i] = this.parse( arr[i] );				
-		this.down();
-		return join( '[', ret, ']' );
-	};
-	
-	var reName = /^function (\w+)/;
-	
-	var jsDump = {
-		parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance
-			var	parser = this.parsers[ type || this.typeOf(obj) ];
-			type = typeof parser;			
-			
-			return type == 'function' ? parser.call( this, obj ) :
-				   type == 'string' ? parser :
-				   this.parsers.error;
-		},
-		typeOf:function( obj ) {
-			var type;
-			if ( obj === null ) {
-				type = "null";
-			} else if (typeof obj === "undefined") {
-				type = "undefined";
-			} else if (QUnit.is("RegExp", obj)) {
-				type = "regexp";
-			} else if (QUnit.is("Date", obj)) {
-				type = "date";
-			} else if (QUnit.is("Function", obj)) {
-				type = "function";
-			} else if (obj.setInterval && obj.document && !obj.nodeType) {
-				type = "window";
-			} else if (obj.nodeType === 9) {
-				type = "document";
-			} else if (obj.nodeType) {
-				type = "node";
-			} else if (typeof obj === "object" && typeof obj.length === "number" && obj.length >= 0) {
-				type = "array";
+function addEvents( elems, type, fn ) {
+	var i = elems.length;
+	while ( i-- ) {
+		addEvent( elems[ i ], type, fn );
+	}
+}
+
+function hasClass( elem, name ) {
+	return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0;
+}
+
+function addClass( elem, name ) {
+	if ( !hasClass( elem, name ) ) {
+		elem.className += ( elem.className ? " " : "" ) + name;
+	}
+}
+
+function toggleClass( elem, name ) {
+	if ( hasClass( elem, name ) ) {
+		removeClass( elem, name );
+	} else {
+		addClass( elem, name );
+	}
+}
+
+function removeClass( elem, name ) {
+	var set = " " + elem.className + " ";
+
+	// Class name may appear multiple times
+	while ( set.indexOf( " " + name + " " ) >= 0 ) {
+		set = set.replace( " " + name + " ", " " );
+	}
+
+	// trim for prettiness
+	elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" );
+}
+
+function id( name ) {
+	return defined.document && document.getElementById && document.getElementById( name );
+}
+
+function getUrlConfigHtml() {
+	var i, j, val,
+		escaped, escapedTooltip,
+		selection = false,
+		len = config.urlConfig.length,
+		urlConfigHtml = "";
+
+	for ( i = 0; i < len; i++ ) {
+		val = config.urlConfig[ i ];
+		if ( typeof val === "string" ) {
+			val = {
+				id: val,
+				label: val
+			};
+		}
+
+		escaped = escapeText( val.id );
+		escapedTooltip = escapeText( val.tooltip );
+
+		if ( config[ val.id ] === undefined ) {
+			config[ val.id ] = QUnit.urlParams[ val.id ];
+		}
+
+		if ( !val.value || typeof val.value === "string" ) {
+			urlConfigHtml += "<input id='qunit-urlconfig-" + escaped +
+				"' name='" + escaped + "' type='checkbox'" +
+				( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) +
+				( config[ val.id ] ? " checked='checked'" : "" ) +
+				" title='" + escapedTooltip + "' /><label for='qunit-urlconfig-" + escaped +
+				"' title='" + escapedTooltip + "'>" + val.label + "</label>";
+		} else {
+			urlConfigHtml += "<label for='qunit-urlconfig-" + escaped +
+				"' title='" + escapedTooltip + "'>" + val.label +
+				": </label><select id='qunit-urlconfig-" + escaped +
+				"' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
+
+			if ( QUnit.is( "array", val.value ) ) {
+				for ( j = 0; j < val.value.length; j++ ) {
+					escaped = escapeText( val.value[ j ] );
+					urlConfigHtml += "<option value='" + escaped + "'" +
+						( config[ val.id ] === val.value[ j ] ?
+							( selection = true ) && " selected='selected'" : "" ) +
+						">" + escaped + "</option>";
+				}
 			} else {
-				type = typeof obj;
+				for ( j in val.value ) {
+					if ( hasOwn.call( val.value, j ) ) {
+						urlConfigHtml += "<option value='" + escapeText( j ) + "'" +
+							( config[ val.id ] === j ?
+								( selection = true ) && " selected='selected'" : "" ) +
+							">" + escapeText( val.value[ j ] ) + "</option>";
+					}
+				}
+			}
+			if ( config[ val.id ] && !selection ) {
+				escaped = escapeText( config[ val.id ] );
+				urlConfigHtml += "<option value='" + escaped +
+					"' selected='selected' disabled='disabled'>" + escaped + "</option>";
+			}
+			urlConfigHtml += "</select>";
+		}
+	}
+
+	return urlConfigHtml;
+}
+
+// Handle "click" events on toolbar checkboxes and "change" for select menus.
+// Updates the URL with the new state of `config.urlConfig` values.
+function toolbarChanged() {
+	var updatedUrl, value,
+		field = this,
+		params = {};
+
+	// Detect if field is a select menu or a checkbox
+	if ( "selectedIndex" in field ) {
+		value = field.options[ field.selectedIndex ].value || undefined;
+	} else {
+		value = field.checked ? ( field.defaultValue || true ) : undefined;
+	}
+
+	params[ field.name ] = value;
+	updatedUrl = setUrl( params );
+
+	if ( "hidepassed" === field.name && "replaceState" in window.history ) {
+		config[ field.name ] = value || false;
+		if ( value ) {
+			addClass( id( "qunit-tests" ), "hidepass" );
+		} else {
+			removeClass( id( "qunit-tests" ), "hidepass" );
+		}
+
+		// It is not necessary to refresh the whole page
+		window.history.replaceState( null, "", updatedUrl );
+	} else {
+		window.location = updatedUrl;
+	}
+}
+
+function setUrl( params ) {
+	var key,
+		querystring = "?";
+
+	params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params );
+
+	for ( key in params ) {
+		if ( hasOwn.call( params, key ) ) {
+			if ( params[ key ] === undefined ) {
+				continue;
+			}
+			querystring += encodeURIComponent( key );
+			if ( params[ key ] !== true ) {
+				querystring += "=" + encodeURIComponent( params[ key ] );
 			}
-			return type;
-		},
-		separator:function() {
-			return this.multiline ?	this.HTML ? '<br />' : '\n' : this.HTML ? '&nbsp;' : ' ';
-		},
-		indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
-			if ( !this.multiline )
-				return '';
-			var chr = this.indentChar;
-			if ( this.HTML )
-				chr = chr.replace(/\t/g,'   ').replace(/ /g,'&nbsp;');
-			return Array( this._depth_ + (extra||0) ).join(chr);
-		},
-		up:function( a ) {
-			this._depth_ += a || 1;
-		},
-		down:function( a ) {
-			this._depth_ -= a || 1;
-		},
-		setParser:function( name, parser ) {
-			this.parsers[name] = parser;
-		},
-		// The next 3 are exposed so you can use them
-		quote:quote, 
-		literal:literal,
-		join:join,
-		//
-		_depth_: 1,
-		// This is the list of parsers, to modify them, use jsDump.setParser
-		parsers:{
-			window: '[Window]',
-			document: '[Document]',
-			error:'[ERROR]', //when no parser is found, shouldn't happen
-			unknown: '[Unknown]',
-			'null':'null',
-			undefined:'undefined',
-			'function':function( fn ) {
-				var ret = 'function',
-					name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE
-				if ( name )
-					ret += ' ' + name;
-				ret += '(';
-				
-				ret = [ ret, this.parse( fn, 'functionArgs' ), '){'].join('');
-				return join( ret, this.parse(fn,'functionCode'), '}' );
-			},
-			array: array,
-			nodelist: array,
-			arguments: array,
-			object:function( map ) {
-				var ret = [ ];
-				this.up();
-				for ( var key in map )
-					ret.push( this.parse(key,'key') + ': ' + this.parse(map[key]) );
-				this.down();
-				return join( '{', ret, '}' );
-			},
-			node:function( node ) {
-				var open = this.HTML ? '&lt;' : '<',
-					close = this.HTML ? '&gt;' : '>';
-					
-				var tag = node.nodeName.toLowerCase(),
-					ret = open + tag;
-					
-				for ( var a in this.DOMAttrs ) {
-					var val = node[this.DOMAttrs[a]];
-					if ( val )
-						ret += ' ' + a + '=' + this.parse( val, 'attribute' );
-				}
-				return ret + close + open + '/' + tag + close;
-			},
-			functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function
-				var l = fn.length;
-				if ( !l ) return '';				
-				
-				var args = Array(l);
-				while ( l-- )
-					args[l] = String.fromCharCode(97+l);//97 is 'a'
-				return ' ' + args.join(', ') + ' ';
-			},
-			key:quote, //object calls it internally, the key part of an item in a map
-			functionCode:'[code]', //function calls it internally, it's the content of the function
-			attribute:quote, //node calls it internally, it's an html attribute value
-			string:quote,
-			date:quote,
-			regexp:literal, //regex
-			number:literal,
-			'boolean':literal
-		},
-		DOMAttrs:{//attributes to dump from nodes, name=>realName
-			id:'id',
-			name:'name',
-			'class':'className'
-		},
-		HTML:false,//if true, entities are escaped ( <, >, \t, space and \n )
-		indentChar:'   ',//indentation unit
-		multiline:false //if true, items in a collection, are separated by a \n, else just a space.
-	};
-
-	return jsDump;
+			querystring += "&";
+		}
+	}
+	return location.protocol + "//" + location.host +
+		location.pathname + querystring.slice( 0, -1 );
+}
+
+function applyUrlParams() {
+	var selectedModule,
+		modulesList = id( "qunit-modulefilter" ),
+		filter = id( "qunit-filter-input" ).value;
+
+	selectedModule = modulesList ?
+		decodeURIComponent( modulesList.options[ modulesList.selectedIndex ].value ) :
+		undefined;
+
+	window.location = setUrl({
+		module: ( selectedModule === "" ) ? undefined : selectedModule,
+		filter: ( filter === "" ) ? undefined : filter,
+
+		// Remove testId filter
+		testId: undefined
+	});
+}
+
+function toolbarUrlConfigContainer() {
+	var urlConfigContainer = document.createElement( "span" );
+
+	urlConfigContainer.innerHTML = getUrlConfigHtml();
+	addClass( urlConfigContainer, "qunit-url-config" );
+
+	// For oldIE support:
+	// * Add handlers to the individual elements instead of the container
+	// * Use "click" instead of "change" for checkboxes
+	addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged );
+	addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged );
+
+	return urlConfigContainer;
+}
+
+function toolbarLooseFilter() {
+	var filter = document.createElement( "form" ),
+		label = document.createElement( "label" ),
+		input = document.createElement( "input" ),
+		button = document.createElement( "button" );
+
+	addClass( filter, "qunit-filter" );
+
+	label.innerHTML = "Filter: ";
+
+	input.type = "text";
+	input.value = config.filter || "";
+	input.name = "filter";
+	input.id = "qunit-filter-input";
+
+	button.innerHTML = "Go";
+
+	label.appendChild( input );
+
+	filter.appendChild( label );
+	filter.appendChild( button );
+	addEvent( filter, "submit", function( ev ) {
+		applyUrlParams();
+
+		if ( ev && ev.preventDefault ) {
+			ev.preventDefault();
+		}
+
+		return false;
+	});
+
+	return filter;
+}
+
+function toolbarModuleFilterHtml() {
+	var i,
+		moduleFilterHtml = "";
+
+	if ( !modulesList.length ) {
+		return false;
+	}
+
+	modulesList.sort(function( a, b ) {
+		return a.localeCompare( b );
+	});
+
+	moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label>" +
+		"<select id='qunit-modulefilter' name='modulefilter'><option value='' " +
+		( QUnit.urlParams.module === undefined ? "selected='selected'" : "" ) +
+		">< All Modules ></option>";
+
+	for ( i = 0; i < modulesList.length; i++ ) {
+		moduleFilterHtml += "<option value='" +
+			escapeText( encodeURIComponent( modulesList[ i ] ) ) + "' " +
+			( QUnit.urlParams.module === modulesList[ i ] ? "selected='selected'" : "" ) +
+			">" + escapeText( modulesList[ i ] ) + "</option>";
+	}
+	moduleFilterHtml += "</select>";
+
+	return moduleFilterHtml;
+}
+
+function toolbarModuleFilter() {
+	var toolbar = id( "qunit-testrunner-toolbar" ),
+		moduleFilter = document.createElement( "span" ),
+		moduleFilterHtml = toolbarModuleFilterHtml();
+
+	if ( !toolbar || !moduleFilterHtml ) {
+		return false;
+	}
+
+	moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
+	moduleFilter.innerHTML = moduleFilterHtml;
+
+	addEvent( moduleFilter.lastChild, "change", applyUrlParams );
+
+	toolbar.appendChild( moduleFilter );
+}
+
+function appendToolbar() {
+	var toolbar = id( "qunit-testrunner-toolbar" );
+
+	if ( toolbar ) {
+		toolbar.appendChild( toolbarUrlConfigContainer() );
+		toolbar.appendChild( toolbarLooseFilter() );
+	}
+}
+
+function appendHeader() {
+	var header = id( "qunit-header" );
+
+	if ( header ) {
+		header.innerHTML = "<a href='" +
+			setUrl({ filter: undefined, module: undefined, testId: undefined }) +
+			"'>" + header.innerHTML + "</a> ";
+	}
+}
+
+function appendBanner() {
+	var banner = id( "qunit-banner" );
+
+	if ( banner ) {
+		banner.className = "";
+	}
+}
+
+function appendTestResults() {
+	var tests = id( "qunit-tests" ),
+		result = id( "qunit-testresult" );
+
+	if ( result ) {
+		result.parentNode.removeChild( result );
+	}
+
+	if ( tests ) {
+		tests.innerHTML = "";
+		result = document.createElement( "p" );
+		result.id = "qunit-testresult";
+		result.className = "result";
+		tests.parentNode.insertBefore( result, tests );
+		result.innerHTML = "Running...<br />&#160;";
+	}
+}
+
+function storeFixture() {
+	var fixture = id( "qunit-fixture" );
+	if ( fixture ) {
+		config.fixture = fixture.innerHTML;
+	}
+}
+
+function appendUserAgent() {
+	var userAgent = id( "qunit-userAgent" );
+
+	if ( userAgent ) {
+		userAgent.innerHTML = "";
+		userAgent.appendChild(
+			document.createTextNode(
+				"QUnit " + QUnit.version  + "; " + navigator.userAgent
+			)
+		);
+	}
+}
+
+function appendTestsList( modules ) {
+	var i, l, x, z, test, moduleObj;
+
+	for ( i = 0, l = modules.length; i < l; i++ ) {
+		moduleObj = modules[ i ];
+
+		if ( moduleObj.name ) {
+			modulesList.push( moduleObj.name );
+		}
+
+		for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) {
+			test = moduleObj.tests[ x ];
+
+			appendTest( test.name, test.testId, moduleObj.name );
+		}
+	}
+}
+
+function appendTest( name, testId, moduleName ) {
+	var title, rerunTrigger, testBlock, assertList,
+		tests = id( "qunit-tests" );
+
+	if ( !tests ) {
+		return;
+	}
+
+	title = document.createElement( "strong" );
+	title.innerHTML = getNameHtml( name, moduleName );
+
+	rerunTrigger = document.createElement( "a" );
+	rerunTrigger.innerHTML = "Rerun";
+	rerunTrigger.href = setUrl({ testId: testId });
+
+	testBlock = document.createElement( "li" );
+	testBlock.appendChild( title );
+	testBlock.appendChild( rerunTrigger );
+	testBlock.id = "qunit-test-output-" + testId;
+
+	assertList = document.createElement( "ol" );
+	assertList.className = "qunit-assert-list";
+
+	testBlock.appendChild( assertList );
+
+	tests.appendChild( testBlock );
+}
+
+// HTML Reporter initialization and load
+QUnit.begin(function( details ) {
+	var qunit = id( "qunit" );
+
+	// Fixture is the only one necessary to run without the #qunit element
+	storeFixture();
+
+	if ( qunit ) {
+		qunit.innerHTML =
+			"<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
+			"<h2 id='qunit-banner'></h2>" +
+			"<div id='qunit-testrunner-toolbar'></div>" +
+			"<h2 id='qunit-userAgent'></h2>" +
+			"<ol id='qunit-tests'></ol>";
+	}
+
+	appendHeader();
+	appendBanner();
+	appendTestResults();
+	appendUserAgent();
+	appendToolbar();
+	appendTestsList( details.modules );
+	toolbarModuleFilter();
+
+	if ( qunit && config.hidepassed ) {
+		addClass( qunit.lastChild, "hidepass" );
+	}
+});
+
+QUnit.done(function( details ) {
+	var i, key,
+		banner = id( "qunit-banner" ),
+		tests = id( "qunit-tests" ),
+		html = [
+			"Tests completed in ",
+			details.runtime,
+			" milliseconds.<br />",
+			"<span class='passed'>",
+			details.passed,
+			"</span> assertions of <span class='total'>",
+			details.total,
+			"</span> passed, <span class='failed'>",
+			details.failed,
+			"</span> failed."
+		].join( "" );
+
+	if ( banner ) {
+		banner.className = details.failed ? "qunit-fail" : "qunit-pass";
+	}
+
+	if ( tests ) {
+		id( "qunit-testresult" ).innerHTML = html;
+	}
+
+	if ( config.altertitle && defined.document && document.title ) {
+
+		// show ✖ for good, ✔ for bad suite result in title
+		// use escape sequences in case file gets loaded with non-utf-8-charset
+		document.title = [
+			( details.failed ? "\u2716" : "\u2714" ),
+			document.title.replace( /^[\u2714\u2716] /i, "" )
+		].join( " " );
+	}
+
+	// clear own sessionStorage items if all tests passed
+	if ( config.reorder && defined.sessionStorage && details.failed === 0 ) {
+		for ( i = 0; i < sessionStorage.length; i++ ) {
+			key = sessionStorage.key( i++ );
+			if ( key.indexOf( "qunit-test-" ) === 0 ) {
+				sessionStorage.removeItem( key );
+			}
+		}
+	}
+
+	// scroll back to top to show results
+	if ( config.scrolltop && window.scrollTo ) {
+		window.scrollTo( 0, 0 );
+	}
+});
+
+function getNameHtml( name, module ) {
+	var nameHtml = "";
+
+	if ( module ) {
+		nameHtml = "<span class='module-name'>" + escapeText( module ) + "</span>: ";
+	}
+
+	nameHtml += "<span class='test-name'>" + escapeText( name ) + "</span>";
+
+	return nameHtml;
+}
+
+QUnit.testStart(function( details ) {
+	var running, testBlock, bad;
+
+	testBlock = id( "qunit-test-output-" + details.testId );
+	if ( testBlock ) {
+		testBlock.className = "running";
+	} else {
+
+		// Report later registered tests
+		appendTest( details.name, details.testId, details.module );
+	}
+
+	running = id( "qunit-testresult" );
+	if ( running ) {
+		bad = QUnit.config.reorder && defined.sessionStorage &&
+			+sessionStorage.getItem( "qunit-test-" + details.module + "-" + details.name );
+
+		running.innerHTML = ( bad ?
+			"Rerunning previously failed test: <br />" :
+			"Running: <br />" ) +
+			getNameHtml( details.name, details.module );
+	}
+
+});
+
+QUnit.log(function( details ) {
+	var assertList, assertLi,
+		message, expected, actual,
+		testItem = id( "qunit-test-output-" + details.testId );
+
+	if ( !testItem ) {
+		return;
+	}
+
+	message = escapeText( details.message ) || ( details.result ? "okay" : "failed" );
+	message = "<span class='test-message'>" + message + "</span>";
+	message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
+
+	// pushFailure doesn't provide details.expected
+	// when it calls, it's implicit to also not show expected and diff stuff
+	// Also, we need to check details.expected existence, as it can exist and be undefined
+	if ( !details.result && hasOwn.call( details, "expected" ) ) {
+		expected = escapeText( QUnit.dump.parse( details.expected ) );
+		actual = escapeText( QUnit.dump.parse( details.actual ) );
+		message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" +
+			expected +
+			"</pre></td></tr>";
+
+		if ( actual !== expected ) {
+			message += "<tr class='test-actual'><th>Result: </th><td><pre>" +
+				actual + "</pre></td></tr>" +
+				"<tr class='test-diff'><th>Diff: </th><td><pre>" +
+				QUnit.diff( expected, actual ) + "</pre></td></tr>";
+		} else {
+			if ( expected.indexOf( "[object Array]" ) !== -1 ||
+					expected.indexOf( "[object Object]" ) !== -1 ) {
+				message += "<tr class='test-message'><th>Message: </th><td>" +
+					"Diff suppressed as the depth of object is more than current max depth (" +
+					QUnit.config.maxDepth + ").<p>Hint: Use <code>QUnit.dump.maxDepth</code> to " +
+					" run with a higher max depth or <a href='" + setUrl({ maxDepth: -1 }) + "'>" +
+					"Rerun</a> without max depth.</p></td></tr>";
+			}
+		}
+
+		if ( details.source ) {
+			message += "<tr class='test-source'><th>Source: </th><td><pre>" +
+				escapeText( details.source ) + "</pre></td></tr>";
+		}
+
+		message += "</table>";
+
+	// this occours when pushFailure is set and we have an extracted stack trace
+	} else if ( !details.result && details.source ) {
+		message += "<table>" +
+			"<tr class='test-source'><th>Source: </th><td><pre>" +
+			escapeText( details.source ) + "</pre></td></tr>" +
+			"</table>";
+	}
+
+	assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
+
+	assertLi = document.createElement( "li" );
+	assertLi.className = details.result ? "pass" : "fail";
+	assertLi.innerHTML = message;
+	assertList.appendChild( assertLi );
+});
+
+QUnit.testDone(function( details ) {
+	var testTitle, time, testItem, assertList,
+		good, bad, testCounts, skipped,
+		tests = id( "qunit-tests" );
+
+	if ( !tests ) {
+		return;
+	}
+
+	testItem = id( "qunit-test-output-" + details.testId );
+
+	assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
+
+	good = details.passed;
+	bad = details.failed;
+
+	// store result when possible
+	if ( config.reorder && defined.sessionStorage ) {
+		if ( bad ) {
+			sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad );
+		} else {
+			sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name );
+		}
+	}
+
+	if ( bad === 0 ) {
+		addClass( assertList, "qunit-collapsed" );
+	}
+
+	// testItem.firstChild is the test name
+	testTitle = testItem.firstChild;
+
+	testCounts = bad ?
+		"<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " :
+		"";
+
+	testTitle.innerHTML += " <b class='counts'>(" + testCounts +
+		details.assertions.length + ")</b>";
+
+	if ( details.skipped ) {
+		testItem.className = "skipped";
+		skipped = document.createElement( "em" );
+		skipped.className = "qunit-skipped-label";
+		skipped.innerHTML = "skipped";
+		testItem.insertBefore( skipped, testTitle );
+	} else {
+		addEvent( testTitle, "click", function() {
+			toggleClass( assertList, "qunit-collapsed" );
+		});
+
+		testItem.className = bad ? "fail" : "pass";
+
+		time = document.createElement( "span" );
+		time.className = "runtime";
+		time.innerHTML = details.runtime + " ms";
+		testItem.insertBefore( time, assertList );
+	}
+});
+
+if ( defined.document ) {
+	if ( document.readyState === "complete" ) {
+		QUnit.load();
+	} else {
+		addEvent( window, "load", QUnit.load );
+	}
+} else {
+	config.pageLoaded = true;
+	config.autorun = true;
+}
+
 })();
-
-})(this);
--- a/devtools/devctl.py	Mon May 09 17:24:03 2016 +0200
+++ b/devtools/devctl.py	Tue Jun 21 07:42:30 2016 +0200
@@ -73,10 +73,12 @@
         return None
     def init_log(self):
         pass
-    def load_configuration(self):
+    def load_configuration(self, **kw):
         pass
     def default_log_file(self):
         return None
+    def default_stats_file(self):
+        return None
 
 
 def cleanup_sys_modules(config):
@@ -580,8 +582,8 @@
 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 # details.
 #
-# You should have received a copy of the GNU Lesser General Public License along
-# with this program. If not, see <http://www.gnu.org/licenses/>.
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
 ''',
 
         'GPL': '''\
@@ -592,7 +594,8 @@
 #
 # This program is distributed in the hope that it will be useful, but WITHOUT
 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
 #
 # You should have received a copy of the GNU General Public License along with
 # this program. If not, see <http://www.gnu.org/licenses/>.
@@ -834,21 +837,11 @@
             p.wait()
 
 
-class GenerateQUnitHTML(Command):
-    """Generate a QUnit html file to see test in your browser"""
-    name = "qunit-html"
-    arguments = '<test file> [<dependancy js file>...]'
-
-    def run(self, args):
-        from cubicweb.devtools.qunit import make_qunit_html
-        print make_qunit_html(args[0], args[1:])
-
 for cmdcls in (UpdateCubicWebCatalogCommand,
                UpdateCubeCatalogCommand,
                #LiveServerCommand,
                NewCubeCommand,
                ExamineLogCommand,
                GenerateSchema,
-               GenerateQUnitHTML,
                ):
     CWCTL.register(cmdcls)
--- a/devtools/fake.py	Mon May 09 17:24:03 2016 +0200
+++ b/devtools/fake.py	Tue Jun 21 07:42:30 2016 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -20,6 +20,8 @@
 
 __docformat__ = "restructuredtext en"
 
+from contextlib import contextmanager
+
 from logilab.database import get_db_helper
 
 from cubicweb.req import RequestSessionBase
@@ -159,6 +161,10 @@
     # for use with enabled_security context manager
     read_security = write_security = True
 
+    @contextmanager
+    def running_hooks_ops(self):
+        yield
+
 class FakeRepo(object):
     querier = None
     def __init__(self, schema, vreg=None, config=None):
@@ -173,7 +179,7 @@
     def internal_session(self):
         return FakeSession(self)
 
-    def extid2eid(self, source, extid, etype, session, insert=True):
+    def extid2eid(self, source, extid, etype, cnx, insert=True):
         try:
             return self.extids[extid]
         except KeyError:
@@ -181,10 +187,10 @@
                 return None
             self._count += 1
             eid = self._count
-            entity = source.before_entity_insertion(session, extid, etype, eid)
+            entity = source.before_entity_insertion(cnx, extid, etype, eid)
             self.extids[extid] = eid
             self.eids[eid] = extid
-            source.after_entity_insertion(session, extid, entity)
+            source.after_entity_insertion(cnx, extid, entity)
             return eid
 
 
--- a/devtools/httptest.py	Mon May 09 17:24:03 2016 +0200
+++ b/devtools/httptest.py	Tue Jun 21 07:42:30 2016 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -78,8 +78,6 @@
         self.global_set_option('port', port) # force rewrite here
         return 'http://127.0.0.1:%d/' % self['port']
 
-    def pyro_enabled(self):
-        return False
 
 
 class CubicWebServerTC(CubicWebTC):
@@ -139,7 +137,6 @@
             passwd = self.admpassword
         if passwd is None:
             passwd = user
-        self.login(user)
         response = self.web_get("login?__login=%s&__password=%s" %
                                 (user, passwd))
         assert response.status == httplib.SEE_OTHER, response.status
--- a/devtools/qunit.py	Mon May 09 17:24:03 2016 +0200
+++ b/devtools/qunit.py	Tue Jun 21 07:42:30 2016 +0200
@@ -29,7 +29,9 @@
 from logilab.common.shellutils import getlogin
 
 import cubicweb
+from cubicweb.view import View
 from cubicweb.web.controller import Controller
+from cubicweb.web.views.staticcontrollers import StaticFileController, STATIC_CONTROLLERS
 from cubicweb.devtools.httptest import CubicWebServerTC
 
 
@@ -66,7 +68,7 @@
         self.firefox_cmd = ['firefox', '-no-remote']
         if os.name == 'posix':
             self.firefox_cmd = [osp.join(osp.dirname(__file__), 'data', 'xvfb-run.sh'),
-                                '-a', '-s', '-noreset -screen 0 640x480x8'] + self.firefox_cmd
+                                '-a', '-s', '-noreset -screen 0 800x600x24'] + self.firefox_cmd
 
     def start(self, url):
         self.stop()
@@ -102,11 +104,14 @@
             test_queue = self.test_queue
         self._qunit_controller = MyQUnitResultController
         self.vreg.register(MyQUnitResultController)
+        self.vreg.register(QUnitView)
+        self.vreg.register(CWSoftwareRootStaticController)
 
     def tearDown(self):
         super(QUnitTestCase, self).tearDown()
         self.vreg.unregister(self._qunit_controller)
-
+        self.vreg.unregister(QUnitView)
+        self.vreg.unregister(CWSoftwareRootStaticController)
 
     def abspath(self, path):
         """use self.__module__ to build absolute path if necessary"""
@@ -130,35 +135,21 @@
                 yield js_test
 
     @with_tempdir
-    def _test_qunit(self, test_file, depends=(), data_files=(), timeout=30):
+    def _test_qunit(self, test_file, depends=(), data_files=(), timeout=10):
         assert osp.exists(test_file), test_file
         for dep in depends:
             assert osp.exists(dep), dep
         for data in data_files:
             assert osp.exists(data), data
 
-        # generate html test file
-        jquery_dir = 'file://' + self.config.locate_resource('jquery.js')[0]
-        html_test_file = NamedTemporaryFile(suffix='.html', delete=False)
-        html_test_file.write(make_qunit_html(test_file, depends,
-                             base_url=self.config['base-url'],
-                             web_data_path=jquery_dir))
-        html_test_file.flush()
-        # copying data file
-        for data in data_files:
-            copyfile(data, tempfile.tempdir)
+        QUnitView.test_file = test_file
+        QUnitView.depends = depends
 
         while not self.test_queue.empty():
             self.test_queue.get(False)
 
         browser = FirefoxHelper()
-        # start firefox once to let it init the profile (and run system-wide
-        # add-ons post setup, blegh), and then kill it ...
-        browser.start('about:blank')
-        import time; time.sleep(5)
-        browser.stop()
-        # ... then actually run the test file
-        browser.start(html_test_file.name)
+        browser.start(self.config['base-url'] + "?vid=qunit")
         test_count = 0
         error = False
         def raise_exception(cls, *data):
@@ -220,100 +211,114 @@
 
     def handle_log(self):
         result = self._cw.form['result']
-        message = self._cw.form['message']
-        self._log_stack.append('%s: %s' % (result, message))
+        message = self._cw.form.get('message', '<no message>')
+        actual = self._cw.form.get('actual')
+        expected = self._cw.form.get('expected')
+        source = self._cw.form.get('source')
+        log = '%s: %s' % (result, message)
+        if result == 'false' and actual is not None and expected is not None:
+            log += ' (got: %s, expected: %s)' % (actual, expected)
+            if source is not None:
+                log += '\n' + source
+        self._log_stack.append(log)
 
 
-def cw_path(*paths):
-  return file_path(osp.join(cubicweb.CW_SOFTWARE_ROOT, *paths))
-
-def file_path(path):
-    return 'file://' + osp.abspath(path)
+class QUnitView(View):
+    __regid__ = 'qunit'
 
-def build_js_script(host):
-    return """
-    var host = '%s';
+    templatable = False
 
-    QUnit.moduleStart = function (name) {
-      jQuery.ajax({
-                  url: host+'/qunit_result',
-                 data: {"event": "module_start",
-                        "name": name},
-                 async: false});
-    }
+    depends = None
+    test_file = None
 
-    QUnit.testDone = function (name, failures, total) {
-      jQuery.ajax({
-                  url: host+'/qunit_result',
-                 data: {"event": "test_done",
-                        "name": name,
-                        "failures": failures,
-                        "total":total},
-                 async: false});
-    }
+    def call(self, **kwargs):
+        w = self.w
+        req = self._cw
+        data = {
+            'jquery': req.data_url('jquery.js'),
+            'web_test': req.build_url('cwsoftwareroot/devtools/data'),
+        }
+        w(u'''<!DOCTYPE html>
+        <html>
+        <head>
+        <meta http-equiv="content-type" content="application/html; charset=UTF-8"/>
+        <!-- JS lib used as testing framework -->
+        <link rel="stylesheet" type="text/css" media="all" href="%(web_test)s/qunit.css" />
+        <script src="%(jquery)s" type="text/javascript"></script>
+        <script src="%(web_test)s/cwmock.js" type="text/javascript"></script>
+        <script src="%(web_test)s/qunit.js" type="text/javascript"></script>'''
+        % data)
+        w(u'<!-- result report tools -->')
+        w(u'<script type="text/javascript">')
+        w(u"var BASE_URL = '%s';" % req.base_url())
+        w(u'''
+            QUnit.moduleStart(function (details) {
+              jQuery.ajax({
+                          url: BASE_URL + 'qunit_result',
+                         data: {"event": "module_start",
+                                "name": details.name},
+                         async: false});
+            });
 
-    QUnit.done = function (failures, total) {
-      jQuery.ajax({
-                   url: host+'/qunit_result',
-                   data: {"event": "done",
-                          "failures": failures,
-                          "total":total},
-                   async: false});
-      window.close();
-    }
+            QUnit.testDone(function (details) {
+              jQuery.ajax({
+                          url: BASE_URL + 'qunit_result',
+                         data: {"event": "test_done",
+                                "name": details.name,
+                                "failures": details.failed,
+                                "total": details.total},
+                         async: false});
+            });
 
-    QUnit.log = function (result, message) {
-      jQuery.ajax({
-                   url: host+'/qunit_result',
-                   data: {"event": "log",
-                          "result": result,
-                          "message": message},
-                   async: false});
-    }
-    """ % host
+            QUnit.done(function (details) {
+              jQuery.ajax({
+                           url: BASE_URL + 'qunit_result',
+                           data: {"event": "done",
+                                  "failures": details.failed,
+                                  "total": details.total},
+                           async: false});
+            });
 
-def make_qunit_html(test_file, depends=(), base_url=None,
-                    web_data_path=cw_path('web', 'data')):
-    """"""
-    data = {
-            'web_data': web_data_path,
-            'web_test': cw_path('devtools', 'data'),
-        }
+            QUnit.log(function (details) {
+              jQuery.ajax({
+                           url: BASE_URL + 'qunit_result',
+                           data: {"event": "log",
+                                  "result": details.result,
+                                  "actual": details.actual,
+                                  "expected": details.expected,
+                                  "source": details.source,
+                                  "message": details.message},
+                           async: false});
+            });''')
+        w(u'</script>')
+        w(u'<!-- Test script dependencies (tested code for example) -->')
 
-    html = ['''<!DOCTYPE html>
-<html>
-  <head>
-    <meta http-equiv="content-type" content="application/html; charset=UTF-8"/>
-    <!-- JS lib used as testing framework -->
-    <link rel="stylesheet" type="text/css" media="all" href="%(web_test)s/qunit.css" />
-    <script src="%(web_data)s/jquery.js" type="text/javascript"></script>
-    <script src="%(web_test)s/cwmock.js" type="text/javascript"></script>
-    <script src="%(web_test)s/qunit.js" type="text/javascript"></script>'''
-    % data]
-    if base_url is not None:
-        html.append('<!-- result report tools -->')
-        html.append('<script type="text/javascript">')
-        html.append(build_js_script(base_url))
-        html.append('</script>')
-    html.append('<!-- Test script dependencies (tested code for example) -->')
+        prefix = len(cubicweb.CW_SOFTWARE_ROOT) + 1
+        for dep in self.depends:
+            dep = req.build_url('cwsoftwareroot/') + dep[prefix:]
+            w(u'    <script src="%s" type="text/javascript"></script>' % dep)
 
-    for dep in depends:
-        html.append('    <script src="%s" type="text/javascript"></script>' % file_path(dep))
+        w(u'    <!-- Test script itself -->')
+        test_url = req.build_url('cwsoftwareroot/') + self.test_file[prefix:]
+        w(u'    <script src="%s" type="text/javascript"></script>' % test_url)
+        w(u'''  </head>
+        <body>
+        <div id="qunit-fixture"></div>
+        <div id="qunit"></div>
+        </body>
+        </html>''')
+
 
-    html.append('    <!-- Test script itself -->')
-    html.append('    <script src="%s" type="text/javascript"></script>'% (file_path(test_file),))
-    html.append('''  </head>
-  <body>
-    <div id="main">
-    </div>
-    <h1 id="qunit-header">QUnit example</h1>
-    <h2 id="qunit-banner"></h2>
-    <h2 id="qunit-userAgent"></h2>
-    <ol id="qunit-tests"></ol>
-  </body>
-</html>''')
-    return u'\n'.join(html)
+class CWSoftwareRootStaticController(StaticFileController):
+    __regid__ = 'cwsoftwareroot'
 
+    def publish(self, rset=None):
+        staticdir = cubicweb.CW_SOFTWARE_ROOT
+        relpath = self.relpath[len(self.__regid__) + 1:]
+        return self.static_file(osp.join(staticdir, relpath))
+
+
+STATIC_CONTROLLERS.append(CWSoftwareRootStaticController)
 
 
 if __name__ == '__main__':
--- a/devtools/repotest.py	Mon May 09 17:24:03 2016 +0200
+++ b/devtools/repotest.py	Tue Jun 21 07:42:30 2016 +0200
@@ -259,12 +259,11 @@
 
     def qexecute(self, rql, args=None, build_descr=True):
         with self.session.new_cnx() as cnx:
-            with cnx.ensure_cnx_set:
-                try:
-                    return self.o.execute(cnx, rql, args, build_descr)
-                finally:
-                    if rql.startswith(('INSERT', 'DELETE', 'SET')):
-                        cnx.commit()
+            try:
+                return self.o.execute(cnx, rql, args, build_descr)
+            finally:
+                if rql.startswith(('INSERT', 'DELETE', 'SET')):
+                    cnx.commit()
 
 
 class BasePlannerTC(BaseQuerierTC):
--- a/devtools/test/data/cubes/i18ntestcube/views.py	Mon May 09 17:24:03 2016 +0200
+++ b/devtools/test/data/cubes/i18ntestcube/views.py	Tue Jun 21 07:42:30 2016 +0200
@@ -26,9 +26,6 @@
 
 _myafs = MyAFS()
 
-# XXX useless ASA logilab.common.registry is fixed
-_myafs.__module__ = "cubes.i18ntestcube.views"
-
 _myafs.tag_object_of(('*', 'in_forum', 'Forum'), 'main', 'inlined')
 
 afs.tag_object_of(('*', 'in_forum', 'Forum'), 'main', 'inlined')
--- a/devtools/test/data/js_examples/test_simple_failure.js	Mon May 09 17:24:03 2016 +0200
+++ b/devtools/test/data/js_examples/test_simple_failure.js	Tue Jun 21 07:42:30 2016 +0200
@@ -1,18 +1,18 @@
 $(document).ready(function() {
 
-  module("air");
+  QUnit.module("air");
 
-  test("test 1", function() {
-      equals(2, 4);
+  QUnit.test("test 1", function (assert) {
+      assert.equal(2, 4);
   });
 
-  test("test 2", function() {
-      equals('', '45');
-      equals('1024', '32');
+  QUnit.test("test 2", function (assert) {
+      assert.equal('', '45');
+      assert.equal('1024', '32');
   });
 
-  module("able");
-  test("test 3", function() {
-      same(1, 1);
+  QUnit.module("able");
+  QUnit.test("test 3", function (assert) {
+      assert.deepEqual(1, 1);
   });
 });
--- a/devtools/test/data/js_examples/test_simple_success.js	Mon May 09 17:24:03 2016 +0200
+++ b/devtools/test/data/js_examples/test_simple_success.js	Tue Jun 21 07:42:30 2016 +0200
@@ -1,17 +1,17 @@
 $(document).ready(function() {
 
-  module("air");
+  QUnit.module("air");
 
-  test("test 1", function() {
-      equals(2, 2);
+  QUnit.test("test 1", function (assert) {
+      assert.equal(2, 2);
   });
 
-  test("test 2", function() {
-      equals('45', '45');
+  QUnit.test("test 2", function (assert) {
+      assert.equal('45', '45');
   });
 
-  module("able");
-  test("test 3", function() {
-      same(1, 1);
+  QUnit.module("able");
+  QUnit.test("test 3", function (assert) {
+      assert.deepEqual(1, 1);
   });
 });
--- a/devtools/test/data/js_examples/test_with_dep.js	Mon May 09 17:24:03 2016 +0200
+++ b/devtools/test/data/js_examples/test_with_dep.js	Tue Jun 21 07:42:30 2016 +0200
@@ -1,9 +1,9 @@
 $(document).ready(function() {
 
-  module("air");
+  QUnit.module("air");
 
-  test("test 1", function() {
-      equals(a, 4);
+  QUnit.test("test 1", function (assert) {
+      assert.equal(a, 4);
   });
 
 });
--- a/devtools/test/data/js_examples/test_with_ordered_deps.js	Mon May 09 17:24:03 2016 +0200
+++ b/devtools/test/data/js_examples/test_with_ordered_deps.js	Tue Jun 21 07:42:30 2016 +0200
@@ -1,9 +1,9 @@
 $(document).ready(function() {
 
-  module("air");
+  QUnit.module("air");
 
-  test("test 1", function() {
-      equals(b, 6);
+  QUnit.test("test 1", function (assert) {
+      assert.equal(b, 6);
   });
 
 });
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/devtools/test/requirements.txt	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,3 @@
+Twisted
+webtest
+cubicweb-person
--- a/devtools/test/unittest_i18n.py	Mon May 09 17:24:03 2016 +0200
+++ b/devtools/test/unittest_i18n.py	Tue Jun 21 07:42:30 2016 +0200
@@ -20,8 +20,9 @@
 
 import os, os.path as osp
 import sys
+import subprocess
 
-from logilab.common.testlib import TestCase, unittest_main
+from unittest import TestCase, main
 
 from cubicweb.cwconfig import CubicWebNoAppConfiguration
 
@@ -52,28 +53,23 @@
 class cubePotGeneratorTC(TestCase):
     """test case for i18n pot file generator"""
 
-    def setUp(self):
-        self._CUBES_PATH = CubicWebNoAppConfiguration.CUBES_PATH[:]
-        CubicWebNoAppConfiguration.CUBES_PATH.append(osp.join(DATADIR, 'cubes'))
-        CubicWebNoAppConfiguration.cls_adjust_sys_path()
-
-    def tearDown(self):
-        CubicWebNoAppConfiguration.CUBES_PATH[:] = self._CUBES_PATH
-
     def test_i18ncube(self):
-        # MUST import here to make, since the import statement fire
-        # the cube paths setup (and then must occur after the setUp)
-        from cubicweb.devtools.devctl import update_cube_catalogs
+        env = os.environ.copy()
+        env['CW_CUBES_PATH'] = osp.join(DATADIR, 'cubes')
+        if 'PYTHONPATH' in env:
+            env['PYTHONPATH'] += os.pathsep
+        else:
+            env['PYTHONPATH'] = ''
+        env['PYTHONPATH'] += DATADIR
+        cwctl = osp.abspath(osp.join(osp.dirname(__file__), '../../bin/cubicweb-ctl'))
+        with open(os.devnull, 'w') as devnull:
+            subprocess.check_call([sys.executable, cwctl, 'i18ncube', 'i18ntestcube'],
+                                  env=env, stdout=devnull)
         cube = osp.join(DATADIR, 'cubes', 'i18ntestcube')
         msgs = load_po(osp.join(cube, 'i18n', 'en.po.ref'))
-        update_cube_catalogs(cube)
         newmsgs = load_po(osp.join(cube, 'i18n', 'en.po'))
         self.assertEqual(msgs, newmsgs)
 
+
 if __name__ == '__main__':
-    # XXX dirty hack to make this test runnable using python (works
-    # fine with pytest, but not with python directly if this hack is
-    # not present)
-    # XXX to remove ASA logilab.common is fixed
-    sys.path.append('')
-    unittest_main()
+    main()
--- a/devtools/test/unittest_testlib.py	Mon May 09 17:24:03 2016 +0200
+++ b/devtools/test/unittest_testlib.py	Tue Jun 21 07:42:30 2016 +0200
@@ -42,7 +42,7 @@
                                 '__maineid': 0,
                                 '__type:0': 'Entity',
                                 '_cw_entity_fields:0': '__type,field',
-                                '_cw_fields': 'file,encoding',
+                                '_cw_fields': 'encoding,file',
                                 'eid': [0],
                                 'encoding': u'utf-8',
                                 'field:0': 'value',
--- a/devtools/testlib.py	Mon May 09 17:24:03 2016 +0200
+++ b/devtools/testlib.py	Tue Jun 21 07:42:30 2016 +0200
@@ -156,30 +156,6 @@
 cwconfig.SMTP = MockSMTP
 
 
-class TestCaseConnectionProxy(object):
-    """thin wrapper around `cubicweb.repoapi.ClientConnection` context-manager
-    used in CubicWebTC (cf. `cubicweb.devtools.testlib.CubicWebTC.login` method)
-
-    It just proxies to the default connection context manager but
-    restores the original connection on exit.
-    """
-    def __init__(self, testcase, cnx):
-        self.testcase = testcase
-        self.cnx = cnx
-
-    def __getattr__(self, attrname):
-        return getattr(self.cnx, attrname)
-
-    def __enter__(self):
-        # already open
-        return self.cnx
-
-    def __exit__(self, exctype, exc, tb):
-        try:
-            return self.cnx.__exit__(exctype, exc, tb)
-        finally:
-            self.testcase.restore_connection()
-
 # Repoaccess utility ###############################################3###########
 
 class RepoAccess(object):
@@ -189,8 +165,7 @@
 
     A repo access can create three type of object:
 
-    .. automethod:: cubicweb.testlib.RepoAccess.repo_cnx
-    .. automethod:: cubicweb.testlib.RepoAccess.client_cnx
+    .. automethod:: cubicweb.testlib.RepoAccess.cnx
     .. automethod:: cubicweb.testlib.RepoAccess.web_request
 
     The RepoAccess need to be closed to destroy the associated Session.
@@ -225,16 +200,13 @@
         return session
 
     @contextmanager
-    def repo_cnx(self):
+    def cnx(self):
         """Context manager returning a server side connection for the user"""
         with self._session.new_cnx() as cnx:
             yield cnx
 
-    @contextmanager
-    def client_cnx(self):
-        """Context manager returning a client side connection for the user"""
-        with repoapi.ClientConnection(self._session) as cnx:
-            yield cnx
+    # aliases for bw compat
+    client_cnx = repo_cnx = cnx
 
     @contextmanager
     def web_request(self, url=None, headers={}, method='GET', **kwargs):
@@ -247,9 +219,10 @@
         """
         req = self.requestcls(self._repo.vreg, url=url, headers=headers,
                               method=method, form=kwargs)
-        clt_cnx = repoapi.ClientConnection(self._session)
-        req.set_cnx(clt_cnx)
-        with clt_cnx:
+        with self._session.new_cnx() as cnx:
+            if 'ecache' in cnx.transaction_data:
+                del cnx.transaction_data['ecache']
+            req.set_cnx(cnx)
             yield req
 
     def close(self):
@@ -261,7 +234,7 @@
     @contextmanager
     def shell(self):
         from cubicweb.server.migractions import ServerMigrationHelper
-        with repoapi.ClientConnection(self._session) as cnx:
+        with self._session.new_cnx() as cnx:
             mih = ServerMigrationHelper(None, repo=self._repo, cnx=cnx,
                                         interactive=False,
                                         # hack so it don't try to load fs schema
@@ -294,17 +267,12 @@
     requestcls = fake.FakeRequest
     tags = TestCase.tags | Tags('cubicweb', 'cw_repo')
     test_db_id = DEFAULT_EMPTY_DB_ID
-    _cnxs = set() # establised connection
-                  # stay on connection for leak detection purpose
 
     # anonymous is logged by default in cubicweb test cases
     anonymous_allowed = True
 
     def __init__(self, *args, **kwargs):
         self._admin_session = None
-        self._admin_clt_cnx = None
-        self._current_session = None
-        self._current_clt_cnx = None
         self.repo = None
         self._open_access = set()
         super(CubicWebTC, self).__init__(*args, **kwargs)
@@ -315,6 +283,7 @@
         """provide a new RepoAccess object for a given user
 
         The access is automatically closed at the end of the test."""
+        login = unicode(login)
         access = RepoAccess(self.repo, login, self.requestcls)
         self._open_access.add(access)
         return access
@@ -326,92 +295,11 @@
             except BadConnectionId:
                 continue # already closed
 
-    @deprecated('[3.19] explicitly use RepoAccess object in test instead')
-    def set_cnx(self, cnx):
-        assert getattr(cnx, '_session', None) is not None
-        if cnx is self._admin_clt_cnx:
-            self._pop_custom_cnx()
-        else:
-            self._cnxs.add(cnx) # register the cnx to make sure it is removed
-            self._current_session = cnx._session
-            self._current_clt_cnx = cnx
-
     @property
-    @deprecated('[3.19] explicitly use RepoAccess object in test instead')
-    def cnx(self):
-        # XXX we want to deprecate this
-        clt_cnx = self._current_clt_cnx
-        if clt_cnx is None:
-            clt_cnx = self._admin_clt_cnx
-        return clt_cnx
-
-    def _close_cnx(self):
-        """ensure that all cnx used by a test have been closed"""
-        for cnx in list(self._cnxs):
-            if cnx._open and not cnx._session.closed:
-                cnx.rollback()
-                cnx.close()
-            self._cnxs.remove(cnx)
-
-    @property
-    @deprecated('[3.19] explicitly use RepoAccess object in test instead')
     def session(self):
-        """return current server side session"""
-        # XXX We want to use a srv_connection instead and deprecate this
-        # property
-        session = self._current_session
-        if session is None:
-            session = self._admin_session
-            # bypassing all sanity to use the same repo cnx in the session
-            #
-            # we can't call set_cnx as the Connection is not managed by the
-            # session.
-            session._Session__threaddata.cnx = self._admin_clt_cnx._cnx
-        else:
-            session._Session__threaddata.cnx = self.cnx._cnx
-        session.set_cnxset()
-        return session
-
-    @property
-    @deprecated('[3.19] explicitly use RepoAccess object in test instead')
-    def websession(self):
-        return self.session
-
-    @property
-    @deprecated('[3.19] explicitly use RepoAccess object in test instead')
-    def adminsession(self):
-        """return current server side session (using default manager account)"""
+        """return admin session"""
         return self._admin_session
 
-    @deprecated('[3.19] explicitly use RepoAccess object in test instead')
-    def login(self, login, **kwargs):
-        """return a connection for the given login/password"""
-        __ = kwargs.pop('autoclose', True) # not used anymore
-        if login == self.admlogin:
-            # undo any previous login, if we're not used as a context manager
-            self.restore_connection()
-            return self.cnx
-        else:
-            if not kwargs:
-                kwargs['password'] = str(login)
-            clt_cnx = repoapi.connect(self.repo, login, **kwargs)
-        self.set_cnx(clt_cnx)
-        clt_cnx.__enter__()
-        return TestCaseConnectionProxy(self, clt_cnx)
-
-    @deprecated('[3.19] explicitly use RepoAccess object in test instead')
-    def restore_connection(self):
-        self._pop_custom_cnx()
-
-    def _pop_custom_cnx(self):
-        if self._current_clt_cnx is not None:
-            if self._current_clt_cnx._open:
-                self._current_clt_cnx.close()
-            if not  self._current_session.closed:
-                self.repo.close(self._current_session.sessionid)
-            self._current_clt_cnx = None
-            self._current_session = None
-
     #XXX this doesn't need to a be classmethod anymore
     def _init_repo(self):
         """init the repository and connection to it.
@@ -425,62 +313,6 @@
         login = unicode(db_handler.config.default_admin_config['login'])
         self.admin_access = self.new_access(login)
         self._admin_session = self.admin_access._session
-        self._admin_clt_cnx = repoapi.ClientConnection(self._admin_session)
-        self._cnxs.add(self._admin_clt_cnx)
-        self._admin_clt_cnx.__enter__()
-
-    # db api ##################################################################
-
-    @nocoverage
-    @deprecated('[3.19] explicitly use RepoAccess object in test instead')
-    def cursor(self, req=None):
-        if req is not None:
-            return req.cnx
-        else:
-            return self.cnx
-
-    @nocoverage
-    @deprecated('[3.19] explicitly use RepoAccess object in test instead')
-    def execute(self, rql, args=None, req=None):
-        """executes <rql>, builds a resultset, and returns a couple (rset, req)
-        where req is a FakeRequest
-        """
-        req = req or self.request(rql=rql)
-        return req.execute(unicode(rql), args)
-
-    @nocoverage
-    @deprecated('[3.19] explicitly use RepoAccess object in test instead')
-    def commit(self):
-        try:
-            return self.cnx.commit()
-        finally:
-            self.session.set_cnxset() # ensure cnxset still set after commit
-
-    @nocoverage
-    @deprecated('[3.19] explicitly use RepoAccess object in test instead')
-    def rollback(self):
-        try:
-            self.cnx.rollback()
-        except ProgrammingError:
-            pass # connection closed
-        finally:
-            self.session.set_cnxset() # ensure cnxset still set after commit
-
-    @deprecated('[3.19] explicitly use RepoAccess object in test instead')
-    def request(self, rollbackfirst=False, url=None, headers={}, **kwargs):
-        """return a web ui request"""
-        if rollbackfirst:
-            self.cnx.rollback()
-        req = self.requestcls(self.vreg, url=url, headers=headers, form=kwargs)
-        req.set_cnx(self.cnx)
-        return req
-
-    # server side db api #######################################################
-
-    @deprecated('[3.19] explicitly use RepoAccess object in test instead')
-    def sexecute(self, rql, args=None):
-        self.session.set_cnxset()
-        return self.session.execute(rql, args)
 
 
     # config management ########################################################
@@ -548,15 +380,6 @@
         """return the application schema"""
         return self.vreg.schema
 
-    @deprecated('[3.19] explicitly use RepoAccess object in test instead')
-    def shell(self):
-        """return a shell session object"""
-        from cubicweb.server.migractions import ServerMigrationHelper
-        return ServerMigrationHelper(None, repo=self.repo, cnx=self.cnx,
-                                     interactive=False,
-                                     # hack so it don't try to load fs schema
-                                     schema=1)
-
     def set_option(self, optname, value):
         self.config.global_set_option(optname, value)
 
@@ -577,24 +400,17 @@
                 self.skipTest('repository is not initialised: %r' % previous_failure)
             try:
                 self._init_repo()
-                self.addCleanup(self._close_cnx)
             except Exception as ex:
                 self.__class__._repo_init_failed = ex
                 raise
             self.addCleanup(self._close_access)
         self.setup_database()
-        self._admin_clt_cnx.commit()
         MAILBOX[:] = [] # reset mailbox
 
     def tearDown(self):
         # XXX hack until logilab.common.testlib is fixed
-        if self._admin_clt_cnx is not None:
-            if self._admin_clt_cnx._open:
-                self._admin_clt_cnx.close()
-            self._admin_clt_cnx = None
         if self._admin_session is not None:
-            if not self._admin_session.closed:
-                self.repo.close(self._admin_session.sessionid)
+            self.repo.close(self._admin_session.sessionid)
             self._admin_session = None
         while self._cleanups:
             cleanup, args, kwargs = self._cleanups.pop(-1)
@@ -634,20 +450,11 @@
     def create_user(self, req, login=None, groups=('users',), password=None,
                     email=None, commit=True, **kwargs):
         """create and return a new user entity"""
-        if isinstance(req, basestring):
-            warn('[3.12] create_user arguments are now (req, login[, groups, password, commit, **kwargs])',
-                 DeprecationWarning, stacklevel=2)
-            if not isinstance(groups, (tuple, list)):
-                password = groups
-                groups = login
-            elif isinstance(login, tuple):
-                groups = login
-            login = req
-            assert not isinstance(self, type)
-            req = self._admin_clt_cnx
         if password is None:
-            password = login.encode('utf8')
-        user = req.create_entity('CWUser', login=unicode(login),
+            password = login
+        if login is not None:
+            login = unicode(login)
+        user = req.create_entity('CWUser', login=login,
                                  upassword=password, **kwargs)
         req.execute('SET X in_group G WHERE X eid %%(x)s, G name IN(%s)'
                     % ','.join(repr(str(g)) for g in groups),
@@ -918,7 +725,7 @@
             if entity_fields:
                 form[eid_param('_cw_entity_fields', entity.eid)] = ','.join(entity_fields)
         if fields:
-            form['_cw_fields'] = ','.join(fields)
+            form['_cw_fields'] = ','.join(sorted(fields))
         return form
 
     @deprecated('[3.19] use .admin_request_from_url instead')
@@ -1038,8 +845,8 @@
     def assertAuthSuccess(self, req, origsession, nbsessions=1):
         sh = self.app.session_handler
         session = self.app.get_session(req)
-        clt_cnx = repoapi.ClientConnection(session)
-        req.set_cnx(clt_cnx)
+        cnx = repoapi.Connection(session)
+        req.set_cnx(cnx)
         self.assertEqual(len(self.open_sessions), nbsessions, self.open_sessions)
         self.assertEqual(session.login, origsession.login)
         self.assertEqual(session.anonymous_session, False)
@@ -1212,7 +1019,8 @@
 
     def assertDocTestFile(self, testfile):
         # doctest returns tuple (failure_count, test_count)
-        result = self.shell().process_script(testfile)
+        with self.admin_access.shell() as mih:
+            result = mih.process_script(testfile)
         if result[0] and result[1]:
             raise self.failureException("doctest file '%s' failed"
                                         % testfile)
@@ -1325,7 +1133,7 @@
         """this method populates the database with `how_many` entities
         of each possible type. It also inserts random relations between them
         """
-        with self.admin_access.repo_cnx() as cnx:
+        with self.admin_access.cnx() as cnx:
             with cnx.security_enabled(read=False, write=False):
                 self._auto_populate(cnx, how_many)
                 cnx.commit()
--- a/doc/3.14.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,164 +0,0 @@
-Whats new in CubicWeb 3.14
-==========================
-
-First notice CW 3.14 depends on yams 0.34 (which is incompatible with prior
-cubicweb releases regarding instance re-creation).
-
-API changes
------------
-
-* `Entity.fetch_rql` `restriction` argument has been deprecated and should be
-  replaced with a call to the new `Entity.fetch_rqlst` method, get the returned
-  value (a rql `Select` node) and use the RQL syntax tree API to include the
-  above-mentionned restrictions.
-
-  Backward compat is kept with proper warning.
-
-* `Entity.fetch_order` and `Entity.fetch_unrelated_order` class methods have been
-  replaced by `Entity.cw_fetch_order` and `Entity.cw_fetch_unrelated_order` with
-  a different prototype:
-
-  - instead of taking (attr, var) as two string argument, they now take (select,
-    attr, var) where select is the rql syntax tree beinx constructed and var the
-    variable *node*.
-
-  - instead of returning some string to be inserted in the ORDERBY clause, it has
-    to modify the syntax tree
-
-  Backward compat is kept with proper warning, BESIDE cases below:
-
-  - custom order method return **something else the a variable name with or
-    without the sorting order** (e.g. cases where you sort on the value of a
-    registered procedure as it was done in the tracker for instance). In such
-    case, an error is logged telling that this sorting is ignored until API
-    upgrade.
-
-  - client code use direct access to one of those methods on an entity (no code
-    known to do that).
-
-* `Entity._rest_attr_info` class method has been renamed to
-  `Entity.cw_rest_attr_info`
-
-  No backward compat yet since this is a protected method an no code is known to
-  use it outside cubicweb itself.
-
-* `AnyEntity.linked_to` has been removed as part of a refactoring of this
-  functionality (link a entity to another one at creation step). It was replaced
-  by a `EntityFieldsForm.linked_to` property.
-
-  In the same refactoring, `cubicweb.web.formfield.relvoc_linkedto`,
-  `cubicweb.web.formfield.relvoc_init` and
-  `cubicweb.web.formfield.relvoc_unrelated` were removed and replaced by
-  RelationField methods with the same names, that take a form as a parameter.
-
-  **No backward compatibility yet**. It's still time to cry for it.
-  Cubes known to be affected: tracker, vcsfile, vcreview.
-
-* `CWPermission` entity type and its associated require_permission relation type
-  (abstract) and require_group relation definitions have been moved to a new
-  `localperms` cube. With this have gone some functions from the
-  `cubicweb.schemas` package as well as some views. This makes cubicweb itself
-  smaller while you get all the local permissions stuff into a single,
-  documented, place.
-
-  Backward compat is kept for existing instances, **though you should have
-  installed the localperms cubes**. A proper error should be displayed when
-  trying to migrate to 3.14 an instance the use `CWPermission` without the new
-  cube installed. For new instances / test, you should add a dependancy on the
-  new cube in cubes using this feature, along with a dependancy on cubicweb >=
-  3.14.
-
-* jQuery has been updated to 1.6.4 and jquery-tablesorter to 2.0.5. No backward
-  compat issue known.
-
-* Table views refactoring : new `RsetTableView` and `EntityTableView`, as well as
-  rewritten an enhanced version of `PyValTableView` on the same bases, with logic
-  moved to some column renderers and a layout. Those should be well documented
-  and deprecates former `TableView`, `EntityAttributesTableView` and `CellView`,
-  which are however kept for backward compat, with some warnings that may not be
-  very clear unfortunatly (you may see your own table view subclass name here,
-  which doesn't make the problem that clear). Notice that `_cw.view('table',
-  rset, *kwargs)` will be routed to the new `RsetTableView` or to the old
-  `TableView` depending on given extra arguments. See #1986413.
-
-* `display_name` don't call .lower() anymore. This may leads to changes in your
-  user interface. Different msgid for upper/lower cases version of entity type
-  names, as this is the only proper way to handle this with some languages.
-
-* `IEditControlAdapter` has been deprecated in favor of `EditController`
-  overloading, which was made easier by adding dedicated selectors called
-  `match_edited_type` and `match_form_id`.
-
-* Pre 3.6 API backward compat has been dropped, though *data* migration
-  compatibility has been kept. You may have to fix errors due to old API usage
-  for your instance before to be able to run migration, but then you should be
-  able to upgrade even a pre 3.6 database.
-
-* Deprecated `cubicweb.web.views.iprogress` in favor of new `iprogress` cube.
-
-* Deprecated `cubicweb.web.views.flot` in favor of new `jqplot` cube.
-
-
-Unintrusive API changes
------------------------
-
-* Refactored properties forms (eg user preferences and site wide properties) as
-  well as pagination components to ease overridding.
-
-* New `cubicweb.web.uihelper` module with high-level helpers for uicfg.
-
-* New `anonymized_request` decorator to temporary run stuff as an anonymous
-  user, whatever the currently logged in user.
-
-* New 'verbatimattr' attribute view.
-
-* New facet and form widget for Integer used to store binary mask.
-
-* New `js_href` function to generated proper javascript href.
-
-* `match_kwargs` and `match_form_params` selectors both accept a new
-  `once_is_enough` argument.
-
-* `printable_value` is now a method of request, and may be given dict of
-   formatters to use.
-
-* `[Rset]TableView` allows to set None in 'headers', meaning the label should be
-  fetched from the result set as done by default.
-
-* Field vocabulary computation on entity creation now takes `__linkto`
-  information into accounet.
-
-* Started a `cubicweb.pylintext` pylint plugin to help pylint analyzing cubes.
-
-
-RQL
----
-
-* Support for HAVING in 'SET' and 'DELETE' queries.
-
-* new `AT_TZ` function to get back a timestamp at a given time-zone.
-
-* new `WEEKDAY` date extraction function
-
-
-User interface changes
-----------------------
-
-* Datafeed source now present an history of the latest import's log, including
-  global status and debug/info/warning/error messages issued during
-  imports. Import logs older than a configurable amount of time are automatically
-  deleted.
-
-* Breadcrumbs component is properly kept when creating an entity with '__linkto'.
-
-* users and groups management now really lead to that (i.e. includes *groups*
-  management).
-
-* New 'jsonp' controller with 'jsonexport' and 'ejsonexport' views.
-
-
-Configuration
-------------
-
-* Added option 'resources-concat' to make javascript/css files concatenation
-  optional.
--- a/doc/3.15.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-What's new in CubicWeb 3.15?
-============================
-
-New functionnalities
---------------------
-
-* Add Zmq server, based on the cutting edge ZMQ (http://www.zeromq.org/) socket
-  library.  This allows to access distant instance, in a similar way as Pyro.
-
-* Publish/subscribe mechanism using ZMQ for communication among cubicweb
-  instances.  The new zmq-address-sub and zmq-address-pub configuration variables
-  define where this communication occurs.  As of this release this mechanism is
-  used for entity cache invalidation.
-
-* Improved WSGI support. While there is still some caveats, most of the code
-  which was twisted only is now generic and allows related functionalities to work
-  with a WSGI front-end.
-
-* Full undo/transaction support : undo of modification has eventually been
-  implemented, and the configuration simplified (basically you activate it or not
-  on an instance basis).
-
-* Controlling HTTP status code used is not much more easier :
-
-  - `WebRequest` now has a `status_out` attribut to control the response status ;
-
-  - most web-side exceptions take an optional ``status`` argument.
-
-API changes
------------
-
-* The base registry implementation has been moved to a new
-  `logilab.common.registry` module (see #1916014). This includes code from :
-
-  * `cubicweb.vreg` (the whole things that was in there)
-  * `cw.appobject` (base selectors and all).
-
-  In the process, some renaming was done:
-
-  * the top level registry is now `RegistryStore` (was `VRegistry`), but that
-    should not impact cubicweb client code ;
-
-  * former selectors functions are now known as "predicate", though you still use
-    predicates to build an object'selector ;
-
-  * for consistency, the `objectify_selector` decoraror has hence be renamed to
-    `objectify_predicate` ;
-
-  * on the CubicWeb side, the `selectors` module has been renamed to
-    `predicates`.
-
-  Debugging refactoring dropped the more need for the `lltrace` decorator.  There
-  should be full backward compat with proper deprecation warnings.  Notice the
-  `yes` predicate and `objectify_predicate` decorator, as well as the
-  `traced_selection` function should now be imported from the
-  `logilab.common.registry` module.
-
-* All login forms are now submitted to <app_root>/login. Redirection to requested
-  page is now handled by the login controller (it was previously handle by the
-  session manager).
-
-* `Publisher.publish` has been renamed to `Publisher.handle_request`. This
-  method now contains generic version of logic previously handled by
-  Twisted. `Controller.publish` is **not** affected.
-
-Unintrusive API changes
------------------------
-
-* New 'ldapfeed' source type, designed to replace 'ldapuser' source with
-  data-feed (i.e. copy based) source ideas.
-
-* New 'zmqrql' source type, similar to 'pyrorql' but using ømq instead of Pyro.
-
-* A new registry called `services` has appeared, where you can register
-  server-side `cubicweb.server.Service` child classes. Their `call` method can be
-  invoked from a web-side AppObject instance using new `self._cw.call_service`
-  method or a server-side one using `self.session.call_service`. This is a new
-  way to call server-side methods, much cleaner than monkey patching the
-  Repository class, which becomes a deprecated way to perform similar tasks.
-
-* a new `ajax-func` registry now hosts all remote functions (i.e. functions
-  callable through the `asyncRemoteExec` JS api). A convenience `ajaxfunc`
-  decorator will let you expose your python function easily without all the
-  appobject standard boilerplate. Backward compatibility is preserved.
-
-* the 'json' controller is now deprecated in favor of the 'ajax' one.
-
-* `WebRequest.build_url` can now take a __secure__ argument. When True cubicweb
-  try to generate an https url.
-
-
-User interface changes
-----------------------
-
-A new 'undohistory' view expose the undoable transactions and give access to undo
-some of them.
--- a/doc/3.16.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,97 +0,0 @@
-What's new in CubicWeb 3.16?
-============================
-
-New functionalities
---------------------
-
-* Add a new dataimport store (`SQLGenObjectStore`). This store enables a fast
-  import of data (entity creation, link creation) in CubicWeb, by directly
-  flushing information in SQL.  This may only be used with PostgreSQL, as it
-  requires the 'COPY FROM' command.
-
-
-API changes
------------
-
-* Orm: `set_attributes` and `set_relations` are unified (and
-  deprecated) in favor of `cw_set` that works in all cases.
-
-* db-api/configuration: all the external repository connection information is
-  now in an URL (see `#2521848 <http://www.cubicweb.org/2521848>`_),
-  allowing to drop specific options of pyro nameserver host, group, etc and fix
-  broken `ZMQ <http://www.zeromq.org/>`_ source. Configuration related changes:
-
-  * Dropped 'pyro-ns-host', 'pyro-instance-id', 'pyro-ns-group' from the client side
-    configuration, in favor of 'repository-uri'. **NO MIGRATION IS DONE**,
-    supposing there is no web-only configuration in the wild.
-
-  * Stop discovering the connection method through `repo_method` class attribute
-    of the configuration, varying according to the configuration class. This is
-    a first step on the way to a simpler configuration handling.
-
-  DB-API related changes:
-
-  * Stop indicating the connection method using `ConnectionProperties`.
-
-  * Drop `_cnxtype` attribute from `Connection` and `cnxtype` from
-    `Session`. The former is replaced by a `is_repo_in_memory` property
-    and the later is totaly useless.
-
-  * Turn `repo_connect` into `_repo_connect` to mark it as a private function.
-
-  * Deprecate `in_memory_cnx` which becomes useless, use `_repo_connect` instead
-    if necessary.
-
-* the "tcp://" uri scheme used for `ZMQ <http://www.zeromq.org/>`_
-  communications (in a way reminiscent of Pyro) is now named
-  "zmqpickle-tcp://", so as to make room for future zmq-based lightweight
-  communications (without python objects pickling).
-
-* Request.base_url gets a `secure=True` optional parameter that yields
-  an https url if possible, allowing hook-generated content to send
-  secure urls (e.g. when sending mail notifications)
-
-* Dataimport ucsvreader gets a new boolean `ignore_errors`
-  parameter.
-
-
-Unintrusive API changes
------------------------
-
-* Drop of `cubicweb.web.uicfg.AutoformSectionRelationTags.bw_tag_map`,
-  deprecated since 3.6.
-
-
-User interface changes
-----------------------
-
-* The RQL search bar has now some auto-completion support. It means
-  relation types or entity types can be suggested while typing. It is
-  an awesome improvement over the current behaviour !
-
-* The `action box` associated with `table` views (from `tableview.py`)
-  has been transformed into a nice-looking series of small tabs; it
-  means that the possible actions are immediately visible and need not
-  be discovered by clicking on an almost invisible icon on the upper
-  right.
-
-* The `uicfg` module has moved to web/views/ and ui configuration
-  objects are now selectable. This will reduce the amount of
-  subclassing and whole methods replacement usually needed to
-  customize the ui behaviour in many cases.
-
-* Remove changelog view, as neither cubicweb nor known
-  cubes/applications were properly feeding related files.
-
-
-Other changes
--------------
-
-* 'pyrorql' sources will be automatically updated to use an URL to locate the source
-  rather than configuration option. 'zmqrql' sources were broken before this change,
-  so no upgrade is needed...
-
-* Debugging filters for Hooks and Operations have been added.
-
-* Some cubicweb-ctl commands used to show the output of `msgcat` and
-  `msgfmt`; they don't anymore.
--- a/doc/3.17.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-What's new in CubicWeb 3.17?
-============================
-
-New functionalities
---------------------
-
-* add a command to compare db schema and file system schema
-  (see `#464991 <http://www.cubicweb.org/464991>`_)
-
-* Add CubicWebRequestBase.content with the content of the HTTP request (see #2742453)
-  (see `#2742453 <http://www.cubicweb.org/2742453>`_)
-
-* Add directive bookmark to ReST rendering
-  (see `#2545595 <http://www.cubicweb.org/ticket/2545595>`_)
-
-* Allow user defined final type
-  (see `#124342 <https://www.logilab.org/ticket/124342>`_)
-
-
-API changes
------------
-
-* drop typed_eid() in favour of int() (see `#2742462 <http://www.cubicweb.org/2742462>`_)
-
-* The SIOC views and adapters have been removed from CubicWeb and moved to the
-  `sioc` cube.
-
-* The web page embedding views and adapters have been removed from CubicWeb and
-  moved to the `embed` cube.
-
-* The email sending views and controllers have been removed from CubicWeb and
-  moved to the `massmailing` cube.
-
-* ``RenderAndSendNotificationView`` is deprecated in favor of
-  ``ActualNotificationOp`` the new operation use the more efficient *data*
-  idiom.
-
-* Looping task can now have a interval <= ``0``. Negative interval disable the
-  looping task entirely.
-
-* We now serve html instead of xhtml.
-  (see `#2065651 <http://www.cubicweb.org/ticket/2065651>`_)
-
-
-Deprecation
----------------------
-
-* ``ldapuser`` have been deprecated. It'll be fully dropped in the next
-  version. If you are still using ldapuser switch to ``ldapfeed`` **NOW**!
-
-* ``hijack_user`` have been deprecated. It will be dropped soon.
-
-Deprecated Code Drops
-----------------------
-
-* The progress views and adapters have been removed from CubicWeb. These
-  classes were deprecated since 3.14.0. They are still available in the
-  `iprogress` cube.
-
-* API deprecated since 3.7 have been dropped.
--- a/doc/3.18.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +0,0 @@
-What's new in CubicWeb 3.18?
-============================
-
-The migration script does not handle sqlite nor mysql instances.
-
-
-New functionalities
---------------------
-
-* add a security debugging tool
-  (see `#2920304 <http://www.cubicweb.org/2920304>`_)
-
-* introduce an `add` permission on attributes, to be interpreted at
-  entity creation time only and allow the implementation of complex
-  `update` rules that don't block entity creation (before that the
-  `update` attribute permission was interpreted at entity creation and
-  update time)
-
-* the primary view display controller (uicfg) now has a
-  `set_fields_order` method similar to the one available for forms
-
-* new method `ResultSet.one(col=0)` to retrive a single entity and enforce the
-  result has only one row (see `#3352314 https://www.cubicweb.org/ticket/3352314`_)
-
-* new method `RequestSessionBase.find` to look for entities
-  (see `#3361290 https://www.cubicweb.org/ticket/3361290`_)
-
-* the embedded jQuery copy has been updated to version 1.10.2, and jQuery UI to
-  version 1.10.3.
-
-* initial support for wsgi for the debug mode, available through the new
-  ``wsgi`` cubicweb-ctl command, which can use either python's builtin
-  wsgi server or the werkzeug module if present.
-
-* a ``rql-table`` directive is now available in ReST fields
-
-* cubicweb-ctl upgrade can now generate the static data resource directory
-  directly, without a manual call to gen-static-datadir.
-
-API changes
------------
-
-* not really an API change, but the entity permission checks are now
-  systematically deferred to an operation, instead of a) trying in a
-  hook and b) if it failed, retrying later in an operation
-
-* The default value storage for attributes is no longer String, but
-  Bytes.  This opens the road to storing arbitrary python objects, e.g.
-  numpy arrays, and fixes a bug where default values whose truth value
-  was False were not properly migrated.
-
-* `symmetric` relations are no more handled by an rql rewrite but are
-  now handled with hooks (from the `activeintegrity` category); this
-  may have some consequences for applications that do low-level database
-  manipulations or at times disable (some) hooks.
-
-* `unique together` constraints (multi-columns unicity constraints)
-  get a `name` attribute that maps the CubicWeb contraint entities to
-  corresponding backend index.
-
-* BreadCrumbEntityVComponent's open_breadcrumbs method now includes
-  the first breadcrumbs separator
-
-* entities can be compared for equality and hashed
-
-* the ``on_fire_transition`` predicate accepts a sequence of possible
-  transition names
-
-* the GROUP_CONCAT rql aggregate function no longer repeats duplicate
-  values, on the sqlite and postgresql backends
-
-Deprecation
----------------------
-
-* ``pyrorql`` sources have been deprecated. Multisource will be fully dropped
-  in the next version. If you are still using pyrorql, switch to ``datafeed``
-  **NOW**!
-
-* the old multi-source system
-
-* `find_one_entity` and `find_entities` in favor of `find`
-  (see `#3361290 https://www.cubicweb.org/ticket/3361290`_)
-
-* the `TmpFileViewMixin` and `TmpPngView` classes (see `#3400448
-  https://www.cubicweb.org/ticket/3400448`_)
-
-Deprecated Code Drops
-----------------------
-
-* ``ldapuser`` have been dropped; use ``ldapfeed`` now
-  (see `#2936496 <http://www.cubicweb.org/2936496>`_)
-
-* action ``GotRhythm`` was removed, make sure you do not
-  import it in your cubes (even to unregister it)
-  (see `#3093362 <http://www.cubicweb.org/3093362>`_)
-
-* all 3.8 backward compat is gone
-
-* all 3.9 backward compat (including the javascript side) is gone
-
-* the ``twisted`` (web-only) instance type has been removed
--- a/doc/3.19.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,180 +0,0 @@
-What's new in CubicWeb 3.19?
-============================
-
-New functionalities
---------------------
-
-* implement Cross Origin Resource Sharing (CORS)
-  (see `#2491768 <http://www.cubicweb.org/2491768>`_)
-
-* system_source.create_eid can get a range of IDs, to reduce overhead of batch
-  entity creation
-
-Behaviour Changes
------------------
-
-* The anonymous property of Session and Connection are now computed from the
-  related user login. If it matches the ``anonymous-user`` in the config the
-  connection is anonymous. Beware that the ``anonymous-user`` config is web
-  specific. Therefore, no session may be anonymous in a repository only setup.
-
-
-New Repository Access API
--------------------------
-
-Connection replaces Session
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-A new explicit Connection object replaces Session as the main repository entry
-point. Connection holds all the necessary methods to be used server-side
-(``execute``, ``commit``, ``rollback``, ``call_service``, ``entity_from_eid``,
-etc...). One obtains a new Connection object using ``session.new_cnx()``.
-Connection objects need to have an explicit begin and end. Use them as a context
-manager to never miss an end::
-
-    with session.new_cnx() as cnx:
-        cnx.execute('INSERT Elephant E, E name "Babar"')
-        cnx.commit()
-        cnx.execute('INSERT Elephant E, E name "Celeste"')
-        cnx.commit()
-    # Once you get out of the "with" clause, the connection is closed.
-
-Using the same Connection object in multiple threads will give you access to the
-same Transaction. However, Connection objects are not thread safe (hence at your
-own risks).
-
-``repository.internal_session`` is deprecated in favor of
-``repository.internal_cnx``. Note that internal connections are now `safe` by default,
-i.e. the integrity hooks are enabled.
-
-Backward compatibility is preserved on Session.
-
-
-dbapi vs repoapi
-~~~~~~~~~~~~~~~~
-
-A new API has been introduced to replace the dbapi. It is called `repoapi`.
-
-There are three relevant functions for now:
-
-* ``repoapi.get_repository`` returns a Repository object either from an
-  URI when used as ``repoapi.get_repository(uri)`` or from a config
-  when used as ``repoapi.get_repository(config=config)``.
-
-* ``repoapi.connect(repo, login, **credentials)`` returns a ClientConnection
-  associated with the user identified by the credentials. The
-  ClientConnection is associated with its own Session that is closed
-  when the ClientConnection is closed. A ClientConnection is a
-  Connection-like object to be used client side.
-
-* ``repoapi.anonymous_cnx(repo)`` returns a ClientConnection associated
-  with the anonymous user if described in the config.
-
-
-repoapi.ClientConnection replace dbapi.Connection and company
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-On the client/web side, the Request is now using a ``repoapi.ClientConnection``
-instead of a ``dbapi.connection``. The ``ClientConnection`` has multiple backward
-compatible methods to make it look like a ``dbapi.Cursor`` and ``dbapi.Connection``.
-
-Session used on the Web side are now the same than the one used Server side.
-Some backward compatibility methods have been installed on the server side Session
-to ease the transition.
-
-The authentication stack has been altered to use the ``repoapi`` instead of
-the ``dbapi``. Cubes adding new element to this stack are likely to break.
-
-Session data can be accessed using the cnx.data dictionary, while
-transaction data is available through cnx.transaction_data.  These
-replace the [gs]et_shared_data methods with optional txid kwarg.
-
-New API in tests
-~~~~~~~~~~~~~~~~
-
-All current methods and attributes used to access the repo on ``CubicWebTC`` are
-deprecated. You may now use a ``RepoAccess`` object. A ``RepoAccess`` object is
-linked to a new ``Session`` for a specified user. It is able to create
-``Connection``, ``ClientConnection`` and web side requests linked to this
-session::
-
-    access = self.new_access('babar') # create a new RepoAccess for user babar
-    with access.repo_cnx() as cnx:
-        # some work with server side cnx
-        cnx.execute(...)
-        cnx.commit()
-        cnx.execute(...)
-        cnx.commit()
-
-    with access.client_cnx() as cnx:
-        # some work with client side cnx
-        cnx.execute(...)
-        cnx.commit()
-
-    with access.web_request(elephant='babar') as req:
-        # some work with client side cnx
-        elephant_name = req.form['elephant']
-        req.execute(...)
-        req.cnx.commit()
-
-By default ``testcase.admin_access`` contains a ``RepoAccess`` object for the
-default admin session.
-
-
-API changes
------------
-
-* ``RepositorySessionManager.postlogin`` is now called with two arguments,
-  request and session. And this now happens before the session is linked to the
-  request.
-
-* ``SessionManager`` and ``AuthenticationManager`` now take a repo object at
-  initialization time instead of a vreg.
-
-* The ``async`` argument of ``_cw.call_service`` has been dropped. All calls are
-  now  synchronous. The zmq notification bus looks like a good replacement for
-  most async use cases.
-
-* ``repo.stats()`` is now deprecated. The same information is available through
-  a service (``_cw.call_service('repo_stats')``).
-
-* ``repo.gc_stats()`` is now deprecated. The same information is available through
-  a service (``_cw.call_service('repo_gc_stats')``).
-
-* ``repo.register_user()`` is now deprecated.  The functionality is now
-  available through a service (``_cw.call_service('register_user')``).
-
-* ``request.set_session`` no longer takes an optional ``user`` argument.
-
-* CubicwebTC does not have repo and cnx as class attributes anymore. They are
-  standard instance attributes. ``set_cnx`` and ``_init_repo`` class methods
-  become instance methods.
-
-* ``set_cnxset`` and ``free_cnxset`` are deprecated. cnxset are now
-  automatically managed.
-
-* The implementation of cascading deletion when deleting `composite`
-  entities has changed. There comes a semantic change: merely deleting
-  a composite relation does not entail any more the deletion of the
-  component side of the relation.
-
-* ``_cw.user_callback`` and ``_cw.user_rql_callback`` are deprecated.  Users
-  are encouraged to write an actual controller (e.g. using ``ajaxfunc``)
-  instead of storing a closure in the session data.
-
-* A new ``entity.cw_linkable_rql`` method provides the rql to fetch all entities
-  that are already or may be related to the current entity using the given
-  relation.
-
-
-Deprecated Code Drops
-----------------------
-
-* session.hijack_user mechanism has been dropped.
-
-* EtypeRestrictionComponent has been removed, its functionality has been
-  replaced by facets a while ago.
-
-* the old multi-source support has been removed.  Only copy-based sources
-  remain, such as datafeed or ldapfeed.
-
--- a/doc/3.20.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-What's new in CubicWeb 3.20
-===========================
-
-New features
-------------
-
-* virtual relations: a new ComputedRelation class can be used in
-  schema.py; its `rule` attribute is an RQL snippet that defines the new
-  relation.
-
-* computed attributes: an attribute can now be defined with a `formula`
-  argument (also an RQL snippet); it will be read-only, and updated
-  automatically.
-
-  Both of these features are described in `CWEP-002`_, and the updated
-  "Data model" chapter of the CubicWeb book.
-
-* cubicweb-ctl plugins can use the ``cubicweb.utils.admincnx`` function
-  to get a Connection object from an instance name.
-
-* new 'tornado' wsgi backend
-
-* session cookies have the HttpOnly flag, so they're no longer exposed to
-  javascript
-
-* rich text fields can be formatted as markdown
-
-* the edit controller detects concurrent editions, and raises a ValidationError
-  if an entity was modified between form generation and submission
-
-* cubicweb can use a postgresql "schema" (namespace) for its tables
-
-* "cubicweb-ctl configure" can be used to set values of the admin user
-  credentials in the sources configuration file
-
-* in debug mode, setting the _cwtracehtml parameter on a request allows tracing
-  where each bit of output is produced
-
-.. _CWEP-002: http://hg.logilab.org/review/cwep/file/tip/CWEP-002.rst
-
-
-API Changes
------------
-
-* ``ucsvreader()`` and ``ucsvreader_pb()`` from the ``dataimport`` module have
-  2 new keyword arguments ``delimiter`` and ``quotechar`` to replace the
-  ``separator`` and ``quote`` arguments respectively. This makes the API match
-  that of Python's ``csv.reader()``.  The old arguments are still supported
-  though deprecated.
-
-* the migration environment's ``remove_cube`` function is now called ``drop_cube``.
-
-* cubicweb.old.css is now cubicweb.css.  The previous "new"
-  cubicweb.css, along with its cubicweb.reset.css companion, have been
-  removed.
-
-* the jquery-treeview plugin was updated to its latest version
-
-
-Deprecated Code Drops
-----------------------
-
-* most of 3.10 and 3.11 backward compat is gone; this includes:
-  - CtxComponent.box_action() and CtxComponent.build_link()
-  - cubicweb.devtools.htmlparser.XMLDemotingValidator
-  - various methods and properties on Entities, replaced by cw_edited and cw_attr_cache
-  - 'commit_event' method on hooks, replaced by 'postcommit_event'
-  - server.hook.set_operation(), replaced by Operation.get_instance(...).add_data()
-  - View.div_id(), View.div_class() and View.create_url()
-  - `*VComponent` classes
-  - in forms, Field.value() and Field.help() must take the form and the field itself as arguments
-  - form.render() must get `w` as a named argument, and renderer.render() must take `w` as first argument
-  - in breadcrumbs, the optional `recurs` argument must be a set, not False
-  - cubicweb.web.views.idownloadable.{download_box,IDownloadableLineView}
-  - primary views no longer have `render_entity_summary` and `summary` methods
-  - WFHistoryVComponent's `cell_call` method is replaced by `render_body`
-  - cubicweb.dataimport.ObjectStore.add(), replaced by create_entity
-  - ManageView.{folders,display_folders}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/Makefile	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,88 @@
+SRC=.
+
+# You can set these sphinx variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+#BUILDDIR      = build
+BUILDDIR      = _build
+CWDIR         = ..
+JSDIR         = ${CWDIR}/web/data
+JSTORST       = tools/pyjsrest.py
+BUILDJS       = js_api
+
+# Internal variables for sphinx
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d ${BUILDDIR}/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+
+
+.PHONY: help clean html web pickle htmlhelp latex changes linkcheck
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  all       to make standalone HTML files, developer manual and API doc"
+	@echo "  html      to make standalone HTML files"
+	@echo "---  "
+	@echo "  pickle    to make pickle files (usable by e.g. sphinx-web)"
+	@echo "  htmlhelp  to make HTML files and a HTML help project"
+	@echo "  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  changes   to make an overview over all changed/added/deprecated items"
+	@echo "  linkcheck to check all external links for integrity"
+
+clean:
+	rm -f *.html
+	-rm -rf ${BUILDDIR}/html ${BUILDDIR}/doctrees
+	-rm -rf ${BUILDJS}
+
+all: html
+
+# run sphinx ###
+html: js
+	mkdir -p ${BUILDDIR}/html ${BUILDDIR}/doctrees
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) ${BUILDDIR}/html
+	@echo
+	@echo "Build finished. The HTML pages are in ${BUILDDIR}/html."
+
+js:
+	mkdir -p ${BUILDJS}
+	$(JSTORST) -p ${JSDIR} -o ${BUILDJS}
+
+pickle:
+	mkdir -p ${BUILDDIR}/pickle ${BUILDDIR}/doctrees
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) ${BUILDDIR}/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files or run"
+	@echo "  sphinx-web ${BUILDDIR}/pickle"
+	@echo "to start the sphinx-web server."
+
+web: pickle
+
+htmlhelp:
+	mkdir -p ${BUILDDIR}/htmlhelp ${BUILDDIR}/doctrees
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) ${BUILDDIR}/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in ${BUILDDIR}/htmlhelp."
+
+latex:
+	mkdir -p ${BUILDDIR}/latex ${BUILDDIR}/doctrees
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) ${BUILDDIR}/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in ${BUILDDIR}/latex."
+	@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+	      "run these through (pdf)latex."
+
+changes:
+	mkdir -p ${BUILDDIR}/changes ${BUILDDIR}/doctrees
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) ${BUILDDIR}/changes
+	@echo
+	@echo "The overview file is in ${BUILDDIR}/changes."
+
+linkcheck:
+	mkdir -p ${BUILDDIR}/linkcheck ${BUILDDIR}/doctrees
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) ${BUILDDIR}/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in ${BUILDDIR}/linkcheck/output.txt."
Binary file doc/_static/cubicweb.png has changed
Binary file doc/_static/logilab.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/_static/sphinx-default.css	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,861 @@
+/**
+ * Sphinx Doc Design
+ */
+
+html, body {
+    background: white;
+}
+
+body {
+    font-family: Verdana, sans-serif;
+    font-size: 100%;
+    background-color: white;
+    color: black;
+    margin: 0;
+    padding: 0;
+}
+
+/* :::: LAYOUT :::: */
+
+div.logilablogo {
+    padding: 10px 10px 10px 10px;
+    height:75;
+}
+
+
+div.document {
+    background-color: white;
+}
+
+div.documentwrapper {
+    float: left;
+    width: 100%;
+}
+
+div.bodywrapper {
+    margin: 0 0 0 230px;
+}
+
+div.body {
+    background-color: white;
+    padding: 0 20px 30px 20px;
+    border-left:solid;
+    border-left-color:#e2e2e2;
+    border-left-width:thin;
+}
+
+div.sphinxsidebarwrapper {
+    padding: 10px 5px 0 10px;
+}
+
+div.sphinxsidebar {
+    float: left;
+    width: 230px;
+    margin-left: -100%;
+    font-size: 90%;
+}
+
+div.clearer {
+    clear: both;
+}
+
+div.footer {
+    color: #ff4500;
+    width: 100%;
+    padding: 9px 0 9px 0;
+    text-align: center;
+    font-size: 75%;
+}
+
+div.footer a {
+    color: #ff4500;
+    text-decoration: underline;
+}
+
+div.related {
+    background-color: #ff7700;
+    color: white;
+    width: 100%;
+    height: 30px;
+    line-height: 30px;
+    font-size: 90%;
+}
+
+div.related h3 {
+    display: none;
+}
+
+div.related ul {
+    margin: 0;
+    padding: 0 0 0 10px;
+    list-style: none;
+}
+
+div.related li {
+    display: inline;
+}
+
+div.related li.right {
+    float: right;
+    margin-right: 5px;
+}
+
+div.related a {
+    color: white;
+    font-weight:bold;
+}
+
+/* ::: TOC :::: */
+
+div.sphinxsidebar {
+    border-style:solid;
+    border-color: white;
+/*    background-color:#e2e2e2;*/
+    padding-bottom:5px;
+}
+
+div.sphinxsidebar h3 {
+    font-family: Verdana, sans-serif;
+    color: black;
+    font-size: 1.2em;
+    font-weight: normal;
+    margin: 0;
+    padding: 0;
+    font-weight:bold;
+    font-style:italic;
+}
+
+div.sphinxsidebar h4 {
+    font-family: Verdana, sans-serif;
+    color: black;
+    font-size: 1.1em;
+    font-weight: normal;
+    margin: 5px 0 0 0;
+    padding: 0;
+    font-weight:bold;
+    font-style:italic;
+}
+
+div.sphinxsidebar p {
+    color: black;
+}
+
+div.sphinxsidebar p.topless {
+    margin: 5px 10px 10px 10px;
+}
+
+div.sphinxsidebar ul {
+    margin: 10px;
+    padding: 0;
+    list-style: none;
+    color: black;
+}
+
+div.sphinxsidebar ul ul,
+div.sphinxsidebar ul.want-points {
+    margin-left: 20px;
+    list-style: square;
+}
+
+div.sphinxsidebar ul ul {
+    margin-top: 0;
+    margin-bottom: 0;
+}
+
+div.sphinxsidebar a {
+    color: black;
+    text-decoration: none;
+}
+
+div.sphinxsidebar form {
+    margin-top: 10px;
+}
+
+div.sphinxsidebar input {
+    border: 1px solid #e2e2e2;
+    font-family: sans-serif;
+    font-size: 1em;
+    padding-bottom: 5px;
+}
+
+/* :::: MODULE CLOUD :::: */
+div.modulecloud {
+    margin: -5px 10px 5px 10px;
+    padding: 10px;
+    line-height: 160%;
+    border: 1px solid #cbe7e5;
+    background-color: #f2fbfd;
+}
+
+div.modulecloud a {
+    padding: 0 5px 0 5px;
+}
+
+/* :::: SEARCH :::: */
+ul.search {
+    margin: 10px 0 0 20px;
+    padding: 0;
+}
+
+ul.search li {
+    padding: 5px 0 5px 20px;
+    background-image: url(file.png);
+    background-repeat: no-repeat;
+    background-position: 0 7px;
+}
+
+ul.search li a {
+    font-weight: bold;
+}
+
+ul.search li div.context {
+    color: #888;
+    margin: 2px 0 0 30px;
+    text-align: left;
+}
+
+ul.keywordmatches li.goodmatch a {
+    font-weight: bold;
+}
+
+/* :::: COMMON FORM STYLES :::: */
+
+div.actions {
+    padding: 5px 10px 5px 10px;
+    border-top: 1px solid #cbe7e5;
+    border-bottom: 1px solid #cbe7e5;
+    background-color: #e0f6f4;
+}
+
+form dl {
+    color: #333;
+}
+
+form dt {
+    clear: both;
+    float: left;
+    min-width: 110px;
+    margin-right: 10px;
+    padding-top: 2px;
+}
+
+input#homepage {
+    display: none;
+}
+
+div.error {
+    margin: 5px 20px 0 0;
+    padding: 5px;
+    border: 1px solid #d00;
+    font-weight: bold;
+}
+
+/* :::: INLINE COMMENTS :::: */
+
+div.inlinecomments {
+    position: absolute;
+    right: 20px;
+}
+
+div.inlinecomments a.bubble {
+    display: block;
+    float: right;
+    background-image: url(style/comment.png);
+    background-repeat: no-repeat;
+    width: 25px;
+    height: 25px;
+    text-align: center;
+    padding-top: 3px;
+    font-size: 0.9em;
+    line-height: 14px;
+    font-weight: bold;
+    color: black;
+}
+
+div.inlinecomments a.bubble span {
+    display: none;
+}
+
+div.inlinecomments a.emptybubble {
+    background-image: url(style/nocomment.png);
+}
+
+div.inlinecomments a.bubble:hover {
+    background-image: url(style/hovercomment.png);
+    text-decoration: none;
+    color: #3ca0a4;
+}
+
+div.inlinecomments div.comments {
+    float: right;
+    margin: 25px 5px 0 0;
+    max-width: 50em;
+    min-width: 30em;
+    border: 1px solid #2eabb0;
+    background-color: #f2fbfd;
+    z-index: 150;
+}
+
+div#comments {
+    border: 1px solid #2eabb0;
+    margin-top: 20px;
+}
+
+div#comments div.nocomments {
+    padding: 10px;
+    font-weight: bold;
+}
+
+div.inlinecomments div.comments h3,
+div#comments h3 {
+    margin: 0;
+    padding: 0;
+    background-color: #2eabb0;
+    color: white;
+    border: none;
+    padding: 3px;
+}
+
+div.inlinecomments div.comments div.actions {
+    padding: 4px;
+    margin: 0;
+    border-top: none;
+}
+
+div#comments div.comment {
+    margin: 10px;
+    border: 1px solid #2eabb0;
+}
+
+div.inlinecomments div.comment h4,
+div.commentwindow div.comment h4,
+div#comments div.comment h4 {
+    margin: 10px 0 0 0;
+    background-color: #2eabb0;
+    color: white;
+    border: none;
+    padding: 1px 4px 1px 4px;
+}
+
+div#comments div.comment h4 {
+    margin: 0;
+}
+
+div#comments div.comment h4 a {
+    color: #d5f4f4;
+}
+
+div.inlinecomments div.comment div.text,
+div.commentwindow div.comment div.text,
+div#comments div.comment div.text {
+    margin: -5px 0 -5px 0;
+    padding: 0 10px 0 10px;
+}
+
+div.inlinecomments div.comment div.meta,
+div.commentwindow div.comment div.meta,
+div#comments div.comment div.meta {
+    text-align: right;
+    padding: 2px 10px 2px 0;
+    font-size: 95%;
+    color: #538893;
+    border-top: 1px solid #cbe7e5;
+    background-color: #e0f6f4;
+}
+
+div.commentwindow {
+    position: absolute;
+    width: 500px;
+    border: 1px solid #cbe7e5;
+    background-color: #f2fbfd;
+    display: none;
+    z-index: 130;
+}
+
+div.commentwindow h3 {
+    margin: 0;
+    background-color: #2eabb0;
+    color: white;
+    border: none;
+    padding: 5px;
+    font-size: 1.5em;
+    cursor: pointer;
+}
+
+div.commentwindow div.actions {
+    margin: 10px -10px 0 -10px;
+    padding: 4px 10px 4px 10px;
+    color: #538893;
+}
+
+div.commentwindow div.actions input {
+    border: 1px solid #2eabb0;
+    background-color: white;
+    color: #135355;
+    cursor: pointer;
+}
+
+div.commentwindow div.form {
+    padding: 0 10px 0 10px;
+}
+
+div.commentwindow div.form input,
+div.commentwindow div.form textarea {
+    border: 1px solid #3c9ea2;
+    background-color: white;
+    color: black;
+}
+
+div.commentwindow div.error {
+    margin: 10px 5px 10px 5px;
+    background-color: #fbe5dc;
+    display: none;
+}
+
+div.commentwindow div.form textarea {
+    width: 99%;
+}
+
+div.commentwindow div.preview {
+    margin: 10px 0 10px 0;
+    background-color: #70d0d4;
+    padding: 0 1px 1px 25px;
+}
+
+div.commentwindow div.preview h4 {
+    margin: 0 0 -5px -20px;
+    padding: 4px 0 0 4px;
+    color: white;
+    font-size: 1.3em;
+}
+
+div.commentwindow div.preview div.comment {
+    background-color: #f2fbfd;
+}
+
+div.commentwindow div.preview div.comment h4 {
+    margin: 10px 0 0 0!important;
+    padding: 1px 4px 1px 4px!important;
+    font-size: 1.2em;
+}
+
+/* :::: SUGGEST CHANGES :::: */
+div#suggest-changes-box input, div#suggest-changes-box textarea {
+    border: 1px solid #ccc;
+    background-color: white;
+    color: black;
+}
+
+div#suggest-changes-box textarea {
+    width: 99%;
+    height: 400px;
+}
+
+
+/* :::: PREVIEW :::: */
+div.preview {
+    background-image: url(style/preview.png);
+    padding: 0 20px 20px 20px;
+    margin-bottom: 30px;
+}
+
+
+/* :::: INDEX PAGE :::: */
+
+table.contentstable {
+    width: 90%;
+}
+
+table.contentstable p.biglink {
+    line-height: 150%;
+}
+
+a.biglink {
+    font-size: 1.3em;
+}
+
+span.linkdescr {
+    font-style: italic;
+    padding-top: 5px;
+    font-size: 90%;
+}
+
+/* :::: INDEX STYLES :::: */
+
+table.indextable td {
+    text-align: left;
+    vertical-align: top;
+}
+
+table.indextable dl, table.indextable dd {
+    margin-top: 0;
+    margin-bottom: 0;
+}
+
+table.indextable tr.pcap {
+    height: 10px;
+}
+
+table.indextable tr.cap {
+    margin-top: 10px;
+    background-color: #f2f2f2;
+}
+
+img.toggler {
+    margin-right: 3px;
+    margin-top: 3px;
+    cursor: pointer;
+}
+
+form.pfform {
+    margin: 10px 0 20px 0;
+}
+
+/* :::: GLOBAL STYLES :::: */
+
+.docwarning {
+    background-color: #ffe4e4;
+    padding: 10px;
+    margin: 0 -20px 0 -20px;
+    border-bottom: 1px solid #f66;
+}
+
+p.subhead {
+    font-weight: bold;
+    margin-top: 20px;
+}
+
+a {
+    color: orangered;
+    text-decoration: none;
+}
+
+a:hover {
+    text-decoration: underline;
+}
+
+div.body h1,
+div.body h2,
+div.body h3,
+div.body h4,
+div.body h5,
+div.body h6 {
+    font-family: 'Verdana', sans-serif;
+    background-color: white;
+    font-weight: bold;
+    color: black;
+    border-bottom: 1px solid #ccc;
+    margin: 20px -20px 10px -20px;
+    padding: 3px 0 3px 10px;
+}
+
+div.body h1 { margin-top: 10pt; font-size: 150%; }
+div.body h2 { font-size: 120%; }
+div.body h3 { font-size: 100%; }
+div.body h4 { font-size: 80%; }
+div.body h5 { font-size: 600%; }
+div.body h6 { font-size: 40%; }
+
+a.headerlink {
+    color: #c60f0f;
+    font-size: 0.8em;
+    padding: 0 4px 0 4px;
+    text-decoration: none;
+    visibility: hidden;
+}
+
+h1:hover > a.headerlink,
+h2:hover > a.headerlink,
+h3:hover > a.headerlink,
+h4:hover > a.headerlink,
+h5:hover > a.headerlink,
+h6:hover > a.headerlink,
+dt:hover > a.headerlink {
+    visibility: visible;
+}
+
+a.headerlink:hover {
+    background-color: #c60f0f;
+    color: white;
+}
+
+div.body p, div.body dd, div.body li {
+    text-align: justify;
+    line-height: 130%;
+}
+
+div.body p.caption {
+    text-align: inherit;
+}
+
+div.body td {
+    text-align: left;
+}
+
+ul.fakelist {
+    list-style: none;
+    margin: 10px 0 10px 20px;
+    padding: 0;
+}
+
+.field-list ul {
+    padding-left: 1em;
+}
+
+.first {
+    margin-top: 0 !important;
+}
+
+/* "Footnotes" heading */
+p.rubric {
+    margin-top: 30px;
+    font-weight: bold;
+}
+
+/* "Topics" */
+
+div.topic {
+    background-color: #eee;
+    border: 1px solid #ccc;
+    padding: 0 7px 0 7px;
+    margin: 10px 0 10px 0;
+}
+
+p.topic-title {
+    font-size: 1.1em;
+    font-weight: bold;
+    margin-top: 10px;
+}
+
+/* Admonitions */
+
+div.admonition {
+    margin-top: 10px;
+    margin-bottom: 10px;
+    padding: 7px;
+}
+
+div.admonition dt {
+    font-weight: bold;
+}
+
+div.admonition dl {
+    margin-bottom: 0;
+}
+
+div.admonition p {
+    display: inline;
+}
+
+div.seealso {
+    background-color: #ffc;
+    border: 1px solid #ff6;
+}
+
+div.warning {
+    background-color: #ffe4e4;
+    border: 1px solid #f66;
+}
+
+div.note {
+    background-color: #eee;
+    border: 1px solid #ccc;
+}
+
+p.admonition-title {
+    margin: 0px 10px 5px 0px;
+    font-weight: bold;
+    display: inline;
+}
+
+p.admonition-title:after {
+    content: ":";
+}
+
+div.body p.centered {
+    text-align: center;
+    margin-top: 25px;
+}
+
+table.docutils {
+    border: 0;
+}
+
+table.docutils td, table.docutils th {
+    padding: 1px 8px 1px 0;
+    border-top: 0;
+    border-left: 0;
+    border-right: 0;
+    border-bottom: 1px solid #aaa;
+}
+
+table.field-list td, table.field-list th {
+    border: 0 !important;
+}
+
+table.footnote td, table.footnote th {
+    border: 0 !important;
+}
+
+.field-list ul {
+    margin: 0;
+    padding-left: 1em;
+}
+
+.field-list p {
+    margin: 0;
+}
+
+dl {
+    margin-bottom: 15px;
+    clear: both;
+}
+
+dd p {
+    margin-top: 0px;
+}
+
+dd ul, dd table {
+    margin-bottom: 10px;
+}
+
+dd {
+    margin-top: 3px;
+    margin-bottom: 10px;
+    margin-left: 30px;
+}
+
+.refcount {
+    color: #060;
+}
+
+dt:target,
+.highlight {
+    background-color: #fbe54e;
+}
+
+dl.glossary dt {
+    font-weight: bold;
+    font-size: 1.1em;
+}
+
+th {
+    text-align: left;
+    padding-right: 5px;
+}
+
+pre {
+    padding: 5px;
+    background-color: #efc;
+    color: #333;
+    border: 1px solid #ac9;
+    border-left: none;
+    border-right: none;
+    overflow: auto;
+}
+
+td.linenos pre {
+    padding: 5px 0px;
+    border: 0;
+    background-color: transparent;
+    color: #aaa;
+}
+
+table.highlighttable {
+    margin-left: 0.5em;
+}
+
+table.highlighttable td {
+    padding: 0 0.5em 0 0.5em;
+}
+
+tt {
+    background-color: #ecf0f3;
+    padding: 0 1px 0 1px;
+    font-size: 0.95em;
+}
+
+tt.descname {
+    background-color: transparent;
+    font-weight: bold;
+    font-size: 1.2em;
+}
+
+tt.descclassname {
+    background-color: transparent;
+}
+
+tt.xref, a tt {
+    background-color: transparent;
+    font-weight: bold;
+}
+
+.footnote:target  { background-color: #ffa }
+
+h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
+    background-color: transparent;
+}
+
+.optional {
+    font-size: 1.3em;
+}
+
+.versionmodified {
+    font-style: italic;
+}
+
+form.comment {
+    margin: 0;
+    padding: 10px 30px 10px 30px;
+    background-color: #eee;
+}
+
+form.comment h3 {
+    background-color: #326591;
+    color: white;
+    margin: -10px -30px 10px -30px;
+    padding: 5px;
+    font-size: 1.4em;
+}
+
+form.comment input,
+form.comment textarea {
+    border: 1px solid #ccc;
+    padding: 2px;
+    font-family: sans-serif;
+    font-size: 100%;
+}
+
+form.comment input[type="text"] {
+    width: 240px;
+}
+
+form.comment textarea {
+    width: 100%;
+    height: 200px;
+    margin-bottom: 10px;
+}
+
+.system-message {
+    background-color: #fda;
+    padding: 5px;
+    border: 3px solid red;
+}
+
+/* :::: PRINT :::: */
+@media print {
+    div.document,
+    div.documentwrapper,
+    div.bodywrapper {
+        margin: 0;
+        width : 100%;
+    }
+
+    div.sphinxsidebar,
+    div.related,
+    div.footer,
+    div#comments div.new-comment-box,
+    #top-link {
+        display: none;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/_templates/layout.html	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,196 @@
+{%- block doctype -%}
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+{%- endblock %}
+{%- set reldelim1 = reldelim1 is not defined and ' &raquo;' or reldelim1 %}
+{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %}
+{%- macro relbar() %}
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        {%- for rellink in rellinks %}
+        <li class="right" {% if loop.first %}style="margin-right: 10px"{% endif %}>
+          <a href="{{ pathto(rellink[0]) }}" title="{{ rellink[1]|striptags }}"
+             accesskey="{{ rellink[2] }}">{{ rellink[3] }}</a>
+          {%- if not loop.first %}{{ reldelim2 }}{% endif %}</li>
+        {%- endfor %}
+        {%- block rootrellink %}
+        <li><a href="{{ pathto('index') }}">{{ shorttitle }}</a>{{ reldelim1 }}</li>
+        {%- endblock %}
+        {%- for parent in parents %}
+          <li><a href="{{ parent.link|e }}" accesskey="U">{{ parent.title }}</a>{{ reldelim1 }}</li>
+        {%- endfor %}
+        {%- block relbaritems %}{% endblock %}
+      </ul>
+    </div>
+{%- endmacro %}
+{%- macro sidebar() %}
+      {%- if builder != 'htmlhelp' %}
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+          {%- block sidebarlogo %}
+          {%- if logo %}
+            <p class="logo"><img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/></p>
+          {%- endif %}
+          {%- endblock %}
+          {%- block sidebartoc %}
+          {%- if display_toc %}
+            <h3>Table Of Contents</h3>
+            {{ toc }}
+          {%- endif %}
+          {%- endblock %}
+          {%- block sidebarrel %}
+          {%- if prev %}
+            <h4>Previous topic</h4>
+            <p class="topless"><a href="{{ prev.link|e }}" title="previous chapter">{{ prev.title }}</a></p>
+          {%- endif %}
+          {%- if next %}
+            <h4>Next topic</h4>
+            <p class="topless"><a href="{{ next.link|e }}" title="next chapter">{{ next.title }}</a></p>
+          {%- endif %}
+          {%- endblock %}
+          {%- if sourcename %}
+            <!--<h3>This Page</h3>
+            <ul class="this-page-menu">
+            {%- if builder == 'web' %}
+              <li><a href="#comments">Comments ({{ comments|length }} so far)</a></li>
+              <li><a href="{{ pathto('@edit/' + sourcename)|e }}">Suggest Change</a></li>
+              <li><a href="{{ pathto('@source/' + sourcename)|e }}">Show Source</a></li>
+            {%- elif builder == 'html' %}
+              <li><a href="{{ pathto('_sources/' + sourcename, true)|e }}">Show Source</a></li>
+            {%- endif %}
+            </ul>-->
+          {%- endif %}
+          {%- if customsidebar %}
+          {{ rendertemplate(customsidebar) }}
+          {%- endif %}
+          {%- block sidebarsearch %}
+          {%- if pagename != "search" %}
+            <h3>{{ builder == 'web' and 'Keyword' or 'Quick' }} search</h3>
+            <form class="search" action="{{ pathto('search') }}" method="get">
+              <input type="text" name="q" size="18" /> <input type="submit" value="Go" />
+              <input type="hidden" name="check_keywords" value="yes" />
+              <input type="hidden" name="area" value="default" />
+            </form>
+            {%- if builder == 'web' %}
+            <p style="font-size: 90%">Enter a module, class or function name.</p>
+            {%- endif %}
+          {%- endif %}
+          {%- endblock %}
+        </div>
+      </div>
+      {%- endif %}
+{%- endmacro -%}
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    {%- if builder != 'htmlhelp' %}
+      {%- set titlesuffix = " &mdash; " + docstitle %}
+    {%- endif %}
+    <title>{{ title|striptags }}{{ titlesuffix }}</title>
+    {%- if builder == 'web' %}
+    <link rel="stylesheet" href="{{ pathto('index') }}?do=stylesheet{%
+      if in_admin_panel %}&admin=yes{% endif %}" type="text/css" />
+    {%- for link, type, title in page_links %}
+    <link rel="alternate" type="{{ type|e(true) }}" title="{{ title|e(true) }}" href="{{ link|e(true) }}" />
+    {%- endfor %}
+    {%- else %}
+    <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
+    <link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" />
+    {%- endif %}
+    {%- if builder != 'htmlhelp' %}
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+          URL_ROOT:    '{{ pathto("", 1) }}',
+          VERSION:     '{{ release }}',
+          COLLAPSE_MODINDEX: false,
+          FILE_SUFFIX: '{{ file_suffix }}'
+      };
+    </script>
+    <script type="text/javascript" src="{{ pathto('_static/jquery.js', 1) }}"></script>
+    <script type="text/javascript" src="{{ pathto('_static/interface.js', 1) }}"></script>
+    <script type="text/javascript" src="{{ pathto('_static/doctools.js', 1) }}"></script>
+    <script type="text/javascript" src="{{ pathto('_static/searchtools.js', 1) }}"></script>
+    {%- if use_opensearch %}
+    <link rel="search" type="application/opensearchdescription+xml"
+          title="Search within {{ docstitle }}"
+          href="{{ pathto('_static/opensearch.xml', 1) }}"/>
+    {%- endif %}
+    {%- if favicon %}
+    <link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/>
+    {%- endif %}
+    {%- endif %}
+{%- block rellinks %}
+    {%- if hasdoc('about') %}
+    <link rel="author" title="About these documents" href="{{ pathto('about') }}" />
+    {%- endif %}
+    <link rel="contents" title="Global table of contents" href="{{ pathto('contents') }}" />
+    <link rel="index" title="Global index" href="{{ pathto('genindex') }}" />
+    <link rel="search" title="Search" href="{{ pathto('search') }}" />
+    {%- if hasdoc('copyright') %}
+    <link rel="copyright" title="Copyright" href="{{ pathto('copyright') }}" />
+    {%- endif %}
+    <link rel="top" title="{{ docstitle }}" href="{{ pathto('index') }}" />
+    {%- if parents %}
+    <link rel="up" title="{{ parents[-1].title|striptags }}" href="{{ parents[-1].link|e }}" />
+    {%- endif %}
+    {%- if next %}
+    <link rel="next" title="{{ next.title|striptags }}" href="{{ next.link|e }}" />
+    {%- endif %}
+    {%- if prev %}
+    <link rel="prev" title="{{ prev.title|striptags }}" href="{{ prev.link|e }}" />
+    {%- endif %}
+{%- endblock %}
+{%- block extrahead %}{% endblock %}
+  </head>
+  <body>
+
+{% block logilablogo %}
+<div class="logilablogo">
+	<a class="logogo" href="http://www.cubicweb.org"><img border="0" src="{{ pathto('_static/cubicweb.png', 1) }}"/></a>
+  </div>
+{% endblock %}
+
+{%- block relbar1 %}{{ relbar() }}{% endblock %}
+
+{%- block sidebar1 %}{# possible location for sidebar #}{% endblock %}
+
+{%- block document %}
+    <div class="document">
+      <div class="documentwrapper">
+      {%- if builder != 'htmlhelp' %}
+        <div class="bodywrapper">
+      {%- endif %}
+          <div class="body">
+            {% block body %}{% endblock %}
+          </div>
+      {%- if builder != 'htmlhelp' %}
+        </div>
+      {%- endif %}
+      </div>
+{%- endblock %}
+
+{%- block sidebar2 %}{{ sidebar() }}{% endblock %}
+      <div class="clearer"></div>
+    </div>
+
+{%- block relbar2 %}{{ relbar() }}{% endblock %}
+
+{%- block footer %}
+    <div class="footer">
+    {%- if hasdoc('copyright') %}
+      &copy; <a href="{{ pathto('copyright') }}">Copyright</a> {{ copyright }}.
+    {%- else %}
+      &copy; Copyright {{ copyright }}.
+    {%- endif %}
+    {%- if last_updated %}
+      Last updated on {{ last_updated }}.
+    {%- endif %}
+    {%- if show_sphinx %}
+      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+    {%- endif %}
+    </div>
+{%- endblock %}
+  </body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/_themes/cubicweb/layout.html	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,61 @@
+{% extends "basic/layout.html" %}
+
+{%- block extrahead %}
+<!--[if lte IE 6]>
+<link rel="stylesheet" href="{{ pathto('_static/ie6.css', 1) }}" type="text/css" media="screen" charset="utf-8" />
+<![endif]-->
+{%- if theme_favicon %}
+<link rel="shortcut icon" href="{{ pathto('_static/'+theme_favicon, 1) }}"/>
+{%- endif %}
+
+{%- if theme_canonical_url %}
+<link rel="canonical" href="{{ theme_canonical_url }}{{ pagename }}.html"/>
+{%- endif %}
+{% endblock %}
+
+{% block header %}
+
+{% if theme_in_progress|tobool %}
+    <img style="position: fixed; display: block; width: 165px; height: 165px; bottom: 60px; right: 0; border: 0;" src="{{ pathto('_static/in_progress.png', 1) }}" alt="Documentation in progress" />
+{% endif %}
+
+{% if theme_outdated|tobool %}
+    <div style="bottom: 60px; right: 20px;position: fixed;"><a href="{{ latest_url }}" class="btn btn-large btn-danger"><strong>&gt;</strong> Read the latest version of this page</a></div>
+{% endif %}
+
+<div class="header-small">
+	{%- if theme_logo %}
+	{% set img, ext = theme_logo.split('.', -1) %}
+	<div class="logo-small">
+		<a href="{{ pathto(master_doc) }}">
+      		<img class="logo" src="{{ pathto('_static/%s-small.%s' % (img, ext), 1)}}" alt="Logo"/>
+		</a>
+  	</div>
+  	{%- endif %}
+</div>
+{% endblock %}
+
+{%- macro relbar() %}
+<div class="related">
+	<h3>{{ _('Navigation') }}</h3>
+	<ul>
+		{%- for rellink in rellinks %}
+		<li class="right" {% if loop.first %}style="margin-right: 10px"{% endif %}>
+			<a href="{{ pathto(rellink[0]) }}" title="{{ rellink[1]|striptags|e }}"
+			{{ accesskey(rellink[2]) }}>{{ rellink[3] }}</a>
+			{%- if not loop.first %}{{ reldelim2 }}{% endif %}
+		</li>
+		{%- endfor %}
+    	{%- block rootrellink %}
+    	<li><a href="{{ pathto(master_doc) }}">{{ docstitle|e }}</a>{{ reldelim1 }}</li>
+    	{%- endblock %}
+    	{%- for parent in parents %}
+          <li><a href="{{ parent.link|e }}" {% if loop.last %}{{ accesskey("U") }}{% endif %}>{{ parent.title }}</a>{{ reldelim1 }}</li>
+        {%- endfor %}
+        {%- block relbaritems %} {% endblock %}
+  	</ul>
+</div>
+{%- endmacro %}
+
+{%- block sidebarlogo %}{%- endblock %}
+{%- block sidebarsourcelink %}{%- endblock %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/_themes/cubicweb/static/cubicweb.css_t	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,33 @@
+/*
+ * cubicweb.css_t
+ * ~~~~~~~~~~~~~~
+ *
+ * Sphinx stylesheet -- cubicweb theme.
+ *
+ * :copyright: Copyright 2014 by the Cubicweb team, see AUTHORS.
+ * :license: LGPL, see LICENSE for details.
+ *
+ */
+ 
+@import url("pyramid.css");
+
+div.header-small {
+  background-image: linear-gradient(white, #e2e2e2);
+  border-bottom: 1px solid #bbb;
+}
+
+div.logo-small {
+  padding: 10px;
+}
+
+img.logo {
+  width: 150px;
+}
+
+div.related a {
+  color: #e6820e;
+}
+
+a, a .pre {
+  color: #e6820e;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/_themes/cubicweb/static/cubicweb.ico	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,1 @@
+../../../../web/data/favicon.ico
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/_themes/cubicweb/static/logo-cubicweb-small.svg	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,1 @@
+logo-cubicweb.svg
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/_themes/cubicweb/static/logo-cubicweb.svg	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,1 @@
+../../../../web/data/logo-cubicweb.svg
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/_themes/cubicweb/theme.conf	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,12 @@
+[theme]
+inherit = pyramid
+pygments_style = sphinx.pygments_styles.PyramidStyle
+stylesheet = cubicweb.css
+
+
+[options]
+logo = logo-cubicweb.svg
+favicon = cubicweb.ico
+in_progress = false
+outdated = false
+canonical_url = 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/api/__init__.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,115 @@
+.. _index_module:
+
+:mod:`cubicweb`
+===============
+
+.. automodule:: cubicweb
+
+    Exceptions
+    ----------
+
+    Base exceptions
+    ~~~~~~~~~~~~~~~
+
+    .. autoexception:: ProgrammingError
+        :show-inheritance:
+
+    .. autoexception:: CubicWebException
+        :show-inheritance:
+
+    .. autoexception:: InternalError
+        :show-inheritance:
+
+    .. autoexception:: SecurityError
+        :show-inheritance:
+
+    .. autoexception:: RepositoryError
+        :show-inheritance:
+
+    .. autoexception:: SourceException
+        :show-inheritance:
+
+    .. autoexception:: CubicWebRuntimeError
+        :show-inheritance:
+
+    Repository exceptions
+    ~~~~~~~~~~~~~~~~~~~~~
+
+    .. autoexception:: ConnectionError
+        :show-inheritance:
+
+    .. autoexception:: AuthenticationError
+        :show-inheritance:
+
+    .. autoexception:: BadConnectionId
+        :show-inheritance:
+
+    .. autoexception:: UnknownEid
+        :show-inheritance:
+
+    .. autoexception:: UniqueTogetherError
+        :show-inheritance:
+
+    Security Exceptions
+    ~~~~~~~~~~~~~~~~~~~
+
+    .. autoexception:: Unauthorized
+        :show-inheritance:
+
+    .. autoexception:: Forbidden
+        :show-inheritance:
+
+    Source exceptions
+    ~~~~~~~~~~~~~~~~~
+
+    .. autoexception:: EidNotInSource
+        :show-inheritance:
+
+    Registry exceptions
+    ~~~~~~~~~~~~~~~~~~~
+
+    .. autoexception:: UnknownProperty
+        :show-inheritance:
+
+    Query exceptions
+    ~~~~~~~~~~~~~~~~
+
+    .. autoexception:: QueryError
+        :show-inheritance:
+
+    .. autoexception:: NotAnEntity
+        :show-inheritance:
+
+    .. autoexception:: MultipleResultsError
+        :show-inheritance:
+
+    .. autoexception:: NoResultError
+        :show-inheritance:
+
+    .. autoexception:: UndoTransactionException
+        :show-inheritance:
+
+
+    Misc
+    ~~~~
+
+    .. autoexception:: ConfigurationError
+        :show-inheritance:
+
+    .. autoexception:: ExecutionError
+        :show-inheritance:
+
+    .. autoexception:: BadCommandUsage
+        :show-inheritance:
+
+    .. autoexception:: ValidationError
+        :show-inheritance:
+
+
+    Utilities
+    ---------
+
+    .. autoclass:: Binary
+    .. autoclass:: CubicWebEventManager
+    .. autofunction:: onevent
+    .. autofunction:: validation_error
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/api/appobject.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,10 @@
+.. _appobject_module:
+
+:mod:`cubicweb.appobject`
+=========================
+
+.. automodule:: cubicweb.appobject
+
+   .. autoclass:: AppObject
+      :show-inheritance:
+      :members:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/api/cwvreg.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,45 @@
+.. _cwvreg_module:
+
+:mod:`cubicweb.cwvreg`
+======================
+
+.. automodule:: cubicweb.cwvreg
+
+    .. autoclass:: CWRegistryStore
+        :show-inheritance:
+        :members:
+        :undoc-members:
+
+    .. autoclass:: CWRegistry
+        :show-inheritance:
+        :members: schema, poss_visible_objects, select
+
+    .. autoclass:: InstancesRegistry
+        :show-inheritance:
+        :members:
+
+    .. autoclass:: ETypeRegistry
+        :show-inheritance:
+        :members:
+
+    .. autoclass:: ViewsRegistry
+        :show-inheritance:
+        :members:
+
+    .. autoclass:: ActionsRegistry
+        :show-inheritance:
+        :members:
+
+    .. autoclass:: CtxComponentsRegistry
+        :show-inheritance:
+        :members:
+
+    .. autoclass:: BwCompatCWRegistry
+        :show-inheritance:
+        :members:
+
+
+:mod:`logilab.common.registry`
+==============================
+
+.. automodule:: logilab.common.registry
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/api/dataimport.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,63 @@
+.. _dataimport_module:
+
+:mod:`cubicweb.dataimport`
+==========================
+
+.. automodule:: cubicweb.dataimport
+
+    Utilities
+    ---------
+
+    .. autofunction:: count_lines
+
+    .. autofunction:: ucsvreader_pb
+
+    .. autofunction:: ucsvreader
+
+    .. autofunction:: callfunc_every
+
+    .. autofunction:: lazytable
+
+    .. autofunction:: lazydbtable
+
+    .. autofunction:: mk_entity
+
+    Sanitizing/coercing functions
+    -----------------------------
+
+    .. autofunction:: optional
+    .. autofunction:: required
+    .. autofunction:: todatetime
+    .. autofunction:: call_transform_method
+    .. autofunction:: call_check_method
+
+    Integrity functions
+    -------------------
+
+    .. autofunction:: check_doubles
+    .. autofunction:: check_doubles_not_none
+
+    Object Stores
+    -------------
+
+    .. autoclass:: ObjectStore
+        :members:
+
+    .. autoclass:: RQLObjectStore
+        :show-inheritance:
+        :members:
+
+    .. autoclass:: NoHookRQLObjectStore
+        :show-inheritance:
+        :members:
+
+    .. autoclass:: SQLGenObjectStore
+        :show-inheritance:
+        :members:
+
+    Import Controller
+    -----------------
+
+    .. autoclass:: CWImportController
+        :show-inheritance:
+        :members:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/api/predicates.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,57 @@
+.. _predicates_module:
+
+:mod:`cubicweb.predicates`
+==========================
+
+.. automodule:: cubicweb.predicates
+
+   .. autoclass:: cubicweb.appobject.yes
+   .. autoclass:: cubicweb.predicates.match_kwargs
+   .. autoclass:: cubicweb.predicates.appobject_selectable
+   .. autoclass:: cubicweb.predicates.adaptable
+   .. autoclass:: cubicweb.predicates.configuration_values
+   
+   .. autoclass:: cubicweb.predicates.none_rset
+   .. autoclass:: cubicweb.predicates.any_rset
+   .. autoclass:: cubicweb.predicates.nonempty_rset
+   .. autoclass:: cubicweb.predicates.empty_rset
+   .. autoclass:: cubicweb.predicates.one_line_rset
+   .. autoclass:: cubicweb.predicates.multi_lines_rset
+   .. autoclass:: cubicweb.predicates.multi_columns_rset
+   .. autoclass:: cubicweb.predicates.paginated_rset
+   .. autoclass:: cubicweb.predicates.sorted_rset
+   .. autoclass:: cubicweb.predicates.one_etype_rset
+   .. autoclass:: cubicweb.predicates.multi_etypes_rset
+   
+   .. autoclass:: cubicweb.predicates.non_final_entity
+   .. autoclass:: cubicweb.predicates.is_instance
+   .. autoclass:: cubicweb.predicates.score_entity
+   .. autoclass:: cubicweb.predicates.rql_condition
+   .. autoclass:: cubicweb.predicates.relation_possible
+   .. autoclass:: cubicweb.predicates.partial_relation_possible
+   .. autoclass:: cubicweb.predicates.has_related_entities
+   .. autoclass:: cubicweb.predicates.partial_has_related_entities
+   .. autoclass:: cubicweb.predicates.has_permission
+   .. autoclass:: cubicweb.predicates.has_add_permission
+   .. autoclass:: cubicweb.predicates.has_mimetype
+   .. autoclass:: cubicweb.predicates.is_in_state
+   .. autofunction:: cubicweb.predicates.on_fire_transition
+   
+   .. autoclass:: cubicweb.predicates.match_user_groups
+   
+   .. autoclass:: cubicweb.predicates.no_cnx
+   .. autoclass:: cubicweb.predicates.anonymous_user
+   .. autoclass:: cubicweb.predicates.authenticated_user
+   .. autoclass:: cubicweb.predicates.match_form_params
+   .. autoclass:: cubicweb.predicates.match_search_state
+   .. autoclass:: cubicweb.predicates.match_context_prop
+   .. autoclass:: cubicweb.predicates.match_context
+   .. autoclass:: cubicweb.predicates.match_view
+   .. autoclass:: cubicweb.predicates.primary_view
+   .. autoclass:: cubicweb.predicates.contextual
+   .. autoclass:: cubicweb.predicates.specified_etype_implements
+   .. autoclass:: cubicweb.predicates.attribute_edited
+   .. autoclass:: cubicweb.predicates.match_transition
+   
+   .. autoclass:: cubicweb.predicates.match_exception
+   .. autoclass:: cubicweb.predicates.debug_mode
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/api/req.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,11 @@
+.. _req_module:
+
+:mod:`cubicweb.req`
+===================
+
+.. automodule:: cubicweb.req
+
+    .. autoexception:: FindEntityError
+
+    .. autoclass:: RequestSessionBase
+        :members:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/api/rset.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,10 @@
+.. _rset_module:
+
+:mod:`cubicweb.rset`
+====================
+
+.. automodule:: cubicweb.rset
+
+    .. autoclass:: ResultSet
+        :members:
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/api/urlpublishing.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,36 @@
+.. _urlpublishing_module:
+
+:mod:`cubicweb.web.views.urlpublishing`
+=======================================
+
+.. automodule:: cubicweb.web.views.urlpublishing
+
+    .. autoexception:: PathDontMatch
+
+    .. autoclass:: URLPublisherComponent
+        :show-inheritance:
+        :members:
+
+    .. autoclass:: URLPathEvaluator
+        :show-inheritance:
+        :members:
+
+    .. autoclass:: RawPathEvaluator
+        :show-inheritance:
+        :members:
+
+    .. autoclass:: EidPathEvaluator
+        :show-inheritance:
+        :members:
+
+    .. autoclass:: RestPathEvaluator
+        :show-inheritance:
+        :members:
+
+    .. autoclass:: URLRewriteEvaluator
+        :show-inheritance:
+        :members:
+
+    .. autoclass:: ActionPathEvaluator
+        :show-inheritance:
+        :members:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/api/urlrewrite.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,18 @@
+.. _urlrewrite_module:
+
+:mod:`cubicweb.web.views.urlrewrite`
+=======================================
+
+.. automodule:: cubicweb.web.views.urlrewrite
+
+    .. autoclass:: URLRewriter
+        :show-inheritance:
+        :members:
+
+    .. autoclass:: SimpleReqRewriter
+        :show-inheritance:
+        :members:
+
+    .. autoclass:: SchemaBasedRewriter
+        :show-inheritance:
+        :members:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/api/web.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,21 @@
+.. _web_module:
+
+:mod:`cubicweb.web`
+===================
+
+.. automodule:: cubicweb.web
+
+    Exceptions
+    ----------
+
+    .. autoexception:: DirectResponse
+    .. autoexception:: InvalidSession
+    .. autoexception:: PublishException
+    .. autoexception:: LogOut
+    .. autoexception:: Redirect
+    .. autoexception:: StatusResponse
+    .. autoexception:: RequestError
+    .. autoexception:: NothingToEdit
+    .. autoexception:: ProcessFormError
+    .. autoexception:: NotFound
+    .. autoexception:: RemoteCallFailed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/MERGE_ME-tut-create-app.en.txt	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,386 @@
+.. -*- coding: utf-8 -*-
+
+
+Tutoriel : créer votre première application web pour Google AppEngine
+=====================================================================
+
+[TRANSLATE ME TO FRENCH]
+
+This tutorial will guide you step by step to build a blog application 
+and discover the unique features of `LAX`. It assumes that you followed
+the :ref:`installation` guidelines and that both the `AppEngine SDK` and the
+`LAX` framework are setup on your computer.
+
+Creating a new application
+--------------------------
+
+We choosed in this tutorial to develop a blog as an example of web application
+and will go through each required steps/actions to have it running with `LAX`.
+When you installed `LAX`, you saw a directory named ``skel``. Make a copy of
+this directory and call it ``BlogDemo``.
+
+The location of this directory does not matter. But once decided, make sure your ``PYTHONPATH`` is properly set (:ref:`installation`).
+
+
+Defining a schema
+-----------------
+
+With `LAX`, the schema/datamodel is the core of the application. This is where
+you will define the type of content you have to hanlde in your application.
+
+Let us start with something simple and improve on it iteratively. 
+
+In schema.py, we define two entities: ``Blog`` and ``BlogEntry``.
+
+::
+
+  class Blog(EntityType):
+      title = String(maxsize=50, required=True)
+      description = String()
+
+  class BlogEntry(EntityType):
+      title = String(maxsize=100, required=True)
+      publish_date = Date(default='TODAY')
+      text = String(fulltextindexed=True)
+      category = String(vocabulary=('important','business'))
+      entry_of = SubjectRelation('Blog', cardinality='?*')
+
+A Blog has a title and a description. The title is a string that is
+required by the class EntityType and must be less than 50 characters. 
+The description is a string that is not constrained.
+
+A BlogEntry has a title, a publish_date and a text. The title is a
+string that is required and must be less than 100 characters. The
+publish_date is a Date with a default value of TODAY, meaning that
+when a BlogEntry is created, its publish_date will be the current day
+unless it is modified. The text is a string that will be indexed in
+the full-text index and has no constraint.
+
+A BlogEntry also has a relationship ``entry_of`` that link it to a
+Blog. The cardinality ``?*`` means that a BlogEntry can be part of
+zero or one Blog (``?`` means `zero or one`) and that a Blog can
+have any number of BlogEntry (``*`` means `any number including
+zero`). For completeness, remember that ``+`` means `one or more`.
+
+Running the application
+-----------------------
+
+Defining this simple schema is enough to get us started. Make sure you
+followed the setup steps described in detail in the installation
+chapter (especially visiting http://localhost:8080/_load as an
+administrator), then launch the application with the command::
+
+   python dev_appserver.py BlogDemo
+
+and point your browser at http://localhost:8080/ (if it is easier for
+you, use the on-line demo at http://lax.appspot.com/).
+
+.. image:: images/lax-book.00-login.en.png
+   :alt: login screen
+
+After you log in, you will see the home page of your application. It
+lists the entity types: Blog and BlogEntry. If these links read
+``blog_plural`` and ``blogentry_plural`` it is because
+internationalization (i18n) is not working for you yet. Please ignore
+this for now.
+
+.. image:: images/lax-book.01-start.en.png
+   :alt: home page
+
+Creating system entities
+------------------------
+You can only create new users if you decided not to use google authentication.
+
+
+[WRITE ME : create users manages permissions etc]
+
+
+
+Creating application entites
+----------------------------
+
+Create a Blog
+~~~~~~~~~~~~~
+
+Let us create a few of these entities. Click on the [+] at the right
+of the link Blog.  Call this new Blog ``Tech-blog`` and type in
+``everything about technology`` as the description, then validate the
+form by clicking on ``Validate``.
+
+.. image:: images/lax-book.02-create-blog.en.png
+   :alt: from to create blog
+
+Click on the logo at top left to get back to the home page, then
+follow the Blog link that will list for you all the existing Blog.
+You should be seeing a list with a single item ``Tech-blog`` you
+just created.
+
+.. image:: images/lax-book.03-list-one-blog.en.png
+   :alt: displaying a list of a single blog
+
+Clicking on this item will get you to its detailed description except
+that in this case, there is not much to display besides the name and
+the phrase ``everything about technology``.
+
+.. image:: images/lax-book.04-detail-one-blog.en.png
+  :alt: displaying the detailed view of a blog
+
+Now get back to the home page by clicking on the top-left logo, then
+create a new Blog called ``MyLife`` and get back to the home page
+again to follow the Blog link for the second time. The list now
+has two items.
+
+.. image:: images/lax-book.05-list-two-blog.en.png
+   :alt: displaying a list of two blogs
+
+
+Create a BlogEntry
+~~~~~~~~~~~~~~~~~~
+
+Get back to the home page and click on [+] at the right of the link
+BlogEntry. Call this new entry ``Hello World`` and type in some text
+before clicking on ``Validate``. You added a new blog entry without
+saying to what blog it belongs. There is a box on the left entitled
+``actions``, click on the menu item ``modify``. You are back to the form
+to edit the blog entry you just created, except that the form now has
+another section with a combobox titled ``add relation``. Chose
+``entry_of`` in this menu and a second combobox appears where you pick
+``MyLife``. 
+
+You could also have, at the time you started to fill the form for a
+new entity BlogEntry, hit ``Apply`` instead of ``Validate`` and the 
+combobox titled ``add relation`` would have showed up.
+
+.. image:: images/lax-book.06-add-relation-entryof.en.png
+   :alt: editing a blog entry to add a relation to a blog
+
+Validate the changes by clicking ``Validate``. The entity BlogEntry
+that is displayed now includes a link to the entity Blog named
+``MyLife``.
+
+.. image:: images/lax-book.07-detail-one-blogentry.en.png
+   :alt: displaying the detailed view of a blogentry
+
+Remember that all of this was handled by the framework and that the
+only input that was provided so far is the schema. To get a graphical
+view of the schema, run the ``laxctl genschema BlogDemo`` command as
+explained in the installation section and point your browser to the
+URL http://localhost:8080/schema
+
+.. image:: images/lax-book.08-schema.en.png
+   :alt: graphical view of the schema (aka data-model)
+
+Site configuration
+------------------
+
+.. image:: images/lax-book.03-site-config-panel.en.png
+
+This panel allows you to configure the appearance of your application site.
+Six menus are available and we will go through each of them to explain how
+to use them.
+
+Navigation
+~~~~~~~~~~
+This menu provides you a way to adjust some navigation options depending on
+your needs, such as the number of entities to display by page of results.
+Follows the detailled list of available options:
+  
+* navigation.combobox-limit: maximum number of entities to display in related
+  combo box (sample format: 23)
+* navigation.page-size: maximum number of objects displayed by page of results 
+  (sample format: 23)
+* navigation.related-limit: maximum number of related entities to display in 
+  the primary view (sample format: 23)
+* navigation.short-line-size: maximum number of characters in short description
+  (sample format: 23)
+
+UI
+~~
+This menu provides you a way to customize the user interface settings such as
+date format or encoding in the produced html.
+Follows the detailled list of available options:
+
+* ui.date-format : how to format date in the ui ("man strftime" for format description)
+* ui.datetime-format : how to format date and time in the ui ("man strftime" for format
+  description)
+* ui.default-text-format : default text format for rich text fields.
+* ui.encoding : user interface encoding
+* ui.fckeditor : should html fields being edited using fckeditor (a HTML WYSIWYG editor).
+  You should also select text/html as default text format to actually get fckeditor.
+* ui.float-format : how to format float numbers in the ui
+* ui.language : language of the user interface
+* ui.main-template : id of main template used to render pages
+* ui.site-title	: site title, which is displayed right next to the logo in the header
+* ui.time-format : how to format time in the ui ("man strftime" for format description)
+
+
+Actions
+~~~~~~~
+This menu provides a way to configure the context in which you expect the actions
+to be displayed to the user and if you want the action to be visible or not. 
+You must have notice that when you view a list of entities, an action box is 
+available on the left column which display some actions as well as a drop-down 
+menu for more actions. 
+
+The context available are:
+
+* mainactions : actions listed in the left box
+* moreactions : actions listed in the `more` menu of the left box
+* addrelated : add actions listed in the left box
+* useractions : actions listed in the first section of drop-down menu 
+  accessible from the right corner user login link
+* siteactions : actions listed in the second section of drop-down menu
+  accessible from the right corner user login link
+* hidden : select this to hide the specific action
+
+Boxes
+~~~~~
+The application has already a pre-defined set of boxes you can use right away. 
+This configuration section allows you to place those boxes where you want in the
+application interface to customize it. 
+
+The available boxes are:
+
+* actions box : box listing the applicable actions on the displayed data
+
+* boxes_blog_archives_box : box listing the blog archives 
+
+* possible views box : box listing the possible views for the displayed data
+
+* rss box : RSS icon to get displayed data as a RSS thread
+
+* search box : search box
+
+* startup views box : box listing the configuration options available for 
+  the application site, such as `Preferences` and `Site Configuration`
+
+Components
+~~~~~~~~~~
+[WRITE ME]
+
+Contextual components
+~~~~~~~~~~~~~~~~~~~~~
+[WRITE ME]
+
+Set-up a workflow
+-----------------
+
+Before starting, make sure you refresh your mind by reading [link to
+definition_workflow chapter].
+
+We want to create a workflow to control the quality of the BlogEntry 
+submitted on your application. When a BlogEntry is created by a user
+its state should be `submitted`. To be visible to all, it needs to
+be in the state `published`. To move from `submitted` to `published`
+we need a transition that we can name `approve_blogentry`.
+
+We do not want every user to be allowed to change the state of a 
+BlogEntry. We need to define a group of user, `moderators`, and 
+this group will have appropriate permissions to approve BlogEntry
+to be published and visible to all.
+
+There are two ways to create a workflow, form the user interface,
+and also by defining it in ``migration/postcreate.py``. This script
+is executed each time a new ``./bin/laxctl db-init`` is done. 
+If you create the states and transitions through the user interface
+this means that next time you will need to initialize the database
+you will have to re-create all the entities. 
+We strongly recommand you create the workflow in ``migration\postcreate.py``
+and we will now show you how.
+The user interface would only be a reference for you to view the states 
+and transitions but is not the appropriate interface to define your
+application workflow.
+
+Update the schema
+~~~~~~~~~~~~~~~~~
+To enable a BlogEntry to have a State, we have to define a relation
+``in_state`` in the schema of BlogEntry. Please do as follows, add
+the line ``in_state (...)``::
+
+  class BlogEntry(EntityType):
+      title = String(maxsize=100, required=True)
+      publish_date = Date(default='TODAY')
+      text_format = String(meta=True, internationalizable=True, maxsize=50,
+                           default='text/rest', constraints=[format_constraint])
+      text = String(fulltextindexed=True)
+      category = String(vocabulary=('important','business'))
+      entry_of = SubjectRelation('Blog', cardinality='?*')
+      in_state = SubjectRelation('State', cardinality='1*')
+
+As you updated the schema, you will have re-execute ``./bin/laxctl db-init``
+to initialize the database and migrate your existing entities.
+[WRITE ABOUT MIGRATION]
+
+Create states, transitions and group permissions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+At the time the ``postcreate.py`` script is executed, several methods
+can be used. They are all defined in the ``class ServerMigrationHelper``.
+We will only discuss the method we use to create a wrokflow here.
+
+To define our workflow for BlogDemo, please add the following lines
+to ``migration/postcreate.py``::
+  
+  _ = unicode
+
+  moderators      = add_entity('CWGroup', name=u"moderators")
+
+  submitted = add_state(_('submitted'), 'BlogEntry', initial=True)
+  published = add_state(_('published'), 'BlogEntry')
+
+  add_transition(_('approve_blogentry'), 'BlogEntry', (submitted,), published, ('moderators', 'managers'),)
+
+  checkpoint()
+
+``add_entity`` is used here to define the new group of users that we
+need to define the transitions, `moderators`.
+If this group required by the transition is not defined before the
+transition is created, it will not create the relation `transition 
+require the group moderator`.
+
+``add_state`` expects as the first argument the name of the state you are
+willing to create, then the entity type on which the state can be applied, 
+and an optionnal argument to set if the state is the initial state
+of the entity type or not.
+
+``add_transition`` expects as the first argument the name of the 
+transition, then the entity type on which we can apply the transition,
+then the list of possible initial states from which the transition
+can be applied, the target state of the transition, and the permissions
+(e.g. list of the groups of users who can apply the transition).
+
+.. image:: images/lax-book.03-transitions-view.en.png
+
+You can now notice that in the actions box of a BlogEntry, the state
+is now listed as well as the possible transitions from this state
+defined by the workflow. This transition, as defined in the workflow,
+will only being displayed for the users belonging to the group
+moderators of managers.
+
+Change view permission
+~~~~~~~~~~~~~~~~~~~~~~
+
+
+
+Conclusion
+----------
+
+Exercise
+~~~~~~~~
+
+Create new blog entries in ``Tech-blog``.
+
+What we learned
+~~~~~~~~~~~~~~~
+
+Creating a simple schema was enough to set up a new application that
+can store blogs and blog entries. 
+
+What is next ?
+~~~~~~~~~~~~~~
+
+Although the application is fully functionnal, its look is very
+basic. In the following section we will learn to create views to
+customize how data is displayed.
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/MERGE_ME-tut-create-gae-app.en.txt	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,218 @@
+.. -*- coding: utf-8 -*-
+
+.. _tutorielGAE:
+
+Tutoriel : créer votre première application web pour Google AppEngine
+=====================================================================
+
+Ce tutoriel va vous guider pas à pas a construire une apllication web 
+de gestion de Blog afin de vous faire découvrir les fonctionnalités de
+*CubicWeb*.
+
+Nous supposons que vous avec déjà suivi le guide :ref:`installationGAE`.
+
+
+Créez une nouvelle application
+------------------------------
+
+Nous choisissons dans ce tutoriel de développer un blog comme un exemple
+d'application web et nous allons expliciter toutes les étapes nécessaires
+à sa réalisation.  
+
+::
+  
+  cubicweb-ctl newgapp blogdemo
+
+`newgapp` est la commande permettant de créer une instance *CubicWeb* pour
+le datastore.
+
+Assurez-vous que votre variable d'environnement ``PYTHONPATH`` est correctement
+initialisée (:ref:`installationGAE`)
+
+Définissez un schéma
+--------------------
+
+Le modèle de données ou schéma est au coeur d'une application *CubicWeb*.
+C'est là où vous allez devoir définir le type de contenu que votre application
+devra gérer.
+
+Commençons par un schéma simple que nous améliorerons progressivemment.
+
+Une fois votre instance ``blogdemo`` crée, vous trouverez un fichier ``schema.py``
+contenant la définition des entités suivantes : ``Blog`` and ``BlogEntry``.
+
+::
+
+  class Blog(EntityType):
+      title = String(maxsize=50, required=True)
+      description = String()
+
+  class BlogEntry(EntityType):
+      title = String(maxsize=100, required=True)
+      publish_date = Date(default='TODAY')
+      text = String(fulltextindexed=True)
+      category = String(vocabulary=('important','business'))
+      entry_of = SubjectRelation('Blog', cardinality='?*')
+
+
+Un ``Blog`` a un titre et une description. Le titre est une chaîne 
+de caractères requise par la classe parente EntityType and ne doit
+pas excéder 50 caractères. La description est une chaîne de 
+caractères sans contraintes.
+
+Une ``BlogEntry`` a un titre, une date de publication et du texte
+étant son contenu. Le titre est une chaîne de caractères qui ne 
+doit pas excéder 100 caractères. La date de publication est de type Date et a
+pour valeur par défaut TODAY, ce qui signifie que lorsqu'une 
+``BlogEntry`` sera créée, sa date de publication sera la date
+courante a moins de modifier ce champ. Le texte est une chaîne de
+caractères qui sera indexée en plein texte et sans contraintes.
+
+Une ``BlogEntry`` a aussi une relation nommée ``entry_of`` qui la
+relie à un ``Blog``. La cardinalité ``?*`` signifie que BlogEntry
+peut faire partie de zero a un Blog (``?`` signifie `zero ou un`) et
+qu'un Blog peut avoir une infinité de BlogEntry (``*`` signifie
+`n'importe quel nombre incluant zero`). 
+Par soucis de complétude, nous rappellerons que ``+`` signifie
+`un ou plus`.
+
+Lancez l'application
+--------------------
+
+Définir ce simple schéma est suffisant pour commencer. Assurez-vous 
+que vous avez suivi les étapes décrites dans la section installation
+(en particulier visitez http://localhost:8080/_load en tant qu'administrateur
+afin d'initialiser le datastore), puis lancez votre application avec la commande ::
+   
+   python dev_appserver.py BlogDemo
+
+puis dirigez vous vers http://localhost:8080/ (ou si c'est plus facile
+vous pouvez utiliser la démo en ligne http://lax.appspot.com/).
+[FIXME] -- changer la demo en ligne en quelque chose qui marche (!)
+
+.. image:: images/lax-book.00-login.en.png
+   :alt: login screen
+
+Après vous être authentifié, vous arrivez sur la page d'accueil de votre 
+application. Cette page liste les types d'entités accessibles dans votre
+application, en l'occurrence : Blog et Articles. Si vous lisez ``blog_plural``
+et ``blogentry_plural`` cela signifie que l'internationalisation (i18n)
+n'a pas encore fonctionné. Ignorez cela pour le moment.
+
+.. image:: images/lax-book.01-start.en.png
+   :alt: home page
+
+Créez des entités système
+-------------------------
+
+Vous ne pourrez créer de nouveaux utilisateurs que dans le cas où vous
+avez choisi de ne pas utiliser l'authentification Google.
+
+
+[WRITE ME : create users manages permissions etc]
+
+
+
+Créez des entités applicatives
+------------------------------
+
+Créez un Blog
+~~~~~~~~~~~~~
+
+Créons à présent quelques entités. Cliquez sur `[+]` sur la
+droite du lien Blog. Appelez cette nouvelle entité Blog ``Tech-Blog``
+et tapez pour la description ``everything about technology``,
+puis validez le formulaire d'édition en cliquant sur le bouton
+``Validate``.
+
+
+.. image:: images/lax-book.02-create-blog.en.png
+   :alt: from to create blog
+
+En cliquant sur le logo situé dans le coin gauche de la fenêtre,
+vous allez être redirigé vers la page d'accueil. Ensuite, si vous allez 
+sur le lien Blog, vous devriez voir la liste des entités Blog, en particulier
+celui que vous venez juste de créer ``Tech-Blog``.
+
+.. image:: images/lax-book.03-list-one-blog.en.png
+   :alt: displaying a list of a single blog
+
+Si vous cliquez sur ``Tech-Blog`` vous devriez obtenir une description
+détaillée, ce qui dans notre cas, n'est rien de plus que le titre
+et la phrase ``everything about technology``
+
+
+.. image:: images/lax-book.04-detail-one-blog.en.png
+   :alt: displaying the detailed view of a blog
+
+Maintenant retournons sur la page d'accueil et créons un nouveau
+Blog ``MyLife`` et retournons sur la page d'accueil, puis suivons
+le lien Blog et nous constatons qu'à présent deux blogs sont listés.
+
+.. image:: images/lax-book.05-list-two-blog.en.png
+   :alt: displaying a list of two blogs
+
+Créons un article
+~~~~~~~~~~~~~~~~~
+
+Revenons sur la page d'accueil et cliquons sur `[+]` à droite du lien
+`articles`. Appellons cette nouvelle entité ``Hello World`` et introduisons
+un peut de texte avant de ``Valider``. Vous venez d'ajouter un article
+sans avoir précisé à quel Blog il appartenait. Dans la colonne de gauche
+se trouve une boite intitulé ``actions``, cliquez sur le menu ``modifier``.
+Vous êtes de retour sur le formulaire d'édition de l'article que vous 
+venez de créer, à ceci près que ce formulaire a maintenant une nouvelle
+section intitulée ``ajouter relation``. Choisissez ``entry_of`` dans ce menu,
+cela va faire apparaitre une deuxième menu déroulant dans lequel vous
+allez pouvoir séléctionner le Blog ``MyLife``.
+
+Vous auriez pu aussi, au moment où vous avez crée votre article, sélectionner
+``appliquer`` au lieu de ``valider`` et le menu ``ajouter relation`` serait apparu.
+
+.. image:: images/lax-book.06-add-relation-entryof.en.png
+   :alt: editing a blog entry to add a relation to a blog
+
+Validez vos modifications en cliquant sur ``Valider``. L'entité article
+qui est listée contient maintenant un lien vers le Blog auquel il 
+appartient, ``MyLife``.
+
+.. image:: images/lax-book.07-detail-one-blogentry.en.png
+   :alt: displaying the detailed view of a blogentry
+
+Rappelez-vous que pour le moment, tout a été géré par la plate-forme
+*CubicWeb* et que la seule chose qui a été fournie est le schéma de
+données. D'ailleurs pour obtenir une vue graphique du schéma, exécutez
+la commande ``laxctl genschema blogdemo`` et vous pourrez visualiser
+votre schéma a l'URL suivante : http://localhost:8080/schema
+
+.. image:: images/lax-book.08-schema.en.png
+   :alt: graphical view of the schema (aka data-model)
+
+
+Change view permission
+~~~~~~~~~~~~~~~~~~~~~~
+
+
+
+Conclusion
+----------
+
+Exercise
+~~~~~~~~
+
+Create new blog entries in ``Tech-blog``.
+
+What we learned
+~~~~~~~~~~~~~~~
+
+Creating a simple schema was enough to set up a new application that
+can store blogs and blog entries. 
+
+What is next ?
+~~~~~~~~~~~~~~
+
+Although the application is fully functionnal, its look is very
+basic. In the following section we will learn to create views to
+customize how data is displayed.
+
+
--- a/doc/book/README	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-====
-Book
-====
-
-----
-Part
-----
-
-Chapter
-=======
-
-.. _Level1AnchorForLaterReference:
-
-Level 1 section
----------------
-
-Level 2 section
-~~~~~~~~~~~~~~~
-
-Level 3 section
-```````````````
-
-
-
-*CubicWeb*
-
-
-inline directives:
-  :file:`directory/file`
-  :envvar:`AN_ENV_VARIABLE`
-  :command:`command --option arguments`
-
-  :ref:, :mod:
-
-
-.. sourcecode:: python
-
-   class SomePythonCode:
-     ...
-
-.. XXX a comment, wont be rendered
-
-
-a [foot note]_
-
-.. [foot note] the foot note content
-
-
-Boxes
-=====
-
-- warning box: 
-    .. warning::
-
-       Warning content
-- note box:
-    .. note::
-
-       Note content
-
-
-
-Cross references
-================
-
-To arbitrary section
---------------------
-
-:ref:`identifier` ou :ref:`label <identifier>`
-
-Label required of referencing node which as no title, else the node's title will be used.
-
-
-To API objects
---------------
-See the autodoc sphinx extension documentation. Quick overview:
-
-* ref to a class: :class:`cubicweb.devtools.testlib.AutomaticWebTest`
-
-* if you can to see only the class name in the generated documentation, add a ~:
-  :class:`~cubicweb.devtools.testlib.AutomaticWebTest`
-
-* you can also use :mod: (module), :exc: (exception), :func: (function), :meth: (method)...
-
-* syntax explained above to specify label explicitly may also be used
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/additionnal_services/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,14 @@
+Additional services
+===================
+
+In this chapter, we introduce services crossing the *web -
+repository - administration* organisation of the first parts of the
+CubicWeb book. Those services can be either proper services (like the
+undo functionality) or mere *topical cross-sections* across CubicWeb.
+
+.. toctree::
+   :maxdepth: 2
+
+   undo
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/additionnal_services/undo.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,337 @@
+Undoing changes in CubicWeb
+---------------------------
+
+Many desktop applications offer the possibility for the user to
+undo its last changes : this *undo feature* has now been
+integrated into the CubicWeb framework. This document will
+introduce you to the *undo feature* both from the end-user and the
+application developer point of view.
+
+But because a semantic web application and a common desktop
+application are not the same thing at all, especially as far as
+undoing is concerned, we will first introduce *what* is the *undo
+feature* for now.
+
+What's *undoing* in a CubicWeb application
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+What is an *undo feature* is quite intuitive in the context of a
+desktop application. But it is a bit subtler in the context of a
+Semantic Web application. This section introduces some of the main
+differences between a classical desktop and a Semantic Web
+applications to keep in mind in order to state precisely *what we
+want*.
+
+The notion transactions
+```````````````````````
+
+A CubicWeb application acts upon an *Entity-Relationship* model,
+described by a schema. This allows to ensure some data integrity
+properties. It also implies that changes are made by all-or-none
+groups called *transactions*, such that the data integrity is
+preserved whether the transaction is completely applied *or* none
+of it is applied.
+
+A transaction can thus include more actions than just those
+directly required by the main purpose of the user.  For example,
+when a user *just* writes a new blog entry, the underlying
+*transaction* holds several *actions* as illustrated below :
+
+* By admin on 2012/02/17 15:18 - Created Blog entry : Torototo
+
+  #. Created Blog entry : Torototo
+  #. Added relation : Torototo owned by admin
+  #. Added relation : Torototo blog entry of Undo Blog
+  #. Added relation : Torototo in state draft (draft)
+  #. Added relation : Torototo created by admin
+
+Because of the very nature (all-or-none) of the transactions, the
+"undoable stuff" are the transactions and not the actions !
+
+Public and private actions within a transaction
+```````````````````````````````````````````````
+
+Actually, within the *transaction* "Created Blog entry :
+Torototo", two of those *actions* are said to be *public* and
+the others are said to be *private*. *Public* here means that the
+public actions (1 and 3) were directly requested by the end user ;
+whereas *private* means that the other actions (2, 4, 5) were
+triggered "under the hood" to fulfill various requirements for the
+user operation (ensuring integrity, security, ... ).
+
+And because quite a lot of actions can be triggered by a "simple"
+end-user request, most of which the end-user is not (and does not
+need or wish to be) aware, only the so-called public actions will
+appear [1]_ in the description of the an undoable transaction.
+
+* By admin on 2012/02/17 15:18 - Created Blog entry : Torototo
+
+  #. Created Blog entry : Torototo
+  #. Added relation : Torototo blog entry of Undo Blog
+
+But note that both public and private actions will be undone
+together when the transaction is undone.
+
+(In)dependent transactions : the simple case
+````````````````````````````````````````````
+
+A CubicWeb application can be used *simultaneously* by different users
+(whereas a single user works on an given office document at a
+given time), so that there is not always a single history
+time-line in the CubicWeb case. Moreover CubicWeb provides
+security through the mechanism of *permissions* granted to each
+user. This can lead to some transactions *not* being undoable in
+some contexts.
+
+In the simple case two (unprivileged) users Alice and Bob make
+relatively independent changes : then both Alice and Bob can undo
+their changes. But in some case there is a clean dependency
+between Alice's and Bob's actions or between actions of one of
+them. For example let's suppose that :
+
+- Alice has created a blog,
+- then has published a first post inside,
+- then Bob has published a second post in the same blog,
+- and finally Alice has updated its post contents.
+
+Then it is clear that Alice can undo her contents changes and Bob
+can undo his post creation independently. But Alice can not undo
+her post creation while she has not first undone her changes.
+It is also clear that Bob should *not* have the
+permissions to undo any of Alice's transactions.
+
+
+More complex dependencies between transactions
+``````````````````````````````````````````````
+
+But more surprising things can quickly happen. Going back to the
+previous example, Alice *can* undo the creation of the blog after
+Bob has published its post in it ! But this is possible only
+because the schema does not *require* for a post to be in a
+blog. Would the *blog entry of* relation have been mandatory, then
+Alice could not have undone the blog creation because it would
+have broken integrity constraint for Bob's post.
+
+When a user attempts to undo a transaction the system will check
+whether a later transaction has explicit dependency on the
+would-be-undone transaction. In this case the system will not even
+attempt the undo operation and inform the user.
+
+If no such dependency is detected the system will attempt the undo
+operation but it can fail, typically because of integrity
+constraint violations. In such a case the undo operation is
+completely [3]_ rollbacked.
+
+
+The *undo feature* for CubicWeb end-users
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The exposition of the undo feature to the end-user through a Web
+interface is still quite basic and will be improved toward a
+greater usability. But it is already fully functional.  For now
+there are two ways to access the *undo feature* as long as the it
+has been activated in the instance configuration file with the
+option *undo-support=yes*.
+
+Immediately after having done the change to be canceled through
+the **undo** link in the message. This allows to undo an
+hastily action immediately. For example, just after having
+validated the creation of the blog entry *A second blog entry* we
+get the following message, allowing to undo the creation.
+
+.. image:: /images/undo_mesage_w600.png
+   :width: 600px
+   :alt: Screenshot of the undo link in the message
+   :align: center
+
+At any time we can access the **undo-history view** accessible from the
+start-up page.
+
+.. image:: /images/undo_startup-link_w600.png
+   :width: 600px
+   :alt: Screenshot of the startup menu with access to the history view
+   :align: center
+
+This view will provide inspection of the transaction and their (public)
+actions. Each transaction provides its own **undo** link. Only the
+transactions the user has permissions to see and undo will be shown.
+
+.. image:: /images/undo_history-view_w600.png
+   :width: 600px
+   :alt: Screenshot of the undo history main view
+   :align: center
+
+If the user attempts to undo a transaction which can't be undone or
+whose undoing fails, then a message will explain the situation and
+no partial undoing will be left behind.
+
+This is all for the end-user side of the undo mechanism : this is
+quite simple indeed ! Now, in the following section, we are going
+to introduce the developer side of the undo mechanism.
+
+The *undo feature* for CubicWeb application developers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A word of warning : this section is intended for developers,
+already having some knowledge of what's under CubicWeb's hood. If
+it is not *yet* the case, please refer to CubicWeb documentation
+http://docs.cubicweb.org/ .
+
+Overview
+````````
+
+The core of the undo mechanisms is at work in the *native source*,
+beyond the RQL. This does mean that *transactions* and *actions*
+are *no entities*. Instead they are represented at the SQL level
+and exposed through the *DB-API* supported by the repository
+*Connection* objects.
+
+Once the *undo feature* has been activated in the instance
+configuration file with the option *undo-support=yes*, each
+mutating operation (cf. [2]_) will be recorded in some special SQL
+table along with its associated transaction. Transaction are
+identified by a *txuuid* through which the functions of the
+*DB-API* handle them.
+
+On the web side the last commited transaction *txuuid* is
+remembered in the request's data to allow for imediate undoing
+whereas the *undo-history view* relies upon the *DB-API* to list
+the accessible transactions. The actual undoing is performed by
+the *UndoController* accessible at URL of the form
+`www.my.host/my/instance/undo?txuuid=...`
+
+The repository side
+```````````````````
+
+Please refer to the file `cubicweb/server/sources/native.py` and
+`cubicweb/transaction.py` for the details.
+
+The undoing information is mainly stored in three SQL tables:
+
+`transactions`
+    Stores the txuuid, the user eid and the date-and-time of
+    the transaction. This table is referenced by the two others.
+
+`tx_entity_actions`
+    Stores the undo information for actions on entities.
+
+`tx_relation_actions`
+    Stores the undo information for the actions on relations.
+
+When the undo support is activated, entries are added to those
+tables for each mutating operation on the data repository, and are
+deleted on each transaction undoing.
+
+Those table are accessible through the following methods of the
+repository `Connection` object :
+
+`undoable_transactions`
+    Returns a list of `Transaction` objects accessible to the user
+    and according to the specified filter(s) if any.
+
+`tx_info`
+    Returns a `Transaction` object from a `txuuid`
+
+`undo_transaction`
+    Returns the list of `Action` object for the given `txuuid`.
+
+    NB:  By default it only return *public* actions.
+
+The web side
+````````````
+
+The exposure of the *undo feature* to the end-user through the Web
+interface relies on the *DB-API* introduced above. This implies
+that the *transactions* and *actions* are not *entities* linked by
+*relations* on which the usual views can be applied directly.
+
+That's why the file `cubicweb/web/views/undohistory.py` defines
+some dedicated views to access the undo information :
+
+`UndoHistoryView`
+    This is a *StartupView*, the one accessible from the home
+    page of the instance which list all transactions.
+
+`UndoableTransactionView`
+    This view handles the display of a single `Transaction` object.
+
+`UndoableActionBaseView`
+    This (abstract) base class provides private methods to build
+    the display of actions whatever their nature.
+
+`Undoable[Add|Remove|Create|Delete|Update]ActionView`
+    Those views all inherit from `UndoableActionBaseView` and
+    each handles a specific kind of action.
+
+`UndoableActionPredicate`
+    This predicate is used as a *selector* to pick the appropriate
+    view for actions.
+
+Apart from this main *undo-history view* a `txuuid` is stored in
+the request's data `last_undoable_transaction` in order to allow
+immediate undoing of a hastily validated operation. This is
+handled in `cubicweb/web/application.py` in the `main_publish` and
+`add_undo_link_to_msg` methods for the storing and displaying
+respectively.
+
+Once the undo information is accessible, typically through a
+`txuuid` in an *undo* URL, the actual undo operation can be
+performed by the `UndoController` defined in
+`cubicweb/web/views/basecontrollers.py`. This controller basically
+extracts the `txuuid` and performs a call to `undo_transaction` and
+in case of an undo-specific error, lets the top level publisher
+handle it as a validation error.
+
+
+Conclusion
+~~~~~~~~~~
+
+The undo mechanism relies upon a low level recording of the
+mutating operation on the repository. Those records are accessible
+through some method added to the *DB-API* and exposed to the
+end-user either through a whole history view of through an
+immediate undoing link in the message box.
+
+The undo feature is functional but the interface and configuration
+options are still quite reduced. One major improvement would be to
+be able to filter with a finer grain which transactions or actions
+one wants to see in the *undo-history view*. Another critical
+improvement would be to enable the undo feature on a part only of
+the entity-relationship schema to avoid storing too much useless
+data and reduce the underlying overhead.
+
+But both functionality are related to the strong design choice not
+to represent transactions and actions as entities and
+relations. This has huge benefits in terms of safety and conceptual
+simplicity but prevents from using lots of convenient CubicWeb
+features such as *facets* to access undo information.
+
+Before developing further the undo feature or eventually revising
+this design choice, it appears that some return of experience is
+strongly needed. So don't hesitate to try the undo feature in your
+application and send us some feedback.
+
+
+Notes
+~~~~~
+
+.. [1] The end-user Web interface could be improved to enable
+       user to choose whether he wishes to see private actions.
+
+.. [2] There is only five kind of elementary actions (beyond
+       merely accessing data for reading):
+
+       * **C** : creating an entity
+       * **D** : deleting an entity
+       * **U** : updating an entity attributes
+       * **A** : adding a relation
+       * **R** : removing a relation
+
+.. [3] Meaning none of the actions in the transaction is
+       undone. Depending upon the application, it might make sense
+       to enable *partial* undo. That is to say undo in which some
+       actions could not be undo without preventing to undo the
+       others actions in the transaction (as long as it does not
+       break schema integrity). This is not forbidden by the
+       back-end but is deliberately not supported by the front-end
+       (for now at least).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/admin/additional-tips.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,64 @@
+
+.. _Additional Tips:
+
+Backups (mostly with postgresql)
+--------------------------------
+
+It is always a good idea to backup. If your system does not do that,
+you should set it up. Note that whenever you do an upgrade,
+`cubicweb-ctl` offers you to backup your database.  There are a number
+of ways for doing backups.
+
+Using postgresql (and only that)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Before you
+go ahead, make sure the following permissions are correct ::
+
+   # chgrp postgres /var/lib/cubicweb/backup
+   # chmod g+ws /var/lib/cubicweb/backup
+   # chgrp postgres /etc/cubicweb.d/*<instance>*/sources
+   # chmod g+r /etc/cubicweb.d/*<instance>*/sources
+
+Simply use the pg_dump in a cron installed for `postgres` user on the database server::
+
+    # m h  dom mon dow   command
+    0 2 * * * pg_dump -Fc --username=cubicweb --no-owner <instance> > /var/backups/<instance>-$(date '+%Y-%m-%d_%H:%M:%S').dump
+
+Using :command:`cubicweb-ctl db-dump`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The CubicWeb way is to use the :command:`db-dump` command. For that,
+you have to put your passwords in a user-only-readable file at the
+home directory of root user.  The file is `.pgpass` (`chmod 0600`), in
+this case for a socket run connection to PostgreSQL ::
+
+    /var/run/postgresql:5432:<instance>:<database user>:<database password>
+
+The postgres documentation for the `.pgpass` format can be found `here`_
+
+Then add the following command to the crontab of the user (`crontab -e`)::
+
+    # m h  dom mon dow   command
+    0 2 * * * cubicweb-ctl db-dump <instance>
+
+
+Backup ninja
+~~~~~~~~~~~~
+
+You can use a combination `backup-ninja`_ (which has a postgres script in the
+example directory), `backuppc`)_ (for versionning).
+
+Please note that in the *CubicWeb way* it adds a second location for your
+password which is error-prone.
+
+.. _`here` : http://www.postgresql.org/docs/current/static/libpq-pgpass.html
+.. _`backup-ninja` : https://labs.riseup.net/code/projects/show/backupninja/
+.. _`backuppc` : http://backuppc.sourceforge.net/
+
+.. warning::
+
+  Remember that these indications will fail you whenever you use
+  another database backend than postgres. Also it does properly handle
+  externally managed data such as files (using the Bytes File System
+  Storage).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/admin/config.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,229 @@
+.. -*- coding: utf-8 -*-
+
+.. _ConfigEnv:
+
+Set-up of a *CubicWeb* environment
+==================================
+
+You can `configure the database`_ system of your choice:
+
+  - `PostgreSQL configuration`_
+  - `MySql configuration`_
+  - `SQLServer configuration`_
+  - `SQLite configuration`_
+
+For advanced features, have a look to:
+
+  - `Cubicweb resources configuration`_
+
+.. _`configure the database`: DatabaseInstallation_
+.. _`PostgreSQL configuration`: PostgresqlConfiguration_
+.. _`MySql configuration`: MySqlConfiguration_
+.. _`SQLServer configuration`: SQLServerConfiguration_
+.. _`SQLite configuration`: SQLiteConfiguration_
+.. _`Cubicweb resources configuration`: RessourcesConfiguration_
+
+
+
+.. _RessourcesConfiguration:
+
+Cubicweb resources configuration
+--------------------------------
+
+.. autodocstring:: cubicweb.cwconfig
+
+
+.. _DatabaseInstallation:
+
+Databases configuration
+-----------------------
+
+Each instance can be configured with its own database connection information,
+that will be stored in the instance's :file:`sources` file. The database to use
+will be chosen when creating the instance. CubicWeb is known to run with
+Postgresql (recommended), SQLServer and SQLite, and may run with MySQL.
+
+Other possible sources of data include CubicWeb, Subversion, LDAP and Mercurial,
+but at least one relational database is required for CubicWeb to work. You do
+not need to install a backend that you do not intend to use for one of your
+instances. SQLite is not fit for production use, but it works well for testing
+and ships with Python, which saves installation time when you want to get
+started quickly.
+
+.. _PostgresqlConfiguration:
+
+PostgreSQL
+~~~~~~~~~~
+
+Many Linux distributions ship with the appropriate PostgreSQL packages.
+Basically, you need to install the following packages:
+
+* `postgresql` and `postgresql-client`, which will pull the respective
+  versioned packages (e.g. `postgresql-9.1` and `postgresql-client-9.1`) and,
+  optionally,
+* a `postgresql-plpython-X.Y` package with a version corresponding to that of
+  the aforementioned packages (e.g. `postgresql-plpython-9.1`).
+
+If you run postgres version prior to 8.3, you'll also need the
+`postgresql-contrib-8.X` package for full-text search extension.
+
+If you run postgres on another host than the |cubicweb| repository, you should
+install the `postgresql-client` package on the |cubicweb| host, and others on the
+database host.
+
+For extra details concerning installation, please refer to the `PostgreSQL
+project online documentation`_.
+
+.. _`PostgreSQL project online documentation`: http://www.postgresql.org/docs
+
+
+Database cluster
+++++++++++++++++
+
+If you already have an existing cluster and PostgreSQL server running, you do
+not need to execute the initilization step of your PostgreSQL database unless
+you want a specific cluster for |cubicweb| databases or if your existing
+cluster doesn't use the UTF8 encoding (see note below).
+
+To initialize a PostgreSQL cluster, use the command ``initdb``::
+
+    $ initdb -E UTF8 -D /path/to/pgsql
+
+Notice the encoding specification. This is necessary since |cubicweb| usually
+want UTF8 encoded database. If you use a cluster with the wrong encoding, you'll
+get error like::
+
+  new encoding (UTF8) is incompatible with the encoding of the template database (SQL_ASCII)
+  HINT:  Use the same encoding as in the template database, or use template0 as template.
+
+Once initialized, start the database server PostgreSQL with the command::
+
+  $ postgres -D /path/to/psql
+
+If you cannot execute this command due to permission issues, please make sure
+that your username has write access on the database.  ::
+
+  $ chown username /path/to/pgsql
+
+Database authentication
++++++++++++++++++++++++
+
+The database authentication is configured in `pg_hba.conf`. It can be either set
+to `ident sameuser` or `md5`.  If set to `md5`, make sure to use an existing
+user of your database.  If set to `ident sameuser`, make sure that your client's
+operating system user name has a matching user in the database. If not, please
+do as follow to create a user::
+
+  $ su
+  $ su - postgres
+  $ createuser -s -P username
+
+The option `-P` (for password prompt), will encrypt the password with the
+method set in the configuration file :file:`pg_hba.conf`.  If you do not use this
+option `-P`, then the default value will be null and you will need to set it
+with::
+
+  $ su postgres -c "echo ALTER USER username WITH PASSWORD 'userpasswd' | psql"
+
+The above login/password will be requested when you will create an instance with
+`cubicweb-ctl create` to initialize the database of your instance.
+
+Notice that the `cubicweb-ctl db-create` does database initialization that
+may requires a postgres superuser. That's why a login/password is explicitly asked
+at this step, so you can use there a superuser without using this user when running
+the instance. Things that require special privileges at this step:
+
+* database creation, require the 'create database' permission
+* install the plpython extension language (require superuser)
+* install the tsearch extension for postgres version prior to 8.3 (require superuser)
+
+To avoid using a super user each time you create an install, a nice trick is to
+install plpython (and tsearch when needed) on the special `template1` database,
+so they will be installed automatically when cubicweb databases are created
+without even with needs for special access rights. To do so, run ::
+
+  # Installation of plpythonu language by default ::
+  $ createlang -U pgadmin plpythonu template1
+  $ psql -U pgadmin template1
+  template1=# update pg_language set lanpltrusted=TRUE where lanname='plpythonu';
+
+Where `pgadmin` is a postgres superuser. The last command is necessary since by
+default plpython is an 'untrusted' language and as such can't be used by non
+superuser. This update fix that problem by making it trusted.
+
+To install the tsearch plain-text index extension on postgres prior to 8.3, run::
+
+    cat /usr/share/postgresql/8.X/contrib/tsearch2.sql | psql -U username template1
+
+
+.. _MySqlConfiguration:
+
+MySql
+~~~~~
+.. warning::
+    CubicWeb's MySQL support is not commonly used, so things may or may not work properly.
+
+You must add the following lines in ``/etc/mysql/my.cnf`` file::
+
+    transaction-isolation=READ-COMMITTED
+    default-storage-engine=INNODB
+    default-character-set=utf8
+    max_allowed_packet = 128M
+
+.. Note::
+    It is unclear whether mysql supports indexed string of arbitrary length or
+    not.
+
+
+.. _SQLServerConfiguration:
+
+SQLServer
+~~~~~~~~~
+
+As of this writing, support for SQLServer 2005 is functional but incomplete. You
+should be able to connect, create a database and go quite far, but some of the
+SQL generated from RQL queries is still currently not accepted by the
+backend. Porting to SQLServer 2008 is also an item on the backlog.
+
+The `source` configuration file may look like this (specific parts only are
+shown)::
+
+  [system]
+  db-driver=sqlserver2005
+  db-user=someuser
+  # database password not needed
+  #db-password=toto123
+  #db-create/init may ask for a pwd: just say anything
+  db-extra-arguments=Trusted_Connection
+  db-encoding=utf8
+
+
+You need to change the default settings on the database by running::
+
+ ALTER DATABASE <databasename> SET READ_COMMITTED_SNAPSHOT ON;
+
+The ALTER DATABASE command above requires some permissions that your
+user may not have. In that case you will have to ask your local DBA to
+run the query for you.
+
+You can check that the setting is correct by running the following
+query which must return '1'::
+
+   SELECT is_read_committed_snapshot_on
+     FROM sys.databases WHERE name='<databasename>';
+
+
+
+.. _SQLiteConfiguration:
+
+SQLite
+~~~~~~
+
+SQLite has the great advantage of requiring almost no configuration. Simply
+use 'sqlite' as db-driver, and set path to the dabase as db-name. Don't specify
+anything for db-user and db-password, they will be ignore anyway.
+
+.. Note::
+  SQLite is great for testing and to play with cubicweb but is not suited for
+  production environments.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/admin/create-instance.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,100 @@
+.. -*- coding: utf-8 -*-
+
+Creation of your first instance
+===============================
+
+Instance creation
+-----------------
+
+Now that we created a cube, we can create an instance and access it via a web
+browser. We will use a `all-in-one` configuration to simplify things ::
+
+  cubicweb-ctl create -c all-in-one mycube myinstance
+
+.. note::
+  Please note that we created a new cube for a demo purposes but
+  you could have used an existing cube available in our standard library
+  such as blog or person for example.
+
+A series of questions will be prompted to you, the default answer is usually
+sufficient. You can anyway modify the configuration later on by editing
+configuration files. When a login/password are requested to access the database
+please use the credentials you created at the time you configured the database
+(:ref:`PostgresqlConfiguration`).
+
+It is important to distinguish here the user used to access the database and the
+user used to login to the cubicweb instance. When an instance starts, it uses
+the login/password for the database to get the schema and handle low level
+transaction. But, when :command:`cubicweb-ctl create` asks for a manager
+login/psswd of *CubicWeb*, it refers to the user you will use during the
+development to administrate your web instance. It will be possible, later on,
+to use this user to create other users for your final web instance.
+
+
+Instance administration
+-----------------------
+
+start / stop
+~~~~~~~~~~~~
+
+When this command is completed, the definition of your instance is
+located in :file:`~/etc/cubicweb.d/myinstance/*`. To launch it, you
+just type ::
+
+  cubicweb-ctl start -D myinstance
+
+The option `-D` specifies the *debug mode* : the instance is not
+running in server mode and does not disconnect from the terminal,
+which simplifies debugging in case the instance is not properly
+launched. You can see how it looks by visiting the URL
+`http://localhost:8080` (the port number depends of your
+configuration). To login, please use the cubicweb administrator
+login/password you defined when you created the instance.
+
+To shutdown the instance, Crtl-C in the terminal window is enough.
+If you did not use the option `-D`, then type ::
+
+  cubicweb-ctl stop myinstance
+
+This is it! All is settled down to start developping your data model...
+
+.. note::
+
+  The output of `cubicweb-ctl start -D myinstance` can be
+  overwhelming. It is possible to reduce the log level with the
+  `--loglevel` parameter as in `cubicweb-ctl start -D myinstance -l
+  info` to filter out all logs under `info` gravity.
+
+upgrade
+~~~~~~~
+
+A manual upgrade step is necessary whenever a new version of CubicWeb or
+a cube is installed, in order to synchronise the instance's
+configuration and schema with the new code.  The command is::
+
+  cubicweb-ctl upgrade myinstance
+
+A series of questions will be asked. It always starts with a proposal
+to make a backup of your sources (where it applies). Unless you know
+exactly what you are doing (i.e. typically fiddling in debug mode, but
+definitely NOT migrating a production instance), you should answer YES
+to that.
+
+The remaining questions concern the migration steps of |cubicweb|,
+then of the cubes that form the whole application, in reverse
+dependency order.
+
+In principle, if the migration scripts have been properly written and
+tested, you should answer YES to all questions.
+
+Somtimes, typically while debugging a migration script, something goes
+wrong and the migration fails. Unfortunately the databse may be in an
+incoherent state. You have two options here:
+
+* fix the bug, restore the database and restart the migration process
+  from scratch (quite recommended in a production environement)
+
+* try to replay the migration up to the last successful commit, that
+  is answering NO to all questions up to the step that failed, and
+  finish by answering YES to the remaining questions.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/admin/cubicweb-ctl.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,111 @@
+.. -*- coding: utf-8 -*-
+
+.. _cubicweb-ctl:
+
+``cubicweb-ctl`` tool
+=====================
+
+`cubicweb-ctl` is the swiss knife to manage *CubicWeb* instances.
+The general syntax is ::
+
+  cubicweb-ctl <command> [options command] <arguments commands>
+
+To view available commands ::
+
+  cubicweb-ctl
+  cubicweb-ctl --help
+
+Please note that the commands available depends on the *CubicWeb* packages
+and cubes that have been installed.
+
+To view the help menu on specific command ::
+
+  cubicweb-ctl <command> --help
+
+Listing available cubes and instance
+-------------------------------------
+
+* ``list``, provides a list of the available configuration, cubes
+  and instances.
+
+
+Creation of a new cube
+-----------------------
+
+Create your new cube cube ::
+
+   cubicweb-ctl newcube
+
+This will create a new cube in
+``/path/to/grshell-cubicweb/cubes/<mycube>`` for a Mercurial
+installation, or in ``/usr/share/cubicweb/cubes`` for a debian
+packages installation.
+
+Create an instance
+-------------------
+
+You must ensure `~/etc/cubicweb.d/` exists prior to this. On windows, the
+'~' part will probably expand to 'Documents and Settings/user'.
+
+To create an instance from an existing cube, execute the following
+command ::
+
+   cubicweb-ctl create <cube_name> <instance_name>
+
+This command will create the configuration files of an instance in
+``~/etc/cubicweb.d/<instance_name>``.
+
+The tool ``cubicweb-ctl`` executes the command ``db-create`` and
+``db-init`` when you run ``create`` so that you can complete an
+instance creation in a single command. But of course it is possible
+to issue these separate commands separately, at a later stage.
+
+Command to create/initialize an instance database
+-------------------------------------------------
+
+* ``db-create``, creates the system database of an instance (tables and
+  extensions only)
+* ``db-init``, initializes the system database of an instance
+  (schema, groups, users, workflows...)
+
+Commands to control instances
+-----------------------------
+
+* ``start``, starts one or more or all instances
+
+of special interest::
+
+  start -D
+
+will start in debug mode (under windows, starting without -D will not
+work; you need instead to setup your instance as a service).
+
+* ``stop``, stops one or more or all instances
+* ``restart``, restarts one or more or all instances
+* ``status``, returns the status of the instance(s)
+
+Commands to maintain instances
+------------------------------
+
+* ``upgrade``, launches the existing instances migration when a new version
+  of *CubicWeb* or the cubes installed is available
+* ``shell``, opens a (Python based) migration shell for manual maintenance of the instance
+* ``db-dump``, creates a dump of the system database
+* ``db-restore``, restores a dump of the system database
+* ``db-check``, checks data integrity of an instance. If the automatic correction
+  is activated, it is recommanded to create a dump before this operation.
+* ``schema-sync``, synchronizes the persistent schema of an instance with
+  the instance schema. It is recommanded to create a dump before this operation.
+
+Commands to maintain i18n catalogs
+----------------------------------
+* ``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:`internationalization`.
+
+Other commands
+--------------
+* ``delete``, deletes an instance (configuration files and database)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/admin/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,28 @@
+.. -*- coding: utf-8 -*-
+
+.. _Part3:
+
+--------------
+Administration
+--------------
+
+This part is for installation and administration of the *CubicWeb* framework and
+instances based on that framework.
+
+.. toctree::
+   :maxdepth: 1
+   :numbered:
+
+   setup
+   setup-windows
+   config
+   cubicweb-ctl
+   create-instance
+   instance-config
+   site-config
+   multisources
+   ldap
+   migration
+   additional-tips
+   rql-logs
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/admin/instance-config.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,200 @@
+.. -*- coding: utf-8 -*-
+
+
+Configure an instance
+=====================
+
+While creating an instance, a configuration file is generated in::
+
+    $ (CW_INSTANCES_DIR) / <instance> / <configuration name>.conf
+
+For example::
+
+    /etc/cubicweb.d/myblog/all-in-one.conf
+
+It is a simple text file in the INI format
+(http://en.wikipedia.org/wiki/INI_file). In the following description,
+each option name is prefixed with its own section and followed by its
+default value if necessary, e.g. "`<section>.<option>` [value]."
+
+.. _`WebServerConfig`:
+
+Configuring the Web server
+--------------------------
+:`web.auth-model` [cookie]:
+    authentication mode, cookie or http
+:`web.realm`:
+    realm of the instance in http authentication mode
+:`web.http-session-time` [0]:
+    period of inactivity of an HTTP session before it closes automatically.
+    Duration in seconds, 0 meaning no expiration (or more exactly at the
+    closing of the browser client)
+
+:`main.anonymous-user`, `main.anonymous-password`:
+    login and password to use to connect to the RQL server with
+    HTTP anonymous connection. CWUser account should exist.
+
+:`main.base-url`:
+    url base site to be used to generate the urls of web pages
+
+Https configuration
+```````````````````
+It is possible to make a site accessible for anonymous http connections
+and https for authenticated users. This requires to
+use apache (for example) for redirection and the variable `main.https-url`
+of configuration file.
+
+For this to work you have to activate the following apache modules :
+
+* rewrite
+* proxy
+* http_proxy
+
+The command on Debian based systems for that is ::
+
+  a2enmod rewrite http_proxy proxy
+  /etc/init.d/apache2 restart
+
+:Example:
+
+   For an apache redirection of a site accessible via `http://localhost/demo`
+   and `https://localhost/demo` and actually running on port 8080, it
+   takes to the http:::
+
+     ProxyPreserveHost On
+     RewriteEngine On
+     RewriteCond %{REQUEST_URI} ^/demo
+     RewriteRule ^/demo$ /demo/
+     RewriteRule ^/demo/(.*) http://127.0.0.1:8080/$1 [L,P]
+
+   and for the https:::
+
+     ProxyPreserveHost On
+     RewriteEngine On
+     RewriteCond %{REQUEST_URI} ^/ demo
+     RewriteRule ^/demo$/demo/
+     RewriteRule ^/demo/(.*) http://127.0.0.1:8080/https/$1 [L,P]
+
+
+   and we will file in the all-in-one.conf of the instance:::
+
+     base-url = http://localhost/demo
+     https-url = https://localhost/demo
+
+Notice that if you simply want a site accessible through https, not *both* http
+and https, simply set `base-url` to the https url and the first section into your
+apache configuration (as you would have to do for an http configuration with an
+apache front-end).
+
+Setting up the web client
+-------------------------
+:`web.embed-allowed`:
+    regular expression matching sites which could be "embedded" in
+    the site (controllers 'embed')
+:`web.submit-url`:
+    url where the bugs encountered in the instance can be mailed to
+
+
+RQL server configuration
+------------------------
+:`main.host`:
+    host name if it can not be detected correctly
+:`main.pid-file`:
+    file where will be written the server pid
+:`main.uid`:
+    user account to use for launching the server when it is
+    root launched by init
+:`main.session-time [30*60]`:
+    timeout of a RQL session
+:`main.query-log-file`:
+    file where all requests RQL executed by the server are written
+
+
+Configuring e-mail
+------------------
+RQL and web server side:
+
+:`email.mangle-mails [no]`:
+    indicates whether the email addresses must be displayed as is or
+    transformed
+
+RQL server side:
+
+:`email.smtp-host [mail]`:
+    hostname hosting the SMTP server to use for outgoing mail
+:`email.smtp-port [25]`:
+    SMTP server port to use for outgoing mail
+:`email.sender-name`:
+    name to use for outgoing mail of the instance
+:`email.sender-addr`:
+    address for outgoing mail of the instance
+:`email.default dest-addrs`:
+    destination addresses by default, if used by the configuration of the
+    dissemination of the model (separated by commas)
+:`email.supervising-addrs`:
+    destination addresses of e-mails of supervision (separated by
+    commas)
+
+
+Configuring logging
+-------------------
+:`main.log-threshold`:
+    level of filtering messages (DEBUG, INFO, WARNING, ERROR)
+:`main.log-file`:
+    file to write messages
+
+
+.. _PersistentProperties:
+
+Configuring persistent properties
+---------------------------------
+Other configuration settings are in the form of entities `CWProperty`
+in the database. It must be edited via the web interface or by
+RQL queries.
+
+:`ui.encoding`:
+    Character encoding to use for the web
+:`navigation.short-line-size`:
+    number of characters for "short" display
+:`navigation.page-size`:
+    maximum number of entities to show per results page
+:`navigation.related-limit`:
+    number of related entities to show up on primary entity view
+:`navigation.combobox-limit`:
+    number of entities unrelated to show up on the drop-down lists of
+    the sight on an editing entity view
+
+Cross-Origin Resource Sharing
+-----------------------------
+
+CubicWeb provides some support for the CORS_ protocol. For now, the
+provided implementation only deals with access to a CubicWeb instance
+as a whole. Support for a finer granularity may be considered in the
+future.
+
+Specificities of the provided implementation:
+
+- ``Access-Control-Allow-Credentials`` is always true
+- ``Access-Control-Allow-Origin`` header in response will never be
+  ``*``
+- ``Access-Control-Expose-Headers`` can be configured globally (see below)
+- ``Access-Control-Max-Age`` can be configured globally (see below)
+- ``Access-Control-Allow-Methods`` can be configured globally (see below)
+- ``Access-Control-Allow-Headers`` can be configured globally (see below)
+
+
+A few parameters can be set to configure the CORS_ capabilities of CubicWeb.
+
+.. _CORS: http://www.w3.org/TR/cors/
+
+:`access-control-allow-origin`:
+   comma-separated list of allowed origin domains or "*" for any domain
+:`access-control-allow-methods`:
+   comma-separated list of allowed HTTP methods
+:`access-control-max-age`:
+   maximum age of cross-origin resource sharing (in seconds)
+:`access-control-allow-headers`:
+   comma-separated list of allowed HTTP custom headers (used in simple requests)
+:`access-control-expose-headers`:
+   comma-separated list of allowed HTTP custom headers (used in preflight requests)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/admin/ldap.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,134 @@
+.. _LDAP:
+
+LDAP integration
+================
+
+Overview
+--------
+
+Using LDAP as a source for user credentials and information is quite
+easy. The most difficult part lies in building an LDAP schema or
+using an existing one.
+
+At cube creation time, one is asked if more sources are wanted. LDAP
+is one possible option at this time. Of course, it is always possible
+to set it up later using the `CWSource` entity type, which we discuss
+there.
+
+It is possible to add as many LDAP sources as wanted, which translates
+in as many `CWSource` entities as needed.
+
+The general principle of the LDAP source is, given a proper
+configuration, to create local users matching the users available in
+the directory and deriving local user attributes from directory users
+attributes. Then a periodic task ensures local user information
+synchronization with the directory.
+
+Users handled by such a source should not be edited directly from
+within the application instance itself. Rather, updates should happen
+at the LDAP server level.
+
+Credential checks are _always_ done against the LDAP server.
+
+.. Note::
+
+  There are currently two ldap source types: the older `ldapuser` and
+  the newer `ldapfeed`. The older will be deprecated anytime soon, as
+  the newer has now gained all the features of the old and does not
+  suffer from some of its illnesses.
+
+  The ldapfeed creates real `CWUser` entities, and then
+  activate/deactivate them depending on their presence/absence in the
+  corresponding LDAP source. Their attribute and state
+  (activated/deactivated) are hence managed by the source mechanism;
+  they should not be altered by other means (as such alterations may
+  be overridden in some subsequent source synchronisation).
+
+
+Configuration of an LDAPfeed source
+-----------------------------------
+
+Additional sources are created at cube creation time or later through the
+user interface.
+
+Configure an `ldapfeed` source from the user interface under `Manage` then
+`data sources`:
+
+* At this point `type` has been set to `ldapfeed`.
+
+* The `parser` attribute shall be set to `ldapfeed`.
+
+* The `url` attribute shall be set to an URL such as ldap://ldapserver.domain/.
+
+* The `configuration` attribute contains many options. They are described in
+  detail in the next paragraph.
+
+
+Options of an LDAPfeed source
+-----------------------------
+
+Let us enumerate the options by categories (LDAP server connection,
+LDAP schema mapping information).
+
+LDAP server connection options:
+
+* `auth-mode`, (choices are simple, cram_md5, digest_md5, gssapi, support
+  for the later being partial as of now)
+
+* `auth-realm`, realm to use when using gssapi/kerberos authentication
+
+* `data-cnx-dn`, user dn to use to open data connection to the ldap (eg
+  used to respond to rql queries)
+
+* `data-cnx-password`, password to use to open data connection to the
+  ldap (eg used to respond to rql queries)
+
+If the LDAP server accepts anonymous binds, then it is possible to
+leave data-cnx-dn and data-cnx-password empty. This is, however, quite
+unlikely in practice. Beware that the LDAP server might hide attributes
+such as "userPassword" while the rest of the attributes remain visible
+through an anonymous binding.
+
+LDAP schema mapping options:
+
+* `user-base-dn`, base DN to lookup for users
+
+* `user-scope`, user search scope (valid values: "BASE", "ONELEVEL",
+  "SUBTREE")
+
+* `user-classes`, classes of user (with Active Directory, you want to
+  say "user" here)
+
+* `user-filter`, additional filters to be set in the ldap query to
+  find valid users
+
+* `user-login-attr`, attribute used as login on authentication (with
+  Active Directory, you want to use "sAMAccountName" here)
+
+* `user-default-group`, name of a group in which ldap users will be by
+  default. You can set multiple groups by separating them by a comma
+
+* `user-attrs-map`, map from ldap user attributes to cubicweb
+  attributes (with Active Directory, you want to use
+  sAMAccountName:login,mail:email,givenName:firstname,sn:surname)
+
+
+Other notes
+-----------
+
+* Cubicweb is able to start if ldap cannot be reached, even on
+  cubicweb-ctl start ... If some source ldap server cannot be used
+  while an instance is running, the corresponding users won't be
+  authenticated but their status will not change (e.g. they will not
+  be deactivated)
+
+* The user-base-dn is a key that helps cubicweb map CWUsers to LDAP
+  users: beware updating it
+
+* When a user is removed from an LDAP source, it is deactivated in the
+  CubicWeb instance; when a deactivated user comes back in the LDAP
+  source, it (automatically) is activated again
+
+* You can use the :class:`CWSourceHostConfig` to have variants for a source
+  configuration according to the host the instance is running on. To do so
+  go on the source's view from the sources management view.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/admin/migration.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,38 @@
+.. -*- coding: utf-8 -*-
+
+Migrating cubicweb instances - benefits from a distributed architecture
+=======================================================================
+
+Migrate apache & cubicweb
+-------------------------
+
+**Aim** : do the migration for N cubicweb instances hosted on a server to another with no downtime.
+
+**Prerequisites** : have an explicit definition of the database host (not default or localhost). In our case, the database is hosted on another host.
+
+**Steps** :
+
+1. *on new machine* : install your environment (*pseudocode*) ::
+
+     apt-get install cubicweb cubicweb-applications apache2
+
+2. *on old machine* : copy your cubicweb and apache configuration to the new machine ::
+
+    scp /etc/cubicweb.d/ newmachine:/etc/cubicweb.d/
+    scp /etc/apache2/sites-available/ newmachine:/etc/apache2/sites-available/
+
+3. *on new machine* : start your instances ::
+
+     cubicweb start
+
+4. *on new machine* : enable sites and modules for apache and start it, test it using by modifying your /etc/host file.
+
+5. change dns entry from your oldmachine to newmachine
+
+6. shutdown your *old machine* (if it doesn't host other services or your database)
+
+7. That's it.
+
+**Possible enhancements** : use right from the start a pound server behind your apache, that way you can add backends and smoothily migrate by shuting down backends that pound will take into account.
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/admin/multisources.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,6 @@
+Multiple sources of data
+========================
+
+Data sources include SQL, LDAP, RQL, mercurial and subversion.
+
+.. XXX feed me
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/admin/rql-logs.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,14 @@
+.. -*- coding: utf-8 -*-
+
+RQL logs
+========
+
+You can configure the *CubicWeb* instance to keep a log
+of the queries executed against your database. To do so,
+edit the configuration file of your instance
+``.../etc/cubicweb.d/myapp/all-in-one.conf`` and uncomment the
+variable ``query-log-file``::
+
+  # web instance query log file
+  query-log-file=/tmp/rql-myapp.log
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/admin/setup-windows.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,146 @@
+.. -*- coding: utf-8 -*-
+
+.. _SetUpWindowsEnv:
+
+Installing a development environement on Windows
+================================================
+
+Setting up a Windows development environment is not too complicated
+but it requires a series of small steps.
+
+We propose an example of a typical |cubicweb| installation on Windows
+from sources. We assume everything goes into ``C:\\`` and for any
+package, without version specification, "the latest is
+the greatest".
+
+Mind that adjusting the installation drive should be straightforward.
+
+
+
+Install the required elements
+-----------------------------
+
+|cubicweb| requires some base elements that must be installed to run
+correctly. So, first of all, you must install them :
+
+* python >= 2.6 and < 3
+  (`Download Python <http://www.python.org/download/>`_).
+  You can also consider the Python(x,y) distribution
+  (`Download Python(x,y) <http://code.google.com/p/pythonxy/wiki/Downloads>`_)
+  as it makes things easier for Windows user by wrapping in a single installer
+  python 2.7 plus numerous useful third-party modules and
+  applications (including Eclipse + pydev, which is an arguably good
+  IDE for Python under Windows).
+
+* `Twisted <http://twistedmatrix.com/trac/>`_ is an event-driven
+  networking engine
+  (`Download Twisted <http://twistedmatrix.com/trac/>`_)
+
+* `lxml <http://codespeak.net/lxml/>`_ library
+  (version >=2.2.1) allows working with XML and HTML
+  (`Download lxml <http://pypi.python.org/pypi/lxml/2.2.1>`_)
+
+* `Postgresql <http://www.postgresql.org/>`_,
+  an object-relational database system
+  (`Download Postgresql <http://www.enterprisedb.com/products/pgdownload.do#windows>`_)
+  and its python drivers
+  (`Download psycopg <http://www.stickpeople.com/projects/python/win-psycopg/#Version2>`_)
+
+* A recent version of `gettext`
+  (`Download gettext <http://download.logilab.org/pub/gettext/gettext-0.17-win32-setup.exe>`_).
+
+* `rql <http://www.logilab.org/project/rql>`_,
+  the recent version of the Relationship Query Language parser.
+
+Install optional elements
+-------------------------
+
+We recommend you to install the following elements. They are not
+mandatory but they activate very interesting features in |cubicweb|:
+
+* `python-ldap <http://pypi.python.org/pypi/python-ldap>`_
+  provides access to LDAP/Active directory directories
+  (`Download python-ldap <http://www.osuch.org/python-ldap>`_).
+
+* `graphviz <http://www.graphviz.org/>`_
+  which allow schema drawings.
+  (`Download graphviz <http://www.graphviz.org/Download_windows.php>`_).
+  It is quite recommended (albeit not mandatory).
+
+Other elements will activate more features once installed. Take a look
+at :ref:`InstallDependencies`.
+
+Useful tools
+------------
+
+Some additional tools could be useful to develop :ref:`cubes <AvailableCubes>`
+with the framework.
+
+* `mercurial <http://mercurial.selenic.com/>`_ and its standard windows GUI
+  (`TortoiseHG <http://tortoisehg.bitbucket.org/>`_) allow you to get the source
+  code of |cubicweb| from control version repositories. So you will be able to
+  get the latest development version and pre-release bugfixes in an easy way
+  (`Download mercurial <http://bitbucket.org/tortoisehg/stable/wiki/download>`_).
+
+* You can also consider the ssh client `Putty` in order to peruse
+  mercurial over ssh (`Download <http://www.putty.org/>`_).
+
+* If you are an Eclipse user, mercurial can be integrated using the
+  `MercurialEclipse` plugin
+  (`Home page <http://www.vectrace.com/mercurialeclipse/>`_).
+
+Getting the sources
+-------------------
+
+There are two ways to get the sources of |cubicweb| and its
+:ref:`cubes <AvailableCubes>`:
+
+* download the latest release (:ref:`SourceInstallation`)
+* get the development version using Mercurial
+  (:ref:`MercurialInstallation`)
+
+Environment variables
+---------------------
+
+You will need some convenience environment variables once all is set up. These
+variables are settable through the GUI by getting at the `System properties`
+window (by righ-clicking on `My Computer` -> `properties`).
+
+In the `advanced` tab, there is an `Environment variables` button. Click on
+it. That opens a small window allowing edition of user-related and system-wide
+variables.
+
+We will consider only user variables. First, the ``PATH`` variable. Assuming
+you are logged as user *Jane*, add the following paths, separated by
+semi-colons::
+
+  C:\Documents and Settings\Jane\My Documents\Python\cubicweb\cubicweb\bin
+  C:\Program Files\Graphviz2.24\bin
+
+The ``PYTHONPATH`` variable should also contain::
+
+  C:\Documents and Settings\Jane\My Documents\Python\cubicweb\
+
+From now, on a fresh `cmd` shell, you should be able to type::
+
+  cubicweb-ctl list
+
+... and get a meaningful output.
+
+Running an instance as a service
+--------------------------------
+
+This currently assumes that the instances configurations is located at
+``C:\\etc\\cubicweb.d``. For a cube 'my_instance', you will find
+``C:\\etc\\cubicweb.d\\my_instance\\win32svc.py``.
+
+Now, register your instance as a windows service with::
+
+  win32svc install
+
+Then start the service with::
+
+  net start cubicweb-my_instance
+
+In case this does not work, you should be able to see error reports in
+the application log, using the windows event log viewer.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/admin/setup.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,239 @@
+.. -*- coding: utf-8 -*-
+
+.. _SetUpEnv:
+
+Installation of a *CubicWeb* environment
+========================================
+
+Official releases are available from the `CubicWeb.org forge`_ and from
+`PyPI`_. Since CubicWeb is developed using `Agile software development
+<http://en.wikipedia.org/wiki/Agile_software_development>`_ techniques, releases
+happen frequently. In a version numbered X.Y.Z, X changes after a few years when
+the API breaks, Y changes after a few weeks when features are added and Z
+changes after a few days when bugs are fixed.
+
+Depending on your needs, you will chose a different way to install CubicWeb on
+your system:
+
+- `Installation on Debian/Ubuntu`_
+- `Installation on Windows`_
+- `Installation in a virtualenv`_
+- `Installation with pip`_
+- `Installation with easy_install`_
+- `Installation from tarball`_
+
+If you are a power-user and need the very latest features, you will
+
+- `Install from version control`_
+
+Once the software is installed, move on to :ref:`ConfigEnv` for better control
+and advanced features of |cubicweb|.
+
+.. _`Installation on Debian/Ubuntu`: DebianInstallation_
+.. _`Installation on Windows`: WindowsInstallation_
+.. _`Installation in a virtualenv`: VirtualenvInstallation_
+.. _`Installation with pip`: PipInstallation_
+.. _`Installation with easy_install`: EasyInstallInstallation_
+.. _`Installation from tarball`: TarballInstallation_
+.. _`Install from version control`: MercurialInstallation_
+
+
+.. _DebianInstallation:
+
+Debian/Ubuntu install
+---------------------
+
+|cubicweb| is packaged for Debian/Ubuntu (and derived
+distributions). Their integrated package-management system make
+installation and upgrade much easier for users since
+dependencies (like databases) are automatically installed.
+
+Depending on the distribution you are using, add the appropriate line to your
+`list of sources` (for example by editing ``/etc/apt/sources.list``), replacing
+``<release>`` with e.g. ``wheezy`` or ``trusty``::
+
+  deb http://download.logilab.org/production/ <release>/
+
+The repositories are signed with `Logilab's gnupg key`_. You can download
+and register the key to avoid warnings::
+
+  wget -O/etc/apt/trusted.gpg.d/logilab.gpg https://www.logilab.fr/logilab-debian-keyring.gpg
+
+Update your list of packages and perform the installation::
+
+  apt-get update
+  apt-get install cubicweb cubicweb-dev
+
+``cubicweb`` installs the framework itself, allowing you to create new
+instances. ``cubicweb-dev`` installs the development environment
+allowing you to develop new cubes.
+
+There is also a wide variety of :ref:`cubes <AvailableCubes>`. You can access a
+list of available cubes using ``apt-cache search cubicweb`` or at the
+`CubicWeb.org forge`_.
+
+.. note::
+
+  `cubicweb-dev` will install basic sqlite support. You can easily setup
+  :ref:`cubicweb with other database <DatabaseInstallation>` using the following
+  virtual packages :
+
+  * `cubicweb-postgresql-support` contains the necessary dependencies for
+    using :ref:`cubicweb with postgresql datatabase <PostgresqlConfiguration>`
+
+  * `cubicweb-mysql-support` contains the necessary dependencies for using
+    :ref:`cubicweb with mysql database <MySqlConfiguration>`.
+
+.. _`list of sources`: http://wiki.debian.org/SourcesList
+.. _`Logilab's gnupg key`: https://www.logilab.fr/logilab-debian-keyring.gpg
+.. _`CubicWeb.org Forge`: http://www.cubicweb.org/project/
+
+.. _WindowsInstallation:
+
+Windows Install
+---------------
+
+You need to have `python`_ version >= 2.5 and < 3 installed.
+
+If you want an automated install, your best option is probably the
+:ref:`EasyInstallInstallation`. EasyInstall is a tool that helps users to
+install python packages along with their dependencies, searching for suitable
+pre-compiled binaries on the `The Python Package Index`_.
+
+If you want better control over the process as well as a suitable development
+environment or if you are having problems with `easy_install`, read on to
+:ref:`SetUpWindowsEnv`.
+
+.. _python:  http://www.python.org/
+.. _`The Python Package Index`: http://pypi.python.org
+
+.. _VirtualenvInstallation:
+
+`Virtualenv` install
+--------------------
+
+|cubicweb| can be safely installed, used and contained inside a
+`virtualenv`_. You can use either :ref:`pip <PipInstallation>` or
+:ref:`easy_install <EasyInstallInstallation>` to install |cubicweb|
+inside an activated virtual environment.
+
+.. _PipInstallation:
+
+`pip` install
+-------------
+
+`pip <http://pip.openplans.org/>`_ is a python tool that helps downloading,
+building, installing, and managing Python packages and their dependencies. It
+is fully compatible with `virtualenv`_ and installs the packages from sources
+published on the `The Python Package Index`_.
+
+.. _`virtualenv`: http://virtualenv.openplans.org/
+
+A working compilation chain is needed to build the modules that include C
+extensions. If you really do not want to compile anything, installing `lxml <http://lxml.de/>`_,
+`Twisted Web <http://twistedmatrix.com/trac/wiki/Downloads/>`_ and `libgecode
+<http://www.gecode.org/>`_ will help.
+
+For Debian, these minimal dependencies can be obtained by doing::
+
+  apt-get install gcc python-pip python-dev python-lxml
+
+or, if you prefer to get as much as possible from pip::
+
+  apt-get install gcc python-pip python-dev libxslt1-dev libxml2-dev
+
+For Windows, you can install pre-built packages (possible `source
+<http://www.lfd.uci.edu/~gohlke/pythonlibs/>`_). For a minimal setup, install:
+
+- pip http://www.lfd.uci.edu/~gohlke/pythonlibs/#pip
+- setuptools http://www.lfd.uci.edu/~gohlke/pythonlibs/#setuptools
+- libxml-python http://www.lfd.uci.edu/~gohlke/pythonlibs/#libxml-python>
+- lxml http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml and
+- twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
+
+Make sure to choose the correct architecture and version of Python.
+
+Finally, install |cubicweb| and its dependencies, by running::
+
+  pip install cubicweb
+
+Many other :ref:`cubes <AvailableCubes>` are available. A list is available at
+`PyPI <http://pypi.python.org/pypi?%3Aaction=search&term=cubicweb&submit=search>`_
+or at the `CubicWeb.org forge`_.
+
+For example, installing the *blog cube* is achieved by::
+
+  pip install cubicweb-blog
+
+.. _EasyInstallInstallation:
+
+`easy_install` install
+----------------------
+
+.. note::
+
+   If you are not a Windows user and you have a compilation environment, we
+   recommend you to use the PipInstallation_.
+
+`easy_install`_ is a python utility that helps downloading, installing, and
+managing python packages and their dependencies.
+
+Install |cubicweb| and its dependencies, run::
+
+  easy_install cubicweb
+
+There is also a wide variety of :ref:`cubes <AvailableCubes>`. You can access a
+list of available cubes on `PyPI
+<http://pypi.python.org/pypi?%3Aaction=search&term=cubicweb&submit=search>`_
+or at the `CubicWeb.org Forge`_.
+
+For example, installing the *blog cube* is achieved by::
+
+  easy_install cubicweb-blog
+
+.. note::
+
+  If you encounter problem with :ref:`cubes <AvailableCubes>` installation,
+  consider using :ref:`PipInstallation` which is more stable
+  but can not installed pre-compiled binaries.
+
+.. _`easy_install`: http://packages.python.org/distribute/easy_install.html
+
+
+.. _SourceInstallation:
+
+Install from source
+-------------------
+
+.. _TarballInstallation:
+
+You can download the archive containing the sources from
+`http://download.logilab.org/pub/cubicweb/ <http://download.logilab.org/pub/cubicweb/>`_.
+
+Make sure you also have all the :ref:`InstallDependencies`.
+
+Once uncompressed, you can install the framework from inside the uncompressed
+folder with::
+
+  python setup.py install
+
+Or you can run |cubicweb| directly from the source directory by
+setting the :ref:`resource mode <RessourcesConfiguration>` to `user`. This will
+ease the development with the framework.
+
+There is also a wide variety of :ref:`cubes <AvailableCubes>`. You can access a
+list of availble cubes at the `CubicWeb.org Forge`_.
+
+
+.. _MercurialInstallation:
+
+Install from version control system
+-----------------------------------
+
+To keep-up with on-going development, clone the :ref:`Mercurial
+<MercurialPresentation>` repository::
+
+  hg clone -u 'last(tag())' http://hg.logilab.org/cubicweb # stable version
+  hg clone http://hg.logilab.org/cubicweb # development branch
+
+Make sure you also have all the :ref:`InstallDependencies`.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/admin/site-config.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,94 @@
+.. -*- coding: utf-8 -*-
+
+User interface for web site configuration
+=========================================
+
+.. image:: ../../images/lax-book_03-site-config-panel_en.png
+
+This panel allows you to configure the appearance of your instance site.
+Six menus are available and we will go through each of them to explain how
+to use them.
+
+Navigation
+~~~~~~~~~~
+This menu provides you a way to adjust some navigation options depending on
+your needs, such as the number of entities to display by page of results.
+Follows the detailled list of available options :
+
+* navigation.combobox-limit : maximum number of entities to display in related
+  combo box (sample format: 23)
+* navigation.page-size : maximum number of objects displayed by page of results
+  (sample format: 23)
+* navigation.related-limit : maximum number of related entities to display in
+  the primary view (sample format: 23)
+* navigation.short-line-size : maximum number of characters in short description
+  (sample format: 23)
+
+UI
+~~
+This menu provides you a way to customize the user interface settings such as
+date format or encoding in the produced html.
+Follows the detailled list of available options :
+
+* ui.date-format : how to format date in the ui ("man strftime" for format description)
+* ui.datetime-format : how to format date and time in the ui ("man strftime" for format
+  description)
+* ui.default-text-format : default text format for rich text fields.
+* ui.encoding : user interface encoding
+* ui.fckeditor :should html fields being edited using fckeditor (a HTML WYSIWYG editor).
+  You should also select text/html as default text format to actually get fckeditor.
+* ui.float-format : how to format float numbers in the ui
+* ui.language : language of the user interface
+* ui.main-template : id of main template used to render pages
+* ui.site-title	: site title, which is displayed right next to the logo in the header
+* ui.time-format : how to format time in the ui ("man strftime" for format description)
+
+
+Actions
+~~~~~~~
+This menu provides a way to configure the context in which you expect the actions
+to be displayed to the user and if you want the action to be visible or not.
+You must have notice that when you view a list of entities, an action box is
+available on the left column which display some actions as well as a drop-down
+menu for more actions.
+
+The context available are :
+
+* mainactions : actions listed in the left box
+* moreactions : actions listed in the `more` menu of the left box
+* addrelated : add actions listed in the left box
+* useractions : actions listed in the first section of drop-down menu
+  accessible from the right corner user login link
+* siteactions : actions listed in the second section of drop-down menu
+  accessible from the right corner user login link
+* hidden : select this to hide the specific action
+
+Boxes
+~~~~~
+The instance has already a pre-defined set of boxes you can use right away.
+This configuration section allows you to place those boxes where you want in the
+instance interface to customize it.
+
+The available boxes are :
+
+* actions box : box listing the applicable actions on the displayed data
+
+* boxes_blog_archives_box : box listing the blog archives
+
+* possible views box : box listing the possible views for the displayed data
+
+* rss box : RSS icon to get displayed data as a RSS thread
+
+* search box : search box
+
+* startup views box : box listing the configuration options available for
+  the instance site, such as `Preferences` and `Site Configuration`
+
+Components
+~~~~~~~~~~
+[WRITE ME]
+
+Contextual components
+~~~~~~~~~~~~~~~~~~~~~
+[WRITE ME]
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/annexes/depends.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,58 @@
+.. -*- coding: utf-8 -*-
+
+.. _InstallDependencies:
+
+Installation dependencies
+=========================
+
+When you run CubicWeb from source, either by downloading the tarball or
+cloning the mercurial tree, here is the list of tools and libraries you need
+to have installed in order for CubicWeb to work:
+
+* yapps - http://theory.stanford.edu/~amitp/yapps/ -
+  http://pypi.python.org/pypi/Yapps2
+
+* pygraphviz - http://networkx.lanl.gov/pygraphviz/ -
+  http://pypi.python.org/pypi/pygraphviz
+
+* docutils - http://docutils.sourceforge.net/ - http://pypi.python.org/pypi/docutils
+
+* lxml - http://codespeak.net/lxml - http://pypi.python.org/pypi/lxml
+
+* twisted - http://twistedmatrix.com/ - http://pypi.python.org/pypi/Twisted
+
+* logilab-common - http://www.logilab.org/project/logilab-common -
+  http://pypi.python.org/pypi/logilab-common/
+
+* logilab-database - http://www.logilab.org/project/logilab-database -
+  http://pypi.python.org/pypi/logilab-database/
+
+* logilab-constraint - http://www.logilab.org/project/logilab-constraint -
+  http://pypi.python.org/pypi/constraint/
+
+* logilab-mtconverter - http://www.logilab.org/project/logilab-mtconverter -
+  http://pypi.python.org/pypi/logilab-mtconverter
+
+* rql - http://www.logilab.org/project/rql - http://pypi.python.org/pypi/rql
+
+* yams - http://www.logilab.org/project/yams - http://pypi.python.org/pypi/yams
+
+* indexer - http://www.logilab.org/project/indexer -
+  http://pypi.python.org/pypi/indexer
+
+* passlib - https://code.google.com/p/passlib/ -
+  http://pypi.python.org/pypi/passlib
+
+If you're using a Postgresql database (recommended):
+
+* psycopg2 - http://initd.org/projects/psycopg2 - http://pypi.python.org/pypi/psycopg2
+* plpythonu extension
+
+Other optional packages:
+
+* fyzz - http://www.logilab.org/project/fyzz -
+  http://pypi.python.org/pypi/fyzz *to activate Sparql querying*
+
+
+Any help with the packaging of CubicWeb for more than Debian/Ubuntu (including
+eggs, buildouts, etc) will be greatly appreciated.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/annexes/docstrings-conventions.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,106 @@
+Javascript docstrings
+=====================
+
+Whereas in Python source code we only need to include a module docstrings
+using the directive `.. automodule:: mypythonmodule`, we will have to
+explicitely define Javascript modules and functions in the doctrings since
+there is no native directive to include Javascript files.
+
+Rest generation
+---------------
+
+`pyjsrest` is a small utility parsing Javascript doctrings and generating the
+corresponding Restructured file used by Sphinx to generate HTML documentation.
+This script will have the following structure::
+
+  ===========
+  filename.js
+  ===========
+  .. module:: filename.js
+
+We use the `.. module::` directive to register a javascript library
+as a Python module for Sphinx. This provides an entry in the module index.
+
+The contents of the docstring found in the javascript file will be added as is
+following the module declaration. No treatment will be done on the doctring.
+All the documentation structure will be in the docstrings and will comply
+with the following rules.
+
+Docstring structure
+-------------------
+
+Basically we document javascript with RestructuredText docstring
+following the same convention as documenting Python code.
+
+The doctring in Javascript files must be contained in standard
+Javascript comment signs, starting with `/**` and ending with `*/`,
+such as::
+
+ /**
+  * My comment starts here.
+  * This is the second line prefixed with a `*`.
+  * ...
+  * ...
+  * All the follwing line will be prefixed with a `*` followed by a space.
+  * ...
+  * ...
+  */
+
+
+Comments line prefixed by `//` will be ignored. They are reserved for source
+code comments dedicated to developers.
+
+
+Javscript functions docstring
+-----------------------------
+
+By default, the `function` directive describes a module-level function.
+
+`function` directive
+~~~~~~~~~~~~~~~~~~~~
+
+Its purpose is to define the function prototype such as::
+
+    .. function:: loadxhtml(url, data, reqtype, mode)
+
+If any namespace is used, we should add it in the prototype for now,
+until we define an appropriate directive::
+
+    .. function:: jQuery.fn.loadxhtml(url, data, reqtype, mode)
+
+Function parameters
+~~~~~~~~~~~~~~~~~~~
+
+We will define function parameters as a bulleted list, where the
+parameter name will be backquoted and followed by its description.
+
+Example of a javascript function docstring::
+
+    .. function:: loadxhtml(url, data, reqtype, mode)
+
+    cubicweb loadxhtml plugin to make jquery handle xhtml response
+
+    fetches `url` and replaces this's content with the result
+
+    Its arguments are:
+
+    * `url`
+
+    * `mode`, 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
+
+
+Optional parameter specification
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Javascript functions handle arguments not listed in the function signature.
+In the javascript code, they will be flagged using `/* ... */`. In the docstring,
+we flag those optional arguments the same way we would define it in
+Python::
+
+    .. function:: asyncRemoteExec(fname, arg1=None, arg2=None)
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/annexes/faq.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,433 @@
+.. -*- coding: utf-8 -*-
+
+Frequently Asked Questions (FAQ)
+================================
+
+
+Generalities
+````````````
+
+Why do you use the LGPL license to prevent me from doing X ?
+------------------------------------------------------------
+
+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 according to the terms of the LGPL. 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
+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.
+
+Why does not CubicWeb have a template language ?
+------------------------------------------------
+
+There are enough template languages out there. You can use your
+preferred template language if you want. [explain how to use a
+template language]
+
+*CubicWeb* does not define its own templating language as this was
+not our goal. Based on our experience, we realized that
+we could gain productivity by letting designers use design tools
+and developpers develop without the use of the templating language
+as an intermediary that could not be anyway efficient for both parties.
+Python is the templating language that we use in *CubicWeb*, but again,
+it does not prevent you from using a templating language.
+
+Moreover, CubicWeb currently supports `simpletal`_ out of the box and
+it is also possible to use the `cwtags`_ library to build html trees
+using the `with statement`_ with more comfort than raw strings.
+
+.. _`simpletal`: http://www.owlfish.com/software/simpleTAL/
+.. _`cwtags`: http://www.cubicweb.org/project/cwtags
+.. _`with statement`: http://www.python.org/dev/peps/pep-0343/
+
+Why do you think using pure python is better than using a template language ?
+-----------------------------------------------------------------------------
+
+Python is an Object Oriented Programming language and as such it
+already provides a consistent and strong architecture and syntax
+a templating language would not reach.
+
+Using Python instead of a template langage for describing the user interface
+makes it to maintain with real functions/classes/contexts without the need of
+learning a new dialect. By using Python, we use standard OOP techniques and
+this is a key factor in a robust application.
+
+CubicWeb looks pretty recent. Is it stable ?
+--------------------------------------------
+
+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.
+
+You can see the roadmap there:
+http://www.cubicweb.org/project/cubicweb?tab=projectroadmap_tab.
+
+
+Why is the RQL query language looking similar to X ?
+----------------------------------------------------
+
+It may remind you of SQL but it is higher level than SQL, more like
+SPARQL. Except that SPARQL did not exist when we started the project.
+With version 3.4, CubicWeb has support for SPARQL.
+
+The RQL language is what is going to make a difference with django-
+like frameworks for several reasons.
+
+1. accessing data is *much* easier with it. One can write complex
+   queries with RQL that would be tedious to define and hard to maintain
+   using an object/filter suite of method calls.
+
+2. it offers an abstraction layer allowing your applications to run
+   on multiple back-ends. That means not only various SQL backends
+   (postgresql, sqlite, sqlserver, mysql), but also non-SQL data stores like
+   LDAP directories and subversion/mercurial repositories (see the `vcsfile`
+   component).
+
+Which ajax library is CubicWeb using ?
+--------------------------------------
+
+CubicWeb uses jQuery_ and provides a few helpers on top of that. Additionally,
+some jQuery plugins are provided (some are provided in specific cubes).
+
+.. _jQuery: http://jquery.com
+
+
+Development
+```````````
+
+How to change the instance logo ?
+---------------------------------
+
+The logo is managed by css. You must provide a custom css that will contain
+the code below: 
+
+::
+   
+     #logo {
+        background-image: url("logo.jpg");
+     }
+
+
+``logo.jpg`` is in ``mycube/data`` directory.
+
+How to create an anonymous user ?
+---------------------------------
+
+This allows to browse the site without being authenticated. In the
+``all-in-one.conf`` file of your instance, define the anonymous user
+as follows ::
+
+  # login of the CubicWeb user account to use for anonymous user (if you want to
+  # allow anonymous)
+  anonymous-user=anon
+
+  # password of the CubicWeb user account matching login
+  anonymous-password=anon
+
+You also must ensure that this `anon` user is a registered user of
+the DB backend. If not, you can create through the administation
+interface of your instance by adding a user with in the group `guests`.
+
+.. note::
+    While creating a new instance, you can decide to allow access
+    to anonymous user, which will automatically execute what is
+    decribed above.
+
+
+How to format an entity date attribute ?
+----------------------------------------
+
+If your schema has an attribute of type `Date` or `Datetime`, you usually want to
+format it when displaying it. First, you should define your preferred format
+using the site configuration panel
+``http://appurl/view?vid=systempropertiesform`` and then set ``ui.date`` and/or
+``ui.datetime``.  Then in the view code, use:
+
+.. sourcecode:: python
+
+    entity.printable_value(date_attribute)
+
+which will always return a string whatever the attribute's type (so it's
+recommended also for other attribute types). By default it expects to generate
+HTML, so it deals with rich text formating, xml escaping...
+
+How to update a database after a schema modification ?
+------------------------------------------------------
+
+It depends on what has been modified in the schema.
+
+* update the permissions and properties of an entity or a relation:
+  ``sync_schema_props_perms('MyEntityOrRelation')``.
+
+* add an attribute: ``add_attribute('MyEntityType', 'myattr')``.
+
+* add a relation: ``add_relation_definition('SubjRelation', 'MyRelation', 'ObjRelation')``.
+
+I get `NoSelectableObject` exceptions, how do I debug selectors ?
+-----------------------------------------------------------------
+
+You just need to put the appropriate context manager around view/component
+selection. One standard place for components is in cubicweb/vregistry.py: 
+
+.. sourcecode:: python
+
+    def possible_objects(self, *args, **kwargs):
+        """return an iterator on possible objects in this registry for the given
+        context
+        """
+        from logilab.common.registry import traced_selection
+        with traced_selection():
+            for appobjects in self.itervalues():
+                try:
+                    yield self._select_best(appobjects, *args, **kwargs)
+                except NoSelectableObject:
+                    continue
+
+This will yield additional WARNINGs, like this::
+
+    2009-01-09 16:43:52 - (cubicweb.selectors) WARNING: selector one_line_rset returned 0 for <class 'cubicweb.web.views.basecomponents.WFHistoryVComponent'>
+
+For views, you can put this context in `cubicweb/web/views/basecontrollers.py` in
+the `ViewController`:
+
+.. sourcecode:: python
+
+    def _select_view_and_rset(self, rset):
+        ...
+        try:
+            from logilab.common.registry import traced_selection
+            with traced_selection():
+                view = self._cw.vreg['views'].select(vid, req, rset=rset)
+        except ObjectNotFound:
+            self.warning("the view %s could not be found", vid)
+            req.set_message(req._("The view %s could not be found") % vid)
+            vid = vid_from_rset(req, rset, self._cw.vreg.schema)
+            view = self._cw.vreg['views'].select(vid, req, rset=rset)
+        ...
+
+I get "database is locked" when executing tests
+-----------------------------------------------
+
+If you have "database is locked" as error when you are executing security tests,
+it is usually because commit or rollback are missing before login() calls.
+
+You can also use a context manager, to avoid such errors, as described
+here: :ref:`securitytest`.
+
+
+What are hooks used for ?
+-------------------------
+
+Hooks are executed around (actually before or after) events.  The most common
+events are data creation, update and deletion.  They permit additional constraint
+checking (those not expressible at the schema level), pre and post computations
+depending on data movements.
+
+As such, they are a vital part of the framework.
+
+Other kinds of hooks, called Operations, are available
+for execution just before commit.
+
+For more information, read :ref:`hooks` section.
+
+
+Configuration
+`````````````
+
+How to configure a LDAP source ?
+--------------------------------
+
+See :ref:`LDAP`.
+
+How to import LDAP users in |cubicweb| ?
+----------------------------------------
+
+  Here is a useful script which enables you to import LDAP users
+  into your *CubicWeb* instance by running the following:
+
+.. sourcecode:: python
+
+    import os
+    import pwd
+    import sys
+
+    from logilab.database import get_connection
+
+    def getlogin():
+        """avoid using os.getlogin() because of strange tty/stdin problems
+        (man 3 getlogin)
+        Another solution would be to use $LOGNAME, $USER or $USERNAME
+        """
+        return pwd.getpwuid(os.getuid())[0]
+
+
+    try:
+        database = sys.argv[1]
+    except IndexError:
+        print 'USAGE: python ldap2system.py <database>'
+        sys.exit(1)
+
+    if raw_input('update %s db ? [y/n]: ' % database).strip().lower().startswith('y'):
+        cnx = get_connection(user=getlogin(), database=database)
+        cursor = cnx.cursor()
+
+        insert = ('INSERT INTO euser (creation_date, eid, modification_date, login, '
+                  ' firstname, surname, last_login_time, upassword) '
+                  "VALUES (%(mtime)s, %(eid)s, %(mtime)s, %(login)s, %(firstname)s, "
+                  "%(surname)s, %(mtime)s, './fqEz5LeZnT6');")
+        update = "UPDATE entities SET source='system' WHERE eid=%(eid)s;"
+        cursor.execute("SELECT eid,type,source,extid,mtime FROM entities WHERE source!='system'")
+        for eid, type, source, extid, mtime in cursor.fetchall():
+            if type != 'CWUser':
+                print "don't know what to do with entity type", type
+                continue
+            if source != 'ldapuser':
+                print "don't know what to do with source type", source
+                continue
+            ldapinfos = dict(x.strip().split('=') for x in extid.split(','))
+            login = ldapinfos['uid']
+            firstname = ldapinfos['uid'][0].upper()
+            surname = ldapinfos['uid'][1:].capitalize()
+            if login != 'jcuissinat':
+                args = dict(eid=eid, type=type, source=source, login=login,
+                            firstname=firstname, surname=surname, mtime=mtime)
+                print args
+                cursor.execute(insert, args)
+                cursor.execute(update, args)
+
+        cnx.commit()
+        cnx.close()
+
+
+Security
+````````
+
+How to reset the password for user joe ?
+----------------------------------------
+
+If you want to reset the admin password for ``myinstance``, do::
+
+    $ cubicweb-ctl reset-admin-pwd myinstance
+
+You need to generate a new encrypted password::
+
+    $ python
+    >>> from cubicweb.server.utils import crypt_password
+    >>> crypt_password('joepass')
+    'qHO8282QN5Utg'
+    >>>
+
+and paste it in the database::
+
+    $ psql mydb
+    mydb=> update cw_cwuser set cw_upassword='qHO8282QN5Utg' where cw_login='joe';
+    UPDATE 1
+
+if you're running over SQL Server, you need to use the CONVERT
+function to convert the string to varbinary(255). The SQL query is
+therefore::
+
+    update cw_cwuser set cw_upassword=CONVERT(varbinary(255), 'qHO8282QN5Utg') where cw_login='joe';
+
+Be careful, the encryption algorithm is different on Windows and on
+Unix. You cannot therefore use a hash generated on Unix to fill in a
+Windows database, nor the other way round.
+
+
+You can prefer use a migration script similar to this shell invocation instead::
+
+    $ cubicweb-ctl shell <instance>
+    >>> from cubicweb import Binary
+    >>> from cubicweb.server.utils import crypt_password
+    >>> crypted = crypt_password('joepass')
+    >>> rset = rql('Any U WHERE U is CWUser, U login "joe"')
+    >>> joe = rset.get_entity(0,0)
+    >>> joe.cw_set(upassword=Binary(crypted))
+
+Please, refer to the script example is provided in the `misc/examples/chpasswd.py` file.
+
+The more experimented people would use RQL request directly::
+
+    >>> rql('SET X upassword %(a)s WHERE X is CWUser, X login "joe"',
+    ...     {'a': crypted})
+
+I've just created a user in a group and it doesn't work !
+---------------------------------------------------------
+
+You are probably getting errors such as ::
+
+  remove {'PR': 'Project', 'C': 'CWUser'} from solutions since your_user has no read access to cost
+
+This is because you have to put your user in the "users" group. The user has to
+be in both groups.
+
+How is security implemented ?
+------------------------------
+
+The basis for security is a mapping from operations to groups or
+arbitrary RQL expressions. These mappings are scoped to entities and
+relations.
+
+This is an example for an Entity Type definition:
+
+.. sourcecode:: python
+
+    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' or to users in group G, if G is linked by a permission
+entity named "add_version" to the version's project.
+
+An example for a Relation Definition (RelationType both defines a
+relation type and implicitly one relation definition, on which the
+permissions actually apply):
+
+.. sourcecode:: python
+
+    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'),) }
+
+The main difference lies in the basic available operations (there is
+no 'update' operation) and the usage of an RRQLExpression (rql
+expression for a relation) instead of an ERQLExpression (rql
+expression for an entity).
+
+You can find additional information in the section :ref:`securitymodel`.
+
+Is it possible to bypass security from the UI (web front) part ?
+----------------------------------------------------------------
+
+No. Only Hooks/Operations can do that.
+
+Can PostgreSQL and CubicWeb authentication work with kerberos ?
+----------------------------------------------------------------
+
+If you have PostgreSQL set up to accept kerberos authentication, you can set
+the db-host, db-name and db-user parameters in the `sources` configuration
+file while leaving the password blank. It should be enough for your
+instance to connect to postgresql with a kerberos ticket.
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/annexes/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,19 @@
+.. -*- coding: utf-8 -*-
+
+.. _Part4:
+
+----------
+Appendixes
+----------
+
+The following chapters are reference material.
+
+.. toctree::
+   :maxdepth: 1
+   :numbered:
+
+   faq
+   rql/index
+   mercurial
+   depends
+   docstrings-conventions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/annexes/mercurial.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,131 @@
+.. -*- coding: utf-8 -*-
+
+.. _MercurialPresentation:
+
+Introducing Mercurial
+=====================
+
+Introduction
+````````````
+Mercurial_ manages a distributed repository containing revisions
+trees (each revision indicates the changes required to obtain the
+next, and so on). Locally, we have a repository containing revisions
+tree, and a working directory. It is possible
+to put in its working directory, one of the versions of its local repository,
+modify and then push it in its repository.
+It is also possible to get revisions from another repository or to export
+its own revisions from the local repository to another repository.
+
+.. _Mercurial: http://www.selenic.com/mercurial/
+
+In contrast to CVS/Subversion, we usually create a repository per
+project to manage.
+
+In a collaborative development, we usually create a central repository
+accessible to all developers of the project. These central repository is used
+as a reference. According to their needs, everyone can have a local repository,
+that they will have to synchronize with the central repository from time to time.
+
+
+Major commands
+``````````````
+* Create a local repository::
+
+     hg clone ssh://myhost//home/src/repo
+
+* See the contents of the local repository (graphical tool in Qt)::
+
+     hgview
+
+* Add a sub-directory or file in the current directory::
+
+     hg add subdir
+
+* Move to the working directory a specific revision (or last
+  revision) from the local repository::
+
+     hg update [identifier-revision]
+     hg up [identifier-revision]
+
+* Get in its local repository, the tree of revisions contained in a
+  remote repository (this does not change the local directory)::
+
+     hg pull ssh://myhost//home/src/repo
+     hg pull -u ssh://myhost//home/src/repo # equivalent to pull + update
+
+* See what are the heads of branches of the local repository if a `pull`
+  returned a new branch::
+
+     hg heads
+
+* Submit the working directory in the local repository (and create a new
+  revision)::
+
+     hg commit
+     hg ci
+
+* Merge with the mother revision of local directory, another revision from
+  the local respository (the new revision will be then two mothers
+  revisions)::
+
+     hg merge identifier-revision
+
+* Export to a remote repository, the tree of revisions in its content
+  local respository (this does not change the local directory)::
+
+     hg push ssh://myhost//home/src/repo
+
+* See what local revisions are not in another repository::
+
+     hg outgoing ssh://myhost//home/src/repo
+
+* See what are the revisions of a repository not found locally::
+
+     hg incoming ssh://myhost//home/src/repo
+
+* See what is the revision of the local repository which has been taken out
+  from the working directory and amended::
+
+     hg parent
+
+* See the differences between the working directory and the mother revision
+  of the local repository, possibly to submit them in the local repository::
+
+     hg diff
+     hg commit-tool
+     hg ct
+
+
+Best Practices
+``````````````
+* Remember to `hg pull -u` regularly, and particularly before
+   a `hg commit`.
+
+* Remember to `hg push` when your repository contains a version
+  relatively stable of your changes.
+
+* If a `hg pull -u` created a new branch head:
+
+   1. find its identifier with `hg head`
+   2. merge with `hg merge`
+   3. `hg ci`
+   4. `hg push`
+
+Installation of the guestrepo extension
+```````````````````````````````````````
+
+Set up the guestrepo extension by getting a copy of the sources
+from https://bitbucket.org/selinc/guestrepo and adding the following
+lines to your ``~/.hgrc``: ::
+
+   [extensions]
+   guestrepo=/path/to/guestrepo/guestrepo
+
+
+More information
+````````````````
+
+For more information about Mercurial, please refer to the Mercurial project online documentation_.
+
+.. _documentation: http://www.selenic.com/mercurial/wiki/
+
Binary file doc/book/annexes/rql/Graph-ex.gif has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/annexes/rql/debugging.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,55 @@
+.. -*- coding: utf-8 -*-
+
+.. _DEBUGGING:
+
+Debugging RQL
+-------------
+
+Available levels
+~~~~~~~~~~~~~~~~
+
+Server debugging flags. They may be combined using binary operators.
+
+.. autodata:: cubicweb.server.DBG_NONE
+.. autodata:: cubicweb.server.DBG_RQL
+.. autodata:: cubicweb.server.DBG_SQL
+.. autodata:: cubicweb.server.DBG_REPO
+.. autodata:: cubicweb.server.DBG_MS
+.. autodata:: cubicweb.server.DBG_HOOKS
+.. autodata:: cubicweb.server.DBG_OPS
+.. autodata:: cubicweb.server.DBG_MORE
+.. autodata:: cubicweb.server.DBG_ALL
+
+
+Enable verbose output
+~~~~~~~~~~~~~~~~~~~~~
+
+To debug your RQL statements, it can be useful to enable a verbose output:
+
+.. sourcecode:: python
+
+    from cubicweb import server
+    server.set_debug(server.DBG_RQL|server.DBG_SQL|server.DBG_ALL)
+
+.. autofunction:: cubicweb.server.set_debug
+
+Another example showing how to debug hooks at a specific code site:
+
+.. sourcecode:: python
+
+    from cubicweb.server import debugged, DBG_HOOKS
+    with debugged(DBG_HOOKS):
+        person.cw_set(works_for=company)
+
+
+Detect largest RQL queries
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+See `Profiling and performance` chapter (see :ref:`PROFILING`).
+
+
+API
+~~~
+
+.. autoclass:: cubicweb.server.debugged
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/annexes/rql/implementation.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,159 @@
+
+
+Implementation
+--------------
+
+BNF grammar
+~~~~~~~~~~~
+
+The terminal elements are in capital letters, non-terminal in lowercase.
+The value of the terminal elements (between quotes) is a Python regular
+expression.
+::
+
+     statement ::= (select | delete | insert | update) ';'
+
+
+     # select specific rules
+     select      ::= 'DISTINCT'? E_TYPE selected_terms restriction? group? sort?
+
+     selected_terms ::= expression ( ',' expression)*
+
+     group       ::= 'GROUPBY' VARIABLE ( ',' VARIABLE)*
+
+     sort        ::= 'ORDERBY' sort_term ( ',' sort_term)*
+
+     sort_term   ::=  VARIABLE sort_method =?
+
+     sort_method ::= 'ASC' | 'DESC'
+
+
+     # delete specific rules
+     delete ::= 'DELETE' (variables_declaration | relations_declaration) restriction?
+
+
+     # insert specific rules
+     insert ::= 'INSERT' variables_declaration ( ':' relations_declaration)? restriction?
+
+
+     # update specific rules
+     update ::= 'SET' relations_declaration restriction
+
+
+     # common rules
+     variables_declaration ::= E_TYPE VARIABLE (',' E_TYPE VARIABLE)*
+
+     relations_declaration ::= simple_relation (',' simple_relation)*
+
+     simple_relation ::= VARIABLE R_TYPE expression
+
+     restriction ::= 'WHERE' relations
+
+     relations   ::= relation (LOGIC_OP relation)*
+                   | '(' relations')'
+
+     relation    ::= 'NOT'? VARIABLE R_TYPE COMP_OP? expression
+                   | 'NOT'? R_TYPE VARIABLE 'IN' '(' expression (',' expression)* ')'
+
+     expression  ::= var_or_func_or_const (MATH_OP var_or_func_or_const) *
+                   | '(' expression ')'
+
+     var_or_func_or_const ::= VARIABLE | function | constant
+
+     function    ::= FUNCTION '(' expression ( ',' expression) * ')'
+
+     constant    ::= KEYWORD | STRING | FLOAT | INT
+
+     # tokens
+     LOGIC_OP ::= ',' | 'OR' | 'AND'
+     MATH_OP  ::= '+' | '-' | '/' | '*'
+     COMP_OP  ::= '>' | '>=' | '=' | '<=' | '<' | '~=' | 'LIKE'
+
+     FUNCTION ::= 'MIN' | 'MAX' | 'SUM' | 'AVG' | 'COUNT' | 'UPPER' | 'LOWER'
+
+     VARIABLE ::= '[A-Z][A-Z0-9]*'
+     E_TYPE   ::= '[A-Z]\w*'
+     R_TYPE   ::= '[a-z_]+'
+
+     KEYWORD  ::= 'TRUE' | 'FALSE' | 'NULL' | 'TODAY' | 'NOW'
+     STRING   ::= "'([^'\]|\\.)*'" |'"([^\"]|\\.)*\"'
+     FLOAT    ::= '\d+\.\d*'
+     INT      ::= '\d+'
+
+
+Internal representation (syntactic tree)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The tree research does not contain the selected variables
+(e.g. there is only what follows "WHERE").
+
+The insertion tree does not contain the variables inserted or relations
+defined on these variables (e.g. there is only what follows "WHERE").
+
+The removal tree does not contain the deleted variables and relations
+(e.g. there is only what follows the "WHERE").
+
+The update tree does not contain the variables and relations updated
+(e.g. there is only what follows the "WHERE").
+
+::
+
+     Select         ((Relationship | And | Or)?, Group?, Sort?)
+     Insert         (Relations | And | Or)?
+     Delete         (Relationship | And | Or)?
+     Update         (Relations | And | Or)?
+
+     And            ((Relationship | And | Or), (Relationship | And | Or))
+     Or             ((Relationship | And | Or), (Relationship | And | Or))
+
+     Relationship   ((VariableRef, Comparison))
+
+     Comparison     ((Function | MathExpression | Keyword | Constant | VariableRef) +)
+
+     Function       (())
+     MathExpression ((MathExpression | Keyword | Constant | VariableRef), (MathExpression | Keyword | Constant | VariableRef))
+
+     Group          (VariableRef +)
+     Sort           (SortTerm +)
+     SortTerm       (VariableRef +)
+
+     VariableRef    ()
+     Variable       ()
+     Keyword        ()
+     Constant       ()
+
+
+Known limitations
+~~~~~~~~~~~~~~~~~
+
+- The current implementation does not support linking two relations of type 'is'
+  with an OR. I do not think that the negation is supported on this type of
+  relation (XXX to be confirmed).
+
+- missing COALESCE and certainly other things...
+
+- writing an rql query requires knowledge of the used schema (with real relation
+  names and entities, not those viewed in the user interface). On the other
+  hand, we cannot really bypass that, and it is the job of a user interface to
+  hide the RQL.
+
+
+Topics
+~~~~~~
+
+It would be convenient to express the schema matching
+relations (non-recursive rules)::
+
+     Document class Type <-> Document occurence_of Fiche class Type
+     Sheet class Type    <-> Form collection Collection class Type
+
+Therefore 1. becomes::
+
+     Document X where
+     X class C, C name 'Cartoon'
+     X owned_by U, U login 'syt'
+     X available true
+
+I'm not sure that we should handle this at RQL level ...
+
+There should also be a special relation 'anonymous'.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/annexes/rql/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,14 @@
+.. _RQLChapter:
+
+Relation Query Language (RQL)
+=============================
+
+This chapter describes the Relation Query Language syntax and its implementation in CubicWeb.
+
+.. toctree::
+   :maxdepth: 2
+
+   intro
+   language
+   debugging
+   implementation
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/annexes/rql/intro.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,162 @@
+
+.. _rql_intro:
+
+Introduction
+------------
+
+Goals of RQL
+~~~~~~~~~~~~
+
+The goal is to have a semantic language in order to:
+
+- query relations in a clear syntax
+- empowers access to data repository manipulation
+- making attributes/relations browsing easy
+
+As such, attributes will be regarded as cases of special relations (in
+terms of usage, the user should see no syntactic difference between an
+attribute and a relation).
+
+Comparison with existing languages
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+SQL
+```
+
+RQL may remind of SQL but works at a higher abstraction level (the *CubicWeb*
+framework generates SQL from RQL to fetch data from relation databases). RQL is
+focused on browsing relations. The user needs only to know about the *CubicWeb*
+data model he is querying, but not about the underlying SQL model.
+
+Sparql
+``````
+
+The query language most similar to RQL is SPARQL_, defined by the W3C to serve
+for the semantic web.
+
+Versa
+`````
+
+We should look in more detail, but here are already some ideas for the moment
+... Versa_ is the language most similar to what we wanted to do, but the model
+underlying data being RDF, there are some things such as namespaces or
+handling of the RDF types which does not interest us. On the functionality
+level, Versa_ is very comprehensive including through many functions of
+conversion and basic types manipulation, which we may want to look at one time
+or another.  Finally, the syntax is a little esoteric.
+
+Datalog
+```````
+
+Datalog_ is a prolog derived query langage which applies to relational
+databases. It is more expressive than RQL in that it accepts either
+extensional_ and intensional_ predicates (or relations). As of now,
+RQL only deals with intensional relations.
+
+The different types of queries
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Search (`Any`)
+   Extract entities and attributes of entities.
+
+Insert entities (`INSERT`)
+   Insert new entities or relations in the database.
+   It can also directly create relationships for the newly created entities.
+
+Update entities, create relations (`SET`)
+   Update existing entities in the database,
+   or create relations between existing entities.
+
+Delete entities or relationship (`DELETE`)
+   Remove entities or relations existing in the database.
+
+
+RQL relation expressions
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+RQL expressions apply to a live database defined by a
+:ref:`datamodel_definition`. Apart from the main type, or head, of the
+expression (search, insert, etc.) the most common constituent of an
+RQL expression is a (set of) relation expression(s).
+
+An RQL relation expression contains three components:
+
+* the subject, which is an entity type
+* the predicate, which is a relation definition (an arc of the schema)
+* the object, which is either an attribute or a relation to another entity
+
+.. image:: Graph-ex.gif
+    :alt: <subject> <predicate> <object>
+    :align: center
+
+.. warning::
+
+ A relation is always expressed in the order: ``subject``,
+ ``predicate``, ``object``.
+
+ It is important to determine if the entity type is subject or object
+ to construct a valid expression. Inverting the subject/object is an
+ error since the relation cannot be found in the schema.
+
+ If one does not have access to the code, one can find the order by
+ looking at the schema image in manager views (the subject is located
+ at the beginning of the arrow).
+
+An example of two related relation expressions::
+
+  P works_for C, P name N
+
+RQL variables represent typed entities. The type of entities is
+either automatically inferred (by looking at the possible relation
+definitions, see :ref:`RelationDefinition`) or explicitely constrained
+using the ``is`` meta relation.
+
+In the example above, we barely need to look at the schema. If
+variable names (in the RQL expression) and relation type names (in the
+schema) are expresssively designed, the human reader can infer as much
+as the |cubicweb| querier.
+
+The ``P`` variable is used twice but it always represent the same set
+of entities. Hence ``P works_for C`` and ``P name N`` must be
+compatible in the sense that all the Ps (which *can* refer to
+different entity types) must accept the ``works_for`` and ``name``
+relation types. This does restrict the set of possible values of P.
+
+Adding another relation expression::
+
+  P works_for C, P name N, C name "logilab"
+
+This further restricts the possible values of P through an indirect
+constraint on the possible values of ``C``. The RQL-level unification_
+happening there is translated to one (or several) joins_ at the
+database level.
+
+.. note::
+
+ In |cubicweb|, the term `relation` is often found without ambiguity
+ instead of `predicate`.  This predicate is also known as the
+ `property` of the triple in `RDF concepts`_
+
+
+RQL Operators
+~~~~~~~~~~~~~
+
+An RQL expression's head can be completed using various operators such
+as ``ORDERBY``, ``GROUPBY``, ``HAVING``, ``LIMIT`` etc.
+
+RQL relation expressions can be grouped with ``UNION`` or
+``WITH``. Predicate oriented keywords such as ``EXISTS``, ``OR``,
+``NOT`` are available.
+
+The complete zoo of RQL operators is described extensively in the
+following chapter (:ref:`RQL`).
+
+.. _RDF concepts: http://www.w3.org/TR/rdf-concepts/
+.. _Versa: http://wiki.xml3k.org/Versa
+.. _SPARQL: http://www.w3.org/TR/rdf-sparql-query/
+.. _unification: http://en.wikipedia.org/wiki/Unification_(computing)
+.. _joins: http://en.wikipedia.org/wiki/Join_(SQL)
+.. _Datalog: http://en.wikipedia.org/wiki/Datalog
+.. _intensional: http://en.wikipedia.org/wiki/Intensional_definition
+.. _extensional: http://en.wikipedia.org/wiki/Extension_(predicate_logic)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/annexes/rql/language.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,804 @@
+.. -*- coding: utf-8 -*-
+
+.. _RQL:
+
+RQL syntax
+----------
+
+.. _RQLKeywords:
+
+Reserved keywords
+~~~~~~~~~~~~~~~~~
+
+::
+
+  AND, ASC, BEING, DELETE, DESC, DISTINCT, EXISTS, FALSE, GROUPBY,
+  HAVING, ILIKE, INSERT, LIKE, LIMIT, NOT, NOW, NULL, OFFSET,
+  OR, ORDERBY, SET, TODAY, TRUE, UNION, WHERE, WITH
+
+The keywords are not case sensitive. You should not use them when defining your
+schema, or as RQL variable names.
+
+
+.. _RQLCase:
+
+Case
+~~~~
+
+* Variables should be all upper-cased.
+
+* Relation should be all lower-cased and match exactly names of relations defined
+  in the schema.
+
+* Entity types should start with an upper cased letter and be followed by at least
+  a lower cased latter.
+
+
+.. _RQLVariables:
+
+Variables and typing
+~~~~~~~~~~~~~~~~~~~~
+
+Entities and values to browse and/or select are represented in the query by
+*variables* that must be written in capital letters.
+
+With RQL, we do not distinguish between entities and attributes. The value of an
+attribute is considered as an entity of a particular type (see below), linked to
+one (real) entity by a relation called the name of the attribute, where the
+entity is the subject and the attribute the object.
+
+The possible type(s) for each variable is derived from the schema according to
+the constraints expressed above and thanks to the relations between each
+variable.
+
+We can restrict the possible types for a variable using the special relation
+**is** in the restrictions.
+
+
+.. _VirtualRelations:
+
+Virtual relations
+~~~~~~~~~~~~~~~~~
+
+Those relations may only be used in RQL query but are not actual attributes of
+your entities.
+
+* `has_text`: relation to use to query the full text index (only for entities
+  having fulltextindexed attributes).
+
+* `identity`: relation to use to tell that a RQL variable is the same as another
+  when you've to use two different variables for querying purpose. On the
+  opposite it's also useful together with the ``NOT`` operator to tell that two
+  variables should not identify the same entity
+
+
+.. _RQLLiterals:
+
+Literal expressions
+~~~~~~~~~~~~~~~~~~~
+
+Bases types supported by RQL are those supported by yams schema. Literal values
+are expressed as explained below:
+
+* string should be between double or single quotes. If the value contains a
+  quote, it should be preceded by a backslash '\\'
+
+* floats separator is dot '.'
+
+* boolean values are ``TRUE`` and ``FALSE`` keywords
+
+* date and time should be expressed as a string with ISO notation : YYYY/MM/DD
+  [hh:mm], or using keywords ``TODAY`` and ``NOW``
+
+You may also use the ``NULL`` keyword, meaning 'unspecified'.
+
+
+.. _RQLOperators:
+
+Operators
+~~~~~~~~~
+
+.. _RQLLogicalOperators:
+
+Logical operators
+`````````````````
+::
+
+     AND, OR, NOT, ','
+
+',' is equivalent to 'AND' but with the smallest among the priority of logical
+operators (see :ref:`RQLOperatorsPriority`).
+
+.. _RQLMathematicalOperators:
+
+Mathematical operators
+``````````````````````
+
++----------+---------------------+-----------+--------+
+| Operator |    Description      | Example   | Result |
++==========+=====================+===========+========+
+|  `+`     | addition            | 2 + 3     | 5      |
++----------+---------------------+-----------+--------+
+|  `-`     | subtraction         | 2 - 3     | -1     |
++----------+---------------------+-----------+--------+
+|  `*`     | multiplication      | 2 * 3     | 6      |
++----------+---------------------+-----------+--------+
+|  /       | division            | 4 / 2     | 2      |
++----------+---------------------+-----------+--------+
+|  %       | modulo (remainder)  | 5 % 4     | 1      |
++----------+---------------------+-----------+--------+
+|  ^       | exponentiation      | 2.0 ^ 3.0 | 8      |
++----------+---------------------+-----------+--------+
+|  &       | bitwise AND         | 91 & 15   | 11     |
++----------+---------------------+-----------+--------+
+|  `|`     | bitwise OR          | 32 | 3    | 35     |
++----------+---------------------+-----------+--------+
+|  #       | bitwise XOR         | 17 # 5    | 20     |
++----------+---------------------+-----------+--------+
+|  ~       | bitwise NOT         | ~1        | -2     |
++----------+---------------------+-----------+--------+
+|  <<      | bitwise shift left  | 1 << 4    | 16     |
++----------+---------------------+-----------+--------+
+|  >>      | bitwise shift right | 8 >> 2    | 2      |
++----------+---------------------+-----------+--------+
+
+
+Notice integer division truncates results depending on the backend behaviour. For
+instance, postgresql does.
+
+
+.. _RQLComparisonOperators:
+
+Comparison operators
+````````````````````
+ ::
+
+     =, !=, <, <=, >=, >, IN
+
+
+The syntax to use comparison operators is:
+
+    `VARIABLE attribute <operator> VALUE`
+
+The `=` operator is the default operator and can be omitted, i.e. :
+
+    `VARIABLE attribute = VALUE`
+
+is equivalent to
+
+    `VARIABLE attribute VALUE`
+
+
+The operator `IN` provides a list of possible values:
+
+.. sourcecode:: sql
+
+    Any X WHERE X name IN ('chauvat', 'fayolle', 'di mascio', 'thenault')
+
+
+.. _RQLStringOperators:
+
+String operators
+````````````````
+::
+
+  LIKE, ILIKE, ~=, REGEXP
+
+The ``LIKE`` string operator can be used with the special character `%` in
+a string as wild-card:
+
+.. sourcecode:: sql
+
+     -- match every entity whose name starts with 'Th'
+     Any X WHERE X name ~= 'Th%'
+     -- match every entity whose name endswith 'lt'
+     Any X WHERE X name LIKE '%lt'
+     -- match every entity whose name contains a 'l' and a 't'
+     Any X WHERE X name LIKE '%l%t%'
+
+``ILIKE`` is the case insensitive version of ``LIKE``. It's not
+available on all backend (e.g. sqlite doesn't support it). If not available for
+your backend, ``ILIKE`` will behave like ``LIKE``.
+
+`~=` is a shortcut version of ``ILIKE``, or of ``LIKE`` when the
+former is not available on the back-end.
+
+
+The ``REGEXP`` is an alternative to ``LIKE`` that supports POSIX
+regular expressions:
+
+.. sourcecode:: sql
+
+   -- match entities whose title starts with a digit
+   Any X WHERE X title REGEXP "^[0-9].*"
+
+
+The underlying SQL operator used is back-end-dependent :
+
+- the ``~`` operator is used for postgresql,
+- the ``REGEXP`` operator for mysql and sqlite.
+
+Other back-ends are not supported yet.
+
+
+.. _RQLOperatorsPriority:
+
+Operators priority
+``````````````````
+
+#. `(`, `)`
+#. `^`, `<<`, `>>`
+#. `*`, `/`, `%`, `&`
+#. `+`, `-`, `|`, `#`
+#. `NOT`
+#. `AND`
+#. `OR`
+#. `,`
+
+
+.. _RQLSearchQuery:
+
+Search Query
+~~~~~~~~~~~~
+
+Simplified grammar of search query: ::
+
+   [ `DISTINCT`] `Any` V1 (, V2) \*
+   [ `GROUPBY` V1 (, V2) \*] [ `ORDERBY` <orderterms>]
+   [ `LIMIT` <value>] [ `OFFSET` <value>]
+   [ `WHERE` <triplet restrictions>]
+   [ `WITH` V1 (, V2)\* BEING (<query>)]
+   [ `HAVING` <other restrictions>]
+   [ `UNION` <query>]
+
+Selection
+`````````
+
+The fist occuring clause is the selection of terms that should be in the result
+set.  Terms may be variable, literals, function calls, arithmetic, etc. and each
+term is separated by a comma.
+
+There will be as much column in the result set as term in this clause, respecting
+order.
+
+Syntax for function call is somewhat intuitive, for instance:
+
+.. sourcecode:: sql
+
+    Any UPPER(N) WHERE P firstname N
+
+
+Grouping and aggregating
+````````````````````````
+
+The ``GROUPBY`` keyword is followed by a list of terms on which results
+should be grouped. They are usually used with aggregate functions, responsible to
+aggregate values for each group (see :ref:`RQLAggregateFunctions`).
+
+For grouped queries, all selected variables must be either aggregated (i.e. used
+by an aggregate function) or grouped (i.e. listed in the ``GROUPBY``
+clause).
+
+
+Sorting
+```````
+
+The ``ORDERBY`` keyword if followed by the definition of the selection
+order: variable or column number followed by sorting method (``ASC``,
+``DESC``), ``ASC`` being the default. If the sorting method is not
+specified, then the sorting is ascendant (`ASC`).
+
+
+Pagination
+``````````
+
+The ``LIMIT`` and ``OFFSET`` keywords may be respectively used to
+limit the number of results and to tell from which result line to start (for
+instance, use `LIMIT 20` to get the first 20 results, then `LIMIT 20 OFFSET 20`
+to get the next 20.
+
+
+Restrictions
+````````````
+
+The ``WHERE`` keyword introduce one of the "main" part of the query, where
+you "define" variables and add some restrictions telling what you're interested
+in.
+
+It's a list of triplets "subject relation object", e.g. `V1 relation
+(V2 | <static value>)`. Triplets are separated using :ref:`RQLLogicalOperators`.
+
+.. note::
+
+  About the negation operator (``NOT``):
+
+  * ``NOT X relation Y`` is equivalent to ``NOT EXISTS(X relation Y)``
+
+  * ``Any X WHERE NOT X owned_by U`` means "entities that have no relation
+    ``owned_by``".
+
+  * ``Any X WHERE NOT X owned_by U, U login "syt"`` means "the entity have no
+     relation ``owned_by`` with the user syt". They may have a relation "owned_by"
+     with another user.
+
+In this clause, you can also use ``EXISTS`` when you want to know if some
+expression is true and do not need the complete set of elements that make it
+true. Testing for existence is much faster than fetching the complete set of
+results, especially when you think about using ``OR`` against several expressions. For instance
+if you want to retrieve versions which are in state "ready" or tagged by
+"priority", you should write :
+
+.. sourcecode:: sql
+
+    Any X ORDERBY PN,N
+    WHERE X num N, X version_of P, P name PN,
+          EXISTS(X in_state S, S name "ready")
+          OR EXISTS(T tags X, T name "priority")
+
+not
+
+.. sourcecode:: sql
+
+    Any X ORDERBY PN,N
+    WHERE X num N, X version_of P, P name PN,
+          (X in_state S, S name "ready")
+          OR (T tags X, T name "priority")
+
+Both queries aren't at all equivalent :
+
+* the former will retrieve all versions, then check for each one which are in the
+  matching state of or tagged by the expected tag,
+
+* the later will retrieve all versions, state and tags (cartesian product!),
+  compute join and then exclude each row which are in the matching state or
+  tagged by the expected tag. This implies that you won't get any result if the
+  in_state or tag tables are empty (ie there is no such relation in the
+  application). This is usually NOT what you want.
+
+Another common case where you may want to use ``EXISTS`` is when you
+find yourself using ``DISTINCT`` at the beginning of your query to
+remove duplicate results. The typical case is when you have a
+multivalued relation such as Version version_of Project and you want
+to retrieve projects which have a version:
+
+.. sourcecode:: sql
+
+  Any P WHERE V version_of P
+
+will return each project number of versions times. So you may be
+tempted to use:
+
+.. sourcecode:: sql
+
+  DISTINCT Any P WHERE V version_of P
+
+This will work, but is not efficient, as it will use the ``SELECT
+DISTINCT`` SQL predicate, which needs to retrieve all projects, then
+sort them and discard duplicates, which can have a very high cost for
+large result sets. So the best way to write this is:
+
+.. sourcecode:: sql
+
+  Any P WHERE EXISTS(V version_of P)
+
+
+You can also use the question mark (`?`) to mark optional relations. This allows
+you to select entities related **or not** to another. It is a similar concept
+to `Left outer join`_:
+
+    the result of a left outer join (or simply left join) for table A and B
+    always contains all records of the "left" table (A), even if the
+    join-condition does not find any matching record in the "right" table (B).
+
+You must use the `?` behind a variable to specify that the relation to
+that variable is optional. For instance:
+
+- Bugs of a project attached or not to a version
+
+   .. sourcecode:: sql
+
+       Any X, V WHERE X concerns P, P eid 42, X corrected_in V?
+
+  You will get a result set containing all the project's tickets, with either the
+  version in which it's fixed or None for tickets not related to a version.
+
+
+- All cards and the project they document if any
+
+  .. sourcecode:: sql
+
+       Any C, P WHERE C is Card, P? documented_by C
+
+Notice you may also use outer join:
+
+- on the RHS of attribute relation, e.g.
+
+  .. sourcecode:: sql
+
+       Any X WHERE X ref XR, Y name XR?
+
+  so that Y is outer joined on X by ref/name attributes comparison
+
+
+- on any side of an ``HAVING`` expression, e.g.
+
+  .. sourcecode:: sql
+
+       Any X WHERE X creation_date XC, Y creation_date YC
+       HAVING YEAR(XC)=YEAR(YC)?
+
+  so that Y is outer joined on X by comparison of the year extracted from their
+  creation date.
+
+  .. sourcecode:: sql
+
+       Any X WHERE X creation_date XC, Y creation_date YC
+       HAVING YEAR(XC)?=YEAR(YC)
+
+  would outer join X on Y instead.
+
+
+Having restrictions
+```````````````````
+
+The ``HAVING`` clause, as in SQL, may be used to restrict a query
+according to value returned by an aggregate function, e.g.
+
+.. sourcecode:: sql
+
+    Any X GROUPBY X WHERE X relation Y HAVING COUNT(Y) > 10
+
+It may however be used for something else: In the ``WHERE`` clause, we are
+limited to triplet expressions, so some things may not be expressed there. Let's
+take an example : if you want to get people whose upper-cased first name equals to
+another person upper-cased first name. There is no proper way to express this
+using triplet, so you should use something like:
+
+.. sourcecode:: sql
+
+    Any X WHERE X firstname XFN, Y firstname YFN, NOT X identity Y HAVING UPPER(XFN) = UPPER(YFN)
+
+Another example: imagine you want person born in 2000:
+
+.. sourcecode:: sql
+
+    Any X WHERE X birthday XB HAVING YEAR(XB) = 2000
+
+Notice that while we would like this to work without the HAVING clause, this
+can't be currently be done because it introduces an ambiguity in RQL's grammar
+that can't be handled by Yapps_, the parser's generator we're using.
+
+
+Sub-queries
+```````````
+
+The ``WITH`` keyword introduce sub-queries clause. Each sub-query has the
+form:
+
+  V1(,V2) BEING (rql query)
+
+Variables at the left of the ``BEING`` keyword defines into which
+variables results from the sub-query will be mapped to into the outer query.
+Sub-queries are separated from each other using a comma.
+
+Let's say we want to retrieve for each project its number of versions and its
+number of tickets. Due to the nature of relational algebra behind the scene, this
+can't be achieved using a single query. You have to write something along the
+line of:
+
+.. sourcecode:: sql
+
+  Any X, VC, TC WHERE X identity XX
+  WITH X, VC BEING (Any X, COUNT(V) GROUPBY X WHERE V version_of X),
+       XX, TC BEING (Any X, COUNT(T) GROUPBY X WHERE T ticket_of X)
+
+Notice that we can't reuse a same variable name as alias for two different
+sub-queries, hence the usage of 'X' and 'XX' in this example, which are then
+unified using the special `identity` relation (see :ref:`VirtualRelations`).
+
+.. warning::
+
+  Sub-queries define a new variable scope, so even if a variable has the same name
+  in the outer query and in the sub-query, they technically **aren't** the same
+  variable. So:
+
+  .. sourcecode:: sql
+
+     Any W, REF WITH W, REF BEING
+         (Any W, REF WHERE W is Workcase, W ref REF,
+                           W concerned_by D, D name "Logilab")
+
+  could be written:
+
+  .. sourcecode:: sql
+
+     Any W, REF WITH W, REF BEING
+        (Any W1, REF1 WHERE W1 is Workcase, W1 ref REF1,
+                            W1 concerned_by D, D name "Logilab")
+
+  Also, when a variable is coming from a sub-query, you currently can't reference
+  its attribute or inlined relations in the outer query, you've to fetch them in
+  the sub-query. For instance, let's say we want to sort by project name in our
+  first example, we would have to write:
+
+  .. sourcecode:: sql
+
+
+    Any X, VC, TC ORDERBY XN WHERE X identity XX
+    WITH X, XN, VC BEING (Any X, COUNT(V) GROUPBY X,XN WHERE V version_of X, X name XN),
+         XX, TC BEING (Any X, COUNT(T) GROUPBY X WHERE T ticket_of X)
+
+  instead of:
+
+  .. sourcecode:: sql
+
+    Any X, VC, TC ORDERBY XN WHERE X identity XX, X name XN,
+    WITH X, XN, VC BEING (Any X, COUNT(V) GROUPBY X WHERE V version_of X),
+         XX, TC BEING (Any X, COUNT(T) GROUPBY X WHERE T ticket_of X)
+
+  which would result in a SQL execution error.
+
+
+Union
+`````
+
+You may get a result set containing the concatenation of several queries using
+the ``UNION``. The selection of each query should have the same number of
+columns.
+
+.. sourcecode:: sql
+
+    (Any X, XN WHERE X is Person, X surname XN) UNION (Any X,XN WHERE X is Company, X name XN)
+
+
+.. _RQLFunctions:
+
+Available functions
+~~~~~~~~~~~~~~~~~~~
+
+Below is the list of aggregate and transformation functions that are supported
+nativly by the framework. Notice that cubes may define additional functions.
+
+.. _RQLAggregateFunctions:
+
+Aggregate functions
+```````````````````
++------------------------+----------------------------------------------------------+
+| ``COUNT(Any)``         | return the number of rows                                |
++------------------------+----------------------------------------------------------+
+| ``MIN(Any)``           | return the minimum value                                 |
++------------------------+----------------------------------------------------------+
+| ``MAX(Any)``           | return the maximum value                                 |
++------------------------+----------------------------------------------------------+
+| ``AVG(Any)``           | return the average value                                 |
++------------------------+----------------------------------------------------------+
+| ``SUM(Any)``           | return the sum of values                                 |
++------------------------+----------------------------------------------------------+
+| ``COMMA_JOIN(String)`` | return each value separated by a comma (for string only) |
++------------------------+----------------------------------------------------------+
+
+All aggregate functions above take a single argument. Take care some aggregate
+functions (e.g. ``MAX``, ``MIN``) may return `None` if there is no
+result row.
+
+.. _RQLStringFunctions:
+
+String transformation functions
+```````````````````````````````
+
++-----------------------------------------------+-----------------------------------------------------------------+
+| ``UPPER(String)``                             | upper case the string                                           |
++-----------------------------------------------+-----------------------------------------------------------------+
+| ``LOWER(String)``                             | lower case the string                                           |
++-----------------------------------------------+-----------------------------------------------------------------+
+| ``LENGTH(String)``                            | return the length of the string                                 |
++-----------------------------------------------+-----------------------------------------------------------------+
+| ``SUBSTRING(String, start, length)``          | extract from the string a string starting at given index and of |
+|                                               | given length                                                    |
++-----------------------------------------------+-----------------------------------------------------------------+
+| ``LIMIT_SIZE(String, max size)``              | if the length of the string is greater than given max size,     |
+|                                               | strip it and add ellipsis ("..."). The resulting string will    |
+|                                               | hence have max size + 3 characters                              |
++-----------------------------------------------+-----------------------------------------------------------------+
+| ``TEXT_LIMIT_SIZE(String, format, max size)`` | similar to the above, but allow to specify the MIME type of the |
+|                                               | text contained by the string. Supported formats are text/html,  |
+|                                               | text/xhtml and text/xml. All others will be considered as plain |
+|                                               | text. For non plain text format, sgml tags will be first removed|
+|                                               | before limiting the string.                                     |
++-----------------------------------------------+-----------------------------------------------------------------+
+
+.. _RQLDateFunctions:
+
+Date extraction functions
+`````````````````````````
+
++----------------------+----------------------------------------+
+| ``YEAR(Date)``       | return the year of a date or datetime  |
++----------------------+----------------------------------------+
+| ``MONTH(Date)``      | return the month of a date or datetime |
++----------------------+----------------------------------------+
+| ``DAY(Date)``        | return the day of a date or datetime   |
++----------------------+----------------------------------------+
+| ``HOUR(Datetime)``   | return the hours of a datetime         |
++----------------------+----------------------------------------+
+| ``MINUTE(Datetime)`` | return the minutes of a datetime       |
++----------------------+----------------------------------------+
+| ``SECOND(Datetime)`` | return the seconds of a datetime       |
++----------------------+----------------------------------------+
+| ``WEEKDAY(Date)``    | return the day of week of a date or    |
+|                      | datetime.  Sunday == 1, Saturday == 7. |
++----------------------+----------------------------------------+
+
+.. _RQLOtherFunctions:
+
+Other functions
+```````````````
++-------------------+--------------------------------------------------------------------+
+| ``ABS(num)``      |  return the absolute value of a number                             |
++-------------------+--------------------------------------------------------------------+
+| ``RANDOM()``      | return a pseudo-random value from 0.0 to 1.0                       |
++-------------------+--------------------------------------------------------------------+
+| ``FSPATH(X)``     | expect X to be an attribute whose value is stored in a             |
+|                   | :class:`BFSStorage` and return its path on the file system         |
++-------------------+--------------------------------------------------------------------+
+| ``FTIRANK(X)``    | expect X to be an entity used in a has_text relation, and return a |
+|                   | number corresponding to the rank order of each resulting entity    |
++-------------------+--------------------------------------------------------------------+
+| ``CAST(Type, X)`` | expect X to be an attribute and return it casted into the given    |
+|                   | final type                                                         |
++-------------------+--------------------------------------------------------------------+
+
+
+.. _RQLExamples:
+
+Examples
+~~~~~~~~
+
+- *Search for the object of identifier 53*
+
+  .. sourcecode:: sql
+
+        Any X WHERE X eid 53
+
+- *Search material such as comics, owned by syt and available*
+
+  .. sourcecode:: sql
+
+        Any X WHERE X is Document,
+                    X occurence_of F, F class C, C name 'Comics',
+                    X owned_by U, U login 'syt',
+                    X available TRUE
+
+- *Looking for people working for eurocopter interested in training*
+
+  .. sourcecode:: sql
+
+        Any P WHERE P is Person, P work_for S, S name 'Eurocopter',
+                    P interested_by T, T name 'training'
+
+- *Search note less than 10 days old written by jphc or ocy*
+
+  .. sourcecode:: sql
+
+        Any N WHERE N is Note, N written_on D, D day> (today -10),
+                    N written_by P, P name 'jphc' or P name 'ocy'
+
+- *Looking for people interested in training or living in Paris*
+
+  .. sourcecode:: sql
+
+        Any P WHERE P is Person, EXISTS(P interested_by T, T name 'training') OR
+                    (P city 'Paris')
+
+- *The surname and firstname of all people*
+
+  .. sourcecode:: sql
+
+        Any N, P WHERE X is Person, X name N, X firstname P
+
+  Note that the selection of several entities generally force
+  the use of "Any" because the type specification applies otherwise
+  to all the selected variables. We could write here
+
+  .. sourcecode:: sql
+
+        String N, P WHERE X is Person, X name N, X first_name P
+
+
+  Note: You can not specify several types with * ... where X is FirstType or X is SecondType*.
+  To specify several types explicitly, you have to do
+
+
+  .. sourcecode:: sql
+
+        Any X WHERE X is IN (FirstType, SecondType)
+
+
+.. _RQLInsertQuery:
+
+Insertion query
+~~~~~~~~~~~~~~~
+
+    `INSERT` <entity type> V1 (, <entity type> V2) \ * `:` <assignments>
+    [ `WHERE` <restriction>]
+
+:assignments:
+   list of relations to assign in the form `V1 relationship V2 | <static value>`
+
+The restriction can define variables used in assignments.
+
+Caution, if a restriction is specified, the insertion is done for
+*each line result returned by the restriction*.
+
+- *Insert a new person named 'foo'*
+
+  .. sourcecode:: sql
+
+        INSERT Person X: X name 'foo'
+
+- *Insert a new person named 'foo', another called 'nice' and a 'friend' relation
+  between them*
+
+  .. sourcecode:: sql
+
+        INSERT Person X, Person Y: X name 'foo', Y name 'nice', X friend Y
+
+- *Insert a new person named 'foo' and a 'friend' relation with an existing
+  person called 'nice'*
+
+  .. sourcecode:: sql
+
+        INSERT Person X: X name 'foo', X friend  Y WHERE Y name 'nice'
+
+.. _RQLSetQuery:
+
+Update and relation creation queries
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    `SET` <assignements>
+    [ `WHERE` <restriction>]
+
+Caution, if a restriction is specified, the update is done *for
+each result line returned by the restriction*.
+
+- *Renaming of the person named 'foo' to 'bar' with the first name changed*
+
+  .. sourcecode:: sql
+
+        SET X name 'bar', X firstname 'original' WHERE X is Person, X name 'foo'
+
+- *Insert a relation of type 'know' between objects linked by
+  the relation of type 'friend'*
+
+  .. sourcecode:: sql
+
+        SET X know Y  WHERE X friend Y
+
+
+.. _RQLDeleteQuery:
+
+Deletion query
+~~~~~~~~~~~~~~
+
+    `DELETE` (<entity type> V) | (V1 relation v2 ),...
+    [ `WHERE` <restriction>]
+
+Caution, if a restriction is specified, the deletion is made *for
+each line result returned by the restriction*.
+
+- *Deletion of the person named 'foo'*
+
+  .. sourcecode:: sql
+
+        DELETE Person X WHERE X name 'foo'
+
+- *Removal of all relations of type 'friend' from the person named 'foo'*
+
+  .. sourcecode:: sql
+
+        DELETE X friend Y WHERE X is Person, X name 'foo'
+
+
+.. _Yapps: http://theory.stanford.edu/~amitp/yapps/
+.. _Left outer join: http://en.wikipedia.org/wiki/Join_(SQL)#Left_outer_join
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/cubes/available-cubes.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,66 @@
+.. _AvailableCubes:
+
+Available cubes
+---------------
+
+An instance is made of several basic cubes. In the set of available
+basic cubes we can find for example:
+
+Base entity types
+~~~~~~~~~~~~~~~~~
+* addressbook_: PhoneNumber and PostalAddress
+* card_: Card, generic documenting card
+* event_: Event (define events, display them in calendars)
+* file_: File (to allow users to upload and store binary or text files)
+* link_: Link (to collect links to web resources)
+* mailinglist_: MailingList (to reference a mailing-list and the URLs
+  for its archives and its admin interface)
+* person_: Person (easily mixed with addressbook)
+* task_: Task (something to be done between start and stop date)
+* zone_: Zone (to define places within larger places, for example a
+  city in a state in a country)
+
+
+Classification
+~~~~~~~~~~~~~~
+* folder_: Folder (to organize things by grouping them in folders)
+* keyword_: Keyword (to define classification schemes)
+* tag_: Tag (to tag anything)
+
+Other features
+~~~~~~~~~~~~~~
+* basket_: Basket (like a shopping cart)
+* blog_: a blogging system using Blog and BlogEntry entity types
+* comment_: system to attach comment threads to entities)
+* email_: archiving management for emails (`Email`, `Emailpart`,
+  `Emailthread`), trigger action in cubicweb through email
+
+
+
+
+
+.. _addressbook: http://www.cubicweb.org/project/cubicweb-addressbook
+.. _basket: http://www.cubicweb.org/project/cubicweb-basket
+.. _card: http://www.cubicweb.org/project/cubicweb-card
+.. _blog: http://www.cubicweb.org/project/cubicweb-blog
+.. _comment: http://www.cubicweb.org/project/cubicweb-comment
+.. _email: http://www.cubicweb.org/project/cubicweb-email
+.. _event: http://www.cubicweb.org/project/cubicweb-event
+.. _file: http://www.cubicweb.org/project/cubicweb-file
+.. _folder: http://www.cubicweb.org/project/cubicweb-folder
+.. _keyword: http://www.cubicweb.org/project/cubicweb-keyword
+.. _link: http://www.cubicweb.org/project/cubicweb-link
+.. _mailinglist: http://www.cubicweb.org/project/cubicweb-mailinglist
+.. _person: http://www.cubicweb.org/project/cubicweb-person
+.. _tag: http://www.cubicweb.org/project/cubicweb-tag
+.. _task: http://www.cubicweb.org/project/cubicweb-task
+.. _zone: http://www.cubicweb.org/project/cubicweb-zone
+
+To declare the use of a cube, once installed, add the name of the cube
+and its dependency relation in the `__depends_cubes__` dictionary
+defined in the file `__pkginfo__.py` of your own component.
+
+.. note::
+  The listed cubes above are available as debian-packages on `CubicWeb's forge`_.
+
+.. _`CubicWeb's forge`: http://www.cubicweb.org/project?vtitle=All%20cubicweb%20projects
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/cubes/cc-newcube.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,57 @@
+Creating a new cube from scratch
+--------------------------------
+
+Let's start by creating the cube environment in which we will develop ::
+
+  cd ~/cubes
+  # use cubicweb-ctl to generate a template for the cube
+  # will ask some questions, most with nice default
+  cubicweb-ctl newcube mycube
+  # makes the cube source code managed by mercurial
+  cd mycube
+  hg init
+  hg add .
+  hg ci
+
+If all went well, you should see the cube you just created in the list
+returned by ``cubicweb-ctl list`` in the  *Available cubes* section.
+If not, please refer to :ref:`ConfigurationEnv`.
+
+To reuse an existing cube, add it to the list named
+``__depends_cubes__`` which is defined in :file:`__pkginfo__.py`.
+This variable is used for the instance packaging (dependencies handled
+by system utility tools such as APT) and to find used cubes when the
+database for the instance is created.
+
+On a Unix system, the available cubes are usually stored in the
+directory :file:`/usr/share/cubicweb/cubes`. If you are using the
+cubicweb mercurial repository (:ref:`SourceInstallation`), the cubes
+are searched in the directory
+:file:`/path/to/cubicweb_toplevel/cubes`. In this configuration
+cubicweb itself ought to be located at
+:file:`/path/to/cubicweb_toplevel/cubicweb`.
+
+.. note::
+
+    Please note that if you do not wish to use default directory for your cubes
+    library, you should set the :envvar:`CW_CUBES_PATH` environment variable to
+    add extra directories where cubes will be search, and you'll then have to use
+    the option `--directory` to specify where you would like to place the source
+    code of your cube:
+
+    ``cubicweb-ctl newcube --directory=/path/to/cubes/library mycube``
+
+
+.. XXX resurrect once live-server is back
+.. Usage of :command:`cubicweb-ctl liveserver`
+.. -------------------------------------------
+
+.. To quickly test a new cube, you can also use the `liveserver` command for cubicweb-ctl
+.. which allows to create an instance in memory (using an SQLite database by
+.. default) and make it accessible through a web server ::
+
+..   cubicweb-ctl live-server mycube
+
+.. or by using an existing database (SQLite or Postgres)::
+
+..   cubicweb-ctl live-server -s myfile_sources mycube
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/cubes/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,11 @@
+Cubes
+=====
+
+This chapter describes how to define your own cubes and reuse already available cubes.
+
+.. toctree::
+   :maxdepth: 1
+
+   layout.rst
+   cc-newcube.rst
+   available-cubes.rst
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/cubes/layout.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,153 @@
+
+.. _foundationsCube:
+
+.. _cubelayout:
+
+Standard structure for a cube
+-----------------------------
+
+A cube is structured as follows:
+
+::
+
+  mycube/
+  |
+  |-- data/
+  |   |-- cubes.mycube.css
+  |   |-- cubes.mycube.js
+  |   `-- external_resources
+  |
+  |-- debian/
+  |   |-- changelog
+  |   |-- compat
+  |   |-- control
+  |   |-- copyright
+  |   |-- cubicweb-mycube.prerm
+  |   `-- rules
+  |
+  |-- entities.py
+  |
+  |-- i18n/
+  |   |-- en.po
+  |   |-- es.po
+  |   `-- fr.po
+  |
+  |-- __init__.py
+  |
+  |-- MANIFEST.in
+  |
+  |-- migration/
+  |   |-- postcreate.py
+  |   `-- precreate.py
+  |
+  |-- __pkginfo__.py
+  |
+  |-- schema.py
+  |
+  |-- setup.py
+  |
+  |-- site_cubicweb.py
+  |
+  |-- hooks.py
+  |
+  |-- test/
+  |   |-- data/
+  |   |   `-- bootstrap_cubes
+  |   |-- pytestconf.py
+  |   |-- realdb_test_mycube.py
+  |   `-- test_mycube.py
+  |
+  `-- views.py
+
+
+We can use subpackages instead of python modules for ``views.py``, ``entities.py``,
+``schema.py`` or ``hooks.py``. For example, we could have:
+
+::
+
+  mycube/
+  |
+  |-- entities.py
+  |-- hooks.py
+  `-- views/
+      |-- __init__.py
+      |-- forms.py
+      |-- primary.py
+      `-- widgets.py
+
+
+where :
+
+* ``schema`` contains the schema definition (server side only)
+* ``entities`` contains the entity definitions (server side and web interface)
+* ``hooks`` contains hooks and/or views notifications (server side only)
+* ``views`` contains the web interface components (web interface only)
+* ``test`` contains tests related to the cube (not installed)
+* ``i18n`` contains message catalogs for supported languages (server side and
+  web interface)
+* ``data`` contains data files for static content (images, css,
+  javascript code)...(web interface only)
+* ``migration`` contains initialization files for new instances (``postcreate.py``)
+  and a file containing dependencies of the component depending on the version
+  (``depends.map``)
+* ``debian`` contains all the files managing debian packaging (you will find
+  the usual files ``control``, ``rules``, ``changelog``... not installed)
+* file ``__pkginfo__.py`` provides component meta-data, especially the distribution
+  and the current version (server side and web interface) or sub-cubes used by
+  the cube.
+
+
+At least you should have the file ``__pkginfo__.py``.
+
+
+The :file:`__init__.py` and :file:`site_cubicweb.py` files
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. XXX WRITEME
+
+The :file:`__pkginfo__.py` file
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It contains metadata describing your cube, mostly useful for packaging.
+
+Two important attributes of this module are __depends__ and __recommends__
+dictionaries that indicates what should be installed (and each version if
+necessary) for the cube to work.
+
+Dependency on other cubes are expected to be of the form 'cubicweb-<cubename>'.
+
+When an instance is created, dependencies are automatically installed, while
+recommends are not.
+
+Recommends may be seen as a kind of 'weak dependency'. Eg, the most important
+effect of recommending a cube is that, if cube A recommends cube B, the cube B
+will be loaded before the cube A (same thing happend when A depends on B).
+
+Having this behaviour is sometime desired: on schema creation, you may rely on
+something defined in the other's schema; on database creation, on something
+created by the other's postcreate, and so on.
+
+
+:file:`migration/precreate.py` and :file:`migration/postcreate.py`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. XXX detail steps of instance creation
+
+
+External resources such as image, javascript and css files
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. XXX naming convention external_resources file
+
+
+Out-of the box testing
+~~~~~~~~~~~~~~~~~~~~~~
+
+.. XXX MANIFEST.in, __pkginfo__.include_dirs, debian
+
+
+Packaging and distribution
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. XXX MANIFEST.in, __pkginfo__.include_dirs, debian
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/dataimport.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,85 @@
+.. -*- coding: utf-8 -*-
+
+.. _dataimport:
+
+Dataimport
+==========
+
+*CubicWeb* is designed to manipulate huge of amount of data, and provides utilities to do so.
+
+The main entry point is :mod:`cubicweb.dataimport.importer` which defines an
+:class:`ExtEntitiesImporter` class responsible for importing data from an external source in the
+form :class:`ExtEntity` objects. An :class:`ExtEntity` is a transitional representation of an
+entity to be imported in the CubicWeb instance; building this representation is usually
+domain-specific -- e.g. dependent of the kind of data source (RDF, CSV, etc.) -- and is thus the
+responsibility of the end-user.
+
+Along with the importer, a *store* must be selected, which is responsible for insertion of data into
+the database. There exists different kind of stores_, allowing to insert data within different
+levels of the *CubicWeb* API and with different speed/security tradeoffs. Those keeping all the
+*CubicWeb* hooks and security will be slower but the possible errors in insertion (bad data types,
+integrity error, ...) will be handled.
+
+
+Example
+-------
+
+Consider the following schema snippet.
+
+.. code-block:: python
+
+    class Person(EntityType):
+        name = String(required=True)
+
+    class knows(RelationDefinition):
+        subject = 'Person'
+        object = 'Person'
+
+along with some data in a ``people.csv`` file::
+
+    # uri,name,knows
+    http://www.example.org/alice,Alice,
+    http://www.example.org/bob,Bob,http://www.example.org/alice
+
+The following code (using a shell context) defines a function `extentities_from_csv` to read
+`Person` external entities coming from a CSV file and calls the :class:`ExtEntitiesImporter` to
+insert corresponding entities and relations into the CubicWeb instance.
+
+.. code-block:: python
+
+    from cubicweb.dataimport import ucsvreader, RQLObjectStore
+    from cubicweb.dataimport.importer import ExtEntity, ExtEntitiesImporter
+
+    def extentities_from_csv(fpath):
+        """Yield Person ExtEntities read from `fpath` CSV file."""
+        with open(fpath) as f:
+            for uri, name, knows in ucsvreader(f, skipfirst=True, skip_empty=False):
+                yield ExtEntity('Personne', uri,
+                                {'nom': set([name]), 'connait': set([knows])})
+
+    extenties = extentities_from_csv('people.csv')
+    store = RQLObjectStore(cnx)
+    importer = ExtEntitiesImporter(schema, store)
+    importer.import_entities(extenties)
+    commit()
+    rset = cnx.execute('String N WHERE X nom N, X connait Y, Y nom "Alice"')
+    assert rset[0][0] == u'Bob', rset
+
+Importer API
+------------
+
+.. automodule:: cubicweb.dataimport.importer
+
+
+Stores
+~~~~~~
+
+.. automodule:: cubicweb.dataimport.stores
+
+
+SQLGenObjectStore
+-----------------
+
+This store relies on *COPY FROM*/execute many sql commands to directly push data using SQL commands
+rather than using the whole *CubicWeb* API. For now, **it only works with PostgresSQL** as it requires
+the *COPY FROM* command.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/datamodel/baseschema.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,42 @@
+.. _pre_defined_entity_types:
+
+Pre-defined entities in the library
+-----------------------------------
+
+The library defines a set of entity schemas that are required by the system
+or commonly used in *CubicWeb* instances.
+
+
+Entity types used to store the schema
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+* _`CWEType`, entity type
+* _`CWRType`, relation type
+* _`CWRelation`, relation definition
+* _`CWAttribute`, attribute relation definition
+* _`CWConstraint`,  `CWConstraintType`, `RQLExpression`
+
+Entity types used to manage users and permissions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+* _`CWUser`, system users
+* _`CWGroup`, users groups
+
+Entity types used to manage workflows
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+* :ref:`Workflow <Workflow>`, workflow entity, linked to some entity types which may use this workflow
+* _`State`, workflow state
+* _`Transition`, workflow transition
+* _`TrInfo`, record of a transition trafic for an entity
+
+Other entity types
+~~~~~~~~~~~~~~~~~~
+* _`CWCache`, cache entities used to improve performances
+* _`CWProperty`, used to configure the instance
+
+* _`EmailAddress`, email address, used by the system to send notifications
+  to the users and also used by others optionnals schemas
+
+* _`Bookmark`, an entity type used to allow a user to customize his links within
+  the instance
+
+* _`ExternalUri`, used for semantic web site to indicate that an entity is the
+  same as another from an external site
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/datamodel/define-workflows.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,160 @@
+.. -*- coding: utf-8 -*-
+
+.. _Workflow:
+
+Defining a Workflow
+===================
+
+General
+-------
+
+A workflow describes how certain entities have to evolve between different
+states. Hence we have a set of states, and a "transition graph", i.e. a set of
+possible transitions from one state to another state.
+
+We will define a simple workflow for a blog, with only the following two states:
+`submitted` and `published`. You may want to take a look at :ref:`TutosBase` if
+you want to quickly setup an instance running a blog.
+
+Setting up a workflow
+---------------------
+
+We want to create a workflow to control the quality of the BlogEntry
+submitted on the instance. When a BlogEntry is created by a user
+its state should be `submitted`. To be visible to all, it has to
+be in the state `published`. To move it from `submitted` to `published`,
+we need a transition that we can call `approve_blogentry`.
+
+A BlogEntry state should not be modifiable by every user.
+So we have to define a group of users, `moderators`, and
+this group will have appropriate permissions to publish a BlogEntry.
+
+There are two ways to create a workflow: from the user interface, or
+by defining it in ``migration/postcreate.py``. This script is executed
+each time a new ``cubicweb-ctl db-init`` is done.  We strongly
+recommend to create the workflow in ``migration/postcreate.py`` and we
+will now show you how. Read `Two bits of warning`_ to understand why.
+
+The state of an entity is managed by the `in_state` attribute which
+can be added to your entity schema by inheriting from
+`cubicweb.schema.WorkflowableEntityType`.
+
+
+About our example of BlogEntry, we must have:
+
+.. sourcecode:: python
+
+  from cubicweb.schema import WorkflowableEntityType
+
+  class BlogEntry(WorkflowableEntityType):
+      ...
+
+
+Creating states, transitions and group permissions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :mod:`postcreate` script is executed in a special environment,
+adding several |cubicweb| primitives that can be used.
+
+They are all defined in the :class:`ServerMigrationHelper` class.
+We will only discuss the methods we use to create a workflow in this example.
+
+A workflow is a collection of entities of type ``State`` and of type
+``Transition`` which are standard *CubicWeb* entity types.
+
+To define a workflow for BlogDemo, please add the following lines
+to ``migration/postcreate.py``:
+
+.. sourcecode:: python
+
+  _ = unicode
+
+  moderators = add_entity('CWGroup', name=u"moderators")
+
+This adds the `moderators` user group.
+
+.. sourcecode:: python
+
+  wf = add_workflow(u'blog publication workflow', 'BlogEntry')
+
+At first, instanciate a new workflow object with a gentle description
+and the concerned entity types (this one can be a tuple for multiple
+value).
+
+.. sourcecode:: python
+
+  submitted = wf.add_state(_('submitted'), initial=True)
+  published = wf.add_state(_('published'))
+
+This will create two entities of type ``State``, one with name
+'submitted', and the other with name 'published'.
+
+``add_state`` expects as first argument the name of the state you want
+to create and an optional argument to say if it is supposed to be the
+initial state of the entity type.
+
+.. sourcecode:: python
+
+  wf.add_transition(_('approve_blogentry'), (submitted,), published, ('moderators', 'managers'),)
+
+This will create an entity of type ``Transition`` with name
+`approve_blogentry` which will be linked to the ``State`` entities
+created before.
+
+``add_transition`` expects
+
+  * as the first argument: the name of the transition
+  * then the list of states on which the transition can be triggered,
+  * the target state of the transition,
+  * and the permissions
+    (e.g. a list of user groups who can apply the transition; the user
+    has to belong to at least one of the listed group to perform the action).
+
+.. sourcecode:: python
+
+  checkpoint()
+
+.. note::
+  Do not forget to add the `_()` in front of all states and
+  transitions names while creating a workflow so that they will be
+  identified by the i18n catalog scripts.
+
+In addition to the user groups (one of which the user needs to belong
+to), we could have added a RQL condition.  In this case, the user can
+only perform the action if the two conditions are satisfied.
+
+If we use an RQL condition on a transition, we can use the following variables:
+
+* `X`, the entity on which we may pass the transition
+* `U`, the user executing that may pass the transition
+
+
+.. image:: ../../../images/03-transitions-view_en.png
+
+You can notice that in the action box of a BlogEntry, the state is now
+listed as well as the possible transitions for the current state
+defined by the workflow.
+
+The transitions will only be displayed for users having the right permissions.
+In our example, the transition `approve_blogentry` will only be displayed
+for the users belonging to the group `moderators` or `managers`.
+
+
+Two bits of warning
+~~~~~~~~~~~~~~~~~~~
+
+We could perfectly use the administration interface to do these
+operations. It is a convenient thing to do at times (when doing
+development, to quick-check things). But it is not recommended beyond
+that because it is a bit complicated to do it right and it will be
+only local to your instance (or, said a bit differently, such a
+workflow only exists in an instance database). Furthermore, you cannot
+write unit tests against deployed instances, and experience shows it
+is mandatory to have tests for any mildly complicated workflow
+setup.
+
+Indeed, if you create the states and transitions through the user
+interface, next time you initialize the database you will have to
+re-create all the workflow entities. The user interface should only be
+a reference for you to view the states and transitions, but is not the
+appropriate interface to define your application workflow.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/datamodel/definition.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,912 @@
+.. -*- coding: utf-8 -*-
+
+.. _datamodel_definition:
+
+Yams *schema*
+-------------
+
+The **schema** is the core piece of a *CubicWeb* instance as it
+defines and handles the data model. It is based on entity types that
+are either already defined in `Yams`_ and the *CubicWeb* standard
+library; or more specific types defined in cubes. The schema for a
+cube is defined in a `schema` python module or package.
+
+.. _`Yams`: http://www.logilab.org/project/yams
+
+.. _datamodel_overview:
+
+Overview
+~~~~~~~~
+
+The core idea of the yams schema is not far from the classical
+`Entity-relationship`_ model. But while an E/R model (or `logical
+model`) traditionally has to be manually translated to a lower-level
+data description language (such as the SQL `create table`
+sublanguage), also often described as the `physical model`, no such
+step is required with |yams| and |cubicweb|.
+
+.. _`Entity-relationship`: http://en.wikipedia.org/wiki/Entity-relationship_model
+
+This is because in addition to high-level, logical |yams| models, one
+uses the |rql| data manipulation language to query, insert, update and
+delete data. |rql| abstracts as much of the underlying SQL database as
+a |yams| schema abstracts from the physical layout. The vagaries of
+SQL are avoided.
+
+As a bonus point, such abstraction make it quite comfortable to build
+or use different backends to which |rql| queries apply.
+
+So, as in the E/R formalism, the building blocks are ``entities``
+(:ref:`EntityType`), ``relationships`` (:ref:`RelationType`,
+:ref:`RelationDefinition`) and ``attributes`` (handled like relation
+with |yams|).
+
+Let us detail a little the divergences between E/R and |yams|:
+
+* all relationship are binary which means that to represent a
+  non-binary relationship, one has to use an entity,
+* relationships do not support attributes (yet, see:
+  http://www.cubicweb.org/ticket/341318), hence the need to reify it
+  as an entity if need arises,
+* all entities have an `eid` attribute (an integer) that is its
+  primary key (but it is possible to declare uniqueness on other
+  attributes)
+
+Also |yams| supports the notions of:
+
+* entity inheritance (quite experimental yet, and completely
+  undocumented),
+* relation type: that is, relationships can be established over a set
+  of couple of entity types (henre the distinction made between
+  `RelationType` and `RelationDefinition` below)
+
+Finally |yams| has a few concepts of its own:
+
+* relationships being oriented and binary, we call the left hand
+  entity type the `subject` and the right hand entity type the
+  `object`
+
+.. note::
+
+   The |yams| schema is available at run time through the .schema
+   attribute of the `vregistry`.  It's an instance of
+   :class:`cubicweb.schema.Schema`, which extends
+   :class:`yams.schema.Schema`.
+
+.. _EntityType:
+
+Entity type
+~~~~~~~~~~~
+
+An entity type is an instance of :class:`yams.schema.EntitySchema`. Each entity type has
+a set of attributes and relations, and some permissions which define who can add, read,
+update or delete entities of this type.
+
+The following built-in types are available: ``String``,
+``Int``, ``BigInt``, ``Float``, ``Decimal``, ``Boolean``,
+``Date``, ``Datetime``, ``Time``, ``Interval``, ``Byte`` and
+``Password``. They can only be used as attributes of an other entity
+type.
+
+There is also a `RichString` kindof type:
+
+.. autofunction:: yams.buildobjs.RichString
+
+The ``__unique_together__`` class attribute is a list of tuples of names of
+attributes or inlined relations.  For each tuple, CubicWeb ensures the unicity
+of the combination.  For example:
+
+.. sourcecode:: python
+
+  class State(EntityType):
+      __unique_together__ = [('name', 'state_of')]
+
+      name = String(required=True)
+      state_of = SubjectRelation('Workflow', cardinality='1*',
+                                 composite='object', inlined=True)
+
+
+You can find more base entity types in
+:ref:`pre_defined_entity_types`.
+
+.. XXX yams inheritance
+
+.. _RelationType:
+
+Relation type
+~~~~~~~~~~~~~
+
+A relation type is an instance of
+:class:`yams.schema.RelationSchema`. A relation type is simply a
+semantic definition of a kind of relationship that may occur in an
+application.
+
+It may be referenced by zero, one or more relation definitions.
+
+It is important to choose a good name, at least to avoid conflicts
+with some semantically different relation defined in other cubes
+(since there's only a shared name space for these names).
+
+A relation type holds the following properties (which are hence shared
+between all relation definitions of that type):
+
+* `inlined`: boolean handling the physical optimization for archiving
+  the relation in the subject entity table, instead of creating a specific
+  table for the relation. This applies to relations where cardinality
+  of subject->relation->object is 0..1 (`?`) or 1..1 (`1`) for *all* its relation
+  definitions.
+
+* `symmetric`: boolean indicating that the relation is symmetrical, which
+  means that `X relation Y` implies `Y relation X`.
+
+.. _RelationDefinition:
+
+Relation definition
+~~~~~~~~~~~~~~~~~~~
+
+A relation definition is an instance of
+:class:`yams.schema.RelationDefinition`. It is a complete triplet
+"<subject entity type> <relation type> <object entity type>".
+
+When creating a new instance of that class, the corresponding
+:class:`RelationType` instance is created on the fly if necessary.
+
+Properties
+``````````
+
+The available properties for relation definitions are enumerated
+here. There are several kind of properties, as some relation
+definitions are actually attribute definitions, and other are not.
+
+Some properties may be completely optional, other may have a default
+value.
+
+Common properties for attributes and relations:
+
+* `description`: a unicode string describing an attribute or a
+  relation. By default this string will be used in the editing form of
+  the entity, which means that it is supposed to help the end-user and
+  should be flagged by the function `_` to be properly
+  internationalized.
+
+* `constraints`: a list of conditions/constraints that the relation has to
+  satisfy (c.f. `Constraints`_)
+
+* `cardinality`: a two character string specifying the cardinality of
+  the relation. The first character defines the cardinality of the
+  relation on the subject, and the second on the object. When a
+  relation can have multiple subjects or objects, the cardinality
+  applies to all, not on a one-to-one basis (so it must be
+  consistent...). Default value is '**'. The possible values are
+  inspired from regular expression syntax:
+
+    * `1`: 1..1
+    * `?`: 0..1
+    * `+`: 1..n
+    * `*`: 0..n
+
+Attributes properties:
+
+* `unique`: boolean indicating if the value of the attribute has to be
+  unique or not within all entities of the same type (false by
+  default)
+
+* `indexed`: boolean indicating if an index needs to be created for
+  this attribute in the database (false by default). This is useful
+  only if you know that you will have to run numerous searches on the
+  value of this attribute.
+
+* `default`: default value of the attribute. In case of date types, the values
+  which could be used correspond to the RQL keywords `TODAY` and `NOW`.
+
+* `metadata`: Is also accepted as an argument of the attribute contructor. It is
+  not really an attribute property. see `Metadata`_ for details.
+
+Properties for `String` attributes:
+
+* `fulltextindexed`: boolean indicating if the attribute is part of
+  the full text index (false by default) (*applicable on the type
+  `Byte` as well*)
+
+* `internationalizable`: boolean indicating if the value of the
+  attribute is internationalizable (false by default)
+
+Relation properties:
+
+* `composite`: string indicating that the subject (composite ==
+  'subject') is composed of the objects of the relations. For the
+  opposite case (when the object is composed of the subjects of the
+  relation), we just set 'object' as value. The composition implies
+  that when the relation is deleted (so when the composite is deleted,
+  at least), the composed are also deleted.
+
+* `fulltext_container`: string indicating if the value if the full
+  text indexation of the entity on one end of the relation should be
+  used to find the entity on the other end. The possible values are
+  'subject' or 'object'. For instance the use_email relation has that
+  property set to 'subject', since when performing a full text search
+  people want to find the entity using an email address, and not the
+  entity representing the email address.
+
+Constraints
+```````````
+
+By default, the available constraint types are:
+
+General Constraints
+......................
+
+* `SizeConstraint`: allows to specify a minimum and/or maximum size on
+  string (generic case of `maxsize`)
+
+* `BoundaryConstraint`: allows to specify a minimum and/or maximum value
+  on numeric types and date
+
+.. sourcecode:: python
+
+   from yams.constraints import BoundaryConstraint, TODAY, NOW, Attribute
+
+   class DatedEntity(EntityType):
+      start = Date(constraints=[BoundaryConstraint('>=', TODAY())])
+      end = Date(constraints=[BoundaryConstraint('>=', Attribute('start'))])
+
+   class Before(EntityType);
+      last_time = DateTime(constraints=[BoundaryConstraint('<=', NOW())])
+
+* `IntervalBoundConstraint`: allows to specify an interval with
+  included values
+
+.. sourcecode:: python
+
+     class Node(EntityType):
+         latitude = Float(constraints=[IntervalBoundConstraint(-90, +90)])
+
+* `UniqueConstraint`: identical to "unique=True"
+
+* `StaticVocabularyConstraint`: identical to "vocabulary=(...)"
+
+Constraints can be dependent on a fixed value (90, Date(2015,3,23)) or a variable.
+In this second case, yams can handle :
+
+* `Attribute`: compare to the value of another attribute.
+* `TODAY`: compare to the current Date.
+* `NOW`: compare to the current Datetime.
+
+RQL Based Constraints
+......................
+
+RQL based constraints may take three arguments. The first one is the ``WHERE``
+clause of a RQL query used by the constraint. The second argument ``mainvars``
+is the ``Any`` clause of the query. By default this include `S` reserved for the
+subject of the relation and `O` for the object. Additional variables could be
+specified using ``mainvars``. The argument expects a single string with all
+variable's name separated by spaces. The last one, ``msg``, is the error message
+displayed when the constraint fails. As RQLVocabularyConstraint never fails the
+third argument is not available.
+
+* `RQLConstraint`: allows to specify a RQL query that has to be satisfied
+  by the subject and/or the object of relation. In this query the variables
+  `S` and `O` are reserved for the relation subject and object entities.
+
+* `RQLVocabularyConstraint`: similar to the previous type of constraint except
+  that it does not express a "strong" constraint, which means it is only used to
+  restrict the values listed in the drop-down menu of editing form, but it does
+  not prevent another entity to be selected.
+
+* `RQLUniqueConstraint`: allows to the specify a RQL query that ensure that an
+  attribute is unique in a specific context. The Query must **never** return more
+  than a single result to be satisfied. In this query the variables `S` is
+  reserved for the relation subject entity. The other variables should be
+  specified with the second constructor argument (mainvars). This constraint type
+  should be used when __unique_together__ doesn't fit.
+
+.. XXX note about how to add new constraint
+
+.. _securitymodel:
+
+The security model
+~~~~~~~~~~~~~~~~~~
+
+The security model of `CubicWeb` is based on `Access Control List`.
+The main principles are:
+
+* users and groups of users
+* a user belongs to at least one group of user
+* permissions (`read`, `update`, `create`, `delete`)
+* permissions are assigned to groups (and not to users)
+
+For *CubicWeb* in particular:
+
+* we associate rights at the entities/relations schema level
+
+* the default groups are: `managers`, `users` and `guests`
+
+* users belong to the `users` group
+
+* there is a virtual group called `owners` to which we can associate only
+  `delete` and `update` permissions
+
+  * we can not add users to the `owners` group, they are implicitly added to it
+    according to the context of the objects they own
+
+  * the permissions of this group are only checked on `update`/`delete` actions
+    if all the other groups the user belongs to do not provide those permissions
+
+Setting permissions is done with the class attribute `__permissions__`
+of entity types and relation definitions. The value of this attribute
+is a dictionary where the keys are the access types (action), and the
+values are the authorized groups or rql expressions.
+
+For an entity type, the possible actions are `read`, `add`, `update` and
+`delete`.
+
+For a relation, the possible actions are `read`, `add`, and `delete`.
+
+For an attribute, the possible actions are `read`, `add` and `update`,
+and they are a refinement of an entity type permission.
+
+.. note::
+
+   By default, the permissions of an entity type attributes are
+   equivalent to the permissions of the entity type itself.
+
+   It is possible to provide custom attribute permissions which are
+   stronger than, or are more lenient than the entity type
+   permissions.
+
+   In a situation where all attributes were given custom permissions,
+   the entity type permissions would not be checked, except for the
+   `delete` action.
+
+For each access type, a tuple indicates the name of the authorized groups and/or
+one or multiple RQL expressions to satisfy to grant access. The access is
+provided if the user is in one of the listed groups or if one of the RQL condition
+is satisfied.
+
+Default permissions
+```````````````````
+
+The default permissions for ``EntityType`` are:
+
+.. sourcecode:: python
+
+   __permissions__ = {
+        'read': ('managers', 'users', 'guests',),
+        'update': ('managers', 'owners',),
+        'delete': ('managers', 'owners'),
+        'add': ('managers', 'users',)
+        }
+
+The default permissions for relations are:
+
+.. sourcecode:: python
+
+   __permissions__ = {'read': ('managers', 'users', 'guests',),
+                    'delete': ('managers', 'users'),
+                    'add': ('managers', 'users',)}
+
+The default permissions for attributes are:
+
+.. sourcecode:: python
+
+   __permissions__ = {'read': ('managers', 'users', 'guests',),
+                      'add': ('managers', ERQLExpression('U has_add_permission X'),
+                      'update': ('managers', ERQLExpression('U has_update_permission X')),}
+
+.. note::
+
+   The default permissions for attributes are not syntactically
+   equivalent to the default permissions of the entity types, but the
+   rql expressions work by delegating to the entity type permissions.
+
+
+The standard user groups
+````````````````````````
+
+* `guests`
+
+* `users`
+
+* `managers`
+
+* `owners`: virtual group corresponding to the entity's owner.
+  This can only be used for the actions `update` and `delete` of an entity
+  type.
+
+It is also possible to use specific groups if they are defined in the precreate
+script of the cube (``migration/precreate.py``). Defining groups in postcreate
+script or later makes them unavailable for security purposes (in this case, an
+`sync_schema_props_perms` command has to be issued in a CubicWeb shell).
+
+
+Use of RQL expression for write permissions
+```````````````````````````````````````````
+
+It is possible to define RQL expression to provide update permission (`add`,
+`delete` and `update`) on entity type / relation definitions. An rql expression
+is a piece of query (corresponds to the WHERE statement of an RQL query), and the
+expression will be considered as satisfied if it returns some results. They can
+not be used in `read` permission.
+
+To use RQL expression in entity type permission:
+
+* you have to use the class :class:`~cubicweb.schema.ERQLExpression`
+
+* in this expression, the variables `X` and `U` are pre-defined references
+  respectively on the current entity (on which the action is verified) and on the
+  user who send the request
+
+For RQL expressions on a relation type, the principles are the same except for
+the following:
+
+* you have to use the class :class:`~cubicweb.schema.RRQLExpression` instead of
+  :class:`~cubicweb.schema.ERQLExpression`
+
+* in the expression, the variables `S`, `O` and `U` are pre-defined references to
+  respectively the subject and the object of the current relation (on which the
+  action is being verified) and the user who executed the query
+
+To define security for attributes of an entity (non-final relation), you have to
+use the class :class:`~cubicweb.schema.ERQLExpression` in which `X` represents
+the entity the attribute belongs to.
+
+It is possible to use in those expression a special relation
+`has_<ACTION>_permission` where the subject is the user (eg 'U') and the object
+is any variable representing an entity (usually 'X' in
+:class:`~cubicweb.schema.ERQLExpression`, 'S' or 'O' in
+:class:`~cubicweb.schema.RRQLExpression`), meaning that the user needs to have
+permission to execute the action <ACTION> on the entities represented by this
+variable. It's recommanded to use this feature whenever possible since it
+simplify greatly complex security definition and upgrade.
+
+
+.. sourcecode:: python
+
+  class my_relation(RelationDefinition):
+    __permissions__ = {'read': ('managers', 'users'),
+                       'add': ('managers', RRQLExpression('U has_update_permission S')),
+                       'delete': ('managers', RRQLExpression('U has_update_permission S'))
+		       }
+
+In the above example, user will be allowed to add/delete `my_relation` if he has
+the `update` permission on the subject of the relation.
+
+.. note::
+
+  Potentially, the `use of an RQL expression to add an entity or a relation` can
+  cause problems for the user interface, because if the expression uses the
+  entity or the relation to create, we are not able to verify the permissions
+  before we actually added the entity (please note that this is not a problem for
+  the RQL server at all, because the permissions checks are done after the
+  creation). In such case, the permission check methods
+  (CubicWebEntitySchema.check_perm and has_perm) can indicate that the user is
+  not allowed to create this entity while it would obtain the permission.  To
+  compensate this problem, it is usually necessary in such case to use an action
+  that reflects the schema permissions but which check properly the permissions
+  so that it would show up only if possible.
+
+
+Use of RQL expression for reading rights
+````````````````````````````````````````
+
+The principles are the same but with the following restrictions:
+
+* you can not use rql expression for the `read` permission of relations and
+  attributes,
+
+* you can not use special `has_<ACTION>_permission` relation in the rql
+  expression.
+
+
+Important notes about write permissions checking
+````````````````````````````````````````````````
+
+Write permissions (e.g. 'add', 'update', 'delete') are checked in core hooks.
+
+When a permission is checked slightly vary according to if it's an entity or
+relation, and if the relation is an attribute relation or not). It's important to
+understand that since according to when a permission is checked, values returned
+by rql expressions may changes, hence the permission being granted or not.
+
+Here are the current rules:
+
+1. permission to add/update entity and its attributes are checked on
+   commit
+
+2. permission to delete an entity is checked in 'before_delete_entity' hook
+
+3. permission to add a relation is checked either:
+
+   - in 'before_add_relation' hook if the relation type is in the
+     `BEFORE_ADD_RELATIONS` set
+
+   - else at commit time if the relation type is in the `ON_COMMIT_ADD_RELATIONS`
+     set
+
+   - else in 'after_add_relation' hook (the default)
+
+4. permission to delete a relation is checked in 'before_delete_relation' hook
+
+Last but not least, remember queries issued from hooks and operation are by
+default 'unsafe', eg there are no read or write security checks.
+
+See :mod:`cubicweb.hooks.security` for more details.
+
+
+.. _yams_example:
+
+
+Derived attributes and relations
+--------------------------------
+
+.. note:: **TODO** Check organisation of the whole chapter of the documentation
+
+Cubicweb offers the possibility to *query* data using so called
+*computed* relations and attributes. Those are *seen* by RQL requests
+as normal attributes and relations but are actually derived from other
+attributes and relations. In a first section we'll informally review
+two typical use cases. Then we see how to use computed attributes and
+relations in your schema. Last we will consider various significant
+aspects of their implementation and the impact on their usage.
+
+Motivating use cases
+~~~~~~~~~~~~~~~~~~~~
+
+Computed (or reified) relations
+```````````````````````````````
+
+It often arises that one must represent a ternary relation, or a
+family of relations. For example, in the context of an exhibition
+catalog you might want to link all *contributors* to the *work* they
+contributed to, but this contribution can be as *illustrator*,
+*author*, *performer*, ...
+
+The classical way to describe this kind of information within an
+entity-relationship schema is to *reify* the relation, that is turn
+the relation into a entity. In our example the schema will have a
+*Contribution* entity type used to represent the family of the
+contribution relations.
+
+
+.. sourcecode:: python
+
+    class ArtWork(EntityType):
+        name = String()
+        ...
+
+    class Person(EntityType):
+        name = String()
+        ...
+
+    class Contribution(EntityType):
+        contributor = SubjectRelation('Person', cardinality='1*', inlined=True)
+        manifestation = SubjectRelation('ArtWork')
+        role = SubjectRelation('Role')
+
+    class Role(EntityType):
+        name = String()
+
+But then, in order to query the illustrator(s) ``I`` of a work ``W``,
+one has to write::
+
+    Any I, W WHERE C is Contribution, C contributor I, C manifestation W,
+                   C role R, R name 'illustrator'
+
+whereas we would like to be able to simply write::
+
+    Any I, W WHERE I illustrator_of W
+
+This is precisely what the computed relations allow.
+
+
+Computed (or synthesized) attribute
+```````````````````````````````````
+
+Assuming a trivial schema for describing employees in companies, one
+can be interested in the total of salaries payed by a company for
+all its employees. One has to write::
+
+    Any C, SUM(SA) GROUPBY S WHERE E works_for C, E salary SA
+
+whereas it would be most convenient to simply write::
+
+    Any C, TS WHERE C total_salary TS
+
+And this is again what computed attributes provide.
+
+
+Using computed attributes and relations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Computed (or reified) relations
+```````````````````````````````
+
+In the above case we would define the *computed relation*
+``illustrator_of`` in the schema by:
+
+.. sourcecode:: python
+
+    class illustrator_of(ComputedRelation):
+        rule  = ('C is Contribution, C contributor S, C manifestation O,'
+                 'C role R, R name "illustrator"')
+
+You will note that:
+
+* the ``S`` and ``O`` RQL variables implicitly identify the subject and
+  object of the defined computed relation, akin to what happens in
+  RRQLExpression
+* the possible subject and object entity types are inferred from the rule;
+* computed relation definitions always have empty *add* and *delete* permissions
+* *read* permissions can be defined, permissions from the relations used in the
+  rewrite rule **are not considered** ;
+* nothing else may be defined on the `ComputedRelation` subclass beside
+  description, permissions and rule (e.g. no cardinality, composite, etc.,).
+  `BadSchemaDefinition` is raised on attempt to specify other attributes;
+* computed relations can not be used in 'SET' and 'DELETE' rql queries
+  (`BadQuery` exception raised).
+
+
+NB: The fact that the *add* and *delete* permissions are *empty* even
+for managers is expected to make the automatic UI not attempt to edit
+them.
+
+Computed (or synthesized) attributes
+````````````````````````````````````
+
+In the above case we would define the *computed attribute*
+``total_salary`` on the ``Company`` entity type in the schema by:
+
+.. sourcecode:: python
+
+    class Company(EntityType):
+        name = String()
+        total_salary = Int(formula='Any SUM(SA) GROUPBY E WHERE P works_for X, E salary SA')
+
+* the ``X`` RQL variable implicitly identifies the entity holding the
+  computed attribute, akin to what happens in ERQLExpression;
+* the type inferred from the formula is checked against the declared type, and
+  `BadSchemaDefinition` is raised if they don't match;
+* the computed attributes always have empty *update* permissions
+* `BadSchemaDefinition` is raised on attempt to set 'update' permissions;
+* 'read' permissions can be defined, permissions regarding the formula
+  **are not considered**;
+* other attribute's property (inlined, ...) can be defined as for normal attributes;
+* Similarly to computed relation, computed attribute can't be used in 'SET' and
+  'DELETE' rql queries (`BadQuery` exception raised).
+
+
+API and implementation
+~~~~~~~~~~~~~~~~~~~~~~
+
+Representation in the data backend
+``````````````````````````````````
+
+Computed relations have no direct representation at the SQL table
+level.  Instead, each time a query is issued the query is rewritten to
+replace the computed relation by its equivalent definition and the
+resulting rewritten query is performed in the usual way.
+
+On the contrary, computed attributes are represented as a column in the
+table for their host entity type, just like normal attributes. Their
+value is kept up-to-date with respect to their defintion by a system
+of hooks (also called triggers in most RDBMS) which recomputes them
+when the relations and attributes they depend on are modified.
+
+Yams API
+````````
+
+When accessing the schema through the *yams API* (not when defining a
+schema in a ``schema.py`` file) the computed attributes and relations
+are represented as follows:
+
+relations
+    The ``yams.RelationSchema`` class has a new ``rule`` attribute
+    holding the rule as a string. If this attribute is set all others
+    must not be set.
+attributes
+    A new property ``formula`` is added on class
+    ``yams.RelationDefinitionSchema`` alomng with a new keyword
+    argument ``formula`` on the initializer.
+
+Migration
+`````````
+
+The migrations are to be handled as summarized in the array below.
+
++------------+---------------------------------------------------+---------------------------------------+
+|            | Computed rtype                                    | Computed attribute                    |
++============+===================================================+=======================================+
+| add        | * add_relation_type                               | * add_attribute                       |
+|            | * add_relation_definition should trigger an error | * add_relation_definition             |
++------------+---------------------------------------------------+---------------------------------------+
+| modify     | * sync_schema_prop_perms:                         | * sync_schema_prop_perms:             |
+|            |   checks the rule is                              |                                       |
+| (rule or   |   synchronized with the database                  |   - empty the cache,                  |
+| formula)   |                                                   |   - check formula,                    |
+|            |                                                   |   - make sure all the values get      |
+|            |                                                   |     updated                           |
++------------+---------------------------------------------------+---------------------------------------+
+| del        | * drop_relation_type                              | * drop_attribute                      |
+|            | * drop_relation_definition should trigger an error| * drop_relation_definition            |
++------------+---------------------------------------------------+---------------------------------------+
+
+
+Defining your schema using yams
+-------------------------------
+
+Entity type definition
+~~~~~~~~~~~~~~~~~~~~~~
+
+An entity type is defined by a Python class which inherits from
+:class:`yams.buildobjs.EntityType`.  The class definition contains the
+description of attributes and relations for the defined entity type.
+The class name corresponds to the entity type name. It is expected to
+be defined in the module ``mycube.schema``.
+
+:Note on schema definition:
+
+ The code in ``mycube.schema`` is not meant to be executed. The class
+ EntityType mentioned above is different from the EntitySchema class
+ described in the previous chapter. EntityType is a helper class to
+ make Entity definition easier. Yams will process EntityType classes
+ and create EntitySchema instances from these class definitions. Similar
+ manipulation happen for relations.
+
+When defining a schema using python files, you may use the following shortcuts:
+
+- `required`: boolean indicating if the attribute is required, ed subject cardinality is '1'
+
+- `vocabulary`: specify static possible values of an attribute
+
+- `maxsize`: integer providing the maximum size of a string (no limit by default)
+
+For example:
+
+.. sourcecode:: python
+
+  class Person(EntityType):
+    """A person with the properties and the relations necessary for my
+    application"""
+
+    last_name = String(required=True, fulltextindexed=True)
+    first_name = String(required=True, fulltextindexed=True)
+    title = String(vocabulary=('Mr', 'Mrs', 'Miss'))
+    date_of_birth = Date()
+    works_for = SubjectRelation('Company', cardinality='?*')
+
+
+The entity described above defines three attributes of type String,
+last_name, first_name and title, an attribute of type Date for the date of
+birth and a relation that connects a `Person` to another entity of type
+`Company` through the semantic `works_for`.
+
+
+
+:Naming convention:
+
+ Entity class names must start with an uppercase letter. The common
+ usage is to use ``CamelCase`` names.
+
+ Attribute and relation names must start with a lowercase letter. The
+ common usage is to use ``underscore_separated_words``. Attribute and
+ relation names starting with a single underscore are permitted, to
+ denote a somewhat "protected" or "private" attribute.
+
+ In any case, identifiers starting with "CW" or "cw" are reserved for
+ internal use by the framework.
+
+ .. _Metadata:
+
+ Some attribute using the name of another attribute as prefix are considered
+ metadata.  For example, if an EntityType have both a ``data`` and
+ ``data_format`` attribute, ``data_format`` is view as the ``format`` metadata
+ of ``data``. Later the :meth:`cw_attr_metadata` method will allow you to fetch
+ metadata related to an attribute. There are only three valid metadata names:
+ ``format``, ``encoding`` and ``name``.
+
+
+The name of the Python attribute corresponds to the name of the attribute
+or the relation in *CubicWeb* application.
+
+An attribute is defined in the schema as follows::
+
+    attr_name = AttrType(*properties, metadata={})
+
+where
+
+* `AttrType`: is one of the type listed in EntityType_,
+
+* `properties`: is a list of the attribute needs to satisfy (see `Properties`_
+  for more details),
+
+* `metadata`: is a dictionary of meta attributes related to ``attr_name``.
+  Dictionary keys are the name of the meta attribute. Dictionary values
+  attributes objects (like the content of ``AttrType``). For each entry of the
+  metadata dictionary a ``<attr_name>_<key> = <value>`` attribute is
+  automaticaly added to the EntityType.  see `Metadata`_ section for details
+  about valid key.
+
+
+ ---
+
+While building your schema
+
+* it is possible to use the attribute `meta` to flag an entity type as a `meta`
+  (e.g. used to describe/categorize other entities)
+
+.. XXX the paragraph below needs clarification and / or moving out in
+.. another place
+
+*Note*: if you end up with an `if` in the definition of your entity, this probably
+means that you need two separate entities that implement the `ITree` interface and
+get the result from `.children()` which ever entity is concerned.
+
+.. Inheritance
+.. ```````````
+.. XXX feed me
+
+
+Definition of relations
+~~~~~~~~~~~~~~~~~~~~~~~
+
+.. XXX add note about defining relation type / definition
+
+A relation is defined by a Python class heriting `RelationType`. The name
+of the class corresponds to the name of the type. The class then contains
+a description of the properties of this type of relation, and could as well
+contain a string for the subject and a string for the object. This allows to create
+new definition of associated relations, (so that the class can have the
+definition properties from the relation) for example ::
+
+  class locked_by(RelationType):
+    """relation on all entities indicating that they are locked"""
+    inlined = True
+    cardinality = '?*'
+    subject = '*'
+    object = 'CWUser'
+
+If provided, the `subject` and `object` attributes denote the subject
+and object of the various relation definitions related to the relation
+type. Allowed values for these attributes are:
+
+* a string corresponding to an entity type
+* a tuple of string corresponding to multiple entity types
+* the '*' special string, meaning all types of entities
+
+When a relation is not inlined and not symmetrical, and it does not require
+specific permissions, it can be defined using a `SubjectRelation`
+attribute in the EntityType class. The first argument of `SubjectRelation` gives
+the entity type for the object of the relation.
+
+:Naming convention:
+
+ Although this way of defining relations uses a Python class, the
+ naming convention defined earlier prevails over the PEP8 conventions
+ used in the framework: relation type class names use
+ ``underscore_separated_words``.
+
+:Historical note:
+
+   It has been historically possible to use `ObjectRelation` which
+   defines a relation in the opposite direction. This feature is
+   deprecated and therefore should not be used in newly written code.
+
+:Future deprecation note:
+
+  In an even more remote future, it is quite possible that the
+  SubjectRelation shortcut will become deprecated, in favor of the
+  RelationType declaration which offers some advantages in the context
+  of reusable cubes.
+
+
+
+
+Handling schema changes
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Also, it should be clear that to properly handle data migration, an
+instance's schema is stored in the database, so the python schema file
+used to defined it is only read when the instance is created or
+upgraded.
+
+.. XXX complete me
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/datamodel/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,12 @@
+Data model
+==========
+
+This chapter describes how you define a schema and how to make it evolves as the time goes.
+
+.. toctree::
+   :maxdepth: 1
+
+   definition
+   metadata
+   baseschema
+   define-workflows
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/datamodel/metadata.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,37 @@
+
+Metadata
+--------
+
+.. index::
+   schema: meta-data;
+   schema: eid; creation_date; modification_data; cwuri
+   schema: created_by; owned_by; is; is_instance;
+
+Each entity type in |cubicweb| has at least the following meta-data attributes and relations:
+
+`eid`
+  entity's identifier which is unique in an instance. We usually call this identifier `eid` for historical reason.
+
+`creation_date`
+  Date and time of the creation of the entity.
+
+`modification_date`
+  Date and time of the latest modification of an entity.
+
+`cwuri`
+  Reference URL of the entity, which is not expected to change.
+
+`created_by`
+  Relation to the :ref:`users <CWUser>` who has created the entity
+
+`owned_by`
+  Relation to :ref:`users <CWUser>` whom the entity belongs; usually the creator but not
+  necessary, and it could have multiple owners notably for permission control
+
+`is`
+  Relation to the :ref:`entity type <CWEType>` of which type the entity is.
+
+`is_instance`
+  Relation to the :ref:`entity types <CWEType>` of which type the
+  entity is an instance of.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/devcore/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,8 @@
+Core APIs
+=========
+
+.. toctree::
+   :maxdepth: 1
+
+   reqbase.rst
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/devcore/reqbase.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,40 @@
+Request and ResultSet methods
+-----------------------------
+
+Those are methods you'll find on both request objects and on
+repository session.
+
+Request methods
+~~~~~~~~~~~~~~~
+
+`URL handling`:
+
+* `build_url(*args, **kwargs)`, returns an absolute URL based on the
+  given arguments. The *controller* supposed to handle the response,
+  can be specified through the first positional parameter (the
+  connection is theoretically done automatically :).
+
+`Data formatting`:
+
+* `format_date(date, date_format=None, time=False)` returns a string for a
+  date time according to instance's configuration
+
+* `format_time(time)` returns a string for a date time according to
+  instance's configuration
+
+`And more...`:
+
+* `tal_render(template, variables)`, renders a precompiled page template with
+  variables in the given dictionary as context
+
+
+Result set methods
+~~~~~~~~~~~~~~~~~~
+
+* `get_entity(row, col)`, returns the entity corresponding to the data position
+  in the *result set*
+
+* `complete_entity(row, col, skip_bytes=True)`, is equivalent to `get_entity` but
+  also call the method `complete()` on the entity before returning it
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/entityclasses/adapters.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,167 @@
+.. _adapters:
+
+Interfaces and Adapters
+-----------------------
+
+Interfaces are the same thing as object-oriented programming `interfaces`_.
+Adapter refers to a well-known `adapter`_ design pattern that helps separating
+concerns in object oriented applications.
+
+.. _`interfaces`: http://java.sun.com/docs/books/tutorial/java/concepts/interface.html
+.. _`adapter`: http://en.wikipedia.org/wiki/Adapter_pattern
+
+In |cubicweb| adapters provide logical functionalities to entity types.
+
+Definition of an adapter is quite trivial. An excerpt from cubicweb
+itself (found in :mod:`cubicweb.entities.adapters`):
+
+.. sourcecode:: python
+
+
+    class ITreeAdapter(EntityAdapter):
+        """This adapter has to be overriden to be configured using the
+        tree_relation, child_role and parent_role class attributes to
+        benefit from this default implementation
+        """
+        __regid__ = 'ITree'
+
+        child_role = 'subject'
+        parent_role = 'object'
+
+        def children_rql(self):
+            """returns RQL to get children """
+            return self.entity.cw_related_rql(self.tree_relation, self.parent_role)
+
+The adapter object has ``self.entity`` attribute which represents the
+entity being adapted.
+
+.. Note::
+
+   Adapters came with the notion of service identified by the registry identifier
+   of an adapters, hence dropping the need for explicit interface and the
+   :class:`cubicweb.predicates.implements` selector. You should instead use
+   :class:`cubicweb.predicates.is_instance` when you want to select on an entity
+   type, or :class:`cubicweb.predicates.adaptable` when you want to select on a
+   service.
+
+
+Specializing and binding an adapter
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. sourcecode:: python
+
+  from cubicweb.entities.adapters import ITreeAdapter
+
+  class MyEntityITreeAdapter(ITreeAdapter):
+      __select__ = is_instance('MyEntity')
+      tree_relation = 'filed_under'
+
+The ITreeAdapter here provides a default implementation. The
+tree_relation class attribute is actually used by this implementation
+to help implement correct behaviour.
+
+Here we provide a specific implementation which will be bound for
+``MyEntity`` entity type (the `adaptee`).
+
+
+.. _interfaces_to_adapters:
+
+Converting code from Interfaces/Mixins to Adapters
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Here we go with a small example. Before:
+
+.. sourcecode:: python
+
+    from cubicweb.predicates import implements
+    from cubicweb.interfaces import ITree
+    from cubicweb.mixins import ITreeMixIn
+
+    class MyEntity(ITreeMixIn, AnyEntity):
+        __implements__ = AnyEntity.__implements__ + (ITree,)
+
+
+    class ITreeView(EntityView):
+        __select__ = implements('ITree')
+        def cell_call(self, row, col):
+            entity = self.cw_rset.get_entity(row, col)
+            children = entity.children()
+
+After:
+
+.. sourcecode:: python
+
+    from cubicweb.predicates import adaptable, is_instance
+    from cubicweb.entities.adapters import ITreeAdapter
+
+    class MyEntityITreeAdapter(ITreeAdapter):
+        __select__ = is_instance('MyEntity')
+
+    class ITreeView(EntityView):
+        __select__ = adaptable('ITree')
+        def cell_call(self, row, col):
+            entity = self.cw_rset.get_entity(row, col)
+            itree = entity.cw_adapt_to('ITree')
+            children = itree.children()
+
+As we can see, the interface/mixin duality disappears and the entity
+class itself is completely freed from these concerns. When you want
+to use the ITree interface of an entity, call its `cw_adapt_to` method
+to get an adapter for this interface, then access to members of the
+interface on the adapter
+
+Let's look at an example where we defined everything ourselves. We
+start from:
+
+.. sourcecode:: python
+
+    class IFoo(Interface):
+        def bar(self, *args):
+            raise NotImplementedError
+
+    class MyEntity(AnyEntity):
+        __regid__ = 'MyEntity'
+        __implements__ = AnyEntity.__implements__ + (IFoo,)
+
+        def bar(self, *args):
+            return sum(captain.age for captain in self.captains)
+
+    class FooView(EntityView):
+        __regid__ = 'mycube.fooview'
+        __select__ = implements('IFoo')
+
+        def cell_call(self, row, col):
+            entity = self.cw_rset.get_entity(row, col)
+            self.w('bar: %s' % entity.bar())
+
+Converting to:
+
+.. sourcecode:: python
+
+   class IFooAdapter(EntityAdapter):
+       __regid__ = 'IFoo'
+       __select__ = is_instance('MyEntity')
+
+       def bar(self, *args):
+           return sum(captain.age for captain in self.entity.captains)
+
+   class FooView(EntityView):
+        __regid__ = 'mycube.fooview'
+        __select__ = adaptable('IFoo')
+
+        def cell_call(self, row, col):
+            entity = self.cw_rset.get_entity(row, col)
+            self.w('bar: %s' % entity.cw_adapt_to('IFoo').bar())
+
+.. note::
+
+   When migrating an entity method to an adapter, the code can be moved as is
+   except for the `self` of the entity class, which in the adapter must become `self.entity`.
+
+Adapters defined in the library
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. automodule:: cubicweb.entities.adapters
+   :members:
+
+More are defined in web/views.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/entityclasses/application-logic.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,177 @@
+How to use entities objects and adapters
+----------------------------------------
+
+The previous chapters detailed the classes and methods available to
+the developer at the so-called `ORM`_ level. However they say little
+about the common patterns of usage of these objects.
+
+.. _`ORM`: http://en.wikipedia.org/wiki/Object-relational_mapping
+
+Entities objects (and their adapters) are used in the repository and
+web sides of CubicWeb. On the repository side of things, one should
+manipulate them in Hooks and Operations.
+
+Hooks and Operations provide support for the implementation of rules
+such as computed attributes, coherency invariants, etc (they play the
+same role as database triggers, but in a way that is independent of
+the actual data sources).
+
+So a lot of an application's business rules will be written in Hooks
+(or Operations).
+
+On the web side, views also typically operate using entity
+objects. Obvious entity methods for use in views are the Dublin Core
+methods like ``dc_title``. For separation of concerns reasons, one
+should ensure no ui logic pervades the entities level, and also no
+business logic should creep into the views.
+
+In the duration of a transaction, entities objects can be instantiated
+many times, in views and hooks, even for the same database entity. For
+instance, in a classic CubicWeb deployment setup, the repository and
+the web front-end are separated process communicating over the
+wire. There is no way state can be shared between these processes
+(there is a specific API for that). Hence, it is not possible to use
+entity objects as messengers between these components of an
+application. It means that an attribute set as in ``obj.x = 42``,
+whether or not x is actually an entity schema attribute, has a short
+life span, limited to the hook, operation or view within which the
+object was built.
+
+Setting an attribute or relation value can be done in the context of a
+Hook/Operation, using the ``obj.cw_set(x=42)`` notation or a plain
+RQL ``SET`` expression.
+
+In views, it would be preferable to encapsulate the necessary logic in
+a method of an adapter for the concerned entity class(es). But of
+course, this advice is also reasonable for Hooks/Operations, though
+the separation of concerns here is less stringent than in the case of
+views.
+
+This leads to the practical role of objects adapters: it's where an
+important part of the application logic lies (the other part being
+located in the Hook/Operations).
+
+Anatomy of an entity class
+--------------------------
+
+We can look now at a real life example coming from the `tracker`_
+cube. Let us begin to study the ``entities/project.py`` content.
+
+.. sourcecode:: python
+
+    from cubicweb.entities.adapters import ITreeAdapter
+
+    class ProjectAdapter(ITreeAdapter):
+        __select__ = is_instance('Project')
+        tree_relation = 'subproject_of'
+
+    class Project(AnyEntity):
+        __regid__ = 'Project'
+        fetch_attrs, cw_fetch_order = fetch_config(('name', 'description',
+                                                    'description_format', 'summary'))
+
+        TICKET_DEFAULT_STATE_RESTR = 'S name IN ("created","identified","released","scheduled")'
+
+        def dc_title(self):
+            return self.name
+
+The fact that the `Project` entity type implements an ``ITree``
+interface is materialized by the ``ProjectAdapter`` class (inheriting
+the pre-defined ``ITreeAdapter`` whose ``__regid__`` is of course
+``ITree``), which will be selected on `Project` entity types because
+of its selector. On this adapter, we redefine the ``tree_relation``
+attribute of the ``ITreeAdapter`` class.
+
+This is typically used in views concerned with the representation of
+tree-like structures (CubicWeb provides several such views).
+
+It is important that the views themselves try not to implement this
+logic, not only because such views would be hardly applyable to other
+tree-like relations, but also because it is perfectly fine and useful
+to use such an interface in Hooks.
+
+In fact, Tree nature is a property of the data model that cannot be
+fully and portably expressed at the level of database entities (think
+about the transitive closure of the child relation). This is a further
+argument to implement it at entity class level.
+
+``fetch_attrs`` configures which attributes should be pre-fetched when using ORM
+methods retrieving entity of this type. In a same manner, the ``cw_fetch_order`` is
+a class method allowing to control sort order. More on this in :ref:`FetchAttrs`.
+
+We can observe the big ``TICKET_DEFAULT_STATE_RESTR`` is a pure
+application domain piece of data. There is, of course, no limitation
+to the amount of class attributes of this kind.
+
+The ``dc_title`` method provides a (unicode string) value likely to be
+consumed by views, but note that here we do not care about output
+encodings. We care about providing data in the most universal format
+possible, because the data could be used by a web view (which would be
+responsible of ensuring XHTML compliance), or a console or file
+oriented output (which would have the necessary context about the
+needed byte stream encoding).
+
+.. note::
+
+  The Dublin Core `dc_xxx` methods are not moved to an adapter as they
+  are extremely prevalent in CubicWeb and assorted cubes and should be
+  available for all entity types.
+
+Let us now dig into more substantial pieces of code, continuing the
+Project class.
+
+.. sourcecode:: python
+
+    def latest_version(self, states=('published',), reverse=None):
+        """returns the latest version(s) for the project in one of the given
+        states.
+
+        when no states specified, returns the latest published version.
+        """
+        order = 'DESC'
+        if reverse is not None:
+            warn('reverse argument is deprecated',
+                 DeprecationWarning, stacklevel=1)
+            if reverse:
+                order = 'ASC'
+        rset = self.versions_in_state(states, order, True)
+        if rset:
+            return rset.get_entity(0, 0)
+        return None
+
+    def versions_in_state(self, states, order='ASC', limit=False):
+        """returns version(s) for the project in one of the given states, sorted
+        by version number.
+
+        If limit is true, limit result to one version.
+        If reverse, versions are returned from the smallest to the greatest.
+        """
+        if limit:
+            order += ' LIMIT 1'
+        rql = 'Any V,N ORDERBY version_sort_value(N) %s ' \
+              'WHERE V num N, V in_state S, S name IN (%s), ' \
+              'V version_of P, P eid %%(p)s' % (order, ','.join(repr(s) for s in states))
+        return self._cw.execute(rql, {'p': self.eid})
+
+.. _`tracker`: http://www.cubicweb.org/project/cubicweb-tracker/
+
+These few lines exhibit the important properties we want to outline:
+
+* entity code is concerned with the application domain
+
+* it is NOT concerned with database consistency (this is the realm of
+  Hooks/Operations); in other words, it assumes a consistent world
+
+* it is NOT (directly) concerned with end-user interfaces
+
+* however it can be used in both contexts
+
+* it does not create or manipulate the internal object's state
+
+* it plays freely with RQL expression as needed
+
+* it is not concerned with internationalization
+
+* it does not raise exceptions
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/entityclasses/data-as-objects.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,142 @@
+Access to persistent data
+--------------------------
+
+Python-level access to persistent data is provided by the
+:class:`Entity <cubicweb.entity>` class.
+
+.. XXX this part is not clear. refactor it.
+
+An entity class is bound to a schema entity type. Descriptors are added when
+classes are registered in order to initialize the class according to its schema:
+
+* the attributes defined in the schema appear as attributes of these classes
+
+* the relations defined in the schema appear as attributes of these classes,
+  but are lists of instances
+
+`Formatting and output generation`:
+
+* :meth:`view(__vid, __registry='views', **kwargs)`, applies the given view to the entity
+  (and returns a unicode string)
+
+* :meth:`absolute_url(*args, **kwargs)`, returns an absolute URL including the base-url
+
+* :meth:`rest_path()`, returns a relative REST URL to get the entity
+
+* :meth:`printable_value(attr, value=_marker, attrtype=None, format='text/html', displaytime=True)`,
+  returns a string enabling the display of an attribute value in a given format
+  (the value is automatically recovered if necessary)
+
+`Data handling`:
+
+* :meth:`as_rset()`, converts the entity into an equivalent result set simulating the
+  request `Any X WHERE X eid _eid_`
+
+* :meth:`complete(skip_bytes=True)`, executes a request that recovers at
+  once all the missing attributes of an entity
+
+* :meth:`get_value(name)`, returns the value associated to the attribute name given
+  in parameter
+
+* :meth:`related(rtype, role='subject', limit=None, entities=False)`,
+  returns a list of entities related to the current entity by the
+  relation given in parameter
+
+* :meth:`unrelated(rtype, targettype, role='subject', limit=None)`,
+  returns a result set corresponding to the entities not (yet)
+  related to the current entity by the relation given in parameter
+  and satisfying its constraints
+
+* :meth:`cw_set(**kwargs)`, updates entity's attributes and/or relation with the
+  corresponding values given named parameters. To set a relation where this
+  entity is the object of the relation, use `reverse_<relation>` as argument
+  name.  Values may be an entity, a list of entities, or None (meaning that all
+  relations of the given type from or to this object should be deleted).
+
+* :meth:`copy_relations(ceid)`, copies the relations of the entities having the eid
+  given in the parameters on the current entity
+
+* :meth:`cw_delete()` allows to delete the entity
+
+
+The :class:`AnyEntity` class
+----------------------------
+
+To provide a specific behavior for each entity, we can define a class
+inheriting from `cubicweb.entities.AnyEntity`. In general, we define this class
+in `mycube.entities` module (or in a submodule if we want to split code among
+multiple files) so that it will be available on both server and client side.
+
+The class `AnyEntity` is a sub-class of Entity that add methods to it,
+and helps specializing (by further subclassing) the handling of a
+given entity type.
+
+Most methods defined for `AnyEntity`, in addition to `Entity`, add
+support for the `Dublin Core`_ metadata.
+
+.. _`Dublin Core`: http://dublincore.org/
+
+`Standard meta-data (Dublin Core)`:
+
+* :meth:`dc_title()`, returns a unicode string corresponding to the
+  meta-data `Title` (used by default is the first non-meta attribute
+  of the entity schema)
+
+* :meth:`dc_long_title()`, same as dc_title but can return a more
+  detailed title
+
+* :meth:`dc_description(format='text/plain')`, returns a unicode string
+  corresponding to the meta-data `Description` (looks for a
+  description attribute by default)
+
+* :meth:`dc_authors()`, returns a unicode string corresponding to the meta-data
+  `Authors` (owners by default)
+
+* :meth:`dc_creator()`, returns a unicode string corresponding to the
+  creator of the entity
+
+* :meth:`dc_date(date_format=None)`, returns a unicode string corresponding to
+  the meta-data `Date` (update date by default)
+
+* :meth:`dc_type(form='')`, returns a string to display the entity type by
+  specifying the preferred form (`plural` for a plural form)
+
+* :meth:`dc_language()`, returns the language used by the entity
+
+Inheritance
+-----------
+
+When describing a data model, entities can inherit from other entities as is
+common in object-oriented programming.
+
+You have the possibility to redefine whatever pleases you, as follow:
+
+.. sourcecode:: python
+
+    from cubes.OTHER_CUBE import entities
+
+    class EntityExample(entities.EntityExample):
+
+        def dc_long_title(self):
+            return '%s (%s)' % (self.name, self.description)
+
+The most specific entity definition will always the one used by the
+ORM. For instance, the new EntityExample above in mycube replaces the
+one in OTHER_CUBE. These types are stored in the `etype` section of
+the `vregistry`.
+
+Notice this is different than yams schema inheritance, which is an
+experimental undocumented feature.
+
+
+Application logic
+-----------------
+
+While a lot of custom behaviour and application logic can be
+implemented using entity classes, the programmer must be aware that
+adding new attributes and method on an entity class adds may shadow
+schema-level attribute or relation definitions.
+
+To keep entities clean (mostly data structures plus a few universal
+methods such as listed above), one should use `adapters` (see
+:ref:`adapters`).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/entityclasses/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,13 @@
+Data as objects
+===============
+
+In this chapter, we will introduce the objects that are used to handle
+the logic associated to the data stored in the database.
+
+.. toctree::
+   :maxdepth: 1
+
+   data-as-objects
+   load-sort
+   adapters
+   application-logic
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/entityclasses/load-sort.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,40 @@
+
+.. _FetchAttrs:
+
+Loaded attributes and default sorting management
+````````````````````````````````````````````````
+
+* The class attribute `fetch_attrs` allows to define in an entity class a list of
+  names of attributes that should be automatically loaded when entities of this
+  type are fetched from the database using ORM methods retrieving entity of this
+  type (such as :meth:`related` and :meth:`unrelated`). You can also put relation
+  names in there, but we are limited to *subject relations of cardinality `?` or
+  `1`*.
+
+* The :meth:`cw_fetch_order` and :meth:`cw_fetch_unrelated_order` class methods
+  are respectively responsible to control how entities will be sorted when:
+
+  - retrieving all entities of a given type, or entities related to another
+
+  - retrieving a list of entities for use in drop-down lists enabling relations
+    creation in the editing view of an entity
+
+By default entities will be listed on their modification date descending,
+i.e. you'll get entities recently modified first. While this is usually a good
+default in drop-down list, you'll probably want to change `cw_fetch_order`.
+
+This may easily be done using the :func:`~cubicweb.entities.fetch_config`
+function, which simplifies the definition of attributes to load and sorting by
+returning a list of attributes to pre-load (considering automatically the
+attributes of `AnyEntity`) and a sorting function as described below:
+
+.. autofunction:: cubicweb.entities.fetch_config
+
+In you want something else (such as sorting on the result of a registered
+procedure), here is the prototype of those methods:
+
+
+.. automethod:: cubicweb.entity.Entity.cw_fetch_order
+
+.. automethod:: cubicweb.entity.Entity.cw_fetch_unrelated_order
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/fti.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,159 @@
+.. _fti:
+
+Full Text Indexing in CubicWeb
+------------------------------
+
+When an attribute is tagged as *fulltext-indexable* in the datamodel,
+CubicWeb will automatically trigger hooks to update the internal
+fulltext index (i.e the ``appears`` SQL table) each time this attribute
+is modified.
+
+CubicWeb also provides a ``db-rebuild-fti`` command to rebuild the whole
+fulltext on demand:
+
+.. sourcecode:: bash
+
+   cubicweb@esope~$ cubicweb db-rebuild-fti my_tracker_instance
+
+You can also rebuild the fulltext index for a given set of entity types:
+
+.. sourcecode:: bash
+
+   cubicweb@esope~$ cubicweb db-rebuild-fti my_tracker_instance Ticket Version
+
+In the above example, only fulltext index of entity types ``Ticket`` and ``Version``
+will be rebuilt.
+
+
+Standard FTI process
+~~~~~~~~~~~~~~~~~~~~
+
+Considering an entity type ``ET``, the default *fti* process is to :
+
+1. fetch all entities of type ``ET``
+
+2. for each entity, adapt it to ``IFTIndexable`` (see
+   :class:`~cubicweb.entities.adapters.IFTIndexableAdapter`)
+
+3. call
+   :meth:`~cubicweb.entities.adapters.IFTIndexableAdapter.get_words` on
+   the adapter which is supposed to return a dictionary *weight* ->
+   *list of words* as expected by
+   :meth:`~logilab.database.fti.FTIndexerMixIn.index_object`. The
+   tokenization of each attribute value is done by
+   :meth:`~logilab.database.fti.tokenize`.
+
+
+See :class:`~cubicweb.entities.adapters.IFTIndexableAdapter` for more documentation.
+
+
+Yams and ``fulltext_container``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It is possible in the datamodel to indicate that fulltext-indexed
+attributes defined for an entity type will be used to index not the
+entity itself but a related entity. This is especially useful for
+composite entities. Let's take a look at (a simplified version of)
+the base schema defined in CubicWeb (see :mod:`cubicweb.schemas.base`):
+
+.. sourcecode:: python
+
+  class CWUser(WorkflowableEntityType):
+      login     = String(required=True, unique=True, maxsize=64)
+      upassword = Password(required=True)
+
+  class EmailAddress(EntityType):
+      address = String(required=True,  fulltextindexed=True,
+                       indexed=True, unique=True, maxsize=128)
+
+
+  class use_email_relation(RelationDefinition):
+      name = 'use_email'
+      subject = 'CWUser'
+      object = 'EmailAddress'
+      cardinality = '*?'
+      composite = 'subject'
+
+
+The schema above states that there is a relation between ``CWUser`` and ``EmailAddress``
+and that the ``address`` field of ``EmailAddress`` is fulltext indexed. Therefore,
+in your application, if you use fulltext search to look for an email address, CubicWeb
+will return the ``EmailAddress`` itself. But the objects we'd like to index
+are more likely to be the associated ``CWUser`` than the ``EmailAddress`` itself.
+
+The simplest way to achieve that is to tag the ``use_email`` relation in
+the datamodel:
+
+.. sourcecode:: python
+
+  class use_email(RelationType):
+      fulltext_container = 'subject'
+
+
+Customizing how entities are fetched during ``db-rebuild-fti``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``db-rebuild-fti`` will call the
+:meth:`~cubicweb.entities.AnyEntity.cw_fti_index_rql_queries` class
+method on your entity type.
+
+.. automethod:: cubicweb.entities.AnyEntity.cw_fti_index_rql_queries
+
+Now, suppose you've got a _huge_ table to index, you probably don't want to
+get all entities at once. So here's a simple customized example that will
+process block of 10000 entities:
+
+.. sourcecode:: python
+
+
+    class MyEntityClass(AnyEntity):
+        __regid__ = 'MyEntityClass'
+
+    @classmethod
+    def cw_fti_index_rql_queries(cls, req):
+        # get the default RQL method and insert LIMIT / OFFSET instructions
+        base_rql = super(SearchIndex, cls).cw_fti_index_rql_queries(req)[0]
+        selected, restrictions = base_rql.split(' WHERE ')
+        rql_template = '%s ORDERBY X LIMIT %%(limit)s OFFSET %%(offset)s WHERE %s' % (
+            selected, restrictions)
+        # count how many entities you'll have to index
+        count = req.execute('Any COUNT(X) WHERE X is MyEntityClass')[0][0]
+        # iterate by blocks of 10000 entities
+        chunksize = 10000
+        for offset in xrange(0, count, chunksize):
+            print 'SENDING', rql_template % {'limit': chunksize, 'offset': offset}
+            yield rql_template % {'limit': chunksize, 'offset': offset}
+
+Since you have access to ``req``, you can more or less fetch whatever you want.
+
+
+Customizing :meth:`~cubicweb.entities.adapters.IFTIndexableAdapter.get_words`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can also customize the FTI process by providing your own ``get_words()``
+implementation:
+
+.. sourcecode:: python
+
+    from cubicweb.entities.adapters import IFTIndexableAdapter
+
+    class SearchIndexAdapter(IFTIndexableAdapter):
+        __regid__ = 'IFTIndexable'
+        __select__ = is_instance('MyEntityClass')
+
+        def fti_containers(self, _done=None):
+            """this should yield any entity that must be considered to
+            fulltext-index self.entity
+
+            CubicWeb's default implementation will look for yams'
+            ``fulltex_container`` property.
+            """
+            yield self.entity
+            yield self.entity.some_related_entity
+
+
+        def get_words(self):
+            # implement any logic here
+            # see http://www.postgresql.org/docs/9.1/static/textsearch-controls.html
+            # for the actual signification of 'C'
+            return {'C': ['any', 'word', 'I', 'want']}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,25 @@
+.. _Part2:
+
+----------------------
+Repository development
+----------------------
+
+This part is about developing applications with the *CubicWeb*
+framework. It is not concerned with the web system, which is a
+separate layer and has its own whole chapter.
+
+.. toctree::
+   :maxdepth: 2
+   :numbered:
+
+   cubes/index
+   vreg.rst
+   datamodel/index
+   entityclasses/index
+   devcore/index
+   repo/index
+   testing.rst
+   migration.rst
+   profiling.rst
+   fti.rst
+   dataimport
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/migration.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,251 @@
+.. -*- coding: utf-8 -*-
+
+.. _migration:
+
+Migration
+=========
+
+One of the main design goals of *CubicWeb* was to support iterative and agile
+development. For this purpose, multiple actions are provided to facilitate the
+improvement of an instance, and in particular to handle the changes to be
+applied to the data model, without loosing existing data.
+
+The current version of a cube (and of cubicweb itself) is provided in the file
+`__pkginfo__.py` as a tuple of 3 integers.
+
+Migration scripts management
+----------------------------
+
+Migration scripts has to be located in the directory `migration` of your
+cube and named accordingly:
+
+::
+
+  <version n° X.Y.Z>[_<description>]_<mode>.py
+
+in which :
+
+* X.Y.Z is the model version number to which the script enables to migrate.
+
+* *mode* (between the last "_" and the extension ".py") is used for
+  distributed installation. It indicates to which part
+  of the application (RQL server, web server) the script applies.
+  Its value could be :
+
+  * `common`, applies to the RQL server as well as the web server and updates
+    files on the hard drive (configuration files migration for example).
+
+  * `web`, applies only to the web server and updates files on the hard drive.
+
+  * `repository`, applies only to the RQL server and updates files on the
+    hard drive.
+
+  * `Any`, applies only to the RQL server and updates data in the database
+    (schema and data migration for example).
+
+Again in the directory `migration`, the file `depends.map` allows to indicate
+that for the migration to a particular model version, you always have to first
+migrate to a particular *CubicWeb* version. This file can contain comments (lines
+starting with `#`) and a dependency is listed as follows: ::
+
+  <model version n° X.Y.Z> : <cubicweb version n° X.Y.Z>
+
+For example: ::
+
+  0.12.0: 2.26.0
+  0.13.0: 2.27.0
+  # 0.14 works with 2.27 <= cubicweb <= 2.28 at least
+  0.15.0: 2.28.0
+
+Base context
+------------
+
+The following identifiers are pre-defined in migration scripts:
+
+* `config`, instance configuration
+
+* `interactive_mode`, boolean indicating that the script is executed in
+  an interactive mode or not
+
+* `versions_map`, dictionary of migrated versions  (key are cubes
+  names, including 'cubicweb', values are (from version, to version)
+
+* `confirm(question)`, function asking the user and returning true
+  if the user answers yes, false otherwise (always returns true in
+  non-interactive mode)
+
+* `_()` is equivalent to `unicode` allowing to flag the strings to
+  internationalize in the migration scripts.
+
+In the `repository` scripts, the following identifiers are also defined:
+
+* `commit(ask_confirm=True)`, request confirming and executing a "commit"
+
+* `schema`, instance schema (readen from the database)
+
+* `fsschema`, installed schema on the file system (e.g. schema of
+  the updated model and cubicweb)
+
+* `repo`, repository object
+
+* `session`, repository session object
+
+
+New cube dependencies
+---------------------
+
+If your code depends on some new cubes, you have to add them in a migration
+script by using:
+
+* `add_cube(cube, update_database=True)`, add a cube.
+* `add_cubes(cubes, update_database=True)`, add a list of cubes.
+
+The `update_database` parameter is telling if the database schema
+should be updated or if only the relevant persistent property should be
+inserted (for the case where a new cube has been extracted from an
+existing one, so the new cube schema is actually already in there).
+
+If some of the added cubes are already used by an instance, they'll simply be
+silently skipped.
+
+To remove a cube use `drop_cube(cube, removedeps=False)`. 
+
+Schema migration
+----------------
+The following functions for schema migration are available in `repository`
+scripts:
+
+* `add_attribute(etype, attrname, attrtype=None, commit=True)`, adds a new
+  attribute to an existing entity type. If the attribute type is not specified,
+  then it is extracted from the updated schema.
+
+* `drop_attribute(etype, attrname, commit=True)`, removes an attribute from an
+  existing entity type.
+
+* `rename_attribute(etype, oldname, newname, commit=True)`, renames an attribute
+
+* `add_entity_type(etype, auto=True, commit=True)`, adds a new entity type.
+  If `auto` is True, all the relations using this entity type and having a known
+  entity type on the other hand will automatically be added.
+
+* `drop_entity_type(etype, commit=True)`, removes an entity type and all the
+  relations using it.
+
+* `rename_entity_type(oldname, newname, commit=True)`, renames an entity type
+
+* `add_relation_type(rtype, addrdef=True, commit=True)`, adds a new relation
+  type. If `addrdef` is True, all the relations definitions of this type will
+  be added.
+
+* `drop_relation_type(rtype, commit=True)`, removes a relation type and all the
+  definitions of this type.
+
+* `rename_relation_type(oldname, newname, commit=True)`, renames a relation type.
+
+* `add_relation_definition(subjtype, rtype, objtype, commit=True)`, adds a new
+  relation definition.
+
+* `drop_relation_definition(subjtype, rtype, objtype, commit=True)`, removes
+  a relation definition.
+
+* `sync_schema_props_perms(ertype=None, syncperms=True, syncprops=True, syncrdefs=True, commit=True)`,
+  synchronizes properties and/or permissions on:
+  - the whole schema if ertype is None
+  - an entity or relation type schema if ertype is a string
+  - a relation definition  if ertype is a 3-uple (subject, relation, object)
+
+* `change_relation_props(subjtype, rtype, objtype, commit=True, **kwargs)`, changes
+  properties of a relation definition by using the named parameters of the properties
+  to change.
+
+* `set_widget(etype, rtype, widget, commit=True)`, changes the widget used for the
+  relation <rtype> of entity type <etype>.
+
+* `set_size_constraint(etype, rtype, size, commit=True)`, changes the size constraints
+  for the relation <rtype> of entity type <etype>.
+
+Data migration
+--------------
+The following functions for data migration are available in `repository` scripts:
+
+* `rql(rql, kwargs=None, cachekey=None, ask_confirm=True)`, executes an arbitrary RQL
+  query, either to interrogate or update. A result set object is returned.
+
+* `add_entity(etype, *args, **kwargs)`, adds a new entity of the given type.
+  The attribute and relation values are specified as named positional
+  arguments.
+
+Workflow creation
+-----------------
+
+The following functions for workflow creation are available in `repository`
+scripts:
+
+* `add_workflow(label, workflowof, initial=False, commit=False, **kwargs)`, adds a new workflow
+  for a given type(s)
+
+You can find more details about workflows in the chapter :ref:`Workflow` .
+
+Configuration migration
+-----------------------
+
+The following functions for configuration migration are available in all
+scripts:
+
+* `option_renamed(oldname, newname)`, indicates that an option has been renamed
+
+* `option_group_change(option, oldgroup, newgroup)`, indicates that an option does not
+  belong anymore to the same group.
+
+* `option_added(oldname, newname)`, indicates that an option has been added.
+
+* `option_removed(oldname, newname)`, indicates that an option has been deleted.
+
+The `config` variable is an object which can be used to access the
+configuration values, for reading and updating, with a dictionary-like
+syntax. 
+
+Example 1: migration script changing the variable 'sender-addr' in
+all-in-one.conf. The script also checks that in that the instance is
+configured with a known value for that variable, and only updates the
+value in that case.
+
+.. sourcecode:: python
+
+ wrong_addr = 'cubicweb@loiglab.fr' # known wrong address
+ fixed_addr = 'cubicweb@logilab.fr'
+ configured_addr = config.get('sender-addr')
+ # check that the address has not been hand fixed by a sysadmin
+ if configured_addr == wrong_addr: 
+     config['sender-addr'] = fixed-addr
+     config.save()
+
+Example 2: checking the value of the database backend driver, which
+can be useful in case you need to issue backend-dependent raw SQL
+queries in a migration script.
+
+.. sourcecode:: python
+
+ dbdriver  = config.sources()['system']['db-driver']
+ if dbdriver == "sqlserver2005":
+     # this is now correctly handled by CW :-)
+     sql('ALTER TABLE cw_Xxxx ALTER COLUMN cw_name varchar(64) NOT NULL;')
+     commit()
+ else: # postgresql
+     sync_schema_props_perms(ertype=('Xxxx', 'name', 'String'),
+     syncperms=False)
+
+
+Others migration functions
+--------------------------
+Those functions are only used for low level operations that could not be
+accomplished otherwise or to repair damaged databases during interactive
+session. They are available in `repository` scripts:
+
+* `sql(sql, args=None, ask_confirm=True)`, executes an arbitrary SQL query on the system source
+* `add_entity_type_table(etype, commit=True)`
+* `add_relation_type_table(rtype, commit=True)`
+* `uninline_relation(rtype, commit=True)`
+
+
+[FIXME] Add explanation on how to use cubicweb-ctl shell
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/profiling.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,57 @@
+.. _PROFILING:
+
+Profiling and performance
+=========================
+
+If you feel that one of your pages takes more time than it should to be
+generated, chances are that you're making too many RQL queries.  Obviously,
+there are other reasons but experience tends to show this is the first thing to
+track down. Luckily, CubicWeb provides a configuration option to log RQL
+queries. In your ``all-in-one.conf`` file, set the **query-log-file** option::
+
+    # web application query log file
+    query-log-file=/home/user/myapp-rql.log
+
+Then restart your application, reload your page and stop your application.
+The file ``myapp-rql.log`` now contains the list of RQL queries that were
+executed during your test. It's a simple text file containing lines such as::
+
+    Any A WHERE X eid %(x)s, X lastname A {'x': 448} -- (0.002 sec, 0.010 CPU sec)
+    Any A WHERE X eid %(x)s, X firstname A {'x': 447} -- (0.002 sec, 0.000 CPU sec)
+
+The structure of each line is::
+
+    <RQL QUERY> <QUERY ARGS IF ANY> -- <TIME SPENT>
+
+CubicWeb also provides the **exlog** command to examine and summarize data found
+in such a file:
+
+.. sourcecode:: sh
+
+    $ cubicweb-ctl exlog /home/user/myapp-rql.log
+    0.07 50 Any A WHERE X eid %(x)s, X firstname A {}
+    0.05 50 Any A WHERE X eid %(x)s, X lastname A {}
+    0.01 1 Any X,AA ORDERBY AA DESC WHERE E eid %(x)s, E employees X, X modification_date AA {}
+    0.01 1 Any X WHERE X eid %(x)s, X owned_by U, U eid %(u)s {, }
+    0.01 1 Any B,T,P ORDERBY lower(T) WHERE B is Bookmark,B title T, B path P, B bookmarked_by U, U eid %(x)s {}
+    0.01 1 Any A,B,C,D WHERE A eid %(x)s,A name B,A creation_date C,A modification_date D {}
+
+This command sorts and uniquifies queries so that it's easy to see where
+is the hot spot that needs optimization.
+
+Do not neglect to set the **fetch_attrs** attribute you can define in your
+entity classes because it can greatly reduce the number of queries executed (see
+:ref:`FetchAttrs`).
+
+You should also know about the **profile** option in the ``all-in-on.conf``. If
+set, this option will make your application run in an `hotshot`_ session and
+store the results in the specified file.
+
+.. _hotshot: http://docs.python.org/library/hotshot.html#module-hotshot
+
+Last but no least, if you're using the PostgreSQL database backend, VACUUMing
+your database can significantly improve the performance of the queries (by
+updating the statistics used by the query optimizer). Nowadays, this is done
+automatically from time to time, but if you've just imported a large amount of
+data in your db, you will want to vacuum it (with the analyse option on). Read
+the documentation of your database for more information.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/repo/hooks.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,279 @@
+.. -*- coding: utf-8 -*-
+.. _hooks:
+
+Hooks and Operations
+====================
+
+.. autodocstring:: cubicweb.server.hook
+
+
+Example using dataflow hooks
+----------------------------
+
+We will use a very simple example to show hooks usage. Let us start with the
+following schema.
+
+.. sourcecode:: python
+
+   class Person(EntityType):
+       age = Int(required=True)
+
+We would like to add a range constraint over a person's age. Let's write an hook
+(supposing yams can not handle this nativly, which is wrong). It shall be placed
+into `mycube/hooks.py`. If this file were to grow too much, we can easily have a
+`mycube/hooks/... package` containing hooks in various modules.
+
+.. sourcecode:: python
+
+   from cubicweb import ValidationError
+   from cubicweb.predicates import is_instance
+   from cubicweb.server.hook import Hook
+
+   class PersonAgeRange(Hook):
+        __regid__ = 'person_age_range'
+        __select__ = Hook.__select__ & is_instance('Person')
+        events = ('before_add_entity', 'before_update_entity')
+
+        def __call__(self):
+	    if 'age' in self.entity.cw_edited:
+                if 0 <= self.entity.age <= 120:
+                   return
+		msg = self._cw._('age must be between 0 and 120')
+		raise ValidationError(self.entity.eid, {'age': msg})
+
+In our example the base `__select__` is augmented with an `is_instance` selector
+matching the desired entity type.
+
+The `events` tuple is used specify that our hook should be called before the
+entity is added or updated.
+
+Then in the hook's `__call__` method, we:
+
+* check if the 'age' attribute is edited
+* if so, check the value is in the range
+* if not, raise a validation error properly
+
+Now Let's augment our schema with new `Company` entity type with some relation to
+`Person` (in 'mycube/schema.py').
+
+.. sourcecode:: python
+
+   class Company(EntityType):
+        name = String(required=True)
+        boss = SubjectRelation('Person', cardinality='1*')
+        subsidiary_of = SubjectRelation('Company', cardinality='*?')
+
+
+We would like to constrain the company's bosses to have a minimum (legal)
+age. Let's write an hook for this, which will be fired when the `boss` relation
+is established (still supposing we could not specify that kind of thing in the
+schema).
+
+.. sourcecode:: python
+
+   class CompanyBossLegalAge(Hook):
+        __regid__ = 'company_boss_legal_age'
+        __select__ = Hook.__select__ & match_rtype('boss')
+        events = ('before_add_relation',)
+
+        def __call__(self):
+            boss = self._cw.entity_from_eid(self.eidto)
+            if boss.age < 18:
+                msg = self._cw._('the minimum age for a boss is 18')
+                raise ValidationError(self.eidfrom, {'boss': msg})
+
+.. Note::
+
+    We use the :class:`~cubicweb.server.hook.match_rtype` selector to select the
+    proper relation type.
+
+    The essential difference with respect to an entity hook is that there is no
+    self.entity, but `self.eidfrom` and `self.eidto` hook attributes which
+    represent the subject and object **eid** of the relation.
+
+Suppose we want to check that there is no cycle by the `subsidiary_of`
+relation. This is best achieved in an operation since all relations are likely to
+be set at commit time.
+
+.. sourcecode:: python
+
+    from cubicweb.server.hook import Hook, DataOperationMixIn, Operation, match_rtype
+
+    def check_cycle(self, session, eid, rtype, role='subject'):
+        parents = set([eid])
+        parent = session.entity_from_eid(eid)
+        while parent.related(rtype, role):
+            parent = parent.related(rtype, role)[0]
+            if parent.eid in parents:
+                msg = session._('detected %s cycle' % rtype)
+                raise ValidationError(eid, {rtype: msg})
+            parents.add(parent.eid)
+
+
+    class CheckSubsidiaryCycleOp(Operation):
+
+        def precommit_event(self):
+            check_cycle(self.session, self.eidto, 'subsidiary_of')
+
+
+    class CheckSubsidiaryCycleHook(Hook):
+        __regid__ = 'check_no_subsidiary_cycle'
+        __select__ = Hook.__select__ & match_rtype('subsidiary_of')
+        events = ('after_add_relation',)
+
+        def __call__(self):
+            CheckSubsidiaryCycleOp(self._cw, eidto=self.eidto)
+
+
+Like in hooks, :exc:`~cubicweb.ValidationError` can be raised in operations. Other
+exceptions are usually programming errors.
+
+In the above example, our hook will instantiate an operation each time the hook
+is called, i.e. each time the `subsidiary_of` relation is set. There is an
+alternative method to schedule an operation from a hook, using the
+:func:`get_instance` class method.
+
+.. sourcecode:: python
+
+   from cubicweb.server.hook import set_operation
+
+   class CheckSubsidiaryCycleHook(Hook):
+       __regid__ = 'check_no_subsidiary_cycle'
+       events = ('after_add_relation',)
+       __select__ = Hook.__select__ & match_rtype('subsidiary_of')
+
+       def __call__(self):
+           CheckSubsidiaryCycleOp.get_instance(self._cw).add_data(self.eidto)
+
+   class CheckSubsidiaryCycleOp(DataOperationMixIn, Operation):
+
+       def precommit_event(self):
+           for eid in self.get_data():
+               check_cycle(self.session, eid, self.rtype)
+
+
+Here, we call :func:`set_operation` so that we will simply accumulate eids of
+entities to check at the end in a single `CheckSubsidiaryCycleOp`
+operation. Value are stored in a set associated to the
+'subsidiary_cycle_detection' transaction data key. The set initialization and
+operation creation are handled nicely by :func:`set_operation`.
+
+A more realistic example can be found in the advanced tutorial chapter
+:ref:`adv_tuto_security_propagation`.
+
+
+Inter-instance communication
+----------------------------
+
+If your application consists of several instances, you may need some means to
+communicate between them.  Cubicweb provides a publish/subscribe mechanism
+using ØMQ_.  In order to use it, use
+:meth:`~cubicweb.server.cwzmq.ZMQComm.add_subscription` on the
+`repo.app_instances_bus` object.  The `callback` will get the message (as a
+list).  A message can be sent by calling
+:meth:`~cubicweb.server.cwzmq.ZMQComm.publish` on `repo.app_instances_bus`.
+The first element of the message is the topic which is used for filtering and
+dispatching messages.
+
+.. _ØMQ: http://www.zeromq.org/
+
+.. sourcecode:: python
+
+  class FooHook(hook.Hook):
+      events = ('server_startup',)
+      __regid__ = 'foo_startup'
+
+      def __call__(self):
+          def callback(msg):
+              self.info('received message: %s', ' '.join(msg))
+          self.repo.app_instances_bus.add_subscription('hello', callback)
+
+.. sourcecode:: python
+
+  def do_foo(self):
+      actually_do_foo()
+      self._cw.repo.app_instances_bus.publish(['hello', 'world'])
+
+The `zmq-address-pub` configuration variable contains the address used
+by the instance for sending messages, e.g. `tcp://*:1234`.  The
+`zmq-address-sub` variable contains a comma-separated list of addresses
+to listen on, e.g. `tcp://localhost:1234, tcp://192.168.1.1:2345`.
+
+
+Hooks writing tips
+------------------
+
+Reminder
+~~~~~~~~
+
+You should never use the `entity.foo = 42` notation to update an entity. It will
+not do what you expect (updating the database). Instead, use the
+:meth:`~cubicweb.entity.Entity.cw_set` method or direct access to entity's
+:attr:`cw_edited` attribute if you're writing a hook for 'before_add_entity' or
+'before_update_entity' event.
+
+
+How to choose between a before and an after event ?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+`before_*` hooks give you access to the old attribute (or relation)
+values. You can also intercept and update edited values in the case of
+entity modification before they reach the database.
+
+Else the question is: should I need to do things before or after the actual
+modification ? If the answer is "it doesn't matter", use an 'after' event.
+
+
+Validation Errors
+~~~~~~~~~~~~~~~~~
+
+When a hook which is responsible to maintain the consistency of the
+data model detects an error, it must use a specific exception named
+:exc:`~cubicweb.ValidationError`. Raising anything but a (subclass of)
+:exc:`~cubicweb.ValidationError` is a programming error. Raising it
+entails aborting the current transaction.
+
+This exception is used to convey enough information up to the user
+interface. Hence its constructor is different from the default Exception
+constructor. It accepts, positionally:
+
+* an entity eid (**not the entity itself**),
+
+* a dict whose keys represent attribute (or relation) names and values
+  an end-user facing message (hence properly translated) relating the
+  problem.
+
+.. sourcecode:: python
+
+  raise ValidationError(earth.eid, {'sea_level': self._cw._('too high'),
+                                    'temperature': self._cw._('too hot')})
+
+
+Checking for object created/deleted in the current transaction
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In hooks, you can use the
+:meth:`~cubicweb.server.session.Session.added_in_transaction` or
+:meth:`~cubicweb.server.session.Session.deleted_in_transaction` of the session
+object to check if an eid has been created or deleted during the hook's
+transaction.
+
+This is useful to enable or disable some stuff if some entity is being added or
+deleted.
+
+.. sourcecode:: python
+
+   if self._cw.deleted_in_transaction(self.eidto):
+      return
+
+
+Peculiarities of inlined relations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Relations which are defined in the schema as `inlined` (see :ref:`RelationType`
+for details) are inserted in the database at the same time as entity attributes.
+
+This may have some side effect, for instance when creating an entity
+and setting an inlined relation in the same rql query, then at
+`before_add_relation` time, the relation will already exist in the
+database (it is otherwise not the case).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/repo/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,13 @@
+.. -*- coding: utf-8 -*-
+
+Repository customization
+++++++++++++++++++++++++
+.. toctree::
+   :maxdepth: 1
+
+   sessions
+   hooks
+   notifications
+   tasks
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/repo/notifications.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,29 @@
+.. -*- coding: utf-8 -*-
+
+Notifications management
+========================
+
+CubicWeb provides a machinery to ease notifications handling. To use it for a
+notification:
+
+* write a view inheriting from
+  :class:`~cubicweb.sobjects.notification.NotificationView`.  The usual view api
+  is used to generated the email (plain text) content, and additional
+  :meth:`~cubicweb.sobjects.notification.NotificationView.subject` and
+  :meth:`~cubicweb.sobjects.notification.NotificationView.recipients` methods
+  are used to build the email's subject and
+  recipients. :class:`NotificationView` provides default implementation for both
+  methods.
+
+* write a hook for event that should trigger this notification, select the view
+  (without rendering it), and give it to
+  :func:`cubicweb.hooks.notification.notify_on_commit` so that the notification
+  will be sent if the transaction succeed.
+
+
+.. XXX explain recipient finder and provide example
+
+API details
+~~~~~~~~~~~
+.. autoclass:: cubicweb.sobjects.notification.NotificationView
+.. autofunction:: cubicweb.hooks.notification.notify_on_commit
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/repo/sessions.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,319 @@
+.. -*- coding: utf-8 -*-
+
+Sessions
+========
+
+Sessions are objects linked to an authenticated user.  The `Session.new_cnx`
+method returns a new Connection linked to that session.
+
+Connections
+===========
+
+Connections provide the `.execute` method to query the data sources, along with
+`.commit` and `.rollback` methods for transaction management.
+
+Kinds of connections
+--------------------
+
+There are two kinds of connections.
+
+* `normal connections` are the most common: they are related to users and
+  carry security checks coming with user credentials
+
+* `internal connections` have all the powers; they are also used in only a
+  few situations where you don't already have an adequate session at
+  hand, like: user authentication, data synchronisation in
+  multi-source contexts
+
+Normal connections are typically named `_cw` in most appobjects or
+sometimes just `session`.
+
+Internal connections are available from the `Repository` object and are
+to be used like this:
+
+.. sourcecode:: python
+
+   with self.repo.internal_cnx() as cnx:
+       do_stuff_with(cnx)
+       cnx.commit()
+
+Connections should always be used as context managers, to avoid leaks.
+
+
+Python/RQL API
+~~~~~~~~~~~~~~
+
+The Python API developped to interface with RQL is inspired from the standard db-api,
+but since `execute` returns its results directly, there is no `cursor` concept.
+
+.. sourcecode:: python
+
+   execute(rqlstring, args=None, build_descr=True)
+
+:rqlstring: the RQL query to execute (unicode)
+:args: if the query contains substitutions, a dictionary containing the values to use
+
+The `Connection` object owns the methods `commit` and `rollback`. You
+*should never need to use them* during the development of the web
+interface based on the *CubicWeb* framework as it determines the end
+of the transaction depending on the query execution success. They are
+however useful in other contexts such as tests or custom controllers.
+
+.. note::
+
+  If a query generates an error related to security (:exc:`Unauthorized`) or to
+  integrity (:exc:`ValidationError`), the transaction can still continue but you
+  won't be able to commit it, a rollback will be necessary to start a new
+  transaction.
+
+  Also, a rollback is automatically done if an error occurs during commit.
+
+.. note::
+
+   A :exc:`ValidationError` has a `entity` attribute. In CubicWeb,
+   this atttribute is set to the entity's eid (not a reference to the
+   entity itself).
+
+Executing RQL queries from a view or a hook
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When you're within code of the web interface, the Connection is handled by the
+request object. You should not have to access it directly, but use the
+`execute` method directly available on the request, eg:
+
+.. sourcecode:: python
+
+   rset = self._cw.execute(rqlstring, kwargs)
+
+Similarly, on the server side (eg in hooks), there is no request object (since
+you're directly inside the data-server), so you'll have to use the execute method
+of the Connection object.
+
+Proper usage of `.execute`
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Let's say you want to get T which is in configuration C, this translates to:
+
+.. sourcecode:: python
+
+   self._cw.execute('Any T WHERE T in_conf C, C eid %s' % entity.eid)
+
+But it must be written in a syntax that will benefit from the use
+of a cache on the RQL server side:
+
+.. sourcecode:: python
+
+   self._cw.execute('Any T WHERE T in_conf C, C eid %(x)s', {'x': entity.eid})
+
+The syntax tree is built once for the "generic" RQL and can be re-used
+with a number of different eids.  The rql IN operator is an exception
+to this rule.
+
+.. sourcecode:: python
+
+   self._cw.execute('Any T WHERE T in_conf C, C name IN (%s)'
+                    % ','.join(['foo', 'bar']))
+
+Alternatively, some of the common data related to an entity can be
+obtained from the `entity.related()` method (which is used under the
+hood by the ORM when you use attribute access notation on an entity to
+get a relation. The initial request would then be translated to:
+
+.. sourcecode:: python
+
+   entity.related('in_conf', 'object')
+
+Additionally this benefits from the fetch_attrs policy (see :ref:`FetchAttrs`)
+optionally defined on the class element, which says which attributes must be
+also loaded when the entity is loaded through the ORM.
+
+.. _resultset:
+
+The `ResultSet` API
+~~~~~~~~~~~~~~~~~~~
+
+ResultSet instances are a very commonly manipulated object. They have
+a rich API as seen below, but we would like to highlight a bunch of
+methods that are quite useful in day-to-day practice:
+
+* `__str__()` (applied by `print`) gives a very useful overview of both
+  the underlying RQL expression and the data inside; unavoidable for
+  debugging purposes
+
+* `printable_rql()` returns a well formed RQL expression as a
+  string; it is very useful to build views
+
+* `entities()` returns a generator on all entities of the result set
+
+* `get_entity(row, col)` gets the entity at row, col coordinates; one
+  of the most used result set methods
+
+.. autoclass:: cubicweb.rset.ResultSet
+   :members:
+   :noindex:
+
+
+Authentication and management of sessions
+-----------------------------------------
+
+The authentication process is a ballet involving a few dancers:
+
+* through its `get_session` method the top-level application object (the
+  `CubicWebPublisher`) will open a session whenever a web request
+  comes in; it asks the `session manager` to open a session (giving
+  the web request object as context) using `open_session`
+
+  * the session manager asks its authentication manager (which is a
+    `component`) to authenticate the request (using `authenticate`)
+
+    * the authentication manager asks, in order, to its authentication
+      information retrievers, a login and an opaque object containing
+      other credentials elements (calling `authentication_information`),
+      giving the request object each time
+
+      * the default retriever (named `LoginPasswordRetriever`)
+        will in turn defer login and password fetching to the request
+        object (which, depending on the authentication mode (`cookie`
+        or `http`), will do the appropriate things and return a login
+        and a password)
+
+    * the authentication manager, on success, asks the `Repository`
+      object to connect with the found credentials (using `connect`)
+
+      * the repository object asks authentication to all of its
+        sources which support the `CWUser` entity with the given
+        credentials; when successful it can build the cwuser entity,
+        from which a regular `Session` object is made; it returns the
+        session id
+
+        * the source in turn will delegate work to an authentifier
+          class that defines the ultimate `authenticate` method (for
+          instance the native source will query the database against
+          the provided credentials)
+
+    * the authentication manager, on success, will call back _all_
+      retrievers with `authenticated` and return its authentication
+      data (on failure, it will try the anonymous login or, if the
+      configuration forbids it, raise an `AuthenticationError`)
+
+Writing authentication plugins
+------------------------------
+
+Sometimes CubicWeb's out-of-the-box authentication schemes (cookie and
+http) are not sufficient. Nowadays there is a plethora of such schemes
+and the framework cannot provide them all, but as the sequence above
+shows, it is extensible.
+
+Two levels have to be considered when writing an authentication
+plugin: the web client and the repository.
+
+We invented a scenario where it makes sense to have a new plugin in
+each side: some middleware will do pre-authentication and under the
+right circumstances add a new HTTP `x-foo-user` header to the query
+before it reaches the CubicWeb instance. For a concrete example of
+this, see the `trustedauth`_ cube.
+
+.. _`trustedauth`: http://www.cubicweb.org/project/cubicweb-trustedauth
+
+Repository authentication plugins
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+On the repository side, it is possible to register a source
+authentifier using the following kind of code:
+
+.. sourcecode:: python
+
+ from cubicweb.server.sources import native
+
+ class FooAuthentifier(native.LoginPasswordAuthentifier):
+     """ a source authentifier plugin
+     if 'foo' in authentication information, no need to check
+     password
+     """
+     auth_rql = 'Any X WHERE X is CWUser, X login %(login)s'
+
+     def authenticate(self, session, login, **kwargs):
+         """return CWUser eid for the given login
+         if this account is defined in this source,
+         else raise `AuthenticationError`
+         """
+         session.debug('authentication by %s', self.__class__.__name__)
+         if 'foo' not in kwargs:
+             return super(FooAuthentifier, self).authenticate(session, login, **kwargs)
+         try:
+             rset = session.execute(self.auth_rql, {'login': login})
+             return rset[0][0]
+         except Exception, exc:
+             session.debug('authentication failure (%s)', exc)
+         raise AuthenticationError('foo user is unknown to us')
+
+Since repository authentifiers are not appobjects, we have to register
+them through a `server_startup` hook.
+
+.. sourcecode:: python
+
+ class ServerStartupHook(hook.Hook):
+     """ register the foo authenticator """
+     __regid__ = 'fooauthenticatorregisterer'
+     events = ('server_startup',)
+
+     def __call__(self):
+         self.debug('registering foo authentifier')
+         self.repo.system_source.add_authentifier(FooAuthentifier())
+
+Web authentication plugins
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. sourcecode:: python
+
+ class XFooUserRetriever(authentication.LoginPasswordRetriever):
+     """ authenticate by the x-foo-user http header
+     or just do normal login/password authentication
+     """
+     __regid__ = 'x-foo-user'
+     order = 0
+
+     def authentication_information(self, req):
+         """retrieve authentication information from the given request, raise
+         NoAuthInfo if expected information is not found
+         """
+         self.debug('web authenticator building auth info')
+         try:
+            login = req.get_header('x-foo-user')
+            if login:
+                return login, {'foo': True}
+            else:
+                return super(XFooUserRetriever, self).authentication_information(self, req)
+         except Exception, exc:
+            self.debug('web authenticator failed (%s)', exc)
+         raise authentication.NoAuthInfo()
+
+     def authenticated(self, retriever, req, cnx, login, authinfo):
+         """callback when return authentication information have opened a
+         repository connection successfully. Take care req has no session
+         attached yet, hence req.execute isn't available.
+
+         Here we set a flag on the request to indicate that the user is
+         foo-authenticated. Can be used by a selector
+         """
+         self.debug('web authenticator running post authentication callback')
+         cnx.foo_user = authinfo.get('foo')
+
+In the `authenticated` method we add (in an admitedly slightly hackish
+way) an attribute to the connection object. This, in turn, can be used
+to build a selector dispatching on the fact that the user was
+preauthenticated or not.
+
+.. sourcecode:: python
+
+ @objectify_selector
+ def foo_authenticated(cls, req, rset=None, **kwargs):
+     if hasattr(req.cnx, 'foo_user') and req.foo_user:
+         return 1
+     return 0
+
+Full Session and Connection API
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. autoclass:: cubicweb.server.session.Session
+.. autoclass:: cubicweb.server.session.Connection
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/repo/tasks.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,9 @@
+.. -*- coding: utf-8 -*-
+
+Tasks
+=========
+
+[WRITE ME]
+
+* repository tasks
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/testing.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,559 @@
+.. -*- coding: utf-8 -*-
+
+Tests
+=====
+
+Unit tests
+----------
+
+The *CubicWeb* framework provides the
+:class:`cubicweb.devtools.testlib.CubicWebTC` test base class .
+
+Tests shall be put into the mycube/test directory. Additional test
+data shall go into mycube/test/data.
+
+It is much advised to write tests concerning entities methods,
+actions, hooks and operations, security. The
+:class:`~cubicweb.devtools.testlib.CubicWebTC` base class has
+convenience methods to help test all of this.
+
+In the realm of views, automatic tests check that views are valid
+XHTML. See :ref:`automatic_views_tests` for details.
+
+Most unit tests need a live database to work against. This is achieved
+by CubicWeb using automatically sqlite (bundled with Python, see
+http://docs.python.org/library/sqlite3.html) as a backend.
+
+The database is stored in the mycube/test/tmpdb,
+mycube/test/tmpdb-template files. If it does not (yet) exist, it will
+be built automatically when the test suite starts.
+
+.. warning::
+
+  Whenever the schema changes (new entities, attributes, relations)
+  one must delete these two files. Changes concerned only with entity
+  or relation type properties (constraints, cardinalities,
+  permissions) and generally dealt with using the
+  `sync_schema_props_perms()` function of the migration environment do
+  not need a database regeneration step.
+
+.. _hook_test:
+
+Unit test by example
+````````````````````
+
+We start with an example extracted from the keyword cube (available
+from http://www.cubicweb.org/project/cubicweb-keyword).
+
+.. sourcecode:: python
+
+    from cubicweb.devtools.testlib import CubicWebTC
+    from cubicweb import ValidationError
+
+    class ClassificationHooksTC(CubicWebTC):
+
+        def setup_database(self):
+            with self.admin_access.repo_cnx() as cnx:
+                group_etype = cnx.find('CWEType', name='CWGroup').one()
+                c1 = cnx.create_entity('Classification', name=u'classif1',
+                                       classifies=group_etype)
+                user_etype = cnx.find('CWEType', name='CWUser').one()
+                c2 = cnx.create_entity('Classification', name=u'classif2',
+                                       classifies=user_etype)
+                self.kw1eid = cnx.create_entity('Keyword', name=u'kwgroup', included_in=c1).eid
+                cnx.commit()
+
+        def test_cannot_create_cycles(self):
+            with self.admin_access.repo_cnx() as cnx:
+                kw1 = cnx.entity_from_eid(self.kw1eid)
+                # direct obvious cycle
+                with self.assertRaises(ValidationError):
+                    kw1.cw_set(subkeyword_of=kw1)
+                cnx.rollback()
+                # testing indirect cycles
+                kw3 = cnx.execute('INSERT Keyword SK: SK name "kwgroup2", SK included_in C, '
+                                  'SK subkeyword_of K WHERE C name "classif1", K eid %(k)s'
+                                  {'k': kw1}).get_entity(0,0)
+                kw3.cw_set(reverse_subkeyword_of=kw1)
+                self.assertRaises(ValidationError, cnx.commit)
+
+The test class defines a :meth:`setup_database` method which populates the
+database with initial data. Each test of the class runs with this
+pre-populated database.
+
+The test case itself checks that an Operation does its job of
+preventing cycles amongst Keyword entities.
+
+The `create_entity` method of connection (or request) objects allows
+to create an entity. You can link this entity to other entities, by
+specifying as argument, the relation name, and the entity to link, as
+value. In the above example, the `Classification` entity is linked to
+a `CWEtype` via the relation `classifies`. Conversely, if you are
+creating a `CWEtype` entity, you can link it to a `Classification`
+entity, by adding `reverse_classifies` as argument.
+
+.. note::
+
+   the :meth:`commit` method is not called automatically. You have to
+   call it explicitly if needed (notably to test operations). It is a
+   good practice to regenerate entities with :meth:`entity_from_eid`
+   after a commit to avoid request cache effects.
+
+You can see an example of security tests in the
+:ref:`adv_tuto_security`.
+
+It is possible to have these tests run continuously using `apycot`_.
+
+.. _apycot: http://www.cubicweb.org/project/apycot
+
+.. _securitytest:
+
+Managing connections or users
++++++++++++++++++++++++++++++
+
+Since unit tests are done with the SQLITE backend and this does not
+support multiple connections at a time, you must be careful when
+simulating security, changing users.
+
+By default, tests run with a user with admin privileges. Connections
+using these credentials are accessible through the `admin_access` object
+of the test classes.
+
+The `repo_cnx()` method returns a connection object that can be used as a
+context manager:
+
+.. sourcecode:: python
+
+   # admin_access is a pre-cooked session wrapping object
+   # it is built with:
+   # self.admin_access = self.new_access('admin')
+   with self.admin_access.repo_cnx() as cnx:
+       cnx.execute(...)
+       self.create_user(cnx, login='user1')
+       cnx.commit()
+
+   user1access = self.new_access('user1')
+   with user1access.web_request() as req:
+       req.execute(...)
+       req.cnx.commit()
+
+On exit of the context manager, a rollback is issued, which releases
+the connection. Don't forget to issue the `cnx.commit()` calls!
+
+.. warning::
+
+   Do not use references kept to the entities created with a
+   connection from another one!
+
+Email notifications tests
+`````````````````````````
+
+When running tests, potentially generated e-mails are not really sent
+but are found in the list `MAILBOX` of module
+:mod:`cubicweb.devtools.testlib`.
+
+You can test your notifications by analyzing the contents of this list, which
+contains objects with two attributes:
+
+* `recipients`, the list of recipients
+* `msg`, email.Message object
+
+Let us look at a simple example from the ``blog`` cube.
+
+.. sourcecode:: python
+
+    from cubicweb.devtools.testlib import CubicWebTC, MAILBOX
+
+    class BlogTestsCubicWebTC(CubicWebTC):
+        """test blog specific behaviours"""
+
+        def test_notifications(self):
+            with self.admin_access.web_request() as req:
+                cubicweb_blog = req.create_entity('Blog', title=u'cubicweb',
+                                    description=u'cubicweb is beautiful')
+                blog_entry_1 = req.create_entity('BlogEntry', title=u'hop',
+                                                 content=u'cubicweb hop')
+                blog_entry_1.cw_set(entry_of=cubicweb_blog)
+                blog_entry_2 = req.create_entity('BlogEntry', title=u'yes',
+                                                 content=u'cubicweb yes')
+                blog_entry_2.cw_set(entry_of=cubicweb_blog)
+                self.assertEqual(len(MAILBOX), 0)
+                req.cnx.commit()
+                self.assertEqual(len(MAILBOX), 2)
+                mail = MAILBOX[0]
+                self.assertEqual(mail.subject, '[data] hop')
+                mail = MAILBOX[1]
+                self.assertEqual(mail.subject, '[data] yes')
+
+Visible actions tests
+`````````````````````
+
+It is easy to write unit tests to test actions which are visible to
+a user or to a category of users. Let's take an example in the
+`conference cube`_.
+
+.. _`conference cube`: http://www.cubicweb.org/project/cubicweb-conference
+.. sourcecode:: python
+
+    class ConferenceActionsTC(CubicWebTC):
+
+        def setup_database(self):
+            with self.admin_access.repo_cnx() as cnx:
+                self.confeid = cnx.create_entity('Conference',
+                                                 title=u'my conf',
+                                                 url_id=u'conf',
+                                                 start_on=date(2010, 1, 27),
+                                                 end_on = date(2010, 1, 29),
+                                                 call_open=True,
+                                                 reverse_is_chair_at=chair,
+                                                 reverse_is_reviewer_at=reviewer).eid
+
+        def test_admin(self):
+            with self.admin_access.web_request() as req:
+                rset = req.find('Conference').one()
+                self.assertListEqual(self.pactions(req, rset),
+                                      [('workflow', workflow.WorkflowActions),
+                                       ('edit', confactions.ModifyAction),
+                                       ('managepermission', actions.ManagePermissionsAction),
+                                       ('addrelated', actions.AddRelatedActions),
+                                       ('delete', actions.DeleteAction),
+                                       ('generate_badge_action', badges.GenerateBadgeAction),
+                                       ('addtalkinconf', confactions.AddTalkInConferenceAction)
+                                       ])
+                self.assertListEqual(self.action_submenu(req, rset, 'addrelated'),
+                                      [(u'add Track in_conf Conference object',
+                                        u'http://testing.fr/cubicweb/add/Track'
+                                        u'?__linkto=in_conf%%3A%(conf)s%%3Asubject&'
+                                        u'__redirectpath=conference%%2Fconf&'
+                                        u'__redirectvid=' % {'conf': self.confeid}),
+                                       ])
+
+You just have to execute a rql query corresponding to the view you want to test,
+and to compare the result of
+:meth:`~cubicweb.devtools.testlib.CubicWebTC.pactions` with the list of actions
+that must be visible in the interface. This is a list of tuples. The first
+element is the action's `__regid__`, the second the action's class.
+
+To test actions in a submenu, you just have to test the result of
+:meth:`~cubicweb.devtools.testlib.CubicWebTC.action_submenu` method. The last
+parameter of the method is the action's category. The result is a list of
+tuples. The first element is the action's title, and the second element the
+action's url.
+
+
+.. _automatic_views_tests:
+
+Automatic views testing
+-----------------------
+
+This is done automatically with the :class:`cubicweb.devtools.testlib.AutomaticWebTest`
+class. At cube creation time, the mycube/test/test_mycube.py file
+contains such a test. The code here has to be uncommented to be
+usable, without further modification.
+
+The ``auto_populate`` method uses a smart algorithm to create
+pseudo-random data in the database, thus enabling the views to be
+invoked and tested.
+
+Depending on the schema, hooks and operations constraints, it is not
+always possible for the automatic auto_populate to proceed.
+
+It is possible of course to completely redefine auto_populate. A
+lighter solution is to give hints (fill some class attributes) about
+what entities and relations have to be skipped by the auto_populate
+mechanism. These are:
+
+* `no_auto_populate`, may contain a list of entity types to skip
+* `ignored_relations`, may contain a list of relation types to skip
+* `application_rql`, may contain a list of rql expressions that
+  auto_populate cannot guess by itself; these must yield resultsets
+  against which views may be selected.
+
+.. warning::
+
+  Take care to not let the imported `AutomaticWebTest` in your test module
+  namespace, else both your subclass *and* this parent class will be run.
+
+Cache heavy database setup
+-------------------------------
+
+Some test suites require a complex setup of the database that takes
+seconds (or even minutes) to complete. Doing the whole setup for each
+individual test makes the whole run very slow. The ``CubicWebTC``
+class offer a simple way to prepare a specific database once for
+multiple tests. The `test_db_id` class attribute of your
+``CubicWebTC`` subclass must be set to a unique identifier and the
+:meth:`pre_setup_database` class method must build the cached content. As
+the :meth:`pre_setup_database` method is not garanteed to be called
+every time a test method is run, you must not set any class attribute
+to be used during test *there*. Databases for each `test_db_id` are
+automatically created if not already in cache. Clearing the cache is
+up to the user. Cache files are found in the :file:`data/database`
+subdirectory of your test directory.
+
+.. warning::
+
+  Take care to always have the same :meth:`pre_setup_database`
+  function for all classes with a given `test_db_id` otherwise your
+  tests will have unpredictable results depending on the first
+  encountered one.
+
+
+Testing on a real-life database
+-------------------------------
+
+The ``CubicWebTC`` class uses the `cubicweb.devtools.ApptestConfiguration`
+configuration class to setup its testing environment (database driver,
+user password, application home, and so on). The `cubicweb.devtools`
+module also provides a `RealDatabaseConfiguration`
+class that will read a regular cubicweb sources file to fetch all
+this information but will also prevent the database to be initalized
+and reset between tests.
+
+For a test class to use a specific configuration, you have to set
+the `_config` class attribute on the class as in:
+
+.. sourcecode:: python
+
+    from cubicweb.devtools import RealDatabaseConfiguration
+    from cubicweb.devtools.testlib import CubicWebTC
+
+    class BlogRealDatabaseTC(CubicWebTC):
+        _config = RealDatabaseConfiguration('blog',
+                                            sourcefile='/path/to/realdb_sources')
+
+        def test_blog_rss(self):
+            with self.admin_access.web_request() as req:
+            rset = req.execute('Any B ORDERBY D DESC WHERE B is BlogEntry, '
+                               'B created_by U, U login "logilab", B creation_date D')
+            self.view('rss', rset, req=req)
+
+
+Testing with other cubes
+------------------------
+
+Sometimes a small component cannot be tested all by itself, so one
+needs to specify other cubes to be used as part of the the unit test
+suite. This is handled by the ``bootstrap_cubes`` file located under
+``mycube/test/data``. One example from the `preview` cube::
+
+ card, file, preview
+
+The format is:
+
+* possibly several empy lines or lines starting with ``#`` (comment lines)
+* one line containing a comma-separated list of cube names.
+
+It is also possible to add a ``schema.py`` file in
+``mycube/test/data``, which will be used by the testing framework,
+therefore making new entity types and relations available to the
+tests. 
+
+Literate programming
+--------------------
+
+CubicWeb provides some literate programming capabilities. The :ref:`cubicweb-ctl`
+`shell` command accepts different file formats. If your file ends with `.txt`
+or `.rst`, the file will be parsed by :mod:`doctest.testfile` with CubicWeb's
+:ref:`migration` API enabled in it.
+
+Create a `scenario.txt` file in the `test/` directory and fill with some content.
+Refer to the :mod:`doctest.testfile` `documentation`_.
+
+.. _documentation: http://docs.python.org/library/doctest.html
+
+Then, you can run it directly by::
+
+    $ cubicweb-ctl shell <cube_instance> test/scenario.txt
+
+When your scenario file is ready, put it in a new test case to be able to run
+it automatically.
+
+.. sourcecode:: python
+
+      from os.path import dirname, join
+      from logilab.common.testlib import unittest_main
+      from cubicweb.devtools.testlib import CubicWebTC
+
+      class AcceptanceTC(CubicWebTC):
+
+              def test_scenario(self):
+                      self.assertDocTestFile(join(dirname(__file__), 'scenario.txt'))
+
+      if __name__ == '__main__':
+              unittest_main()
+
+Skipping a scenario
+```````````````````
+
+If you want to set up initial conditions that you can't put in your unit test
+case, you have to use a :exc:`KeyboardInterrupt` exception only because of the
+way :mod:`doctest` module will catch all the exceptions internally.
+
+    >>> if condition_not_met:
+    ...     raise KeyboardInterrupt('please, check your fixture.')
+
+Passing paramaters
+``````````````````
+Using extra arguments to parametrize your scenario is possible by prepending them
+by double dashes.
+
+Please refer to the `cubicweb-ctl shell --help` usage.
+
+.. important::
+    Your scenario file must be utf-8 encoded.
+
+Test APIS
+---------
+
+Using Pytest
+````````````
+
+The `pytest` utility (shipping with `logilab-common`_, which is a
+mandatory dependency of CubicWeb) extends the Python unittest
+functionality and is the preferred way to run the CubicWeb test
+suites. Bare unittests also work the usual way.
+
+.. _logilab-common: http://www.logilab.org/project/logilab-common
+
+To use it, you may:
+
+* just launch `pytest` in your cube to execute all tests (it will
+  discover them automatically)
+* launch `pytest unittest_foo.py` to execute one test file
+* launch `pytest unittest_foo.py bar` to execute all test methods and
+  all test cases whose name contains `bar`
+
+Additionally, the `-x` option tells pytest to exit at the first error
+or failure. The `-i` option tells pytest to drop into pdb whenever an
+exception occurs in a test.
+
+When the `-x` option has been used and the run stopped on a test, it
+is possible, after having fixed the test, to relaunch pytest with the
+`-R` option to tell it to start testing again from where it previously
+failed.
+
+Using the `TestCase` base class
+```````````````````````````````
+
+The base class of CubicWebTC is logilab.common.testlib.TestCase, which
+provides a lot of convenient assertion methods.
+
+.. autoclass:: logilab.common.testlib.TestCase
+   :members:
+
+CubicWebTC API
+``````````````
+.. autoclass:: cubicweb.devtools.testlib.CubicWebTC
+   :members:
+
+
+What you need to know about request and session
+-----------------------------------------------
+
+.. image:: ../../images/request_session.png
+
+First, remember to think that some code run on a client side, some
+other on the repository side. More precisely:
+
+* client side: web interface, raw repoapi connection (cubicweb-ctl shell for
+  instance);
+
+* repository side: RQL query execution, that may trigger hooks and operation.
+
+The client interacts with the repository through a repoapi connection.
+
+
+.. note::
+
+   These distinctions are going to disappear in cubicweb 3.21 (if not
+   before).
+
+A repoapi connection is tied to a session in the repository. The connection and
+request objects are inaccessible from repository code / the session object is
+inaccessible from client code (theoretically at least).
+
+The web interface provides a request class.  That `request` object provides
+access to all cubicweb resources, eg:
+
+* the registry (which itself provides access to the schema and the
+  configuration);
+
+* an underlying repoapi connection (when using req.execute, you actually call the
+  repoapi);
+
+* other specific resources depending on the client type (url generation according
+  to base url, form parameters, etc.).
+
+
+A `session` provides an api similar to a request regarding RQL execution and
+access to global resources (registry and all), but also has the following
+responsibilities:
+
+* handle transaction data, that will live during the time of a single
+  transaction. This includes the database connections that will be used to
+  execute RQL queries.
+
+* handle persistent data that may be used across different (web) requests
+
+* security and hooks control (not possible through a request)
+
+
+The `_cw` attribute
+```````````````````
+The `_cw` attribute available on every application object provides access to all
+cubicweb resources, i.e.:
+
+- For code running on the client side (eg web interface view), `_cw` is a request
+  instance.
+
+- For code running on the repository side (hooks and operation), `_cw` is a
+  Connection or Session instance.
+
+
+Beware some views may be called with a session (e.g. notifications) or with a
+request.
+
+
+Request, session and transaction
+````````````````````````````````
+
+In the web interface, an HTTP request is handled by a single request, which will
+be thrown away once the response is sent.
+
+The web publisher handles the transaction:
+
+* commit / rollback is done automatically
+
+* you should not commit / rollback explicitly, except if you really
+  need it
+
+Let's detail the process:
+
+1. an incoming RQL query comes from a client to the web stack
+
+2. the web stack opens an authenticated database connection for the
+   request, which is associated to a user session
+
+3. the query is executed (through the repository connection)
+
+4. this query may trigger hooks. Hooks and operations may execute some rql queries
+   through `cnx.execute`.
+
+5. the repository gets the result of the query in 1. If it was a RQL read query,
+   the database connection is released. If it was a write query, the connection
+   is then tied to the session until the transaction is commited or rolled back.
+
+6. results are sent back to the client
+
+This implies several things:
+
+* when using a request, or code executed in hooks, this database
+  connection handling is totally transparent
+
+* however, take care when writing tests: you are usually faking /
+  testing both the server and the client side, so you have to decide
+  when to use RepoAccess.client_cnx or RepoAccess.repo_cnx. Ask
+  yourself "where will the code I want to test be running, client or
+  repository side?". The response is usually: use a repo (since the
+  "client connection" concept is going away in a couple of releases).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devrepo/vreg.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,471 @@
+The Registry, selectors and application objects
+===============================================
+
+This chapter deals with some of the  core concepts of the |cubicweb| framework
+which make it different from other frameworks (and maybe not easy to
+grasp at a first glance). To be able to do advanced development with
+|cubicweb| you need a good understanding of what is explained below.
+
+This chapter goes deep into details. You don't have to remember them
+all but keep it in mind so you can go back there later.
+
+An overview of AppObjects, the VRegistry and Selectors is given in the
+:ref:`VRegistryIntro` chapter.
+
+
+
+The :class:`CWRegistryStore`
+----------------------------
+
+The :class:`CWRegistryStore <cubicweb.cwvreg.CWRegistryStore>` can be
+seen as a two-level dictionary. It contains all dynamically loaded
+objects (subclasses of :class:`AppObject <cubicweb.appobject.AppObject>`)
+to build a |cubicweb| application. Basically:
+
+* the first level key returns a *registry*. This key corresponds to the
+  `__registry__` attribute of application object classes
+
+* the second level key returns a list of application objects which
+  share the same identifier. This key corresponds to the `__regid__`
+  attribute of application object classes.
+
+A *registry* holds a specific kind of application objects. There is
+for instance a registry for entity classes, another for views, etc...
+
+The :class:`CWRegistryStore <cubicweb.cwvreg.CWRegistryStore>` has two
+main responsibilities:
+
+- being the access point to all registries
+
+- handling the registration process at startup time, and during automatic
+  reloading in debug mode.
+
+
+Details of the recording process
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. index::
+   vregistry: registration_callback
+
+On startup, |cubicweb| loads application objects defined in its library
+and in cubes used by the instance. Application objects from the
+library are loaded first, then those provided by cubes are loaded in
+dependency order (e.g. if your cube depends on an other, objects from
+the dependency will be loaded first). The layout of the modules or packages
+in a cube  is explained in :ref:`cubelayout`.
+
+For each module:
+
+* by default all objects are registered automatically
+
+* if some objects have to replace other objects, or have to be
+  included only if some condition is met, you'll have to define a
+  `registration_callback(vreg)` function in your module and explicitly
+  register **all objects** in this module, using the api defined
+  below.
+
+.. Note::
+    Once the function `registration_callback(vreg)` is implemented in a module,
+    all the objects from this module have to be explicitly registered as it
+    disables the automatic objects registration.
+
+
+API for objects registration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Here are the registration methods that you can use in the
+`registration_callback` to register your objects to the
+:class:`CWRegistryStore` instance given as argument (usually named
+`vreg`):
+
+- :py:meth:`register_all() <cubicweb.cwvreg.CWRegistryStore.register_all>`
+- :py:meth:`register_and_replace() <cubicweb.cwvreg.CWRegistryStore.register_and_replace>`
+- :py:meth:`register() <cubicweb.cwvreg.CWRegistryStore.register>`
+- :py:meth:`unregister() <logilab.common.registry.RegistryStore.unregister>`
+
+Examples:
+
+.. sourcecode:: python
+
+   # web/views/basecomponents.py
+   def registration_callback(vreg):
+      # register everything in the module except SeeAlsoComponent
+      vreg.register_all(globals().itervalues(), __name__, (SeeAlsoVComponent,))
+      # conditionally register SeeAlsoVComponent
+      if 'see_also' in vreg.schema:
+          vreg.register(SeeAlsoVComponent)
+
+In this example, we register all application object classes defined in the module
+except `SeeAlsoVComponent`. This class is then registered only if the 'see_also'
+relation type is defined in the instance'schema.
+
+.. sourcecode:: python
+
+   # goa/appobjects/sessions.py
+   def registration_callback(vreg):
+      vreg.register(SessionsCleaner)
+      # replace AuthenticationManager by GAEAuthenticationManager
+      vreg.register_and_replace(GAEAuthenticationManager, AuthenticationManager)
+      # replace PersistentSessionManager by GAEPersistentSessionManager
+      vreg.register_and_replace(GAEPersistentSessionManager, PersistentSessionManager)
+
+In this example, we explicitly register classes one by one:
+
+* the `SessionCleaner` class
+* the `GAEAuthenticationManager` to replace the `AuthenticationManager`
+* the `GAEPersistentSessionManager` to replace the `PersistentSessionManager`
+
+If at some point we register a new appobject class in this module, it won't be
+registered at all without modification to the `registration_callback`
+implementation. The previous example will register it though, thanks to the call
+to the `register_all` method.
+
+
+
+Runtime objects selection
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Now that we have all application objects loaded, the question is : when
+I want some specific object, for instance the primary view for a given
+entity, how do I get the proper object ? This is what we call the
+**selection mechanism**.
+
+As explained in the :ref:`Concepts` section:
+
+* each application object has a **selector**, defined by its
+  `__select__` class attribute
+
+* this selector is responsible to return a **score** for a given context
+
+  - 0 score means the object doesn't apply to this context
+
+  - else, the higher the score, the better the object suits the context
+
+* the object with the highest score is selected.
+
+.. Note::
+
+  When no single object has the highest score, an exception is raised in development
+  mode to let you know that the engine was not able to identify the view to
+  apply. This error is silenced in production mode and one of the objects with
+  the highest score is picked.
+
+  In such cases you would need to review your design and make sure
+  your selectors or appobjects are properly defined. Such an error is
+  typically caused by either forgetting to change the __regid__ in a
+  derived class, or by having copy-pasted some code.
+
+For instance, if you are selecting the primary (`__regid__ =
+'primary'`) view (`__registry__ = 'views'`) for a result set
+containing a `Card` entity, two objects will probably be selectable:
+
+* the default primary view (`__select__ = is_instance('Any')`), meaning
+  that the object is selectable for any kind of entity type
+
+* the specific `Card` primary view (`__select__ = is_instance('Card')`,
+  meaning that the object is selectable for Card entities
+
+Other primary views specific to other entity types won't be selectable in this
+case. Among selectable objects, the `is_instance('Card')` selector will return a higher
+score since it's more specific, so the correct view will be selected as expected.
+
+
+API for objects selections
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Here is the selection API you'll get on every registry. Some of them, as the
+'etypes' registry, containing entity classes, extend it. In those methods,
+`*args, **kwargs` is what we call the **context**. Those arguments are given to
+selectors that will inspect their content and return a score accordingly.
+
+:py:meth:`select() <logilab.common.registry.Registry.select>`
+
+:py:meth:`select_or_none() <logilab.common.registry.Registry.select_or_none>`
+
+:py:meth:`possible_objects() <logilab.common.registry.Registry.possible_objects>`
+
+:py:meth:`object_by_id() <logilab.common.registry.Registry.object_by_id>`
+
+
+The `AppObject` class
+---------------------
+
+The :py:class:`cubicweb.appobject.AppObject` class is the base class
+for all dynamically loaded objects (application objects) accessible
+through the :py:class:`cubicweb.cwvreg.CWRegistryStore`.
+
+
+Predicates and selectors
+------------------------
+
+Predicates are scoring functions that are called by the registry to tell whenever
+an appobject can be selected in a given context. Predicates may be chained
+together using operators to build a selector. A selector is the glue that tie
+views to the data model or whatever input context. Using them appropriately is an
+essential part of the construction of well behaved cubes.
+
+Of course you may have to write your own set of predicates as your needs grows
+and you get familiar with the framework (see :ref:`CustomPredicates`).
+
+A predicate is a class testing a particular aspect of a context. A selector is
+built by combining existant predicates or even selectors.
+
+Using and combining existant predicates
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can combine predicates using the `&`, `|` and `~` operators.
+
+When two predicates are combined using the `&` operator, it means that
+both should return a positive score. On success, the sum of scores is
+returned.
+
+When two predicates are combined using the `|` operator, it means that
+one of them should return a positive score. On success, the first
+positive score is returned.
+
+You can also "negate" a predicate by precedeing it by the `~` unary operator.
+
+Of course you can use parenthesis to balance expressions.
+
+Example
+~~~~~~~
+
+The goal: when on a blog, one wants the RSS link to refer to blog entries, not to
+the blog entity itself.
+
+To do that, one defines a method on entity classes that returns the
+RSS stream url for a given entity. The default implementation on
+:class:`~cubicweb.entities.AnyEntity` (the generic entity class used
+as base for all others) and a specific implementation on `Blog` will
+do what we want.
+
+But when we have a result set containing several `Blog` entities (or
+different entities), we don't know on which entity to call the
+aforementioned method. In this case, we keep the generic behaviour.
+
+Hence we have two cases here, one for a single-entity rsets, the other for
+multi-entities rsets.
+
+In web/views/boxes.py lies the RSSIconBox class. Look at its selector:
+
+.. sourcecode:: python
+
+  class RSSIconBox(box.Box):
+    ''' just display the RSS icon on uniform result set '''
+    __select__ = box.Box.__select__ & non_final_entity()
+
+It takes into account:
+
+* the inherited selection criteria (one has to look them up in the class
+  hierarchy to know the details)
+
+* :class:`~cubicweb.predicates.non_final_entity`, which filters on result sets
+  containing non final entities (a 'final entity' being synonym for entity
+  attributes type, eg `String`, `Int`, etc)
+
+This matches our second case. Hence we have to provide a specific component for
+the first case:
+
+.. sourcecode:: python
+
+  class EntityRSSIconBox(RSSIconBox):
+    '''just display the RSS icon on uniform result set for a single entity'''
+    __select__ = RSSIconBox.__select__ & one_line_rset()
+
+Here, one adds the :class:`~cubicweb.predicates.one_line_rset` predicate, which
+filters result sets of size 1. Thus, on a result set containing multiple
+entities, :class:`one_line_rset` makes the EntityRSSIconBox class non
+selectable. However for a result set with one entity, the `EntityRSSIconBox`
+class will have a higher score than `RSSIconBox`, which is what we wanted.
+
+Of course, once this is done, you have to:
+
+* fill in the call method of `EntityRSSIconBox`
+
+* provide the default implementation of the method returning the RSS stream url
+  on :class:`~cubicweb.entities.AnyEntity`
+
+* redefine this method on `Blog`.
+
+
+When to use selectors?
+~~~~~~~~~~~~~~~~~~~~~~
+
+Selectors are to be used whenever arises the need of dispatching on the shape or
+content of a result set or whatever else context (value in request form params,
+authenticated user groups, etc...). That is, almost all the time.
+
+Here is a quick example:
+
+.. sourcecode:: python
+
+    class UserLink(component.Component):
+	'''if the user is the anonymous user, build a link to login else a link
+	to the connected user object with a logout link
+	'''
+	__regid__ = 'loggeduserlink'
+
+	def call(self):
+	    if self._cw.session.anonymous_session:
+		# display login link
+		...
+	    else:
+		# display a link to the connected user object with a loggout link
+		...
+
+The proper way to implement this with |cubicweb| is two have two different
+classes sharing the same identifier but with different selectors so you'll get
+the correct one according to the context.
+
+.. sourcecode:: python
+
+    class UserLink(component.Component):
+	'''display a link to the connected user object with a loggout link'''
+	__regid__ = 'loggeduserlink'
+	__select__ = component.Component.__select__ & authenticated_user()
+
+	def call(self):
+            # display useractions and siteactions
+	    ...
+
+    class AnonUserLink(component.Component):
+	'''build a link to login'''
+	__regid__ = 'loggeduserlink'
+	__select__ = component.Component.__select__ & anonymous_user()
+
+	def call(self):
+	    # display login link
+            ...
+
+The big advantage, aside readability once you're familiar with the
+system, is that your cube becomes much more easily customizable by
+improving componentization.
+
+
+.. _CustomPredicates:
+
+Defining your own predicates
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can use the :py:func:`objectify_predicate <logilab.common.registry.objectify_predicate>`
+decorator to easily write your own predicates as simple python
+functions.
+
+In other cases, you can take a look at the following abstract base classes:
+
+- :py:class:`ExpectedValuePredicate <cubicweb.predicates.ExpectedValuePredicate>`
+- :py:class:`EClassPredicate <cubicweb.predicates.EClassPredicate>`
+- :py:class:`EntityPredicate <cubicweb.predicates.EntityPredicate>`
+
+
+.. _DebuggingSelectors:
+
+Debugging selection
+~~~~~~~~~~~~~~~~~~~
+
+Once in a while, one needs to understand why a view (or any
+application object) is, or is not selected appropriately. Looking at
+which predicates fired (or did not) is the way. The
+:class:`traced_selection <logilab.common.registry.traced_selection>`
+context manager to help with that, *if you're running your instance in
+debug mode*.
+
+
+Base predicates
+---------------
+
+Here is a description of generic predicates provided by CubicWeb that should suit
+most of your needs.
+
+Bare predicates
+~~~~~~~~~~~~~~~
+
+Those predicates are somewhat dumb, which doesn't mean they're not (very) useful.
+
+- :py:class:`yes <cubicweb.appobject.yes>`
+- :py:class:`match_kwargs <cubicweb.predicates.match_kwargs>`
+- :py:class:`appobject_selectable <cubicweb.predicates.appobject_selectable>`
+- :py:class:`adaptable <cubicweb.predicates.adaptable>`
+- :py:class:`configuration_values <cubicweb.predicates.configuration_values>`
+
+
+Result set predicates
+~~~~~~~~~~~~~~~~~~~~~
+
+Those predicates are looking for a result set in the context ('rset' argument or
+the input context) and match or not according to its shape. Some of these
+predicates have different behaviour if a particular cell of the result set is
+specified using 'row' and 'col' arguments of the input context or not.
+
+- :py:class:`none_rset <cubicweb.predicates.none_rset>`
+- :py:class:`any_rset <cubicweb.predicates.any_rset>`
+- :py:class:`nonempty_rset <cubicweb.predicates.nonempty_rset>`
+- :py:class:`empty_rset <cubicweb.predicates.empty_rset>`
+- :py:class:`one_line_rset <cubicweb.predicates.one_line_rset>`
+- :py:class:`multi_lines_rset <cubicweb.predicates.multi_lines_rset>`
+- :py:class:`multi_columns_rset <cubicweb.predicates.multi_columns_rset>`
+- :py:class:`paginated_rset <cubicweb.predicates.paginated_rset>`
+- :py:class:`sorted_rset <cubicweb.predicates.sorted_rset>`
+- :py:class:`one_etype_rset <cubicweb.predicates.one_etype_rset>`
+- :py:class:`multi_etypes_rset <cubicweb.predicates.multi_etypes_rset>`
+
+
+Entity predicates
+~~~~~~~~~~~~~~~~~
+
+Those predicates are looking for either an `entity` argument in the input context,
+or entity found in the result set ('rset' argument or the input context) and
+match or not according to entity's (instance or class) properties.
+
+- :py:class:`non_final_entity <cubicweb.predicates.non_final_entity>`
+- :py:class:`is_instance <cubicweb.predicates.is_instance>`
+- :py:class:`score_entity <cubicweb.predicates.score_entity>`
+- :py:class:`rql_condition <cubicweb.predicates.rql_condition>`
+- :py:class:`relation_possible <cubicweb.predicates.relation_possible>`
+- :py:class:`partial_relation_possible <cubicweb.predicates.partial_relation_possible>`
+- :py:class:`has_related_entities <cubicweb.predicates.has_related_entities>`
+- :py:class:`partial_has_related_entities <cubicweb.predicates.partial_has_related_entities>`
+- :py:class:`has_permission <cubicweb.predicates.has_permission>`
+- :py:class:`has_add_permission <cubicweb.predicates.has_add_permission>`
+- :py:class:`has_mimetype <cubicweb.predicates.has_mimetype>`
+- :py:class:`is_in_state <cubicweb.predicates.is_in_state>`
+- :py:func:`on_fire_transition <cubicweb.predicates.on_fire_transition>`
+
+
+Logged user predicates
+~~~~~~~~~~~~~~~~~~~~~~
+
+Those predicates are looking for properties of the user issuing the request.
+
+- :py:class:`match_user_groups <cubicweb.predicates.match_user_groups>`
+
+
+Web request predicates
+~~~~~~~~~~~~~~~~~~~~~~
+
+Those predicates are looking for properties of *web* request, they can not be
+used on the data repository side.
+
+- :py:class:`no_cnx <cubicweb.predicates.no_cnx>`
+- :py:class:`anonymous_user <cubicweb.predicates.anonymous_user>`
+- :py:class:`authenticated_user <cubicweb.predicates.authenticated_user>`
+- :py:class:`match_form_params <cubicweb.predicates.match_form_params>`
+- :py:class:`match_search_state <cubicweb.predicates.match_search_state>`
+- :py:class:`match_context_prop <cubicweb.predicates.match_context_prop>`
+- :py:class:`match_context <cubicweb.predicates.match_context>`
+- :py:class:`match_view <cubicweb.predicates.match_view>`
+- :py:class:`primary_view <cubicweb.predicates.primary_view>`
+- :py:class:`contextual <cubicweb.predicates.contextual>`
+- :py:class:`specified_etype_implements <cubicweb.predicates.specified_etype_implements>`
+- :py:class:`attribute_edited <cubicweb.predicates.attribute_edited>`
+- :py:class:`match_transition <cubicweb.predicates.match_transition>`
+
+
+Other predicates
+~~~~~~~~~~~~~~~~
+
+- :py:class:`match_exception <cubicweb.predicates.match_exception>`
+- :py:class:`debug_mode <cubicweb.predicates.debug_mode>`
+
+You'll also find some other (very) specific predicates hidden in other modules
+than :mod:`cubicweb.predicates`.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/ajax.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,12 @@
+.. _ajax:
+
+Ajax
+----
+
+CubicWeb provides a few helpers to facilitate *javascript <-> python* communications.
+
+You can, for instance, register some python functions that will become
+callable from javascript through ajax calls. All the ajax URLs are handled
+by the :class:`cubicweb.web.views.ajaxcontroller.AjaxController` controller.
+
+.. automodule:: cubicweb.web.views.ajaxcontroller
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/controllers.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,93 @@
+.. _controllers:
+
+Controllers
+-----------
+
+Overview
+++++++++
+
+Controllers are responsible for taking action upon user requests
+(loosely following the terminology of the MVC meta pattern).
+
+The following controllers are provided out-of-the box in CubicWeb. We
+list them by category. They are all defined in
+(:mod:`cubicweb.web.views.basecontrollers`).
+
+`Browsing`:
+
+* the View controller is associated with most browsing actions within a
+  CubicWeb application: it always instantiates a
+  :ref:`the_main_template_layout` and lets the ResultSet/Views dispatch system
+  build up the whole content; it handles :exc:`ObjectNotFound` and
+  :exc:`NoSelectableObject` errors that may bubble up to its entry point, in an
+  end-user-friendly way (but other programming errors will slip through)
+
+* the JSonpController is a wrapper around the ``ViewController`` that
+  provides jsonp_ services. Padding can be specified with the
+  ``callback`` request parameter. Only *jsonexport* / *ejsonexport*
+  views can be used. If another ``vid`` is specified, it will be
+  ignored and replaced by *jsonexport*. Request is anonymized
+  to avoid returning sensitive data and reduce the risks of CSRF attacks;
+
+* the Login/Logout controllers make effective user login or logout
+  requests
+
+
+.. _jsonp: http://en.wikipedia.org/wiki/JSONP
+
+`Edition`:
+
+* the Edit controller (see :ref:`edit_controller`) handles CRUD
+  operations in response to a form being submitted; it works in close
+  association with the Forms, to which it delegates some of the work
+
+* the ``Form validator controller`` provides form validation from Ajax
+  context, using the Edit controller, to implement the classic form
+  handling loop (user edits, hits `submit/apply`, validation occurs
+  server-side by way of the Form validator controller, and the UI is
+  decorated with failure information, either global or per-field ,
+  until it is valid)
+
+`Other`:
+
+* the ``SendMail controller`` (web/views/basecontrollers.py) is reponsible
+  for outgoing email notifications
+
+* the MailBugReport controller (web/views/basecontrollers.py) allows
+  to quickly have a `reportbug` feature in one's application
+
+* the :class:`cubicweb.web.views.ajaxcontroller.AjaxController`
+  (:mod:`cubicweb.web.views.ajaxcontroller`) provides
+  services for Ajax calls, typically using JSON as a serialization format
+  for input, and sometimes using either JSON or XML for output. See
+  :ref:`ajax` chapter for more information.
+
+
+Registration
+++++++++++++
+
+All controllers (should) live in the 'controllers' namespace within
+the global registry.
+
+Concrete controllers
+++++++++++++++++++++
+
+Most API details should be resolved by source code inspection, as the
+various controllers have differing goals. See for instance the
+:ref:`edit_controller` chapter.
+
+:mod:`cubicweb.web.controller` contains the top-level abstract
+Controller class and its unimplemented entry point
+`publish(rset=None)` method.
+
+A handful of helpers are also provided there:
+
+* process_rql builds a result set from an rql query typically issued
+  from the browser (and available through _cw.form['rql'])
+
+* validate_cache will force cache validation handling with respect to
+  the HTTP Cache directives (that were typically originally issued
+  from a previous server -> client response); concrete Controller
+  implementations dealing with HTTP (thus, for instance, not the
+  SendMail controller) may very well call this in their publication
+  process.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/css.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,30 @@
+.. -*- coding: utf-8 -*-
+
+CSS Stylesheet
+---------------
+Conventions
+~~~~~~~~~~~
+
+.. XXX external_resources variable
+..    naming convention
+..    request.add_css
+
+
+Extending / overriding existing styles
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+We cannot modify the order in which the application is reading the CSS. In
+the case we want to create new CSS style, the best is to define it a in a new
+CSS located under ``myapp/data/`` and use those new styles while writing
+customized views and templates.
+
+If you want to modify an existing CSS styling property, you will have to use
+``!important`` declaration to override the existing property. The application
+apply a higher priority on the default CSS and you can not change that.
+Customized CSS will not be read first.
+
+
+CubicWeb stylesheets
+~~~~~~~~~~~~~~~~~~~~
+
+.. XXX explain diffenrent files and main classes
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/edition/dissection.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,316 @@
+
+.. _form_dissection:
+
+Dissection of an entity form
+----------------------------
+
+This is done (again) with a vanilla instance of the `tracker`_
+cube. We will populate the database with a bunch of entities and see
+what kind of job the automatic entity form does.
+
+.. _`tracker`: http://www.cubicweb.org/project/cubicweb-tracker
+
+Populating the database
+~~~~~~~~~~~~~~~~~~~~~~~
+
+We should start by setting up a bit of context: a project with two
+unpublished versions, and a ticket linked to the project and the first
+version.
+
+.. sourcecode:: python
+
+ >>> p = rql('INSERT Project P: P name "cubicweb"')
+ >>> for num in ('0.1.0', '0.2.0'):
+ ...  rql('INSERT Version V: V num "%s", V version_of P WHERE P eid %%(p)s' % num, {'p': p[0][0]})
+ ...
+ <resultset 'INSERT Version V: V num "0.1.0", V version_of P WHERE P eid %(p)s' (1 rows): [765L] (('Version',))>
+ <resultset 'INSERT Version V: V num "0.2.0", V version_of P WHERE P eid %(p)s' (1 rows): [766L] (('Version',))>
+ >>> t = rql('INSERT Ticket T: T title "let us write more doc", T done_in V, '
+             'T concerns P WHERE V num "0.1.0"', P eid %(p)s', {'p': p[0][0]})
+ >>> commit()
+
+Now let's see what the edition form builds for us.
+
+.. sourcecode:: python
+
+ >>> cnx.use_web_compatible_requests('http://fakeurl.com')
+ >>> req = cnx.request()
+ >>> form = req.vreg['forms'].select('edition', req, rset=rql('Ticket T'))
+ >>> html = form.render()
+
+.. note::
+
+  In order to play interactively with web side application objects, we have to
+  cheat a bit to have request object that will looks like HTTP request object, by
+  calling :meth:`use_web_compatible_requests()` on the connection.
+
+This creates an automatic entity form. The ``.render()`` call yields
+an html (unicode) string. The html output is shown below (with
+internal fieldset omitted).
+
+Looking at the html output
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The form enveloppe
+''''''''''''''''''
+
+.. sourcecode:: html
+
+ <div class="iformTitle"><span>main informations</span></div>
+ <div class="formBody">
+  <form action="http://crater:9999/validateform" method="post" enctype="application/x-www-form-urlencoded"
+        id="entityForm" onsubmit="return freezeFormButtons(&#39;entityForm&#39;);"
+        class="entityForm" target="eformframe">
+    <div id="progress">validating...</div>
+    <fieldset>
+      <input name="__form_id" type="hidden" value="edition" />
+      <input name="__errorurl" type="hidden" value="http://perdu.com#entityForm" />
+      <input name="__domid" type="hidden" value="entityForm" />
+      <input name="__type:763" type="hidden" value="Ticket" />
+      <input name="eid" type="hidden" value="763" />
+      <input name="__maineid" type="hidden" value="763" />
+      <input name="_cw_edited_fields:763" type="hidden"
+             value="concerns-subject,done_in-subject,priority-subject,type-subject,title-subject,description-subject,__type,_cw_generic_field" />
+      ...
+    </fieldset>
+    <iframe width="0px" height="0px" name="eformframe" id="eformframe" src="javascript: void(0);"></iframe>
+   </form>
+ </div>
+
+The main fieldset encloses a set of hidden fields containing various
+metadata, that will be used by the `edit controller` to process it
+back correctly.
+
+The `freezeFormButtons(...)` javascript callback defined on the
+``onlick`` event of the form element prevents accidental multiple
+clicks in a row.
+
+The ``action`` of the form is mapped to the ``validateform`` controller
+(situated in :mod:`cubicweb.web.views.basecontrollers`).
+
+A full explanation of the validation loop is given in
+:ref:`validation_process`.
+
+.. _attributes_section:
+
+The attributes section
+''''''''''''''''''''''
+
+We can have a look at some of the inner nodes of the form. Some fields
+are omitted as they are redundant for our purposes.
+
+.. sourcecode:: html
+
+      <fieldset class="default">
+        <table class="attributeForm">
+          <tr class="title_subject_row">
+            <th class="labelCol"><label class="required" for="title-subject:763">title</label></th>
+            <td>
+              <input id="title-subject:763" maxlength="128" name="title-subject:763" size="45"
+                     tabindex="1" type="text" value="let us write more doc" />
+            </td>
+          </tr>
+          ... (description field omitted) ...
+          <tr class="priority_subject_row">
+            <th class="labelCol"><label class="required" for="priority-subject:763">priority</label></th>
+            <td>
+              <select id="priority-subject:763" name="priority-subject:763" size="1" tabindex="4">
+                <option value="important">important</option>
+                <option selected="selected" value="normal">normal</option>
+                <option value="minor">minor</option>
+              </select>
+              <div class="helper">importance</div>
+            </td>
+          </tr>
+          ... (type field omitted) ...
+          <tr class="concerns_subject_row">
+            <th class="labelCol"><label class="required" for="concerns-subject:763">concerns</label></th>
+            <td>
+              <select id="concerns-subject:763" name="concerns-subject:763" size="1" tabindex="6">
+                <option selected="selected" value="760">Foo</option>
+              </select>
+            </td>
+          </tr>
+          <tr class="done_in_subject_row">
+            <th class="labelCol"><label for="done_in-subject:763">done in</label></th>
+            <td>
+              <select id="done_in-subject:763" name="done_in-subject:763" size="1" tabindex="7">
+                <option value="__cubicweb_internal_field__"></option>
+                <option selected="selected" value="761">Foo 0.1.0</option>
+                <option value="762">Foo 0.2.0</option>
+              </select>
+              <div class="helper">version in which this ticket will be / has been  done</div>
+            </td>
+          </tr>
+        </table>
+      </fieldset>
+
+
+Note that the whole form layout has been computed by the form
+renderer. It is the renderer which produces the table
+structure. Otherwise, the fields html structure is emitted by their
+associated widget.
+
+While it is called the `attributes` section of the form, it actually
+contains attributes and *mandatory relations*. For each field, we
+observe:
+
+* a dedicated row with a specific class, such as ``title_subject_row``
+  (responsability of the form renderer)
+
+* an html widget (input, select, ...) with:
+
+  * an id built from the ``rtype-role:eid`` pattern
+
+  * a name built from the same pattern
+
+  * possible values or preselected options
+
+The relations section
+'''''''''''''''''''''
+
+.. sourcecode:: html
+
+      <fieldset class="This ticket :">
+        <legend>This ticket :</legend>
+        <table class="attributeForm">
+          <tr class="_cw_generic_field_None_row">
+            <td colspan="2">
+              <table id="relatedEntities">
+                <tr><th>&#160;</th><td>&#160;</td></tr>
+                <tr id="relationSelectorRow_763" class="separator">
+                  <th class="labelCol">
+                    <select id="relationSelector_763" tabindex="8"
+                            onchange="javascript:showMatchingSelect(this.options[this.selectedIndex].value,763);">
+                      <option value="">select a relation</option>
+                      <option value="appeared_in_subject">appeared in</option>
+                      <option value="custom_workflow_subject">custom workflow</option>
+                      <option value="depends_on_object">dependency of</option>
+                      <option value="depends_on_subject">depends on</option>
+                      <option value="identical_to_subject">identical to</option>
+                      <option value="see_also_subject">see also</option>
+                    </select>
+                  </th>
+                  <td id="unrelatedDivs_763"></td>
+                </tr>
+              </table>
+            </td>
+          </tr>
+        </table>
+      </fieldset>
+
+The optional relations are grouped into a drop-down combo
+box. Selection of an item triggers a javascript function which will:
+
+* show already related entities in the div of id `relatedentities`
+  using a two-colown layout, with an action to allow deletion of
+  individual relations (there are none in this example)
+
+* provide a relation selector in the div of id `relationSelector_EID`
+  to allow the user to set up relations and trigger dynamic action on
+  the last div
+
+* fill the div of id `unrelatedDivs_EID` with a dynamically computed
+  selection widget allowing direct selection of an unrelated (but
+  relatable) entity or a switch towards the `search mode` of
+  |cubicweb| which allows full browsing and selection of an entity
+  using a dedicated action situated in the left column boxes.
+
+
+The buttons zone
+''''''''''''''''
+
+Finally comes the buttons zone.
+
+.. sourcecode:: html
+
+      <table width="100%">
+        <tbody>
+          <tr>
+            <td align="center">
+              <button class="validateButton" tabindex="9" type="submit" value="validate">
+                <img alt="OK_ICON" src="http://myapp/datafd8b5d92771209ede1018a8d5da46a37/ok.png" />
+                validate
+              </button>
+            </td>
+            <td style="align: right; width: 50%;">
+              <button class="validateButton"
+                      onclick="postForm(&#39;__action_apply&#39;, &#39;button_apply&#39;, &#39;entityForm&#39;)"
+                      tabindex="10" type="button" value="apply">
+                <img alt="APPLY_ICON" src="http://myapp/datafd8b5d92771209ede1018a8d5da46a37/plus.png" />
+                apply
+              </button>
+              <button class="validateButton"
+                      onclick="postForm(&#39;__action_cancel&#39;, &#39;button_cancel&#39;, &#39;entityForm&#39;)"
+                      tabindex="11" type="button" value="cancel">
+                <img alt="CANCEL_ICON" src="http://myapp/datafd8b5d92771209ede1018a8d5da46a37/cancel.png" />
+                cancel
+              </button>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+
+The most notable artifacts here are the ``postForm(...)`` calls
+defined on click events on these buttons. This function basically
+submits the form.
+
+.. _validation_process:
+
+The form validation process
+---------------------------
+
+Validation loop
+~~~~~~~~~~~~~~~
+
+On form submission, the form.action is invoked. Basically, the
+``validateform`` controller is called and its output lands in the
+specified ``target``, an invisible ``<iframe>`` at the end of the
+form.
+
+Hence, the main page is not replaced, only the iframe contents. The
+``validateform`` controller only outputs a tiny javascript fragment
+which is then immediately executed.
+
+.. sourcecode:: html
+
+ <iframe width="0px" height="0px" name="eformframe" id="eformframe" src="javascript: void(0);">
+   <script type="text/javascript">
+     window.parent.handleFormValidationResponse('entityForm', null, null,
+                                                [false, [2164, {"name-subject": "required field"}], null],
+                                                null);
+   </script>
+ </iframe>
+
+The ``window.parent`` part ensures the javascript function is called
+on the right context (that is: the form element). We will describe its
+parameters:
+
+* first comes the form id (`entityForm`)
+
+* then two optional callbacks for the success and failure case
+
+* an array containing:
+
+  * a boolean which indicates status (success or failure), and then, on error:
+
+    * an array structured as ``[eid, {'rtype-role': 'error msg'}, ...]``
+
+  * on success:
+
+    * a url (string) representing the next thing to jump to
+
+Given the array structure described above, it is quite simple to
+manipulate the DOM to show the errors at appropriate places.
+
+Explanation
+~~~~~~~~~~~
+
+This mecanism may seem a bit overcomplicated but we have to deal with
+two realities:
+
+* in the (strict) XHTML world, there are no iframes (hence the dynamic
+  inclusion, tolerated by Firefox)
+
+* no (or not all) browser(s) support file input field handling through
+  ajax.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/edition/editcontroller.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,113 @@
+.. _edit_controller:
+
+The `edit controller`
+---------------------
+
+It can be found in (:mod:`cubicweb.web.views.editcontroller`). This
+controller processes data received from an html form to create or
+update entities.
+
+Edition handling
+~~~~~~~~~~~~~~~~
+
+The parameters related to entities to edit are specified as follows
+(first seen in :ref:`attributes_section`)::
+
+  <rtype-role>:<entity eid>
+
+where entity eid could be a letter in case of an entity to create. We
+name those parameters as *qualified*.
+
+* Retrieval of entities to edit is done by using the forms parameters
+  `eid` and `__type`
+
+* For all the attributes and the relations of an entity to edit
+  (attributes and relations are handled a bit differently but these
+  details are not much relevant here) :
+
+   * using the ``rtype``, ``role`` and ``__type`` information, fetch
+     an appropriate field instance
+
+   * check if the field has been modified (if not, proceed to the next
+     relation)
+
+   * build an rql expression to update the entity
+
+At the end, all rql expressions are executed.
+
+* For each entity to edit:
+
+   * if a qualified parameter `__linkto` is specified, its value has
+     to be a string (or a list of strings) such as: ::
+
+        <relation type>:<eids>:<target>
+
+     where <target> is either `subject` or `object` and each eid could
+     be separated from the others by a `_`. Target specifies if the
+     *edited entity* is subject or object of the relation and each
+     relation specified will be inserted.
+
+    * if a qualified parameter `__clone_eid` is specified for an entity, the
+      relations of the specified entity passed as value of this parameter are
+      copied on the edited entity.
+
+    * if a qualified parameter `__delete` is specified, its value must be
+      a string or a list of string such as follows: ::
+
+          <subjects eids>:<relation type>:<objects eids>
+
+      where each eid subject or object can be seperated from the other
+      by `_`. Each specified relation will be deleted.
+
+
+* If no entity is edited but the form contains the parameters `__linkto`
+  and `eid`, this one is interpreted by using the value specified for `eid`
+  to designate the entity on which to add the relations.
+
+.. note::
+
+   * if the parameter `__action_delete` is found, all the entities specified
+     as to be edited will be deleted.
+
+   * if the parameter `__action_cancel` is found, no action is completed.
+
+   * if the parameter `__action_apply` is found, the editing is
+     applied normally but the redirection is done on the form (see
+     :ref:`RedirectionControl`).
+
+   * if no entity is found to be edited and if there is no parameter
+     `__action_delete`, `__action_cancel`, `__linkto`, `__delete` or
+     `__insert`, an error is raised.
+
+   * using the parameter `__message` in the form will allow to use its value
+     as a message to provide the user once the editing is completed.
+
+
+.. _RedirectionControl:
+
+Redirection control
+~~~~~~~~~~~~~~~~~~~
+Once editing is completed, there is still an issue left: where should we go
+now? If nothing is specified, the controller will do his job but it does not
+mean we will be happy with the result. We can control that by using the
+following parameters:
+
+* `__redirectpath`: path of the URL (relative to the root URL of the site,
+  no form parameters
+
+* `__redirectparams`: forms parameters to add to the path
+
+* `__redirectrql`: redirection RQL request
+
+* `__redirectvid`: redirection view identifier
+
+* `__errorurl`: initial form URL, used for redirecting in case a validation
+  error is raised during editing. If this one is not specified, an error page
+  is displayed instead of going back to the form (which is, if necessary,
+  responsible for displaying the errors)
+
+* `__form_id`: initial view form identifier, used if `__action_apply` is
+  found
+
+In general we use either `__redirectpath` and `__redirectparams` or
+`__redirectrql` and `__redirectvid`.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/edition/examples.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,232 @@
+Examples
+--------
+
+(Automatic) Entity form
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Looking at some cubes available on the `cubicweb forge`_ we find some
+with form manipulation. The following example comes from the the
+`conference`_ cube. It extends the change state form for the case
+where a ``Talk`` entity is getting into ``submitted`` state. The goal
+is to select reviewers for the submitted talk.
+
+.. _`cubicweb forge`: http://www.cubicweb.org/view?rql=Any+P+ORDERBY+N+WHERE+P+name+LIKE+%22cubicweb-%25%22%2C+P+is+Project%2C+P+name+N
+.. _`conference`: http://www.cubicweb.org/project/cubicweb-conference
+
+.. sourcecode:: python
+
+ from cubicweb.web import formfields as ff, formwidgets as fwdgs
+ class SendToReviewerStatusChangeView(ChangeStateFormView):
+     __select__ = (ChangeStateFormView.__select__ &
+                   is_instance('Talk') &
+                   rql_condition('X in_state S, S name "submitted"'))
+
+     def get_form(self, entity, transition, **kwargs):
+         form = super(SendToReviewerStatusChangeView, self).get_form(entity, transition, **kwargs)
+         relation = ff.RelationField(name='reviews', role='object',
+                                     eidparam=True,
+                                     label=_('select reviewers'),
+                                     widget=fwdgs.Select(multiple=True))
+         form.append_field(relation)
+         return form
+
+Simple extension of a form can be done from within the `FormView`
+wrapping the form. FormView instances have a handy ``get_form`` method
+that returns the form to be rendered. Here we add a ``RelationField``
+to the base state change form.
+
+One notable point is the ``eidparam`` argument: it tells both the
+field and the ``edit controller`` that the field is linked to a
+specific entity.
+
+It is hence entirely possible to add ad-hoc fields that will be
+processed by some specialized instance of the edit controller.
+
+
+Ad-hoc fields form
+~~~~~~~~~~~~~~~~~~
+
+We want to define a form doing something else than editing an entity. The idea is
+to propose a form to send an email to entities in a resultset which implements
+:class:`IEmailable`.  Let's take a simplified version of what you'll find in
+:mod:`cubicweb.web.views.massmailing`.
+
+Here is the source code:
+
+.. sourcecode:: python
+
+    def sender_value(form, field):
+	return '%s <%s>' % (form._cw.user.dc_title(), form._cw.user.get_email())
+
+    def recipient_choices(form, field):
+	return [(e.get_email(), e.eid)
+                 for e in form.cw_rset.entities()
+		 if e.get_email()]
+
+    def recipient_value(form, field):
+	return [e.eid for e in form.cw_rset.entities()
+                if e.get_email()]
+
+    class MassMailingForm(forms.FieldsForm):
+	__regid__ = 'massmailing'
+
+	needs_js = ('cubicweb.widgets.js',)
+	domid = 'sendmail'
+	action = 'sendmail'
+
+	sender = ff.StringField(widget=TextInput({'disabled': 'disabled'}),
+				label=_('From:'),
+				value=sender_value)
+
+	recipient = ff.StringField(widget=CheckBox(),
+	                           label=_('Recipients:'),
+				   choices=recipient_choices,
+				   value=recipients_value)
+
+	subject = ff.StringField(label=_('Subject:'), max_length=256)
+
+	mailbody = ff.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')]
+
+Let's detail what's going on up there. Our form will hold four fields:
+
+* a sender field, which is disabled and will simply contains the user's name and
+  email
+
+* a recipients field, which will be displayed as a list of users in the context
+  result set with checkboxes so user can still choose who will receive his mailing
+  by checking or not the checkboxes. By default all of them will be checked since
+  field's value return a list containing same eids as those returned by the
+  vocabulary function.
+
+* a subject field, limited to 256 characters (hence we know a
+  :class:`~cubicweb.web.formwidgets.TextInput` will be used, as explained in
+  :class:`~cubicweb.web.formfields.StringField`)
+
+* a mailbody field. This field use an ajax widget, defined in `cubicweb.widgets.js`,
+  and whose definition won't be shown here. Notice though that we tell this form
+  need this javascript file by using `needs_js`
+
+Last but not least, we add two buttons control: one to post the form using
+javascript (`$('#sendmail')` being the jQuery call to get the element with DOM id
+set to 'sendmail', which is our form DOM id as specified by its `domid`
+attribute), another to cancel the form which will go back to the previous page
+using another javascript call. Also we specify an image to use as button icon as a
+resource identifier (see :ref:`uiprops`) given as last argument to
+:class:`cubicweb.web.formwidgets.ImgButton`.
+
+To see this form, we still have to wrap it in a view. This is pretty simple:
+
+.. sourcecode:: python
+
+    class MassMailingFormView(form.FormViewMixIn, EntityView):
+	__regid__ = 'massmailing'
+	__select__ = is_instance(IEmailable) & authenticated_user()
+
+	def call(self):
+	    form = self._cw.vreg['forms'].select('massmailing', self._cw,
+	                                         rset=self.cw_rset)
+	    form.render(w=self.w)
+
+As you see, we simply define a view with proper selector so it only apply to a
+result set containing :class:`IEmailable` entities, and so that only users in the
+managers or users group can use it. Then in the `call()` method for this view we
+simply select the above form and call its `.render()` method with our output
+stream as argument.
+
+When this form is submitted, a controller with id 'sendmail' will be called (as
+specified using `action`). This controller will be responsible to actually send
+the mail to specified recipients.
+
+Here is what it looks like:
+
+.. sourcecode:: python
+
+   class SendMailController(Controller):
+       __regid__ = 'sendmail'
+       __select__ = (authenticated_user() &
+                     match_form_params('recipient', 'mailbody', 'subject'))
+
+       def publish(self, rset=None):
+           body = self._cw.form['mailbody']
+           subject = self._cw.form['subject']
+           eids = self._cw.form['recipient']
+           # eids may be a string if only one recipient was specified
+           if isinstance(eids, basestring):
+               rset = self._cw.execute('Any X WHERE X eid %(x)s', {'x': eids})
+           else:
+               rset = self._cw.execute('Any X WHERE X eid in (%s)' % (','.join(eids)))
+           recipients = list(rset.entities())
+           msg = format_mail({'email' : self._cw.user.get_email(),
+                              'name' : self._cw.user.dc_title()},
+                             recipients, body, subject)
+           if not self._cw.vreg.config.sendmails([(msg, recipients)]):
+               msg = self._cw._('could not connect to the SMTP server')
+           else:
+               msg = self._cw._('emails successfully sent')
+           raise Redirect(self._cw.build_url(__message=msg))
+
+
+The entry point of a controller is the publish method. In that case we simply get
+back post values in request's `form` attribute, get user instances according
+to eids found in the 'recipient' form value, and send email after calling
+:func:`format_mail` to get a proper email message. If we can't send email or
+if we successfully sent email, we redirect to the index page with proper message
+to inform the user.
+
+Also notice that our controller has a selector that deny access to it
+to anonymous users (we don't want our instance to be used as a spam
+relay), but also checks if the expected parameters are specified in
+forms. That avoids later defensive programming (though it's not enough
+to handle all possible error cases).
+
+To conclude our example, suppose we wish a different form layout and that existent
+renderers are not satisfying (we would check that first of course :). We would then
+have to define our own renderer:
+
+.. sourcecode:: python
+
+    class MassMailingFormRenderer(formrenderers.FormRenderer):
+        __regid__ = 'massmailing'
+
+        def _render_fields(self, fields, w, form):
+            w(u'<table class="headersform">')
+            for field in fields:
+                if field.name == 'mailbody':
+                    w(u'</table>')
+                    w(u'<div id="toolbar">')
+                    w(u'<ul>')
+                    for button in form.form_buttons:
+                        w(u'<li>%s</li>' % button.render(form))
+                    w(u'</ul>')
+                    w(u'</div>')
+                    w(u'<div>')
+                    w(field.render(form, self))
+                    w(u'</div>')
+                else:
+                    w(u'<tr>')
+                    w(u'<td class="hlabel">%s</td>' %
+                      self.render_label(form, field))
+                    w(u'<td class="hvalue">')
+                    w(field.render(form, self))
+                    w(u'</td></tr>')
+
+        def render_buttons(self, w, form):
+            pass
+
+We simply override the `_render_fields` and `render_buttons` method of the base form renderer
+to arrange fields as we desire it: here we'll have first a two columns table with label and
+value of the sender, recipients and subject field (form order respected), then form controls,
+then a div containing the textarea for the email's content.
+
+To bind this renderer to our form, we should add to our form definition above:
+
+.. sourcecode:: python
+
+    form_renderer_id = 'massmailing'
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/edition/form.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,377 @@
+.. _webform:
+
+HTML form construction
+----------------------
+
+CubicWeb provides the somewhat usual form / field / widget / renderer abstraction
+to provide generic building blocks which will greatly help you in building forms
+properly integrated with CubicWeb (coherent display, error handling, etc...),
+while keeping things as flexible as possible.
+
+A ``form`` basically only holds a set of ``fields``, and has te be bound to a
+``renderer`` which is responsible to layout them. Each field is bound to a
+``widget`` that will be used to fill in value(s) for that field (at form
+generation time) and 'decode' (fetch and give a proper Python type to) values
+sent back by the browser.
+
+The ``field`` should be used according to the type of what you want to edit.
+E.g. if you want to edit some date, you'll have to use the
+:class:`cubicweb.web.formfields.DateField`. Then you can choose among multiple
+widgets to edit it, for instance :class:`cubicweb.web.formwidgets.TextInput` (a
+bare text field), :class:`~cubicweb.web.formwidgets.DateTimePicker` (a simple
+calendar) or even :class:`~cubicweb.web.formwidgets.JQueryDatePicker` (the JQuery
+calendar).  You can of course also write your own widget.
+
+Exploring the available forms
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A small excursion into a |cubicweb| shell is the quickest way to
+discover available forms (or application objects in general).
+
+.. sourcecode:: python
+
+ >>> from pprint import pprint
+ >>> pprint( session.vreg['forms'] )
+ {'base': [<class 'cubicweb.web.views.forms.FieldsForm'>,
+           <class 'cubicweb.web.views.forms.EntityFieldsForm'>],
+  'changestate': [<class 'cubicweb.web.views.workflow.ChangeStateForm'>,
+                  <class 'cubes.tracker.views.forms.VersionChangeStateForm'>],
+  'composite': [<class 'cubicweb.web.views.forms.CompositeForm'>,
+                <class 'cubicweb.web.views.forms.CompositeEntityForm'>],
+  'deleteconf': [<class 'cubicweb.web.views.editforms.DeleteConfForm'>],
+  'edition': [<class 'cubicweb.web.views.autoform.AutomaticEntityForm'>,
+              <class 'cubicweb.web.views.workflow.TransitionEditionForm'>,
+              <class 'cubicweb.web.views.workflow.StateEditionForm'>],
+  'logform': [<class 'cubicweb.web.views.basetemplates.LogForm'>],
+  'massmailing': [<class 'cubicweb.web.views.massmailing.MassMailingForm'>],
+  'muledit': [<class 'cubicweb.web.views.editforms.TableEditForm'>],
+  'sparql': [<class 'cubicweb.web.views.sparql.SparqlForm'>]}
+
+
+The two most important form families here (for all practical purposes) are `base`
+and `edition`. Most of the time one wants alterations of the
+:class:`AutomaticEntityForm` to generate custom forms to handle edition of an
+entity.
+
+The Automatic Entity Form
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. automodule:: cubicweb.web.views.autoform
+
+Anatomy of a choices function
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Let's have a look at the `ticket_done_in_choices` function given to
+the `choices` parameter of the relation tag that is applied to the
+('Ticket', 'done_in', '*') relation definition, as it is both typical
+and sophisticated enough. This is a code snippet from the `tracker`_
+cube.
+
+.. _`tracker`: http://www.cubicweb.org/project/cubicweb-tracker
+
+The ``Ticket`` entity type can be related to a ``Project`` and a
+``Version``, respectively through the ``concerns`` and ``done_in``
+relations. When a user is about to edit a ticket, we want to fill the
+combo box for the ``done_in`` relation with values pertinent with
+respect to the context. The important context here is:
+
+* creation or modification (we cannot fetch values the same way in
+  either case)
+
+* ``__linkto`` url parameter given in a creation context
+
+.. sourcecode:: python
+
+    from cubicweb.web import formfields
+
+    def ticket_done_in_choices(form, field):
+        entity = form.edited_entity
+        # first see if its specified by __linkto form parameters
+        linkedto = form.linked_to[('done_in', 'subject')]
+        if linkedto:
+            return linkedto
+        # it isn't, get initial values
+        vocab = field.relvoc_init(form)
+        veid = None
+        # try to fetch the (already or pending) related version and project
+        if not entity.has_eid():
+            peids = form.linked_to[('concerns', 'subject')]
+            peid = peids and peids[0]
+        else:
+            peid = entity.project.eid
+            veid = entity.done_in and entity.done_in[0].eid
+        if peid:
+            # we can complete the vocabulary with relevant values
+            rschema = form._cw.vreg.schema['done_in'].rdef('Ticket', 'Version')
+            rset = form._cw.execute(
+                'Any V, VN ORDERBY version_sort_value(VN) '
+                'WHERE V version_of P, P eid %(p)s, V num VN, '
+                'V in_state ST, NOT ST name "published"', {'p': peid}, 'p')
+            vocab += [(v.view('combobox'), v.eid) for v in rset.entities()
+                      if rschema.has_perm(form._cw, 'add', toeid=v.eid)
+                      and v.eid != veid]
+        return vocab
+
+The first thing we have to do is fetch potential values from the ``__linkto`` url
+parameter that is often found in entity creation contexts (the creation action
+provides such a parameter with a predetermined value; for instance in this case,
+ticket creation could occur in the context of a `Version` entity). The
+:class:`~cubicweb.web.formfields.RelationField` field class provides a
+:meth:`~cubicweb.web.formfields.RelationField.relvoc_linkedto` method that gets a
+list suitably filled with vocabulary values.
+
+.. sourcecode:: python
+
+        linkedto = field.relvoc_linkedto(form)
+        if linkedto:
+            return linkedto
+
+Then, if no ``__linkto`` argument was given, we must prepare the vocabulary with
+an initial empty value (because `done_in` is not mandatory, we must allow the
+user to not select a verson) and already linked values. This is done with the
+:meth:`~cubicweb.web.formfields.RelationField.relvoc_init` method.
+
+.. sourcecode:: python
+
+        vocab = field.relvoc_init(form)
+
+But then, we have to give more: if the ticket is related to a project,
+we should provide all the non published versions of this project
+(`Version` and `Project` can be related through the `version_of`
+relation). Conversely, if we do not know yet the project, it would not
+make sense to propose all existing versions as it could potentially
+lead to incoherences. Even if these will be caught by some
+RQLConstraint, it is wise not to tempt the user with error-inducing
+candidate values.
+
+The "ticket is related to a project" part must be decomposed as:
+
+* this is a new ticket which is created is the context of a project
+
+* this is an already existing ticket, linked to a project (through the
+  `concerns` relation)
+
+* there is no related project (quite unlikely given the cardinality of
+  the `concerns` relation, so it can only mean that we are creating a
+  new ticket, and a project is about to be selected but there is no
+  ``__linkto`` argument)
+
+.. note::
+
+   the last situation could happen in several ways, but of course in a
+   polished application, the paths to ticket creation should be
+   controlled so as to avoid a suboptimal end-user experience
+
+Hence, we try to fetch the related project.
+
+.. sourcecode:: python
+
+        veid = None
+        if not entity.has_eid():
+            peids = form.linked_to[('concerns', 'subject')]
+            peid = peids and peids[0]
+        else:
+            peid = entity.project.eid
+            veid = entity.done_in and entity.done_in[0].eid
+
+We distinguish between entity creation and entity modification using
+the ``Entity.has_eid()`` method, which returns `False` on creation. At
+creation time the only way to get a project is through the
+``__linkto`` parameter. Notice that we fetch the version in which the
+ticket is `done_in` if any, for later.
+
+.. note::
+
+  the implementation above assumes that if there is a ``__linkto``
+  parameter, it is only about a project. While it makes sense most of
+  the time, it is not an absolute. Depending on how an entity creation
+  action action url is built, several outcomes could be possible
+  there
+
+If the ticket is already linked to a project, fetching it is
+trivial. Then we add the relevant version to the initial vocabulary.
+
+.. sourcecode:: python
+
+        if peid:
+            rschema = form._cw.vreg.schema['done_in'].rdef('Ticket', 'Version')
+            rset = form._cw.execute(
+                'Any V, VN ORDERBY version_sort_value(VN) '
+                'WHERE V version_of P, P eid %(p)s, V num VN, '
+                'V in_state ST, NOT ST name "published"', {'p': peid})
+            vocab += [(v.view('combobox'), v.eid) for v in rset.entities()
+                      if rschema.has_perm(form._cw, 'add', toeid=v.eid)
+                      and v.eid != veid]
+
+.. warning::
+
+   we have to defend ourselves against lack of a project eid. Given
+   the cardinality of the `concerns` relation, there *must* be a
+   project, but this rule can only be enforced at validation time,
+   which will happen of course only after form subsmission
+
+Here, given a project eid, we complete the vocabulary with all
+unpublished versions defined in the project (sorted by number) for
+which the current user is allowed to establish the relation.
+
+
+Building self-posted form with custom fields/widgets
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sometimes you want a form that is not related to entity edition. For those,
+you'll have to handle form posting by yourself. Here is a complete example on how
+to achieve this (and more).
+
+Imagine you want a form that selects a month period. There are no proper
+field/widget to handle this in CubicWeb, so let's start by defining them:
+
+.. sourcecode:: python
+
+    # let's have the whole import list at the beginning, even those necessary for
+    # subsequent snippets
+    from logilab.common import date
+    from logilab.mtconverter import xml_escape
+    from cubicweb.view import View
+    from cubicweb.predicates import match_kwargs
+    from cubicweb.web import RequestError, ProcessFormError
+    from cubicweb.web import formfields as fields, formwidgets as wdgs
+    from cubicweb.web.views import forms, calendar
+
+    class MonthSelect(wdgs.Select):
+        """Custom widget to display month and year. Expect value to be given as a
+        date instance.
+        """
+
+        def format_value(self, form, field, value):
+            return u'%s/%s' % (value.year, value.month)
+
+        def process_field_data(self, form, field):
+            val = super(MonthSelect, self).process_field_data(form, field)
+            try:
+                year, month = val.split('/')
+                year = int(year)
+                month = int(month)
+                return date.date(year, month, 1)
+            except ValueError:
+                raise ProcessFormError(
+                    form._cw._('badly formated date string %s') % val)
+
+
+    class MonthPeriodField(fields.CompoundField):
+        """custom field composed of two subfields, 'begin_month' and 'end_month'.
+
+        It expects to be used on form that has 'mindate' and 'maxdate' in its
+        extra arguments, telling the range of month to display.
+        """
+
+        def __init__(self, *args, **kwargs):
+            kwargs.setdefault('widget', wdgs.IntervalWidget())
+            super(MonthPeriodField, self).__init__(
+                [fields.StringField(name='begin_month',
+                                    choices=self.get_range, sort=False,
+                                    value=self.get_mindate,
+                                    widget=MonthSelect()),
+                 fields.StringField(name='end_month',
+                                    choices=self.get_range, sort=False,
+                                    value=self.get_maxdate,
+                                    widget=MonthSelect())], *args, **kwargs)
+
+        @staticmethod
+        def get_range(form, field):
+            mindate = date.todate(form.cw_extra_kwargs['mindate'])
+            maxdate = date.todate(form.cw_extra_kwargs['maxdate'])
+            assert mindate <= maxdate
+            _ = form._cw._
+            months = []
+            while mindate <= maxdate:
+                label = '%s %s' % (_(calendar.MONTHNAMES[mindate.month - 1]),
+                                   mindate.year)
+                value = field.widget.format_value(form, field, mindate)
+                months.append( (label, value) )
+                mindate = date.next_month(mindate)
+            return months
+
+        @staticmethod
+        def get_mindate(form, field):
+            return form.cw_extra_kwargs['mindate']
+
+        @staticmethod
+        def get_maxdate(form, field):
+            return form.cw_extra_kwargs['maxdate']
+
+        def process_posted(self, form):
+            for field, value in super(MonthPeriodField, self).process_posted(form):
+                if field.name == 'end_month':
+                    value = date.last_day(value)
+                yield field, value
+
+
+Here we first define a widget that will be used to select the beginning and the
+end of the period, displaying months like '<month> YYYY' but using 'YYYY/mm' as
+actual value.
+
+We then define a field that will actually hold two fields, one for the beginning
+and another for the end of the period. Each subfield uses the widget we defined
+earlier, and the outer field itself uses the standard
+:class:`IntervalWidget`. The field adds some logic:
+
+* a vocabulary generation function `get_range`, used to populate each sub-field
+
+* two 'value' functions `get_mindate` and `get_maxdate`, used to tell to
+  subfields which value they should consider on form initialization
+
+* overriding of `process_posted`, called when the form is being posted, so that
+  the end of the period is properly set to the last day of the month.
+
+Now, we can define a very simple form:
+
+.. sourcecode:: python
+
+    class MonthPeriodSelectorForm(forms.FieldsForm):
+        __regid__ = 'myform'
+        __select__ = match_kwargs('mindate', 'maxdate')
+
+        form_buttons = [wdgs.SubmitButton()]
+        form_renderer_id = 'onerowtable'
+        period = MonthPeriodField()
+
+
+where we simply add our field, set a submit button and use a very simple renderer
+(try others!). Also we specify a selector that ensures form will have arguments
+necessary to our field.
+
+Now, we need a view that will wrap the form and handle post when it occurs,
+simply displaying posted values in the page:
+
+.. sourcecode:: python
+
+    class SelfPostingForm(View):
+        __regid__ = 'myformview'
+
+        def call(self):
+            mindate, maxdate = date.date(2010, 1, 1), date.date(2012, 1, 1)
+            form = self._cw.vreg['forms'].select(
+                'myform', self._cw, mindate=mindate, maxdate=maxdate, action='')
+            try:
+                posted = form.process_posted()
+                self.w(u'<p>posted values %s</p>' % xml_escape(repr(posted)))
+            except RequestError: # no specified period asked
+                pass
+            form.render(w=self.w, formvalues=self._cw.form)
+
+
+Notice usage of the :meth:`process_posted` method, that will return a dictionary
+of typed values (because they have been processed by the field). In our case, when
+the form is posted you should see a dictionary with 'begin_month' and 'end_month'
+as keys with the selected dates as value (as a python `date` object).
+
+
+APIs
+~~~~
+
+.. automodule:: cubicweb.web.formfields
+.. automodule:: cubicweb.web.formwidgets
+.. automodule:: cubicweb.web.views.forms
+.. automodule:: cubicweb.web.views.formrenderers
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/edition/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,15 @@
+Edition control
+===============
+
+This chapter covers the editing capabilities of |cubicweb|. It
+explains html Form construction, the Edit Controller and their
+interactions.
+
+
+.. toctree::
+   :maxdepth: 2
+
+   form
+   dissection
+   editcontroller
+   examples
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/facets.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,23 @@
+The facets system
+-----------------
+
+Facets allow to restrict searches according to some user friendly criterias.
+CubicWeb has a builtin `facet`_ system to define restrictions `filters`_ really
+as easily as possible.
+
+Here is an exemple of the facets rendering picked from our
+http://www.cubicweb.org web site:
+
+.. image:: ../../images/facet_overview.png
+
+Facets will appear on each page presenting more than one entity that may be
+filtered according to some known criteria.
+
+Base classes for facets
+~~~~~~~~~~~~~~~~~~~~~~~
+.. automodule:: cubicweb.web.facet
+
+
+.. _facet: http://en.wikipedia.org/wiki/Faceted_browser
+.. _filters: http://www.cubicweb.org/blogentry/154152
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/httpcaching.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,21 @@
+HTTP cache management
+=====================
+
+.. automodule:: cubicweb.web.httpcache
+
+Cache policies
+--------------
+.. autoclass:: cubicweb.web.httpcache.NoHTTPCacheManager
+.. autoclass:: cubicweb.web.httpcache.MaxAgeHTTPCacheManager
+.. autoclass:: cubicweb.web.httpcache.EtagHTTPCacheManager
+.. autoclass:: cubicweb.web.httpcache.EntityHTTPCacheManager
+
+Exception
+---------
+.. autoexception:: cubicweb.web.httpcache.NoEtag
+
+Helper functions
+----------------
+.. autofunction:: cubicweb.web.httpcache.set_http_cache_headers
+
+.. NOT YET AVAILABLE IN STABLE autofunction:: cubicweb.web.httpcache.lastmodified
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,24 @@
+Web side development
+====================
+
+In this chapter, we will describe the core APIs for web development in
+the *CubicWeb* framework.
+
+.. toctree::
+   :maxdepth: 2
+
+   publisher
+   controllers
+   request
+   searchbar
+   views/index
+   rtags
+   ajax
+   js
+   css
+   edition/index
+   facets
+   internationalization
+   property
+   httpcaching
+   resource
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/internationalization.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,229 @@
+.. -*- coding: utf-8 -*-
+
+.. _internationalization:
+
+Internationalization
+---------------------
+
+Cubicweb fully supports the internalization of its content and interface.
+
+Cubicweb's interface internationalization is based on the translation project `GNU gettext`_.
+
+.. _`GNU gettext`: http://www.gnu.org/software/gettext/
+
+Cubicweb' internalization involves two steps:
+
+* in your Python code and cubicweb-tal templates : mark translatable strings
+
+* in your instance : handle the translation catalog, edit translations
+
+String internationalization
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+User defined string
+```````````````````
+
+In the Python code and cubicweb-tal templates translatable strings can be
+marked in one of the following ways :
+
+ * by using the *built-in* function `_`:
+
+   .. sourcecode:: python
+
+     class PrimaryView(EntityView):
+         """the full view of an non final entity"""
+         __regid__ = 'primary'
+         title = _('primary')
+
+  OR
+
+ * by using the equivalent request's method:
+
+   .. sourcecode:: python
+
+     class NoResultView(View):
+         """default view when no result has been found"""
+         __regid__ = 'noresult'
+
+         def call(self, **kwargs):
+             self.w(u'<div class="searchMessage"><strong>%s</strong></div>\n'
+                 % self._cw._('No result matching query'))
+
+The goal of the *built-in* function `_` is only **to mark the
+translatable strings**, it will only return the string to translate
+itself, but not its translation (it's actually another name for the
+`unicode` builtin).
+
+In the other hand the request's method `self._cw._` is also meant to
+retrieve the proper translation of translation strings in the
+requested language.
+
+Finally you can also use the `__` attribute of request object to get a
+translation for a string *which should not itself added to the catalog*,
+usually in case where the actual msgid is created by string interpolation ::
+
+  self._cw.__('This %s' % etype)
+
+In this example ._cw.__` is used instead of ._cw._` so we don't have 'This %s' in
+messages catalogs.
+
+Translations in cubicweb-tal template can also be done with TAL tags
+`i18n:content` and `i18n:replace`.
+
+If you need to add messages on top of those that can be found in the source,
+you can create a file named `i18n/static-messages.pot`.
+
+You could put there messages not found in the python sources or
+overrides for some messages of used cubes.
+
+Generated string
+````````````````
+
+We do not need to mark the translation strings of entities/relations used by a
+particular instance's schema as they are generated automatically. String for
+various actions are also generated.
+
+For exemple the following schema:
+
+.. sourcecode:: python
+
+
+  class EntityA(EntityType):
+      relation_a2b = SubjectRelation('EntityB')
+
+  class EntityB(EntityType):
+      pass
+
+May generate the following message ::
+
+  add EntityA relation_a2b EntityB subject
+
+This message will be used in views of ``EntityA`` for creation of a new
+``EntityB`` with a preset relation ``relation_a2b`` between the current
+``EntityA`` and the new ``EntityB``. The opposite message ::
+
+  add EntityA relation_a2b EntityB object
+
+Is used for similar creation of an ``EntityA`` from a view of ``EntityB``. The
+title of they respective creation form will be ::
+
+  creating EntityB (EntityA %(linkto)s relation_a2b EntityB)
+
+  creating EntityA (EntityA relation_a2b %(linkto)s EntityA)
+
+In the translated string you can use ``%(linkto)s`` for reference to the source
+``entity``.
+
+Handling the translation catalog
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Once the internationalization is done in your code, you need to populate and
+update the translation catalog. Cubicweb provides the following commands for this
+purpose:
+
+
+* `i18ncubicweb` updates Cubicweb framework's translation
+  catalogs. Unless you actually work on the framework itself, you
+  don't need to use this command.
+
+* `i18ncube` updates the translation catalogs of *one particular cube*
+  (or of all cubes). After this command is executed you must update
+  the translation files *.po* in the "i18n" directory of your
+  cube. This command will of course not remove existing translations
+  still in use. It will mark unused translation but not remove them.
+
+* `i18ninstance` recompiles the translation catalogs of *one particular
+  instance* (or of all instances) after the translation catalogs of
+  its cubes have been updated. This command is automatically
+  called every time you create or update your instance. The compiled
+  catalogs (*.mo*) are stored in the i18n/<lang>/LC_MESSAGES of
+  instance where `lang` is the language identifier ('en' or 'fr'
+  for exemple).
+
+
+Example
+```````
+
+You have added and/or modified some translation strings in your cube
+(after creating a new view or modifying the cube's schema for exemple).
+To update the translation catalogs you need to do:
+
+1. `cubicweb-ctl i18ncube <cube>`
+2. Edit the <cube>/i18n/xxx.po  files and add missing translations (empty `msgstr`)
+3. `hg ci -m "updated i18n catalogs"`
+4. `cubicweb-ctl i18ninstance <myinstance>`
+
+Editing po files
+~~~~~~~~~~~~~~~~
+
+Using a PO aware editor
+````````````````````````
+
+Many tools exist to help maintain .po (PO) files. Common editors or
+development environment provides modes for these. One can also find
+dedicated PO files editor, such as `poedit`_.
+
+.. _`poedit`:  http://www.poedit.net/
+
+While usage of such a tool is commendable, PO files are perfectly
+editable with a (unicode aware) plain text editor. It is also useful
+to know their structure for troubleshooting purposes.
+
+Structure of a PO file
+``````````````````````
+
+In this section, we selectively quote passages of the `GNU gettext`_
+manual chapter on PO files, available there::
+
+ http://www.gnu.org/software/hello/manual/gettext/PO-Files.html
+
+One PO file entry has the following schematic structure::
+
+     white-space
+     #  translator-comments
+     #. extracted-comments
+     #: reference...
+     #, flag...
+     #| msgid previous-untranslated-string
+     msgid untranslated-string
+     msgstr translated-string
+
+
+A simple entry can look like this::
+
+     #: lib/error.c:116
+     msgid "Unknown system error"
+     msgstr "Error desconegut del sistema"
+
+It is also possible to have entries with a context specifier. They
+look like this::
+
+     white-space
+     #  translator-comments
+     #. extracted-comments
+     #: reference...
+     #, flag...
+     #| msgctxt previous-context
+     #| msgid previous-untranslated-string
+     msgctxt context
+     msgid untranslated-string
+     msgstr translated-string
+
+
+The context serves to disambiguate messages with the same
+untranslated-string. It is possible to have several entries with the
+same untranslated-string in a PO file, provided that they each have a
+different context. Note that an empty context string and an absent
+msgctxt line do not mean the same thing.
+
+Contexts and CubicWeb
+`````````````````````
+
+CubicWeb PO files have both non-contextual and contextual msgids.
+
+Contextual entries are automatically used in some cases. For instance,
+entity.dc_type(), eschema.display_name(req) or display_name(etype,
+req, form, context) methods/function calls will use them.
+
+It is also possible to explicitly use the with _cw.pgettext(context,
+msgid).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/js.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,394 @@
+.. -*- coding: utf-8 -*-
+
+Javascript
+----------
+
+*CubicWeb* uses quite a bit of javascript in its user interface and
+ships with jquery (1.3.x) and parts of the jquery UI library, plus a
+number of homegrown files and also other third party libraries.
+
+All javascript files are stored in cubicweb/web/data/. There are
+around thirty js files there. In a cube it goes to data/.
+
+Obviously one does not want javascript pieces to be loaded all at
+once, hence the framework provides a number of mechanisms and
+conventions to deal with javascript resources.
+
+Conventions
+~~~~~~~~~~~
+
+It is good practice to name cube specific js files after the name of
+the cube, like this : 'cube.mycube.js', so as to avoid name clashes.
+
+.. XXX external_resources variable (which needs love)
+
+Server-side Javascript API
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Javascript resources are typically loaded on demand, from views. The
+request object (available as self._cw from most application objects,
+for instance views and entities objects) has a few methods to do that:
+
+* `add_js(self, jsfiles, localfile=True)` which takes a sequence of
+  javascript files and writes proper entries into the HTML header
+  section. The localfile parameter allows to declare resources which
+  are not from web/data (for instance, residing on a content delivery
+  network).
+
+* `add_onload(self, jscode)` which adds one raw javascript code
+  snippet inline in the html headers. This is quite useful for setting
+  up early jQuery(document).ready(...) initialisations.
+
+Javascript events
+~~~~~~~~~~~~~~~~~
+
+* ``server-response``: this event is triggered on HTTP responses (both
+  standard and ajax). The two following extra parameters are passed
+  to callbacks :
+
+  - ``ajax``: a boolean that says if the reponse was issued by an
+    ajax request
+
+  - ``node``: the DOM node returned by the server in case of an
+    ajax request, otherwise the document itself for standard HTTP
+    requests.
+
+Important javascript AJAX APIS
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* `asyncRemoteExec` and `remoteExec` are the base building blocks for
+  doing arbitrary async (resp. sync) communications with the server
+
+* `reloadComponent` is a convenience function to replace a DOM node
+  with server supplied content coming from a specific registry (this
+  is quite handy to refresh the content of some boxes for instances)
+
+* `jQuery.fn.loadxhtml` is an important extension to jQuery which
+  allows proper loading and in-place DOM update of xhtml views. It is
+  suitably augmented to trigger necessary events, and process CubicWeb
+  specific elements such as the facet system, fckeditor, etc.
+
+
+A simple example with asyncRemoteExec
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+On the python side, we have to define an
+:class:`cubicweb.web.views.ajaxcontroller.AjaxFunction` object. The
+simplest way to do that is to use the
+:func:`cubicweb.web.views.ajaxcontroller.ajaxfunc` decorator (for more
+details on this, refer to :ref:`ajax`).
+
+.. sourcecode: python
+
+    from cubicweb.web.views.ajaxcontroller import ajaxfunc
+
+    # serialize output to json to get it back easily on the javascript side
+    @ajaxfunc(output_type='json')
+    def js_say_hello(self, name):
+        return u'hello %s' % name
+
+On the javascript side, we do the asynchronous call. Notice how it
+creates a `deferred` object. Proper treatment of the return value or
+error handling has to be done through the addCallback and addErrback
+methods.
+
+.. sourcecode: javascript
+
+    function asyncHello(name) {
+        var deferred = asyncRemoteExec('say_hello', name);
+        deferred.addCallback(function (response) {
+            alert(response);
+        });
+        deferred.addErrback(function (error) {
+            alert('something fishy happened');
+        });
+     }
+
+     function syncHello(name) {
+         alert( remoteExec('say_hello', name) );
+     }
+
+Anatomy of a reloadComponent call
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+`reloadComponent` allows to dynamically replace some DOM node with new
+elements. It has the following signature:
+
+* `compid` (mandatory) is the name of the component to be reloaded
+
+* `rql` (optional) will be used to generate a result set given as
+  argument to the selected component
+
+* `registry` (optional) defaults to 'components' but can be any other
+  valid registry name
+
+* `nodeid` (optional) defaults to compid + 'Component' but can be any
+  explicitly specified DOM node id
+
+* `extraargs` (optional) should be a dictionary of values that will be
+  given to the cell_call method of the component
+
+A simple reloadComponent example
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The server side implementation of `reloadComponent` is the
+:func:`cubicweb.web.views.ajaxcontroller.component` *AjaxFunction* appobject.
+
+The following function implements a two-steps method to delete a
+standard bookmark and refresh the UI, while keeping the UI responsive.
+
+.. sourcecode:: javascript
+
+    function removeBookmark(beid) {
+        d = asyncRemoteExec('delete_bookmark', beid);
+        d.addCallback(function(boxcontent) {
+	    reloadComponent('bookmarks_box', '', 'boxes', 'bookmarks_box');
+            document.location.hash = '#header';
+            updateMessage(_("bookmark has been removed"));
+         });
+    }
+
+`reloadComponent` is called with the id of the bookmark box as
+argument, no rql expression (because the bookmarks display is actually
+independant of any dataset context), a reference to the 'boxes'
+registry (which hosts all left, right and contextual boxes) and
+finally an explicit 'bookmarks_box' nodeid argument that stipulates
+the target DOM node.
+
+Anatomy of a loadxhtml call
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+`jQuery.fn.loadxhtml` is an important extension to jQuery which allows
+proper loading and in-place DOM update of xhtml views. The existing
+`jQuery.load`_ function does not handle xhtml, hence the addition. The
+API of loadxhtml is roughly similar to that of `jQuery.load`_.
+
+.. _`jQuery.load`: http://api.jquery.com/load/
+
+
+* `url` (mandatory) should be a complete url (typically referencing
+  the :class:`cubicweb.web.views.ajaxcontroller.AjaxController`,
+  but this is not strictly mandatory)
+
+* `data` (optional) is a dictionary of values given to the
+  controller specified through an `url` argument; some keys may have a
+  special meaning depending on the choosen controller (such as `fname`
+  for the JSonController); the `callback` key, if present, must refer
+  to a function to be called at the end of loadxhtml (more on this
+  below)
+
+* `reqtype` (optional) specifies the request method to be used (get or
+  post); if the argument is 'post', then the post method is used,
+  otherwise the get method is used
+
+* `mode` (optional) is one of `replace` (the default) which means the
+  loaded node will replace the current node content, `swap` to replace
+  the current node with the loaded node, and `append` which will
+  append the loaded node to the current node content
+
+About the `callback` option:
+
+* it is called with two parameters: the current node, and a list
+  containing the loaded (and post-processed node)
+
+* whenever it returns another function, this function is called in
+  turn with the same parameters as above
+
+This mechanism allows callback chaining.
+
+
+A simple example with loadxhtml
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Here we are concerned with the retrieval of a specific view to be
+injected in the live DOM. The view will be of course selected
+server-side using an entity eid provided by the client side.
+
+.. sourcecode:: python
+
+    from cubicweb.web.views.ajaxcontroller import ajaxfunc
+
+    @ajaxfunc(output_type='xhtml')
+    def frob_status(self, eid, frobname):
+        entity = self._cw.entity_from_eid(eid)
+        return entity.view('frob', name=frobname)
+
+.. sourcecode:: javascript
+
+    function updateSomeDiv(divid, eid, frobname) {
+        var params = {fname:'frob_status', eid: eid, frobname:frobname};
+        jQuery('#'+divid).loadxhtml(JSON_BASE_URL, params, 'post');
+     }
+
+In this example, the url argument is the base json url of a cube
+instance (it should contain something like
+`http://myinstance/ajax?`). The actual AjaxController method name is
+encoded in the `params` dictionary using the `fname` key.
+
+A more real-life example
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+A frequent need of Web 2 applications is the delayed (or demand
+driven) loading of pieces of the DOM. This is typically achieved using
+some preparation of the initial DOM nodes, jQuery event handling and
+proper use of loadxhtml.
+
+We present here a skeletal version of the mecanism used in CubicWeb
+and available in web/views/tabs.py, in the `LazyViewMixin` class.
+
+.. sourcecode:: python
+
+    def lazyview(self, vid, rql=None):
+        """ a lazy version of wview """
+        w = self.w
+        self._cw.add_js('cubicweb.lazy.js')
+        urlparams = {'vid' : vid, 'fname' : 'view'}
+        if rql is not None:
+            urlparams['rql'] = rql
+        w(u'<div id="lazy-%s" cubicweb:loadurl="%s">' % (
+            vid, xml_escape(self._cw.build_url('json', **urlparams))))
+        w(u'</div>')
+        self._cw.add_onload(u"""
+            jQuery('#lazy-%(vid)s').bind('%(event)s', function() {
+                   loadNow('#lazy-%(vid)s');});"""
+            % {'event': 'load_%s' % vid, 'vid': vid})
+
+This creates a `div` with a specific event associated to it.
+
+The full version deals with:
+
+* optional parameters such as an entity eid, an rset
+
+* the ability to further reload the fragment
+
+* the ability to display a spinning wheel while the fragment is still
+  not loaded
+
+* handling of browsers that do not support ajax (search engines,
+  text-based browsers such as lynx, etc.)
+
+The javascript side is quite simple, due to loadxhtml awesomeness.
+
+.. sourcecode:: javascript
+
+    function loadNow(eltsel) {
+        var lazydiv = jQuery(eltsel);
+        lazydiv.loadxhtml(lazydiv.attr('cubicweb:loadurl'));
+    }
+
+This is all significantly different of the previous `simple example`
+(albeit this example actually comes from real-life code).
+
+Notice how the `cubicweb:loadurl` is used to convey the url
+information. The base of this url is similar to the global javascript
+JSON_BASE_URL. According to the pattern described earlier,
+the `fname` parameter refers to the standard `js_view` method of the
+JSonController. This method renders an arbitrary view provided a view
+id (or `vid`) is provided, and most likely an rql expression yielding
+a result set against which a proper view instance will be selected.
+
+The `cubicweb:loadurl` is one of the 29 attributes extensions to XHTML
+in a specific cubicweb namespace. It is a means to pass information
+without breaking HTML nor XHTML compliance and without resorting to
+ungodly hacks.
+
+Given all this, it is easy to add a small nevertheless useful feature
+to force the loading of a lazy view (for instance, a very
+computation-intensive web page could be scinded into one fast-loading
+part and a delayed part).
+
+On the server side, a simple call to a javascript function is
+sufficient.
+
+.. sourcecode:: python
+
+    def forceview(self, vid):
+        """trigger an event that will force immediate loading of the view
+        on dom readyness
+        """
+        self._cw.add_onload("triggerLoad('%s');" % vid)
+
+The browser-side definition follows.
+
+.. sourcecode:: javascript
+
+    function triggerLoad(divid) {
+        jQuery('#lazy-' + divd).trigger('load_' + divid);
+    }
+
+
+Javascript library: overview
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* jquery.* : jquery and jquery UI library
+
+* cubicweb.ajax.js : concentrates all ajax related facilities (it
+  extends jQuery with the loahxhtml function, provides a handfull of
+  high-level ajaxy operations like asyncRemoteExec, reloadComponent,
+  replacePageChunk, getDomFromResponse)
+
+* cubicweb.python.js : adds a number of practical extension to stdanrd
+  javascript objects (on Date, Array, String, some list and dictionary
+  operations), and a pythonesque way to build classes. Defines a
+  CubicWeb namespace.
+
+* cubicweb.htmlhelpers.js : a small bag of convenience functions used
+  in various other cubicweb javascript resources (baseuri, progress
+  cursor handling, popup login box, html2dom function, etc.)
+
+* cubicweb.widgets.js : provides a widget namespace and constructors
+  and helpers for various widgets (mainly facets and timeline)
+
+* cubicweb.edition.js : used by edition forms
+
+* cubicweb.preferences.js : used by the preference form
+
+* cubicweb.facets.js : used by the facets mechanism
+
+There is also javascript support for massmailing, gmap (google maps),
+fckcwconfig (fck editor), timeline, calendar, goa (CubicWeb over
+AppEngine), flot (charts drawing), tabs and bookmarks.
+
+API
+~~~
+
+.. toctree::
+    :maxdepth: 1
+
+    /js_api/index
+
+
+Testing javascript
+~~~~~~~~~~~~~~~~~~
+
+You with the ``cubicweb.qunit.QUnitTestCase`` can include standard Qunit tests
+inside the python unittest run . You simply have to define a new class that
+inherit from ``QUnitTestCase`` and register your javascript test file in the
+``all_js_tests`` lclass attribut. This  ``all_js_tests`` is a sequence a
+3-tuple (<test_file, [<dependencies> ,] [<data_files>]):
+
+The <test_file> should contains the qunit test. <dependencies> defines the list
+of javascript file that must be imported before the test script.  Dependencies
+are included their definition order. <data_files> are additional files copied in the
+test directory. both <dependencies> and <data_files> are optionnal.
+``jquery.js`` is preincluded in for all test.
+
+.. sourcecode:: python
+
+    from cubicweb.qunit import QUnitTestCase
+
+    class MyQUnitTest(QUnitTestCase):
+
+        all_js_tests = (
+            ("relative/path/to/my_simple_testcase.js",)
+            ("relative/path/to/my_qunit_testcase.js",(
+                "rel/path/to/dependency_1.js",
+                "rel/path/to/dependency_2.js",)),
+            ("relative/path/to/my_complexe_qunit_testcase.js",(
+                 "rel/path/to/dependency_1.js",
+                 "rel/path/to/dependency_2.js",
+               ),(
+                 "rel/path/file_dependency.html",
+                 "path/file_dependency.json")
+                ),
+            )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/property.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,15 @@
+.. _cwprops:
+
+The property mecanism
+---------------------
+
+.. XXX CWProperty and co
+
+
+Property API
+~~~~~~~~~~~~
+.. XXX feed me
+
+Registering and using your own property
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.. XXX feed me
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/publisher.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,65 @@
+.. _publisher:
+
+Publisher
+---------
+
+What happens when an HTTP request is issued ?
+
+The story begins with the ``CubicWebPublisher.main_publish``
+method. We do not get upper in the bootstrap process because it is
+dependant on the used HTTP library. With `twisted`_ however,
+``cubicweb.etwist.server.CubicWebRootResource.render_request`` is the
+real entry point.
+
+.. _`twisted`: http://twistedmatrix.com/trac/
+
+What main_publish does:
+
+* get a controller id and a result set from the path (this is actually
+  delegated to the `urlpublisher` component)
+
+* the controller is then selected (if not, this is considered an
+  authorization failure and signaled as such) and called
+
+* then either a proper result is returned, in which case the
+  request/connection object issues a ``commit`` and returns the result
+
+* or error handling must happen:
+
+  * ``ValidationErrors`` pop up there and may lead to a redirect to a
+    previously arranged url or standard error handling applies
+  * an HTTP 500 error (`Internal Server Error`) is issued
+
+
+Now, let's turn to the controller. There are many of them in
+:mod:`cubicweb.web.views.basecontrollers`. We can just follow the
+default `view` controller that is selected on a `view` path. See the
+:ref:`controllers` chapter for more information on controllers.
+
+The `View` controller's entry point is the `publish` method. It does
+the following:
+
+* compute the `main` view to be applied, using either the given result
+  set or building one from a user provided rql string (`rql` and `vid`
+  can be forced from the url GET parameters), that is:
+
+    * compute the `vid` using the result set and the schema (see
+      `cubicweb.web.views.vid_from_rset`)
+    * handle all error cases that could happen in this phase
+
+* do some cache management chores
+
+* select a main template (typically `TheMainTemplate`, see chapter
+  :ref:`templates`)
+
+* call it with the result set and the computed view.
+
+What happens next actually depends on the template and the view, but
+in general this is the rendering phase.
+
+
+CubicWebPublisher API
+`````````````````````
+
+.. autoclass:: cubicweb.web.application.CubicWebPublisher
+   :members:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/request.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,131 @@
+The `Request` class (`cubicweb.web.request`)
+--------------------------------------------
+
+Overview
+````````
+
+A request instance is created when an HTTP request is sent to the web
+server.  It contains informations such as form parameters,
+authenticated user, etc. It is a very prevalent object and is used
+throughout all of the framework and applications, as you'll access to
+almost every resources through it.
+
+**A request represents a user query, either through HTTP or not (we
+also talk about RQL queries on the server side for example).**
+
+Here is a non-exhaustive list of attributes and methods available on
+request objects (grouped by category):
+
+* `Browser control`:
+
+  * `ie_browser`: tells if the browser belong to the Internet Explorer
+    family
+
+* `User and identification`:
+
+  * `user`, instance of `cubicweb.entities.authobjs.CWUser` corresponding to the
+    authenticated user
+
+* `Session data handling`
+
+  * `session.data` is the dictionary of the session data; it can be
+    manipulated like an ordinary Python dictionary
+
+* `Edition` (utilities for edition control):
+
+  * `cancel_edition`: resets error url and cleans up pending operations
+  * `create_entity`: utility to create an entity (from an etype,
+    attributes and relation values)
+  * `datadir_url`: returns the url to the merged external resources
+    (|cubicweb|'s `web/data` directory plus all `data` directories of
+    used cubes)
+  * `edited_eids`: returns the list of eids of entities that are
+    edited under the current http request
+  * `eid_rset(eid)`: utility which returns a result set from an eid
+  * `entity_from_eid(eid)`: returns an entity instance from the given eid
+  * `encoding`: returns the encoding of the current HTTP request
+  * `ensure_ro_rql(rql)`: ensure some rql query is a data request
+  * etype_rset
+  * `form`, dictionary containing the values of a web form
+  * `encoding`, character encoding to use in the response
+  * `next_tabindex()`: returns a monotonically growing integer used to
+    build the html tab index of forms
+
+* `HTTP`
+
+  * `authmode`: returns a string describing the authentication mode
+    (http, cookie, ...)
+  * `lang`: returns the user agents/browser's language as carried by
+    the http request
+  * `demote_to_html()`: in the context of an XHTML compliant browser,
+    this will force emission of the response as an HTML document
+    (using the http content negociation)
+
+*  `Cookies handling`
+
+  * `get_cookie()`, returns a dictionary containing the value of the header
+    HTTP 'Cookie'
+  * `set_cookie(cookie, key, maxage=300)`, adds a header HTTP `Set-Cookie`,
+    with a minimal 5 minutes length of duration by default (`maxage` = None
+    returns a *session* cookie which will expire when the user closes the browser
+    window)
+  * `remove_cookie(cookie, key)`, forces a value to expire
+
+* `URL handling`
+
+  * `build_url(__vid, *args, **kwargs)`: return an absolute URL using
+    params dictionary key/values as URL parameters. Values are
+    automatically URL quoted, and the publishing method to use may be
+    specified or will be guessed.
+  * `build_url_params(**kwargs)`: returns a properly prepared (quoted,
+    separators, ...) string from the given parameters
+  * `url()`, returns the full URL of the HTTP request
+  * `base_url()`, returns the root URL of the web application
+  * `relative_path()`, returns the relative path of the request
+
+* `Web resource (.css, .js files, etc.) handling`:
+
+  * `add_css(cssfiles)`: adds the given list of css resources to the current
+    html headers
+  * `add_js(jsfiles)`: adds the given list of javascript resources to the
+    current html headers
+  * `add_onload(jscode)`: inject the given jscode fragment (a unicode
+    string) into the current html headers, wrapped inside a
+    document.ready(...) or another ajax-friendly one-time trigger event
+  * `add_header(header, values)`: adds the header/value pair to the
+    current html headers
+  * `status_out`: control the HTTP status of the response
+
+* `And more...`
+
+  * `set_content_type(content_type, filename=None)`, adds the header HTTP
+    'Content-Type'
+  * `get_header(header)`, returns the value associated to an arbitrary header
+    of the HTTP request
+  * `set_header(header, value)`, adds an arbitrary header in the response
+  * `execute(*args, **kwargs)`, executes an RQL query and return the result set
+  * `property_value(key)`, properties management (`CWProperty`)
+  * dictionary `data` to store data to share informations between components
+    *while a request is executed*
+
+Please note that this class is abstract and that a concrete implementation
+will be provided by the *frontend* web used (in particular *twisted* as of
+today). For the views or others that are executed on the server side,
+most of the interface of `Request` is defined in the session associated
+to the client.
+
+API
+```
+
+The elements we gave in overview for above are built in three layers,
+from ``cubicweb.req.RequestSessionBase``, ``cubicweb.repoapi.Connection`` and
+``cubicweb.web.ConnectionCubicWebRequestBase``.
+
+.. autoclass:: cubicweb.req.RequestSessionBase
+   :members:
+
+.. autoclass:: cubicweb.repoapi.Connection
+   :members:
+
+.. autoclass:: cubicweb.web.request.ConnectionCubicWebRequestBase
+   :members:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/resource.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,18 @@
+.. _resources:
+
+Locate resources
+----------------
+
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.locate_resource
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.locate_doc_file
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.locate_all_files
+
+Static files handling
+---------------------
+
+.. autoattribute:: cubicweb.web.webconfig.WebConfiguration.static_directory
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.static_file_exists
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.static_file_open
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.static_file_add
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.static_file_del
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/rtags.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,27 @@
+Configuring the user interface
+------------------------------
+
+.. _relation_tags:
+
+Relation tags
+~~~~~~~~~~~~~
+.. automodule:: cubicweb.rtags
+
+.. _uicfg:
+
+The uicfg module
+~~~~~~~~~~~~~~~~
+
+.. note::
+
+ The part of uicfg that deals with primary views is in the
+ :ref:`primary_view_configuration` chapter.
+
+.. automodule:: cubicweb.web.views.uicfg
+
+
+The uihelper module
+~~~~~~~~~~~~~~~~~~~
+
+.. automodule:: cubicweb.web.uihelper
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/searchbar.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,41 @@
+.. _searchbar:
+
+RQL search bar
+--------------
+
+The RQL search bar is a visual component, hidden by default, the tiny *search*
+input being enough for common use cases.
+
+An autocompletion helper is provided to help you type valid queries, both
+in terms of syntax and in terms of schema validity.
+
+.. autoclass:: cubicweb.web.views.magicsearch.RQLSuggestionsBuilder
+
+
+How search is performed
++++++++++++++++++++++++
+
+You can use the *rql search bar* to either type RQL queries, plain text queries
+or standard shortcuts such as *<EntityType>* or *<EntityType> <attrname> <value>*.
+
+Ultimately, all queries are translated to rql since it's the only
+language understood on the server (data) side. To transform the user
+query into RQL, CubicWeb uses the so-called *magicsearch component*,
+defined in :mod:`cubicweb.web.views.magicsearch`, which in turn
+delegates to a number of query preprocessor that are responsible of
+interpreting the user query and generating corresponding RQL.
+
+The code of the main processor loop is easy to understand:
+
+.. sourcecode:: python
+
+  for proc in self.processors:
+      try:
+          return proc.process_query(uquery, req)
+      except (RQLSyntaxError, BadRQLQuery):
+          pass
+
+The idea is simple: for each query processor, try to translate the
+query. If it fails, try with the next processor, if it succeeds,
+we're done and the RQL query will be executed.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/views/basetemplates.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,161 @@
+.. -*- coding: utf-8 -*-
+
+.. _templates:
+
+Templates
+=========
+
+Templates are the entry point for the |cubicweb| view system. As seen
+in :ref:`views_base_class`, there are two kinds of views: the
+templatable and non-templatable.
+
+
+Non-templatable views
+---------------------
+
+Non-templatable views are standalone. They are responsible for all the details
+such as setting a proper content type (or mime type), the proper document
+headers, namespaces, etc. Examples are pure xml views such as RSS or Semantic Web
+views (`SIOC`_, `DOAP`_, `FOAF`_, `Linked Data`_, etc.), and views which generate
+binary files (pdf, excel files, etc.)
+
+.. _`SIOC`: http://sioc-project.org/
+.. _`DOAP`: http://trac.usefulinc.com/doap
+.. _`FOAF`: http://www.foaf-project.org/
+.. _`Linked Data`: http://linkeddata.org/
+
+
+To notice that a view is not templatable, you just have to set the
+view's class attribute `templatable` to `False`. In this case, it
+should set the `content_type` class attribute to the correct MIME
+type. By default, it is text/xhtml. Additionally, if your view
+generate a binary file, you have to set the view's class attribute
+`binary` to `True` too.
+
+
+Templatable views
+-----------------
+
+Templatable views are not concerned with such pesky details. They
+leave it to the template. Conversely, the template's main job is to:
+
+* set up the proper document header and content type
+* define the general layout of a document
+* invoke adequate views in the various sections of the document
+
+
+Look at :mod:`cubicweb.web.views.basetemplates` and you will find the base
+templates used to generate (X)HTML for your application. The most important
+template there is :class:`~cubicweb.web.views.basetemplates.TheMainTemplate`.
+
+.. _the_main_template_layout:
+
+TheMainTemplate
+~~~~~~~~~~~~~~~
+
+.. _the_main_template_sections:
+
+Layout and sections
+```````````````````
+
+A page is composed as indicated on the schema below :
+
+.. image:: ../../../images/main_template.png
+
+The sections dispatches specific views:
+
+* `header`: the rendering of the header is delegated to the
+  `htmlheader` view, whose default implementation can be found in
+  ``basetemplates.py`` and which does the following things:
+
+    * inject the favicon if there is one
+    * inject the global style sheets and javascript resources
+    * call and display a link to an rss component if there is one available
+
+  it also sets up the page title, and fills the actual
+  `header` section with top-level components, using the `header` view, which:
+
+    * tries to display a logo, the name of the application and the `breadcrumbs`
+    * provides a login status area
+    * provides a login box (hiden by default)
+
+* `left column`: this is filled with all selectable boxes matching the
+  `left` context (there is also a right column but nowadays it is
+  seldom used due to bad usability)
+
+* `contentcol`: this is the central column; it is filled with:
+
+    * the `rqlinput` view (hidden by default)
+    * the `applmessages` component
+    * the `contentheader` view which in turns dispatches all available
+      content navigation components having the `navtop` context (this
+      is used to navigate through entities implementing the IPrevNext
+      interface)
+    * the view that was given as input to the template's `call`
+      method, also dealing with pagination concerns
+    * the `contentfooter`
+
+* `footer`: adds all footer actions
+
+.. note::
+
+  How and why a view object is given to the main template is explained
+  in the :ref:`publisher` chapter.
+
+Configure the main template
+```````````````````````````
+
+You can overload some methods of the
+:class:`~cubicweb.web.views.basetemplates.TheMainTemplate`, in order to fulfil
+your needs. There are also some attributes and methods which can be defined on a
+view to modify the base template behaviour:
+
+* `paginable`: if the result set is bigger than a configurable size, your result
+  page will be paginated by default. You can set this attribute to `False` to
+  avoid this.
+
+* `binary`: boolean flag telling if the view generates some text or a binary
+  stream.  Default to False. When view generates text argument given to `self.w`
+  **must be a unicode string**, encoded string otherwise.
+
+* `content_type`, view's content type, default to 'text/xhtml'
+
+* `templatable`, boolean flag telling if the view's content should be returned
+  directly (when `False`) or included in the main template layout (including
+  header, boxes and so on).
+
+* `page_title()`, method that should return a title that will be set as page
+  title in the html headers.
+
+* `html_headers()`, method that should return a list of HTML headers to be
+  included the html headers.
+
+
+You can also modify certain aspects of the main template of a page
+when building a url or setting these parameters in the req.form:
+
+* `__notemplate`, if present (whatever the value assigned), only the content view
+  is returned
+
+* `__force_display`, if present and its value is not null, no pagination whatever
+  the number of entities to display (e.g. similar effect as view's `paginable`
+  attribute described above.
+
+* `__method`, if the result set to render contains only one entity and this
+  parameter is set, it refers to a method to call on the entity by passing it the
+  dictionary of the forms parameters, before going the classic way (through step
+  1 and 2 described juste above)
+
+* `vtitle`, a title to be set as <h1> of the content
+
+Other templates
+~~~~~~~~~~~~~~~
+
+There are also the following other standard templates:
+
+* :class:`cubicweb.web.views.basetemplates.LogInTemplate`
+* :class:`cubicweb.web.views.basetemplates.LogOutTemplate`
+* :class:`cubicweb.web.views.basetemplates.ErrorTemplate` specializes
+  :class:`~cubicweb.web.views.basetemplates.TheMainTemplate` to do
+  proper end-user output if an error occurs during the computation of
+  TheMainTemplate (it is a fallback view).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/views/baseviews.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,21 @@
+Base views
+----------
+
+|cubicweb| provides a lot of standard views, that can be found in
+:mod:`cubicweb.web.views` sub-modules.
+
+A certain number of views are used to build the web interface, which apply to one
+or more entities. As other appobjects, their identifier is what distinguish them
+from each others. The most generic ones, found in
+:mod:`cubicweb.web.views.baseviews`, are described below.
+
+You'll probably want to customize one or more of the described views which are
+default, generic, implementations.
+
+
+.. automodule:: cubicweb.web.views.baseviews
+
+You will also find modules providing some specific services:
+
+.. automodule:: cubicweb.web.views.navigation
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/views/boxes.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,36 @@
+Boxes
+-----
+
+(:mod:`cubicweb.web.views.boxes`)
+
+*sidebox*
+  This view displays usually a side box of some related entities
+  in a primary view.
+
+The action box
+~~~~~~~~~~~~~~~
+
+The ``add_related`` is an automatic menu in the action box that allows to create
+an entity automatically related to the initial entity (context in
+which the box is displayed). By default, the links generated in this
+box are computed from the schema properties of the displayed entity,
+but it is possible to explicitly specify them thanks to the
+`cubicweb.web.views.uicfg.rmode` *relation tag*:
+
+* `link`, indicates that a relation is in general created pointing
+  to an existing entity and that we should not to display a link
+  for this relation
+
+* `create`, indicates that a relation is in general created pointing
+  to new entities and that we should display a link to create a new
+  entity and link to it automatically
+
+
+If necessary, it is possible to overwrite the method
+`relation_mode(rtype, targettype, x='subject')` to dynamically
+compute a relation creation category.
+
+Please note that if at least one action belongs to the `addrelated` category,
+the automatic behavior is desactivated in favor of an explicit behavior
+(e.g. display of `addrelated` category actions only).
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/views/breadcrumbs.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,60 @@
+Breadcrumbs
+-----------
+
+Breadcrumbs are a navigation component to help the user locate himself
+along a path of entities.
+
+Display
+~~~~~~~
+
+Breadcrumbs are displayed by default in the header section (see
+:ref:`the_main_template_sections`).  With the default main template,
+the header section is composed by the logo, the application name,
+breadcrumbs and, at the most right, the login box. Breadcrumbs are
+displayed just next to the application name, thus they begin with a
+separator.
+
+Here is the header section of the CubicWeb's forge:
+
+.. image:: ../../../images/breadcrumbs_header.png
+
+There are three breadcrumbs components defined in
+:mod:`cubicweb.web.views.ibreadcrumbs`:
+
+- `BreadCrumbEntityVComponent`: displayed for a result set with one line
+  if the entity is adaptable to ``IBreadCrumbsAdapter``.
+- `BreadCrumbETypeVComponent`: displayed for a result set with more than
+  one line, but with all entities of the same type which can adapt to
+  ``IBreadCrumbsAdapter``.
+- `BreadCrumbAnyRSetVComponent`: displayed for any other result set.
+
+Building breadcrumbs
+~~~~~~~~~~~~~~~~~~~~
+
+The ``IBreadCrumbsAdapter`` adapter is defined in the
+:mod:`cubicweb.web.views.ibreadcrumbs` module. It specifies that an
+entity which implements this interface must have a ``breadcrumbs`` and
+a ``parent_entity`` method. A default implementation for each is
+provided. This implementation expoits the ITreeAdapter.
+
+.. note::
+
+   Redefining the breadcrumbs is the hammer way to do it. Another way
+   is to define an `ITreeAdapter` adapter on an entity type. If
+   available, it will be used to compute breadcrumbs.
+
+Here is the API of the ``IBreadCrumbsAdapter`` class:
+
+.. automethod:: cubicweb.web.views.ibreadcrumbs.IBreadCrumbsAdapter.parent_entity
+.. automethod:: cubicweb.web.views.ibreadcrumbs.IBreadCrumbsAdapter.breadcrumbs
+
+If the breadcrumbs method return a list of entities, the
+``cubicweb.web.views.ibreadcrumbs.BreadCrumbView`` is used to display
+the elements.
+
+By default, for any entity, if recurs=True, breadcrumbs method returns
+a list of entities, else a list of a simple string.
+
+In order to see a hierarchical breadcrumbs, entities must have a
+``parent`` method which returns the parent entity. By default this
+method doesn't exist on entity, given that it can not be guessed.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/views/idownloadable.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,23 @@
+The 'download' views
+====================
+
+.. automodule:: cubicweb.web.views.idownloadable
+
+Components
+----------
+
+.. autoclass:: cubicweb.web.views.idownloadable.DownloadBox
+
+Download views
+--------------
+
+.. autoclass:: cubicweb.web.views.idownloadable.DownloadView
+.. autoclass:: cubicweb.web.views.idownloadable.DownloadLinkView
+.. autoclass:: cubicweb.web.views.idownloadable.IDownloadablePrimaryView
+.. autoclass:: cubicweb.web.views.idownloadable.IDownloadableOneLineView
+
+Embedded views
+--------------
+
+.. autoclass:: cubicweb.web.views.idownloadable.ImageView
+.. autoclass:: cubicweb.web.views.idownloadable.EHTMLView
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/views/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,28 @@
+The View system
+===============
+
+This chapter aims to describe the concept of a `view` used all along
+the development of a web application and how it has been implemented
+in |cubicweb|.
+
+
+.. toctree::
+   :maxdepth: 3
+
+   views
+   basetemplates
+   primary
+   reledit
+   baseviews
+   startup
+   boxes
+   table
+   xmlrss
+   urlpublish
+   breadcrumbs
+   idownloadable
+   wdoc
+
+..   editforms
+..   embedding
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/views/primary.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,276 @@
+.. _primary_view:
+
+The Primary View
+-----------------
+
+By default, *CubicWeb* provides a view that fits every available
+entity type. This is the first view you might be interested in
+modifying. It is also one of the richest and most complex.
+
+It is automatically selected on a one line result set containing an
+entity.
+
+It lives in the :mod:`cubicweb.web.views.primary` module.
+
+The *primary* view is supposed to render a maximum of informations about the
+entity.
+
+.. _primary_view_layout:
+
+Layout
+``````
+
+The primary view has the following layout.
+
+.. image:: ../../../images/primaryview_template.png
+
+.. _primary_view_configuration:
+
+Primary view configuration
+``````````````````````````
+
+If you want to customize the primary view of an entity, overriding the primary
+view class may not be necessary. For simple adjustments (attributes or relations
+display locations and styles), a much simpler way is to use uicfg.
+
+Attributes/relations display location
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In the primary view, there are three sections where attributes and
+relations can be displayed (represented in pink in the image above):
+
+* 'attributes'
+* 'relations'
+* 'sideboxes'
+
+**Attributes** can only be displayed in the attributes section (default
+  behavior). They can also be hidden. By default, attributes of type `Password`
+  and `Bytes` are hidden.
+
+For instance, to hide the ``title`` attribute of the ``Blog`` entity:
+
+.. sourcecode:: python
+
+   from cubicweb.web.views import uicfg
+   uicfg.primaryview_section.tag_attribute(('Blog', 'title'), 'hidden')
+
+**Relations** can be either displayed in one of the three sections or hidden.
+
+For relations, there are two methods:
+
+* ``tag_object_of`` for modifying the primary view of the object
+* ``tag_subject_of`` for modifying the primary view of the subject
+
+These two methods take two arguments:
+
+* a triplet ``(subject, relation_name, object)``, where subject or object can be replaced with ``'*'``
+* the section name or ``hidden``
+
+.. sourcecode:: python
+
+   pv_section = uicfg.primaryview_section
+   # hide every relation `entry_of` in the `Blog` primary view
+   pv_section.tag_object_of(('*', 'entry_of', 'Blog'), 'hidden')
+
+   # display `entry_of` relations in the `relations`
+   # section in the `BlogEntry` primary view
+   pv_section.tag_subject_of(('BlogEntry', 'entry_of', '*'), 'relations')
+
+
+Display content
+^^^^^^^^^^^^^^^
+
+You can use ``primaryview_display_ctrl`` to customize the display of attributes
+or relations. Values of ``primaryview_display_ctrl`` are dictionaries.
+
+
+Common keys for attributes and relations are:
+
+* ``vid``: specifies the regid of the view for displaying the attribute or the relation.
+
+  If ``vid`` is not specified, the default value depends on the section:
+    * ``attributes`` section: 'reledit' view
+    * ``relations`` section: 'autolimited' view
+    * ``sideboxes`` section: 'sidebox' view
+
+* ``order``: int used to control order within a section. When not specified,
+  automatically set according to order in which tags are added.
+
+* ``label``: label for the relations section or side box
+
+* ``showlabel``: boolean telling whether the label is displayed
+
+.. sourcecode:: python
+
+   # let us remind the schema of a blog entry
+   class BlogEntry(EntityType):
+       title = String(required=True, fulltextindexed=True, maxsize=256)
+       publish_date = Date(default='TODAY')
+       content = String(required=True, fulltextindexed=True)
+       entry_of = SubjectRelation('Blog', cardinality='?*')
+
+   # now, we want to show attributes
+   # with an order different from that in the schema definition
+   view_ctrl = uicfg.primaryview_display_ctrl
+   for index, attr in enumerate('title', 'content', 'publish_date'):
+       view_ctrl.tag_attribute(('BlogEntry', attr), {'order': index})
+
+By default, relations displayed in the 'relations' section are being displayed by
+the 'autolimited' view. This view will use comma separated values, or list view
+and/or limit your rset if there is too much items in it (and generate the "view
+all" link in this case).
+
+You can control this view by setting the following values in the
+`primaryview_display_ctrl` relation tag:
+
+* `limit`, maximum number of entities to display. The value of the
+  'navigation.related-limit'  cwproperty is used by default (which is 8 by default).
+  If None, no limit.
+
+* `use_list_limit`, number of entities until which they should be display as a list
+  (eg using the 'list' view). Below that limit, the 'csv' view is used. If None,
+  display using 'csv' anyway.
+
+* `subvid`, the subview identifier (eg view that should be used of each item in the
+  list)
+
+Notice you can also use the `filter` key to set up a callback taking the related
+result set as argument and returning it filtered, to do some arbitrary filtering
+that can't be done using rql for instance.
+
+
+.. sourcecode:: python
+
+   pv_section = uicfg.primaryview_section
+   # in `CWUser` primary view, display `created_by`
+   # relations in relations section
+   pv_section.tag_object_of(('*', 'created_by', 'CWUser'), 'relations')
+
+   # display this relation as a list, sets the label,
+   # limit the number of results and filters on comments
+   def filter_comment(rset):
+       return rset.filtered_rset(lambda x: x.e_schema == 'Comment')
+   pv_ctrl = uicfg.primaryview_display_ctrl
+   pv_ctrl.tag_object_of(('*', 'created_by', 'CWUser'),
+                         {'vid': 'list', 'label': _('latest comment(s):'),
+                          'limit': True,
+                          'filter': filter_comment})
+
+.. warning:: with the ``primaryview_display_ctrl`` rtag, the subject or the
+   object of the relation is ignored for respectively ``tag_object_of`` or
+   ``tag_subject_of``. To avoid warnings during execution, they should be set to
+   ``'*'``.
+
+
+.. automodule:: cubicweb.web.views.primary
+
+
+Example of customization and creation
+`````````````````````````````````````
+
+We'll show you now an example of a ``primary`` view and how to customize it.
+
+If you want to change the way a ``BlogEntry`` is displayed, just
+override the method ``cell_call()`` of the view ``primary`` in
+``BlogDemo/views.py``.
+
+.. sourcecode:: python
+
+   from cubicweb.predicates import is_instance
+   from cubicweb.web.views.primary import Primaryview
+
+   class BlogEntryPrimaryView(PrimaryView):
+       __select__ = PrimaryView.__select__ & is_instance('BlogEntry')
+
+       def render_entity_attributes(self, entity):
+           self.w(u'<p>published on %s</p>' %
+                  entity.publish_date.strftime('%Y-%m-%d'))
+           super(BlogEntryPrimaryView, self).render_entity_attributes(entity)
+
+
+The above source code defines a new primary view for
+``BlogEntry``. The `__reid__` class attribute is not repeated there since it
+is inherited through the `primary.PrimaryView` class.
+
+The selector for this view chains the selector of the inherited class
+with its own specific criterion.
+
+The view method ``self.w()`` is used to output data. Here `lines
+08-09` output HTML for the publication date of the entry.
+
+.. image:: ../../../images/lax-book_09-new-view-blogentry_en.png
+   :alt: blog entries now look much nicer
+
+Let us now improve the primary view of a blog
+
+.. sourcecode:: python
+
+ from logilab.mtconverter import xml_escape
+ from cubicweb.predicates import is_instance, one_line_rset
+ from cubicweb.web.views.primary import Primaryview
+
+ class BlogPrimaryView(PrimaryView):
+     __regid__ = 'primary'
+     __select__ = PrimaryView.__select__ & is_instance('Blog')
+     rql = 'Any BE ORDERBY D DESC WHERE BE entry_of B, BE publish_date D, B eid %(b)s'
+
+     def render_entity_relations(self, entity):
+         rset = self._cw.execute(self.rql, {'b' : entity.eid})
+         for entry in rset.entities():
+             self.w(u'<p>%s</p>' % entry.view('inblogcontext'))
+
+ class BlogEntryInBlogView(EntityView):
+     __regid__ = 'inblogcontext'
+     __select__ = is_instance('BlogEntry')
+
+     def cell_call(self, row, col):
+         entity = self.cw_rset.get_entity(row, col)
+         self.w(u'<a href="%s" title="%s">%s</a>' %
+                entity.absolute_url(),
+                xml_escape(entity.content[:50]),
+                xml_escape(entity.description))
+
+This happens in two places. First we override the
+render_entity_relations method of a Blog's primary view. Here we want
+to display our blog entries in a custom way.
+
+At `line 10`, a simple request is made to build a result set with all
+the entities linked to the current ``Blog`` entity by the relationship
+``entry_of``. The part of the framework handling the request knows
+about the schema and infers that such entities have to be of the
+``BlogEntry`` kind and retrieves them (in the prescribed publish_date
+order).
+
+The request returns a selection of data called a result set. Result
+set objects have an .entities() method returning a generator on
+requested entities (going transparently through the `ORM` layer).
+
+At `line 13` the view 'inblogcontext' is applied to each blog entry to
+output HTML. (Note that the 'inblogcontext' view is not defined
+whatsoever in *CubicWeb*. You are absolutely free to define whole view
+families.) We juste arrange to wrap each blogentry output in a 'p'
+html element.
+
+Next, we define the 'inblogcontext' view. This is NOT a primary view,
+with its well-defined sections (title, metadata, attribtues,
+relations/boxes). All a basic view has to define is cell_call.
+
+Since views are applied to result sets which can be tables of data, we
+have to recover the entity from its (row,col)-coordinates (`line
+20`). Then we can spit some HTML.
+
+.. warning::
+
+  Be careful: all strings manipulated in *CubicWeb* are actually
+  unicode strings. While web browsers are usually tolerant to
+  incoherent encodings they are being served, we should not abuse
+  it. Hence we have to properly escape our data. The xml_escape()
+  function has to be used to safely fill (X)HTML elements from Python
+  unicode strings.
+
+Assuming we added entries to the blog titled `MyLife`, displaying it
+now allows to read its description and all its entries.
+
+.. image:: ../../../images/lax-book_10-blog-with-two-entries_en.png
+   :alt: a blog and all its entries
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/views/reledit.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,149 @@
+.. _reledit:
+
+The "Click and Edit" (also `reledit`) View
+------------------------------------------
+
+The principal way to update data through the Web UI is through the
+`modify` action on entities, which brings a full form. This is
+described in the :ref:`webform` chapter.
+
+There is however another way to perform piecewise edition of entities
+and relations, using a specific `reledit` (for *relation edition*)
+view from the :mod:`cubicweb.web.views.reledit` module.
+
+This is typically applied from the default Primary View (see
+:ref:`primary_view`) on the attributes and relation section. It makes
+small editions more convenient.
+
+Of course, this can be used customely in any other view. Here come
+some explanation about its capabilities and instructions on the way to
+use it.
+
+Using `reledit`
+***************
+
+Let's start again with a simple example:
+
+.. sourcecode:: python
+
+   class Company(EntityType):
+        name = String(required=True, unique=True)
+        boss = SubjectRelation('Person', cardinality='1*')
+        status = SubjectRelation('File', cardinality='?*', composite='subject')
+
+In some view code we might want to show these attributes/relations and
+allow the user to edit each of them in turn without having to leave
+the current page. We would write code as below:
+
+.. sourcecode:: python
+
+   company.view('reledit', rtype='name', default_value='<name>') # editable name attribute
+   company.view('reledit', rtype='boss') # editable boss relation
+   company.view('reledit', rtype='status') # editable attribute-like relation
+
+If one wanted to edit the company from a boss's point of view, one
+would have to indicate the proper relation's role. By default the role
+is `subject`.
+
+.. sourcecode:: python
+
+   person.view('reledit', rtype='boss', role='object')
+
+Each of these will provide with a different editing widget. The `name`
+attribute will obviously get a text input field. The `boss` relation
+will be edited through a selection box, allowing to pick another
+`Person` as boss. The `status` relation, given that it defines Company
+as a composite entity with one file inside, will provide additional actions
+
+* to `add` a `File` when there is one
+* to `delete` the `File` (if the cardinality allows it)
+
+Moreover, editing the relation or using the `add` action leads to an
+embedded edition/creation form allowing edition of the target entity
+(which is `File` in our example) instead of merely allowing to choose
+amongst existing files.
+
+The `reledit_ctrl` rtag
+***********************
+
+The behaviour of reledited attributes/relations can be finely
+controlled using the reledit_ctrl rtag, defined in
+:mod:`cubicweb.web.views.uicfg`.
+
+This rtag provides four control variables:
+
+* ``default_value``: alternative default value
+   The default value is what is shown when there is no value.
+* ``reload``: boolean, eid (to reload to) or function taking subject
+   and returning bool/eid This is useful when editing a relation (or
+   attribute) that impacts the url or another parts of the current
+   displayed page. Defaults to false.
+* ``rvid``: alternative view id (as str) for relation or composite
+   edition Default is 'incontext' or 'csv' depending on the
+   cardinality. They can also be statically changed by subclassing
+   ClickAndEditFormView and redefining _one_rvid (resp. _many_rvid).
+* ``edit_target``: 'rtype' (to edit the relation) or 'related' (to
+   edit the related entity) This controls whether to edit the relation
+   or the target entity of the relation.  Currently only one-to-one
+   relations support target entity edition. By default, the 'related'
+   option is taken whenever the relation is composite and one-to-one.
+
+Let's see how to use these controls.
+
+.. sourcecode:: python
+
+    from logilab.mtconverter import xml_escape
+    from cubicweb.web.views.uicfg import reledit_ctrl
+    reledit_ctrl.tag_attribute(('Company', 'name'),
+                               {'reload': lambda x:x.eid,
+                                'default_value': xml_escape(u'<logilab tastes better>')})
+    reledit_ctrl.tag_object_of(('*', 'boss', 'Person'), {'edit_target': 'related'})
+
+The `default_value` needs to be an xml escaped unicode string.
+
+The `edit_target` tag on the `boss` relation being set to `related` will
+ensure edition of the `Person` entity instead (using a standard
+automatic form) of the association of Company and Person.
+
+Finally, the `reload` key accepts either a boolean, an eid or a
+unicode string representing a url. If an eid is provided, it will be
+internally transformed into a url. The eid/url case helps when one
+needs to reload and the current url is inappropriate. A common case is
+edition of a key attribute, which is part of the current url. If one
+user changed the Company's name from `lozilab` to `logilab`, reloading
+on http://myapp/company/lozilab would fail. Providing the entity's
+eid, then, forces to reload on something like http://myapp/company/42,
+which always work.
+
+
+Disable `reledit`
+*****************
+
+By default, `reledit` is available on attributes and relations displayed in
+the 'attribute' section of the default primary view.  If you want to disable
+it for some attribute or relation, you have use `uicfg`:
+
+.. sourcecode:: python
+
+    from cubicweb.web.views.uicfg import primaryview_display_ctrl as _pvdc
+    _pvdc.tag_attribute(('Company', 'name'), {'vid': 'incontext'})
+
+To deactivate it everywhere it's used automatically, you may use the code snippet
+below somewhere in your cube's views:
+
+.. sourcecode:: python
+
+    from cubicweb.web.views import reledit
+
+    class DeactivatedAutoClickAndEditFormView(reledit.AutoClickAndEditFormView):
+	def _should_edit_attribute(self, rschema):
+	    return False
+
+	def _should_edit_attribute(self, rschema, role):
+	    return False
+
+    def registration_callback(vreg):
+	vreg.register_and_replace(DeactivatedAutoClickAndEditFormView,
+				  reledit.AutoClickAndEditFormView)
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/views/startup.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,18 @@
+Startup views
+-------------
+
+Startup views are views requiring no context, from which you usually start
+browsing (for instance the index page). The usual selectors are
+:class:`~cubicweb.predicates.none_rset` or :class:`~logilab.common.registry.yes`.
+
+You'll find here a description of startup views provided by the framework.
+
+.. automodule:: cubicweb.web.views.startup
+
+
+Other startup views:
+
+*schema*
+    A view dedicated to the display of the schema of the instance
+
+.. XXX to be continued
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/views/table.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,145 @@
+Table views
+-----------
+
+.. automodule:: cubicweb.web.views.tableview
+
+Example
+```````
+
+Let us take an example from the timesheet cube:
+
+.. sourcecode:: python
+
+    class ActivityResourcesTable(EntityView):
+        __regid__ = 'activity.resources.table'
+        __select__ = is_instance('Activity')
+
+        def call(self, showresource=True):
+            eids = ','.join(str(row[0]) for row in self.cw_rset)
+            rql = ('Any R,D,DUR,WO,DESCR,S,A, SN,RT,WT ORDERBY D DESC '
+                   'WHERE '
+                   '   A is Activity, A done_by R, R title RT, '
+                   '   A diem D, A duration DUR, '
+                   '   A done_for WO, WO title WT, '
+                   '   A description DESCR, A in_state S, S name SN, '
+                   '   A eid IN (%s)' % eids)
+            rset = self._cw.execute(rql)
+            self.wview('resource.table', rset, 'null')
+
+    class ResourcesTable(RsetTableView):
+        __regid__ = 'resource.table'
+        # notice you may wish a stricter selector to check rql's shape
+        __select__ = is_instance('Resource')
+        # my table headers
+        headers  = ['Resource', 'diem', 'duration', 'workpackage', 'description', 'state']
+        # I want a table where attributes are editable (reledit inside)
+        finalvid = 'editable-final'
+
+        cellvids = {3: 'editable-final'}
+        # display facets and actions with a menu
+        layout_args = {'display_filter': 'top',
+                       'add_view_actions': None}
+
+To obtain an editable table, you may specify the 'editable-table' view identifier
+using some of `cellvids`, `finalvid` or `nonfinalvid`.
+
+The previous example results in:
+
+.. image:: ../../../images/views-table-shadow.png
+
+In order to activate table filter mechanism, the `display_filter` option is given
+as a layout argument. A small arrow will be displayed at the table's top right
+corner. Clicking on `show filter form` action, will display the filter form as
+below:
+
+.. image:: ../../../images/views-table-filter-shadow.png
+
+By the same way, you can display additional actions for the selected entities
+by setting `add_view_actions` layout option to `True`. This will add actions
+returned by the view's :meth:`~cubicweb.web.views.tableview.TableMixIn.table_actions`.
+
+You can notice that all columns of the result set are not displayed. This is
+because of given `headers`, implying to display only columns from 0 to
+len(headers).
+
+Also Notice that the `ResourcesTable` view relies on a particular rql shape
+(which is not ensured by the way, the only checked thing is that the result set
+contains instance of the `Resource` type). That usually implies that you can't
+use this view for user specific queries (e.g. generated by facets or typed
+manually).
+
+
+So another option would be to write this view using
+:class:`~cubicweb.web.views.tableview.EntityTableView`, as below.
+
+.. sourcecode:: python
+
+    class ResourcesTable(EntityTableView):
+        __regid__ = 'resource.table'
+        __select__ = is_instance('Resource')
+        # table columns definition
+        columns  = ['resource', 'diem', 'duration', 'workpackage', 'description', 'in_state']
+        # I want a table where attributes are editable (reledit inside)
+        finalvid = 'editable-final'
+        # display facets and actions with a menu
+        layout_args = {'display_filter': 'top',
+                       'add_view_actions': None}
+
+        def workpackage_cell(entity):
+            activity = entity.reverse_done_in[0]
+            activity.view('reledit', rtype='done_for', role='subject', w=w)
+        def workpackage_sortvalue(entity):
+            activity = entity.reverse_done_in[0]
+            return activity.done_for[0].sortvalue()
+
+        column_renderers = {
+            'resource': MainEntityColRenderer(),
+            'workpackage': EntityTableColRenderer(
+               header='Workpackage',
+               renderfunc=worpackage_cell,
+               sortfunc=worpackage_sortvalue,),
+            'in_state': EntityTableColRenderer(
+               renderfunc=lambda w,x: w(x.cw_adapt_to('IWorkflowable').printable_state),
+               sortfunc=lambda x: x.cw_adapt_to('IWorkflowable').printable_state),
+         }
+
+Notice the following point:
+
+* `cell_<column>(w, entity)` will be searched for rendering the content of a
+  cell. If not found, `column` is expected to be an attribute of `entity`.
+
+* `cell_sortvalue_<column>(entity)` should return a typed value to use for
+  javascript sorting or None for not sortable columns (the default).
+
+* The :func:`etable_entity_sortvalue` decorator will set a 'sortvalue' function
+  for the column containing the main entity (the one given as argument to all
+  methods), which will call `entity.sortvalue()`.
+
+* You can set a column header using the :func:`etable_header_title` decorator.
+  This header will be translated. If it's not an already existing msgid, think
+  to mark it using `_()` (the example supposes headers are schema defined msgid).
+
+
+Pro/cons of each approach
+`````````````````````````
+:class:`EntityTableView` and :class:`RsetableView` provides basically the same
+set of features, though they don't share the same properties. Let's try to sum
+up pro and cons of each class.
+
+* `EntityTableView` view is:
+
+  - more verbose, but usually easier to understand
+
+  - easily extended (easy to add/remove columns for instance)
+
+  - doesn't rely on a particular rset shape. Simply give it a title and will be
+    listed in the 'possible views' box if any.
+
+* `RsetTableView` view is:
+
+  - hard to beat to display barely a result set, or for cases where some of
+    `headers`, `displaycols` or `cellvids` could be defined to enhance the table
+    while you don't care about e.g. pagination or facets.
+
+  - hardly extensible, as you usually have to change places where the view is
+    called to modify the RQL (hence the view's result set shape).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/views/urlpublish.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,137 @@
+.. -*- coding: utf-8 -*-
+
+URL publishing
+--------------
+
+(:mod:`cubicweb.web.views.urlpublishing`)
+
+.. automodule:: cubicweb.web.views.urlpublishing
+
+.. autoclass:: cubicweb.web.views.urlpublishing.URLPublisherComponent
+   :members:
+
+
+You can write your own *URLPathEvaluator* class to handle custom paths.
+For instance, if you want */my-card-id* to redirect to the corresponding
+card's primary view, you would write:
+
+.. sourcecode:: python
+
+    class CardWikiidEvaluator(URLPathEvaluator):
+        priority = 3 # make it be evaluated *before* RestPathEvaluator
+
+        def evaluate_path(self, req, segments):
+            if len(segments) != 1:
+                raise PathDontMatch()
+            rset = req.execute('Any C WHERE C wikiid %(w)s',
+                               {'w': segments[0]})
+            if len(rset) == 0:
+                # Raise NotFound if no card is found
+                raise PathDontMatch()
+            return None, rset
+
+On the other hand, you can also deactivate some of the standard
+evaluators in your final application. The only thing you have to
+do is to unregister them, for instance in a *registration_callback*
+in your cube:
+
+.. sourcecode:: python
+
+    def registration_callback(vreg):
+        vreg.unregister(RestPathEvaluator)
+
+You can even replace the :class:`cubicweb.web.views.urlpublishing.URLPublisherComponent`
+class if you want to customize the whole toolchain process or if you want
+to plug into an early enough extension point to control your request
+parameters:
+
+.. sourcecode:: python
+
+    class SanitizerPublisherComponent(URLPublisherComponent):
+        """override default publisher component to explicitly ignore
+        unauthorized request parameters in anonymous mode.
+        """
+        unauthorized_form_params = ('rql', 'vid', '__login', '__password')
+
+        def process(self, req, path):
+            if req.session.anonymous_session:
+                self._remove_unauthorized_params(req)
+            return super(SanitizerPublisherComponent, self).process(req, path)
+
+        def _remove_unauthorized_params(self, req):
+            for param in req.form.keys():
+                if param in self.unauthorized_form_params:
+                     req.form.pop(param)
+
+
+    def registration_callback(vreg):
+        vreg.register_and_replace(SanitizerPublisherComponent, URLPublisherComponent)
+
+
+.. autoclass:: cubicweb.web.views.urlpublishing.RawPathEvaluator
+.. autoclass:: cubicweb.web.views.urlpublishing.EidPathEvaluator
+.. autoclass:: cubicweb.web.views.urlpublishing.URLRewriteEvaluator
+.. autoclass:: cubicweb.web.views.urlpublishing.RestPathEvaluator
+.. autoclass:: cubicweb.web.views.urlpublishing.ActionPathEvaluator
+
+URL rewriting
+-------------
+
+(:mod:`cubicweb.web.views.urlrewrite`)
+
+.. autoclass:: cubicweb.web.views.urlrewrite.URLRewriter
+   :members:
+
+.. autoclass:: cubicweb.web.views.urlrewrite.SimpleReqRewriter
+   :members:
+
+.. autoclass:: cubicweb.web.views.urlrewrite.SchemaBasedRewriter
+   :members:
+
+
+``SimpleReqRewriter`` is enough for a certain number of simple cases. If it is not sufficient, ``SchemaBasedRewriter`` allows to do more elaborate things.
+
+Here is an example of ``SimpleReqRewriter`` usage with plain string:
+
+.. sourcecode:: python
+
+   from cubicweb.web.views.urlrewrite import SimpleReqRewriter
+   class TrackerSimpleReqRewriter(SimpleReqRewriter):
+       rules = [
+        ('/versions', dict(vid='versionsinfo')),
+        ]
+
+When the url is `<base_url>/versions`, the view with the __regid__ `versionsinfo` is displayed.
+
+Here is an example of ``SimpleReqRewriter`` usage with regular expressions:
+
+.. sourcecode:: python
+
+    from cubicweb.web.views.urlrewrite import (
+        SimpleReqRewriter, rgx)
+
+    class BlogReqRewriter(SimpleReqRewriter):
+        rules = [
+            (rgx('/blogentry/([a-z_]+)\.rss'),
+             dict(rql=('Any X ORDERBY CD DESC LIMIT 20 WHERE X is BlogEntry,'
+                       'X creation_date CD, X created_by U, '
+                       'U login "%(user)s"'
+                       % {'user': r'\1'}), vid='rss'))
+            ]
+
+When a url matches the regular expression, the view with the __regid__
+`rss` which match the result set is displayed.
+
+Here is an example of ``SchemaBasedRewriter`` usage:
+
+.. sourcecode:: python
+
+    from cubicweb.web.views.urlrewrite import (
+        SchemaBasedRewriter, rgx, build_rset)
+
+    class TrackerURLRewriter(SchemaBasedRewriter):
+        rules = [
+            (rgx('/project/([^/]+)/([^/]+)/tests'),
+             build_rset(rql='Version X WHERE X version_of P, P name %(project)s, X num %(num)s',
+                        rgxgroups=[('project', 1), ('num', 2)], vid='versiontests')),
+            ]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/views/views.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,120 @@
+
+.. _Views:
+
+Principles
+----------
+
+We'll start with a description of the interface providing a basic
+understanding of the available classes and methods, then detail the
+view selection principle.
+
+A `View` is an object responsible for the rendering of data from the
+model into an end-user consummable form. They typically churn out an
+XHTML stream, but there are views concerned with email other non-html
+outputs.
+
+.. _views_base_class:
+
+Discovering possible views
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It is possible to configure the web user interface to have a left box
+showing all the views than can be applied to the current result set.
+
+To enable this, click on your login at the top right corner. Chose
+"user preferences", then "boxes", then "possible views box" and check
+"visible = yes" before validating your changes.
+
+The views listed there we either not selected because of a lower
+score, or they were deliberately excluded by the main template logic.
+
+
+Basic class for views
+~~~~~~~~~~~~~~~~~~~~~
+
+Class :class:`~cubicweb.view.View`
+``````````````````````````````````
+
+.. autoclass:: cubicweb.view.View
+
+The basic interface for views is as follows (remember that the result
+set has a tabular structure with rows and columns, hence cells):
+
+* `render(**context)`, render the view by calling `call` or
+  `cell_call` depending on the context
+
+* `call(**kwargs)`, call the view for a complete result set or null
+  (the default implementation calls `cell_call()` on each cell of the
+  result set)
+
+* `cell_call(row, col, **kwargs)`, call the view for a given cell of a
+  result set (`row` and `col` being integers used to access the cell)
+
+* `url()`, returns the URL enabling us to get the view with the current
+  result set
+
+* `wview(__vid, rset, __fallback_vid=None, **kwargs)`, call the view of
+  identifier `__vid` on the given result set. It is possible to give a
+  fallback view identifier that will be used if the requested view is
+  not applicable to the result set.
+
+* `html_headers()`, returns a list of HTML headers to be set by the
+  main template
+
+* `page_title()`, returns the title to use in the HTML header `title`
+
+Other basic view classes
+````````````````````````
+Here are some of the subclasses of :class:`~cubicweb.view.View` defined in :mod:`cubicweb.view`
+that are more concrete as they relate to data rendering within the application:
+
+.. autoclass:: cubicweb.view.EntityView
+.. autoclass:: cubicweb.view.StartupView
+.. autoclass:: cubicweb.view.EntityStartupView
+.. autoclass:: cubicweb.view.AnyRsetView
+
+Examples of views class
+```````````````````````
+
+- Using `templatable`, `content_type` and HTTP cache configuration
+
+.. sourcecode:: python
+
+    class RSSView(XMLView):
+        __regid__ = 'rss'
+        title = _('rss')
+        templatable = False
+        content_type = 'text/xml'
+        http_cache_manager = MaxAgeHTTPCacheManager
+        cache_max_age = 60*60*2 # stay in http cache for 2 hours by default
+
+
+- Using a custom selector
+
+.. sourcecode:: python
+
+    class SearchForAssociationView(EntityView):
+        """view called by the edition view when the user asks
+        to search for something to link to the edited eid
+        """
+        __regid__ = 'search-associate'
+        title = _('search for association')
+        __select__ = one_line_rset() & match_search_state('linksearch') & is_instance('Any')
+
+
+XML views, binaries views...
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For views generating other formats than HTML (an image generated dynamically
+for example), and which can not simply be included in the HTML page generated
+by the main template (see above), you have to:
+
+* set the attribute `templatable` of the class to `False`
+* set, through the attribute `content_type` of the class, the MIME
+  type generated by the view to `application/octet-stream` or any
+  relevant and more specialised mime type
+
+For views dedicated to binary content creation (like dynamically generated
+images), we have to set the attribute `binary` of the class to `True` (which
+implies that `templatable == False`, so that the attribute `w` of the view could be
+replaced by a binary flow instead of unicode).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/views/wdoc.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,15 @@
+.. -*- coding: utf-8 -*-
+
+Online documentation system
+===========================
+
+.. automodule:: cubicweb.web.views.wdoc
+
+Help views
+----------
+.. autoclass:: cubicweb.web.views.wdoc.InlineHelpView
+
+Actions
+-------
+.. autoclass:: cubicweb.web.views.wdoc.HelpAction
+.. autoclass:: cubicweb.web.views.wdoc.AboutAction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/devweb/views/xmlrss.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,66 @@
+.. _XmlAndRss:
+
+XML and RSS views
+-----------------
+
+(:mod:`cubicweb.web.views.xmlrss`)
+
+Overview
++++++++++
+
+*rss*
+    Creates a RSS/XML view and call the view `rssitem` for each entity of
+    the result set.
+
+*rssitem*
+    Create a RSS/XML view for each entity based on the results of the dublin core
+    methods of the entity (`dc_*`)
+
+RSS Channel Example
+++++++++++++++++++++
+
+Assuming you have several blog entries, click on the title of the
+search box in the left column. A larger search box should appear. Enter:
+
+.. sourcecode:: sql
+
+   Any X ORDERBY D WHERE X is BlogEntry, X creation_date D
+
+and you get a list of blog entries.
+
+Click on your login at the top right corner. Chose "user preferences",
+then "boxes", then "possible views box" and check "visible = yes"
+before validating your changes.
+
+Enter the same query in the search box and you will see the same list,
+plus a box titled "possible views" in the left column. Click on
+"entityview", then "RSS".
+
+You just applied the "RSS" view to the RQL selection you requested.
+
+That's it, you have a RSS channel for your blog.
+
+Try again with:
+
+.. sourcecode:: sql
+
+    Any X ORDERBY D WHERE X is BlogEntry, X creation_date D,
+    X entry_of B, B title "MyLife"
+
+Another RSS channel, but a bit more focused.
+
+A last one for the road:
+
+.. sourcecode:: sql
+
+    Any C ORDERBY D WHERE C is Comment, C creation_date D LIMIT 15
+
+displayed with the RSS view, that's a channel for the last fifteen
+comments posted.
+
+[WRITE ME]
+
+* show that the RSS view can be used to display an ordered selection
+  of blog entries, thus providing a RSS channel
+
+* show that a different selection (by category) means a different channel
Binary file doc/book/en/.static/cubicweb.png has changed
Binary file doc/book/en/.static/logilab.png has changed
--- a/doc/book/en/.static/sphinx-default.css	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,861 +0,0 @@
-/**
- * Sphinx Doc Design
- */
-
-html, body {
-    background: white;
-}
-
-body {
-    font-family: Verdana, sans-serif;
-    font-size: 100%;
-    background-color: white;
-    color: black;
-    margin: 0;
-    padding: 0;
-}
-
-/* :::: LAYOUT :::: */
-
-div.logilablogo {
-    padding: 10px 10px 10px 10px;
-    height:75;
-}
-
-
-div.document {
-    background-color: white;
-}
-
-div.documentwrapper {
-    float: left;
-    width: 100%;
-}
-
-div.bodywrapper {
-    margin: 0 0 0 230px;
-}
-
-div.body {
-    background-color: white;
-    padding: 0 20px 30px 20px;
-    border-left:solid;
-    border-left-color:#e2e2e2;
-    border-left-width:thin;
-}
-
-div.sphinxsidebarwrapper {
-    padding: 10px 5px 0 10px;
-}
-
-div.sphinxsidebar {
-    float: left;
-    width: 230px;
-    margin-left: -100%;
-    font-size: 90%;
-}
-
-div.clearer {
-    clear: both;
-}
-
-div.footer {
-    color: #ff4500;
-    width: 100%;
-    padding: 9px 0 9px 0;
-    text-align: center;
-    font-size: 75%;
-}
-
-div.footer a {
-    color: #ff4500;
-    text-decoration: underline;
-}
-
-div.related {
-    background-color: #ff7700;
-    color: white;
-    width: 100%;
-    height: 30px;
-    line-height: 30px;
-    font-size: 90%;
-}
-
-div.related h3 {
-    display: none;
-}
-
-div.related ul {
-    margin: 0;
-    padding: 0 0 0 10px;
-    list-style: none;
-}
-
-div.related li {
-    display: inline;
-}
-
-div.related li.right {
-    float: right;
-    margin-right: 5px;
-}
-
-div.related a {
-    color: white;
-    font-weight:bold;
-}
-
-/* ::: TOC :::: */
-
-div.sphinxsidebar {
-    border-style:solid;
-    border-color: white;
-/*    background-color:#e2e2e2;*/
-    padding-bottom:5px;
-}
-
-div.sphinxsidebar h3 {
-    font-family: Verdana, sans-serif;
-    color: black;
-    font-size: 1.2em;
-    font-weight: normal;
-    margin: 0;
-    padding: 0;
-    font-weight:bold;
-    font-style:italic;
-}
-
-div.sphinxsidebar h4 {
-    font-family: Verdana, sans-serif;
-    color: black;
-    font-size: 1.1em;
-    font-weight: normal;
-    margin: 5px 0 0 0;
-    padding: 0;
-    font-weight:bold;
-    font-style:italic;
-}
-
-div.sphinxsidebar p {
-    color: black;
-}
-
-div.sphinxsidebar p.topless {
-    margin: 5px 10px 10px 10px;
-}
-
-div.sphinxsidebar ul {
-    margin: 10px;
-    padding: 0;
-    list-style: none;
-    color: black;
-}
-
-div.sphinxsidebar ul ul,
-div.sphinxsidebar ul.want-points {
-    margin-left: 20px;
-    list-style: square;
-}
-
-div.sphinxsidebar ul ul {
-    margin-top: 0;
-    margin-bottom: 0;
-}
-
-div.sphinxsidebar a {
-    color: black;
-    text-decoration: none;
-}
-
-div.sphinxsidebar form {
-    margin-top: 10px;
-}
-
-div.sphinxsidebar input {
-    border: 1px solid #e2e2e2;
-    font-family: sans-serif;
-    font-size: 1em;
-    padding-bottom: 5px;
-}
-
-/* :::: MODULE CLOUD :::: */
-div.modulecloud {
-    margin: -5px 10px 5px 10px;
-    padding: 10px;
-    line-height: 160%;
-    border: 1px solid #cbe7e5;
-    background-color: #f2fbfd;
-}
-
-div.modulecloud a {
-    padding: 0 5px 0 5px;
-}
-
-/* :::: SEARCH :::: */
-ul.search {
-    margin: 10px 0 0 20px;
-    padding: 0;
-}
-
-ul.search li {
-    padding: 5px 0 5px 20px;
-    background-image: url(file.png);
-    background-repeat: no-repeat;
-    background-position: 0 7px;
-}
-
-ul.search li a {
-    font-weight: bold;
-}
-
-ul.search li div.context {
-    color: #888;
-    margin: 2px 0 0 30px;
-    text-align: left;
-}
-
-ul.keywordmatches li.goodmatch a {
-    font-weight: bold;
-}
-
-/* :::: COMMON FORM STYLES :::: */
-
-div.actions {
-    padding: 5px 10px 5px 10px;
-    border-top: 1px solid #cbe7e5;
-    border-bottom: 1px solid #cbe7e5;
-    background-color: #e0f6f4;
-}
-
-form dl {
-    color: #333;
-}
-
-form dt {
-    clear: both;
-    float: left;
-    min-width: 110px;
-    margin-right: 10px;
-    padding-top: 2px;
-}
-
-input#homepage {
-    display: none;
-}
-
-div.error {
-    margin: 5px 20px 0 0;
-    padding: 5px;
-    border: 1px solid #d00;
-    font-weight: bold;
-}
-
-/* :::: INLINE COMMENTS :::: */
-
-div.inlinecomments {
-    position: absolute;
-    right: 20px;
-}
-
-div.inlinecomments a.bubble {
-    display: block;
-    float: right;
-    background-image: url(style/comment.png);
-    background-repeat: no-repeat;
-    width: 25px;
-    height: 25px;
-    text-align: center;
-    padding-top: 3px;
-    font-size: 0.9em;
-    line-height: 14px;
-    font-weight: bold;
-    color: black;
-}
-
-div.inlinecomments a.bubble span {
-    display: none;
-}
-
-div.inlinecomments a.emptybubble {
-    background-image: url(style/nocomment.png);
-}
-
-div.inlinecomments a.bubble:hover {
-    background-image: url(style/hovercomment.png);
-    text-decoration: none;
-    color: #3ca0a4;
-}
-
-div.inlinecomments div.comments {
-    float: right;
-    margin: 25px 5px 0 0;
-    max-width: 50em;
-    min-width: 30em;
-    border: 1px solid #2eabb0;
-    background-color: #f2fbfd;
-    z-index: 150;
-}
-
-div#comments {
-    border: 1px solid #2eabb0;
-    margin-top: 20px;
-}
-
-div#comments div.nocomments {
-    padding: 10px;
-    font-weight: bold;
-}
-
-div.inlinecomments div.comments h3,
-div#comments h3 {
-    margin: 0;
-    padding: 0;
-    background-color: #2eabb0;
-    color: white;
-    border: none;
-    padding: 3px;
-}
-
-div.inlinecomments div.comments div.actions {
-    padding: 4px;
-    margin: 0;
-    border-top: none;
-}
-
-div#comments div.comment {
-    margin: 10px;
-    border: 1px solid #2eabb0;
-}
-
-div.inlinecomments div.comment h4,
-div.commentwindow div.comment h4,
-div#comments div.comment h4 {
-    margin: 10px 0 0 0;
-    background-color: #2eabb0;
-    color: white;
-    border: none;
-    padding: 1px 4px 1px 4px;
-}
-
-div#comments div.comment h4 {
-    margin: 0;
-}
-
-div#comments div.comment h4 a {
-    color: #d5f4f4;
-}
-
-div.inlinecomments div.comment div.text,
-div.commentwindow div.comment div.text,
-div#comments div.comment div.text {
-    margin: -5px 0 -5px 0;
-    padding: 0 10px 0 10px;
-}
-
-div.inlinecomments div.comment div.meta,
-div.commentwindow div.comment div.meta,
-div#comments div.comment div.meta {
-    text-align: right;
-    padding: 2px 10px 2px 0;
-    font-size: 95%;
-    color: #538893;
-    border-top: 1px solid #cbe7e5;
-    background-color: #e0f6f4;
-}
-
-div.commentwindow {
-    position: absolute;
-    width: 500px;
-    border: 1px solid #cbe7e5;
-    background-color: #f2fbfd;
-    display: none;
-    z-index: 130;
-}
-
-div.commentwindow h3 {
-    margin: 0;
-    background-color: #2eabb0;
-    color: white;
-    border: none;
-    padding: 5px;
-    font-size: 1.5em;
-    cursor: pointer;
-}
-
-div.commentwindow div.actions {
-    margin: 10px -10px 0 -10px;
-    padding: 4px 10px 4px 10px;
-    color: #538893;
-}
-
-div.commentwindow div.actions input {
-    border: 1px solid #2eabb0;
-    background-color: white;
-    color: #135355;
-    cursor: pointer;
-}
-
-div.commentwindow div.form {
-    padding: 0 10px 0 10px;
-}
-
-div.commentwindow div.form input,
-div.commentwindow div.form textarea {
-    border: 1px solid #3c9ea2;
-    background-color: white;
-    color: black;
-}
-
-div.commentwindow div.error {
-    margin: 10px 5px 10px 5px;
-    background-color: #fbe5dc;
-    display: none;
-}
-
-div.commentwindow div.form textarea {
-    width: 99%;
-}
-
-div.commentwindow div.preview {
-    margin: 10px 0 10px 0;
-    background-color: #70d0d4;
-    padding: 0 1px 1px 25px;
-}
-
-div.commentwindow div.preview h4 {
-    margin: 0 0 -5px -20px;
-    padding: 4px 0 0 4px;
-    color: white;
-    font-size: 1.3em;
-}
-
-div.commentwindow div.preview div.comment {
-    background-color: #f2fbfd;
-}
-
-div.commentwindow div.preview div.comment h4 {
-    margin: 10px 0 0 0!important;
-    padding: 1px 4px 1px 4px!important;
-    font-size: 1.2em;
-}
-
-/* :::: SUGGEST CHANGES :::: */
-div#suggest-changes-box input, div#suggest-changes-box textarea {
-    border: 1px solid #ccc;
-    background-color: white;
-    color: black;
-}
-
-div#suggest-changes-box textarea {
-    width: 99%;
-    height: 400px;
-}
-
-
-/* :::: PREVIEW :::: */
-div.preview {
-    background-image: url(style/preview.png);
-    padding: 0 20px 20px 20px;
-    margin-bottom: 30px;
-}
-
-
-/* :::: INDEX PAGE :::: */
-
-table.contentstable {
-    width: 90%;
-}
-
-table.contentstable p.biglink {
-    line-height: 150%;
-}
-
-a.biglink {
-    font-size: 1.3em;
-}
-
-span.linkdescr {
-    font-style: italic;
-    padding-top: 5px;
-    font-size: 90%;
-}
-
-/* :::: INDEX STYLES :::: */
-
-table.indextable td {
-    text-align: left;
-    vertical-align: top;
-}
-
-table.indextable dl, table.indextable dd {
-    margin-top: 0;
-    margin-bottom: 0;
-}
-
-table.indextable tr.pcap {
-    height: 10px;
-}
-
-table.indextable tr.cap {
-    margin-top: 10px;
-    background-color: #f2f2f2;
-}
-
-img.toggler {
-    margin-right: 3px;
-    margin-top: 3px;
-    cursor: pointer;
-}
-
-form.pfform {
-    margin: 10px 0 20px 0;
-}
-
-/* :::: GLOBAL STYLES :::: */
-
-.docwarning {
-    background-color: #ffe4e4;
-    padding: 10px;
-    margin: 0 -20px 0 -20px;
-    border-bottom: 1px solid #f66;
-}
-
-p.subhead {
-    font-weight: bold;
-    margin-top: 20px;
-}
-
-a {
-    color: orangered;
-    text-decoration: none;
-}
-
-a:hover {
-    text-decoration: underline;
-}
-
-div.body h1,
-div.body h2,
-div.body h3,
-div.body h4,
-div.body h5,
-div.body h6 {
-    font-family: 'Verdana', sans-serif;
-    background-color: white;
-    font-weight: bold;
-    color: black;
-    border-bottom: 1px solid #ccc;
-    margin: 20px -20px 10px -20px;
-    padding: 3px 0 3px 10px;
-}
-
-div.body h1 { margin-top: 10pt; font-size: 150%; }
-div.body h2 { font-size: 120%; }
-div.body h3 { font-size: 100%; }
-div.body h4 { font-size: 80%; }
-div.body h5 { font-size: 600%; }
-div.body h6 { font-size: 40%; }
-
-a.headerlink {
-    color: #c60f0f;
-    font-size: 0.8em;
-    padding: 0 4px 0 4px;
-    text-decoration: none;
-    visibility: hidden;
-}
-
-h1:hover > a.headerlink,
-h2:hover > a.headerlink,
-h3:hover > a.headerlink,
-h4:hover > a.headerlink,
-h5:hover > a.headerlink,
-h6:hover > a.headerlink,
-dt:hover > a.headerlink {
-    visibility: visible;
-}
-
-a.headerlink:hover {
-    background-color: #c60f0f;
-    color: white;
-}
-
-div.body p, div.body dd, div.body li {
-    text-align: justify;
-    line-height: 130%;
-}
-
-div.body p.caption {
-    text-align: inherit;
-}
-
-div.body td {
-    text-align: left;
-}
-
-ul.fakelist {
-    list-style: none;
-    margin: 10px 0 10px 20px;
-    padding: 0;
-}
-
-.field-list ul {
-    padding-left: 1em;
-}
-
-.first {
-    margin-top: 0 !important;
-}
-
-/* "Footnotes" heading */
-p.rubric {
-    margin-top: 30px;
-    font-weight: bold;
-}
-
-/* "Topics" */
-
-div.topic {
-    background-color: #eee;
-    border: 1px solid #ccc;
-    padding: 0 7px 0 7px;
-    margin: 10px 0 10px 0;
-}
-
-p.topic-title {
-    font-size: 1.1em;
-    font-weight: bold;
-    margin-top: 10px;
-}
-
-/* Admonitions */
-
-div.admonition {
-    margin-top: 10px;
-    margin-bottom: 10px;
-    padding: 7px;
-}
-
-div.admonition dt {
-    font-weight: bold;
-}
-
-div.admonition dl {
-    margin-bottom: 0;
-}
-
-div.admonition p {
-    display: inline;
-}
-
-div.seealso {
-    background-color: #ffc;
-    border: 1px solid #ff6;
-}
-
-div.warning {
-    background-color: #ffe4e4;
-    border: 1px solid #f66;
-}
-
-div.note {
-    background-color: #eee;
-    border: 1px solid #ccc;
-}
-
-p.admonition-title {
-    margin: 0px 10px 5px 0px;
-    font-weight: bold;
-    display: inline;
-}
-
-p.admonition-title:after {
-    content: ":";
-}
-
-div.body p.centered {
-    text-align: center;
-    margin-top: 25px;
-}
-
-table.docutils {
-    border: 0;
-}
-
-table.docutils td, table.docutils th {
-    padding: 1px 8px 1px 0;
-    border-top: 0;
-    border-left: 0;
-    border-right: 0;
-    border-bottom: 1px solid #aaa;
-}
-
-table.field-list td, table.field-list th {
-    border: 0 !important;
-}
-
-table.footnote td, table.footnote th {
-    border: 0 !important;
-}
-
-.field-list ul {
-    margin: 0;
-    padding-left: 1em;
-}
-
-.field-list p {
-    margin: 0;
-}
-
-dl {
-    margin-bottom: 15px;
-    clear: both;
-}
-
-dd p {
-    margin-top: 0px;
-}
-
-dd ul, dd table {
-    margin-bottom: 10px;
-}
-
-dd {
-    margin-top: 3px;
-    margin-bottom: 10px;
-    margin-left: 30px;
-}
-
-.refcount {
-    color: #060;
-}
-
-dt:target,
-.highlight {
-    background-color: #fbe54e;
-}
-
-dl.glossary dt {
-    font-weight: bold;
-    font-size: 1.1em;
-}
-
-th {
-    text-align: left;
-    padding-right: 5px;
-}
-
-pre {
-    padding: 5px;
-    background-color: #efc;
-    color: #333;
-    border: 1px solid #ac9;
-    border-left: none;
-    border-right: none;
-    overflow: auto;
-}
-
-td.linenos pre {
-    padding: 5px 0px;
-    border: 0;
-    background-color: transparent;
-    color: #aaa;
-}
-
-table.highlighttable {
-    margin-left: 0.5em;
-}
-
-table.highlighttable td {
-    padding: 0 0.5em 0 0.5em;
-}
-
-tt {
-    background-color: #ecf0f3;
-    padding: 0 1px 0 1px;
-    font-size: 0.95em;
-}
-
-tt.descname {
-    background-color: transparent;
-    font-weight: bold;
-    font-size: 1.2em;
-}
-
-tt.descclassname {
-    background-color: transparent;
-}
-
-tt.xref, a tt {
-    background-color: transparent;
-    font-weight: bold;
-}
-
-.footnote:target  { background-color: #ffa }
-
-h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
-    background-color: transparent;
-}
-
-.optional {
-    font-size: 1.3em;
-}
-
-.versionmodified {
-    font-style: italic;
-}
-
-form.comment {
-    margin: 0;
-    padding: 10px 30px 10px 30px;
-    background-color: #eee;
-}
-
-form.comment h3 {
-    background-color: #326591;
-    color: white;
-    margin: -10px -30px 10px -30px;
-    padding: 5px;
-    font-size: 1.4em;
-}
-
-form.comment input,
-form.comment textarea {
-    border: 1px solid #ccc;
-    padding: 2px;
-    font-family: sans-serif;
-    font-size: 100%;
-}
-
-form.comment input[type="text"] {
-    width: 240px;
-}
-
-form.comment textarea {
-    width: 100%;
-    height: 200px;
-    margin-bottom: 10px;
-}
-
-.system-message {
-    background-color: #fda;
-    padding: 5px;
-    border: 3px solid red;
-}
-
-/* :::: PRINT :::: */
-@media print {
-    div.document,
-    div.documentwrapper,
-    div.bodywrapper {
-        margin: 0;
-        width : 100%;
-    }
-
-    div.sphinxsidebar,
-    div.related,
-    div.footer,
-    div#comments div.new-comment-box,
-    #top-link {
-        display: none;
-    }
-}
--- a/doc/book/en/.templates/layout.html	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,196 +0,0 @@
-{%- block doctype -%}
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-{%- endblock %}
-{%- set reldelim1 = reldelim1 is not defined and ' &raquo;' or reldelim1 %}
-{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %}
-{%- macro relbar() %}
-    <div class="related">
-      <h3>Navigation</h3>
-      <ul>
-        {%- for rellink in rellinks %}
-        <li class="right" {% if loop.first %}style="margin-right: 10px"{% endif %}>
-          <a href="{{ pathto(rellink[0]) }}" title="{{ rellink[1]|striptags }}"
-             accesskey="{{ rellink[2] }}">{{ rellink[3] }}</a>
-          {%- if not loop.first %}{{ reldelim2 }}{% endif %}</li>
-        {%- endfor %}
-        {%- block rootrellink %}
-        <li><a href="{{ pathto('index') }}">{{ shorttitle }}</a>{{ reldelim1 }}</li>
-        {%- endblock %}
-        {%- for parent in parents %}
-          <li><a href="{{ parent.link|e }}" accesskey="U">{{ parent.title }}</a>{{ reldelim1 }}</li>
-        {%- endfor %}
-        {%- block relbaritems %}{% endblock %}
-      </ul>
-    </div>
-{%- endmacro %}
-{%- macro sidebar() %}
-      {%- if builder != 'htmlhelp' %}
-      <div class="sphinxsidebar">
-        <div class="sphinxsidebarwrapper">
-          {%- block sidebarlogo %}
-          {%- if logo %}
-            <p class="logo"><img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/></p>
-          {%- endif %}
-          {%- endblock %}
-          {%- block sidebartoc %}
-          {%- if display_toc %}
-            <h3>Table Of Contents</h3>
-            {{ toc }}
-          {%- endif %}
-          {%- endblock %}
-          {%- block sidebarrel %}
-          {%- if prev %}
-            <h4>Previous topic</h4>
-            <p class="topless"><a href="{{ prev.link|e }}" title="previous chapter">{{ prev.title }}</a></p>
-          {%- endif %}
-          {%- if next %}
-            <h4>Next topic</h4>
-            <p class="topless"><a href="{{ next.link|e }}" title="next chapter">{{ next.title }}</a></p>
-          {%- endif %}
-          {%- endblock %}
-          {%- if sourcename %}
-            <!--<h3>This Page</h3>
-            <ul class="this-page-menu">
-            {%- if builder == 'web' %}
-              <li><a href="#comments">Comments ({{ comments|length }} so far)</a></li>
-              <li><a href="{{ pathto('@edit/' + sourcename)|e }}">Suggest Change</a></li>
-              <li><a href="{{ pathto('@source/' + sourcename)|e }}">Show Source</a></li>
-            {%- elif builder == 'html' %}
-              <li><a href="{{ pathto('_sources/' + sourcename, true)|e }}">Show Source</a></li>
-            {%- endif %}
-            </ul>-->
-          {%- endif %}
-          {%- if customsidebar %}
-          {{ rendertemplate(customsidebar) }}
-          {%- endif %}
-          {%- block sidebarsearch %}
-          {%- if pagename != "search" %}
-            <h3>{{ builder == 'web' and 'Keyword' or 'Quick' }} search</h3>
-            <form class="search" action="{{ pathto('search') }}" method="get">
-              <input type="text" name="q" size="18" /> <input type="submit" value="Go" />
-              <input type="hidden" name="check_keywords" value="yes" />
-              <input type="hidden" name="area" value="default" />
-            </form>
-            {%- if builder == 'web' %}
-            <p style="font-size: 90%">Enter a module, class or function name.</p>
-            {%- endif %}
-          {%- endif %}
-          {%- endblock %}
-        </div>
-      </div>
-      {%- endif %}
-{%- endmacro -%}
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-  <head>
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-    {%- if builder != 'htmlhelp' %}
-      {%- set titlesuffix = " &mdash; " + docstitle %}
-    {%- endif %}
-    <title>{{ title|striptags }}{{ titlesuffix }}</title>
-    {%- if builder == 'web' %}
-    <link rel="stylesheet" href="{{ pathto('index') }}?do=stylesheet{%
-      if in_admin_panel %}&admin=yes{% endif %}" type="text/css" />
-    {%- for link, type, title in page_links %}
-    <link rel="alternate" type="{{ type|e(true) }}" title="{{ title|e(true) }}" href="{{ link|e(true) }}" />
-    {%- endfor %}
-    {%- else %}
-    <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
-    <link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" />
-    {%- endif %}
-    {%- if builder != 'htmlhelp' %}
-    <script type="text/javascript">
-      var DOCUMENTATION_OPTIONS = {
-          URL_ROOT:    '{{ pathto("", 1) }}',
-          VERSION:     '{{ release }}',
-          COLLAPSE_MODINDEX: false,
-          FILE_SUFFIX: '{{ file_suffix }}'
-      };
-    </script>
-    <script type="text/javascript" src="{{ pathto('_static/jquery.js', 1) }}"></script>
-    <script type="text/javascript" src="{{ pathto('_static/interface.js', 1) }}"></script>
-    <script type="text/javascript" src="{{ pathto('_static/doctools.js', 1) }}"></script>
-    <script type="text/javascript" src="{{ pathto('_static/searchtools.js', 1) }}"></script>
-    {%- if use_opensearch %}
-    <link rel="search" type="application/opensearchdescription+xml"
-          title="Search within {{ docstitle }}"
-          href="{{ pathto('_static/opensearch.xml', 1) }}"/>
-    {%- endif %}
-    {%- if favicon %}
-    <link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/>
-    {%- endif %}
-    {%- endif %}
-{%- block rellinks %}
-    {%- if hasdoc('about') %}
-    <link rel="author" title="About these documents" href="{{ pathto('about') }}" />
-    {%- endif %}
-    <link rel="contents" title="Global table of contents" href="{{ pathto('contents') }}" />
-    <link rel="index" title="Global index" href="{{ pathto('genindex') }}" />
-    <link rel="search" title="Search" href="{{ pathto('search') }}" />
-    {%- if hasdoc('copyright') %}
-    <link rel="copyright" title="Copyright" href="{{ pathto('copyright') }}" />
-    {%- endif %}
-    <link rel="top" title="{{ docstitle }}" href="{{ pathto('index') }}" />
-    {%- if parents %}
-    <link rel="up" title="{{ parents[-1].title|striptags }}" href="{{ parents[-1].link|e }}" />
-    {%- endif %}
-    {%- if next %}
-    <link rel="next" title="{{ next.title|striptags }}" href="{{ next.link|e }}" />
-    {%- endif %}
-    {%- if prev %}
-    <link rel="prev" title="{{ prev.title|striptags }}" href="{{ prev.link|e }}" />
-    {%- endif %}
-{%- endblock %}
-{%- block extrahead %}{% endblock %}
-  </head>
-  <body>
-
-{% block logilablogo %}
-<div class="logilablogo">
-	<a class="logogo" href="http://www.cubicweb.org"><img border="0" src="{{ pathto('_static/cubicweb.png', 1) }}"/></a>
-  </div>
-{% endblock %}
-
-{%- block relbar1 %}{{ relbar() }}{% endblock %}
-
-{%- block sidebar1 %}{# possible location for sidebar #}{% endblock %}
-
-{%- block document %}
-    <div class="document">
-      <div class="documentwrapper">
-      {%- if builder != 'htmlhelp' %}
-        <div class="bodywrapper">
-      {%- endif %}
-          <div class="body">
-            {% block body %}{% endblock %}
-          </div>
-      {%- if builder != 'htmlhelp' %}
-        </div>
-      {%- endif %}
-      </div>
-{%- endblock %}
-
-{%- block sidebar2 %}{{ sidebar() }}{% endblock %}
-      <div class="clearer"></div>
-    </div>
-
-{%- block relbar2 %}{{ relbar() }}{% endblock %}
-
-{%- block footer %}
-    <div class="footer">
-    {%- if hasdoc('copyright') %}
-      &copy; <a href="{{ pathto('copyright') }}">Copyright</a> {{ copyright }}.
-    {%- else %}
-      &copy; Copyright {{ copyright }}.
-    {%- endif %}
-    {%- if last_updated %}
-      Last updated on {{ last_updated }}.
-    {%- endif %}
-    {%- if show_sphinx %}
-      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
-    {%- endif %}
-    </div>
-{%- endblock %}
-  </body>
-</html>
--- a/doc/book/en/MERGE_ME-tut-create-app.en.txt	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,386 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-
-Tutoriel : créer votre première application web pour Google AppEngine
-=====================================================================
-
-[TRANSLATE ME TO FRENCH]
-
-This tutorial will guide you step by step to build a blog application 
-and discover the unique features of `LAX`. It assumes that you followed
-the :ref:`installation` guidelines and that both the `AppEngine SDK` and the
-`LAX` framework are setup on your computer.
-
-Creating a new application
---------------------------
-
-We choosed in this tutorial to develop a blog as an example of web application
-and will go through each required steps/actions to have it running with `LAX`.
-When you installed `LAX`, you saw a directory named ``skel``. Make a copy of
-this directory and call it ``BlogDemo``.
-
-The location of this directory does not matter. But once decided, make sure your ``PYTHONPATH`` is properly set (:ref:`installation`).
-
-
-Defining a schema
------------------
-
-With `LAX`, the schema/datamodel is the core of the application. This is where
-you will define the type of content you have to hanlde in your application.
-
-Let us start with something simple and improve on it iteratively. 
-
-In schema.py, we define two entities: ``Blog`` and ``BlogEntry``.
-
-::
-
-  class Blog(EntityType):
-      title = String(maxsize=50, required=True)
-      description = String()
-
-  class BlogEntry(EntityType):
-      title = String(maxsize=100, required=True)
-      publish_date = Date(default='TODAY')
-      text = String(fulltextindexed=True)
-      category = String(vocabulary=('important','business'))
-      entry_of = SubjectRelation('Blog', cardinality='?*')
-
-A Blog has a title and a description. The title is a string that is
-required by the class EntityType and must be less than 50 characters. 
-The description is a string that is not constrained.
-
-A BlogEntry has a title, a publish_date and a text. The title is a
-string that is required and must be less than 100 characters. The
-publish_date is a Date with a default value of TODAY, meaning that
-when a BlogEntry is created, its publish_date will be the current day
-unless it is modified. The text is a string that will be indexed in
-the full-text index and has no constraint.
-
-A BlogEntry also has a relationship ``entry_of`` that link it to a
-Blog. The cardinality ``?*`` means that a BlogEntry can be part of
-zero or one Blog (``?`` means `zero or one`) and that a Blog can
-have any number of BlogEntry (``*`` means `any number including
-zero`). For completeness, remember that ``+`` means `one or more`.
-
-Running the application
------------------------
-
-Defining this simple schema is enough to get us started. Make sure you
-followed the setup steps described in detail in the installation
-chapter (especially visiting http://localhost:8080/_load as an
-administrator), then launch the application with the command::
-
-   python dev_appserver.py BlogDemo
-
-and point your browser at http://localhost:8080/ (if it is easier for
-you, use the on-line demo at http://lax.appspot.com/).
-
-.. image:: images/lax-book.00-login.en.png
-   :alt: login screen
-
-After you log in, you will see the home page of your application. It
-lists the entity types: Blog and BlogEntry. If these links read
-``blog_plural`` and ``blogentry_plural`` it is because
-internationalization (i18n) is not working for you yet. Please ignore
-this for now.
-
-.. image:: images/lax-book.01-start.en.png
-   :alt: home page
-
-Creating system entities
-------------------------
-You can only create new users if you decided not to use google authentication.
-
-
-[WRITE ME : create users manages permissions etc]
-
-
-
-Creating application entites
-----------------------------
-
-Create a Blog
-~~~~~~~~~~~~~
-
-Let us create a few of these entities. Click on the [+] at the right
-of the link Blog.  Call this new Blog ``Tech-blog`` and type in
-``everything about technology`` as the description, then validate the
-form by clicking on ``Validate``.
-
-.. image:: images/lax-book.02-create-blog.en.png
-   :alt: from to create blog
-
-Click on the logo at top left to get back to the home page, then
-follow the Blog link that will list for you all the existing Blog.
-You should be seeing a list with a single item ``Tech-blog`` you
-just created.
-
-.. image:: images/lax-book.03-list-one-blog.en.png
-   :alt: displaying a list of a single blog
-
-Clicking on this item will get you to its detailed description except
-that in this case, there is not much to display besides the name and
-the phrase ``everything about technology``.
-
-.. image:: images/lax-book.04-detail-one-blog.en.png
-  :alt: displaying the detailed view of a blog
-
-Now get back to the home page by clicking on the top-left logo, then
-create a new Blog called ``MyLife`` and get back to the home page
-again to follow the Blog link for the second time. The list now
-has two items.
-
-.. image:: images/lax-book.05-list-two-blog.en.png
-   :alt: displaying a list of two blogs
-
-
-Create a BlogEntry
-~~~~~~~~~~~~~~~~~~
-
-Get back to the home page and click on [+] at the right of the link
-BlogEntry. Call this new entry ``Hello World`` and type in some text
-before clicking on ``Validate``. You added a new blog entry without
-saying to what blog it belongs. There is a box on the left entitled
-``actions``, click on the menu item ``modify``. You are back to the form
-to edit the blog entry you just created, except that the form now has
-another section with a combobox titled ``add relation``. Chose
-``entry_of`` in this menu and a second combobox appears where you pick
-``MyLife``. 
-
-You could also have, at the time you started to fill the form for a
-new entity BlogEntry, hit ``Apply`` instead of ``Validate`` and the 
-combobox titled ``add relation`` would have showed up.
-
-.. image:: images/lax-book.06-add-relation-entryof.en.png
-   :alt: editing a blog entry to add a relation to a blog
-
-Validate the changes by clicking ``Validate``. The entity BlogEntry
-that is displayed now includes a link to the entity Blog named
-``MyLife``.
-
-.. image:: images/lax-book.07-detail-one-blogentry.en.png
-   :alt: displaying the detailed view of a blogentry
-
-Remember that all of this was handled by the framework and that the
-only input that was provided so far is the schema. To get a graphical
-view of the schema, run the ``laxctl genschema BlogDemo`` command as
-explained in the installation section and point your browser to the
-URL http://localhost:8080/schema
-
-.. image:: images/lax-book.08-schema.en.png
-   :alt: graphical view of the schema (aka data-model)
-
-Site configuration
-------------------
-
-.. image:: images/lax-book.03-site-config-panel.en.png
-
-This panel allows you to configure the appearance of your application site.
-Six menus are available and we will go through each of them to explain how
-to use them.
-
-Navigation
-~~~~~~~~~~
-This menu provides you a way to adjust some navigation options depending on
-your needs, such as the number of entities to display by page of results.
-Follows the detailled list of available options:
-  
-* navigation.combobox-limit: maximum number of entities to display in related
-  combo box (sample format: 23)
-* navigation.page-size: maximum number of objects displayed by page of results 
-  (sample format: 23)
-* navigation.related-limit: maximum number of related entities to display in 
-  the primary view (sample format: 23)
-* navigation.short-line-size: maximum number of characters in short description
-  (sample format: 23)
-
-UI
-~~
-This menu provides you a way to customize the user interface settings such as
-date format or encoding in the produced html.
-Follows the detailled list of available options:
-
-* ui.date-format : how to format date in the ui ("man strftime" for format description)
-* ui.datetime-format : how to format date and time in the ui ("man strftime" for format
-  description)
-* ui.default-text-format : default text format for rich text fields.
-* ui.encoding : user interface encoding
-* ui.fckeditor : should html fields being edited using fckeditor (a HTML WYSIWYG editor).
-  You should also select text/html as default text format to actually get fckeditor.
-* ui.float-format : how to format float numbers in the ui
-* ui.language : language of the user interface
-* ui.main-template : id of main template used to render pages
-* ui.site-title	: site title, which is displayed right next to the logo in the header
-* ui.time-format : how to format time in the ui ("man strftime" for format description)
-
-
-Actions
-~~~~~~~
-This menu provides a way to configure the context in which you expect the actions
-to be displayed to the user and if you want the action to be visible or not. 
-You must have notice that when you view a list of entities, an action box is 
-available on the left column which display some actions as well as a drop-down 
-menu for more actions. 
-
-The context available are:
-
-* mainactions : actions listed in the left box
-* moreactions : actions listed in the `more` menu of the left box
-* addrelated : add actions listed in the left box
-* useractions : actions listed in the first section of drop-down menu 
-  accessible from the right corner user login link
-* siteactions : actions listed in the second section of drop-down menu
-  accessible from the right corner user login link
-* hidden : select this to hide the specific action
-
-Boxes
-~~~~~
-The application has already a pre-defined set of boxes you can use right away. 
-This configuration section allows you to place those boxes where you want in the
-application interface to customize it. 
-
-The available boxes are:
-
-* actions box : box listing the applicable actions on the displayed data
-
-* boxes_blog_archives_box : box listing the blog archives 
-
-* possible views box : box listing the possible views for the displayed data
-
-* rss box : RSS icon to get displayed data as a RSS thread
-
-* search box : search box
-
-* startup views box : box listing the configuration options available for 
-  the application site, such as `Preferences` and `Site Configuration`
-
-Components
-~~~~~~~~~~
-[WRITE ME]
-
-Contextual components
-~~~~~~~~~~~~~~~~~~~~~
-[WRITE ME]
-
-Set-up a workflow
------------------
-
-Before starting, make sure you refresh your mind by reading [link to
-definition_workflow chapter].
-
-We want to create a workflow to control the quality of the BlogEntry 
-submitted on your application. When a BlogEntry is created by a user
-its state should be `submitted`. To be visible to all, it needs to
-be in the state `published`. To move from `submitted` to `published`
-we need a transition that we can name `approve_blogentry`.
-
-We do not want every user to be allowed to change the state of a 
-BlogEntry. We need to define a group of user, `moderators`, and 
-this group will have appropriate permissions to approve BlogEntry
-to be published and visible to all.
-
-There are two ways to create a workflow, form the user interface,
-and also by defining it in ``migration/postcreate.py``. This script
-is executed each time a new ``./bin/laxctl db-init`` is done. 
-If you create the states and transitions through the user interface
-this means that next time you will need to initialize the database
-you will have to re-create all the entities. 
-We strongly recommand you create the workflow in ``migration\postcreate.py``
-and we will now show you how.
-The user interface would only be a reference for you to view the states 
-and transitions but is not the appropriate interface to define your
-application workflow.
-
-Update the schema
-~~~~~~~~~~~~~~~~~
-To enable a BlogEntry to have a State, we have to define a relation
-``in_state`` in the schema of BlogEntry. Please do as follows, add
-the line ``in_state (...)``::
-
-  class BlogEntry(EntityType):
-      title = String(maxsize=100, required=True)
-      publish_date = Date(default='TODAY')
-      text_format = String(meta=True, internationalizable=True, maxsize=50,
-                           default='text/rest', constraints=[format_constraint])
-      text = String(fulltextindexed=True)
-      category = String(vocabulary=('important','business'))
-      entry_of = SubjectRelation('Blog', cardinality='?*')
-      in_state = SubjectRelation('State', cardinality='1*')
-
-As you updated the schema, you will have re-execute ``./bin/laxctl db-init``
-to initialize the database and migrate your existing entities.
-[WRITE ABOUT MIGRATION]
-
-Create states, transitions and group permissions
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-At the time the ``postcreate.py`` script is executed, several methods
-can be used. They are all defined in the ``class ServerMigrationHelper``.
-We will only discuss the method we use to create a wrokflow here.
-
-To define our workflow for BlogDemo, please add the following lines
-to ``migration/postcreate.py``::
-  
-  _ = unicode
-
-  moderators      = add_entity('CWGroup', name=u"moderators")
-
-  submitted = add_state(_('submitted'), 'BlogEntry', initial=True)
-  published = add_state(_('published'), 'BlogEntry')
-
-  add_transition(_('approve_blogentry'), 'BlogEntry', (submitted,), published, ('moderators', 'managers'),)
-
-  checkpoint()
-
-``add_entity`` is used here to define the new group of users that we
-need to define the transitions, `moderators`.
-If this group required by the transition is not defined before the
-transition is created, it will not create the relation `transition 
-require the group moderator`.
-
-``add_state`` expects as the first argument the name of the state you are
-willing to create, then the entity type on which the state can be applied, 
-and an optionnal argument to set if the state is the initial state
-of the entity type or not.
-
-``add_transition`` expects as the first argument the name of the 
-transition, then the entity type on which we can apply the transition,
-then the list of possible initial states from which the transition
-can be applied, the target state of the transition, and the permissions
-(e.g. list of the groups of users who can apply the transition).
-
-.. image:: images/lax-book.03-transitions-view.en.png
-
-You can now notice that in the actions box of a BlogEntry, the state
-is now listed as well as the possible transitions from this state
-defined by the workflow. This transition, as defined in the workflow,
-will only being displayed for the users belonging to the group
-moderators of managers.
-
-Change view permission
-~~~~~~~~~~~~~~~~~~~~~~
-
-
-
-Conclusion
-----------
-
-Exercise
-~~~~~~~~
-
-Create new blog entries in ``Tech-blog``.
-
-What we learned
-~~~~~~~~~~~~~~~
-
-Creating a simple schema was enough to set up a new application that
-can store blogs and blog entries. 
-
-What is next ?
-~~~~~~~~~~~~~~
-
-Although the application is fully functionnal, its look is very
-basic. In the following section we will learn to create views to
-customize how data is displayed.
-
-
--- a/doc/book/en/MERGE_ME-tut-create-gae-app.en.txt	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,218 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _tutorielGAE:
-
-Tutoriel : créer votre première application web pour Google AppEngine
-=====================================================================
-
-Ce tutoriel va vous guider pas à pas a construire une apllication web 
-de gestion de Blog afin de vous faire découvrir les fonctionnalités de
-*CubicWeb*.
-
-Nous supposons que vous avec déjà suivi le guide :ref:`installationGAE`.
-
-
-Créez une nouvelle application
-------------------------------
-
-Nous choisissons dans ce tutoriel de développer un blog comme un exemple
-d'application web et nous allons expliciter toutes les étapes nécessaires
-à sa réalisation.  
-
-::
-  
-  cubicweb-ctl newgapp blogdemo
-
-`newgapp` est la commande permettant de créer une instance *CubicWeb* pour
-le datastore.
-
-Assurez-vous que votre variable d'environnement ``PYTHONPATH`` est correctement
-initialisée (:ref:`installationGAE`)
-
-Définissez un schéma
---------------------
-
-Le modèle de données ou schéma est au coeur d'une application *CubicWeb*.
-C'est là où vous allez devoir définir le type de contenu que votre application
-devra gérer.
-
-Commençons par un schéma simple que nous améliorerons progressivemment.
-
-Une fois votre instance ``blogdemo`` crée, vous trouverez un fichier ``schema.py``
-contenant la définition des entités suivantes : ``Blog`` and ``BlogEntry``.
-
-::
-
-  class Blog(EntityType):
-      title = String(maxsize=50, required=True)
-      description = String()
-
-  class BlogEntry(EntityType):
-      title = String(maxsize=100, required=True)
-      publish_date = Date(default='TODAY')
-      text = String(fulltextindexed=True)
-      category = String(vocabulary=('important','business'))
-      entry_of = SubjectRelation('Blog', cardinality='?*')
-
-
-Un ``Blog`` a un titre et une description. Le titre est une chaîne 
-de caractères requise par la classe parente EntityType and ne doit
-pas excéder 50 caractères. La description est une chaîne de 
-caractères sans contraintes.
-
-Une ``BlogEntry`` a un titre, une date de publication et du texte
-étant son contenu. Le titre est une chaîne de caractères qui ne 
-doit pas excéder 100 caractères. La date de publication est de type Date et a
-pour valeur par défaut TODAY, ce qui signifie que lorsqu'une 
-``BlogEntry`` sera créée, sa date de publication sera la date
-courante a moins de modifier ce champ. Le texte est une chaîne de
-caractères qui sera indexée en plein texte et sans contraintes.
-
-Une ``BlogEntry`` a aussi une relation nommée ``entry_of`` qui la
-relie à un ``Blog``. La cardinalité ``?*`` signifie que BlogEntry
-peut faire partie de zero a un Blog (``?`` signifie `zero ou un`) et
-qu'un Blog peut avoir une infinité de BlogEntry (``*`` signifie
-`n'importe quel nombre incluant zero`). 
-Par soucis de complétude, nous rappellerons que ``+`` signifie
-`un ou plus`.
-
-Lancez l'application
---------------------
-
-Définir ce simple schéma est suffisant pour commencer. Assurez-vous 
-que vous avez suivi les étapes décrites dans la section installation
-(en particulier visitez http://localhost:8080/_load en tant qu'administrateur
-afin d'initialiser le datastore), puis lancez votre application avec la commande ::
-   
-   python dev_appserver.py BlogDemo
-
-puis dirigez vous vers http://localhost:8080/ (ou si c'est plus facile
-vous pouvez utiliser la démo en ligne http://lax.appspot.com/).
-[FIXME] -- changer la demo en ligne en quelque chose qui marche (!)
-
-.. image:: images/lax-book.00-login.en.png
-   :alt: login screen
-
-Après vous être authentifié, vous arrivez sur la page d'accueil de votre 
-application. Cette page liste les types d'entités accessibles dans votre
-application, en l'occurrence : Blog et Articles. Si vous lisez ``blog_plural``
-et ``blogentry_plural`` cela signifie que l'internationalisation (i18n)
-n'a pas encore fonctionné. Ignorez cela pour le moment.
-
-.. image:: images/lax-book.01-start.en.png
-   :alt: home page
-
-Créez des entités système
--------------------------
-
-Vous ne pourrez créer de nouveaux utilisateurs que dans le cas où vous
-avez choisi de ne pas utiliser l'authentification Google.
-
-
-[WRITE ME : create users manages permissions etc]
-
-
-
-Créez des entités applicatives
-------------------------------
-
-Créez un Blog
-~~~~~~~~~~~~~
-
-Créons à présent quelques entités. Cliquez sur `[+]` sur la
-droite du lien Blog. Appelez cette nouvelle entité Blog ``Tech-Blog``
-et tapez pour la description ``everything about technology``,
-puis validez le formulaire d'édition en cliquant sur le bouton
-``Validate``.
-
-
-.. image:: images/lax-book.02-create-blog.en.png
-   :alt: from to create blog
-
-En cliquant sur le logo situé dans le coin gauche de la fenêtre,
-vous allez être redirigé vers la page d'accueil. Ensuite, si vous allez 
-sur le lien Blog, vous devriez voir la liste des entités Blog, en particulier
-celui que vous venez juste de créer ``Tech-Blog``.
-
-.. image:: images/lax-book.03-list-one-blog.en.png
-   :alt: displaying a list of a single blog
-
-Si vous cliquez sur ``Tech-Blog`` vous devriez obtenir une description
-détaillée, ce qui dans notre cas, n'est rien de plus que le titre
-et la phrase ``everything about technology``
-
-
-.. image:: images/lax-book.04-detail-one-blog.en.png
-   :alt: displaying the detailed view of a blog
-
-Maintenant retournons sur la page d'accueil et créons un nouveau
-Blog ``MyLife`` et retournons sur la page d'accueil, puis suivons
-le lien Blog et nous constatons qu'à présent deux blogs sont listés.
-
-.. image:: images/lax-book.05-list-two-blog.en.png
-   :alt: displaying a list of two blogs
-
-Créons un article
-~~~~~~~~~~~~~~~~~
-
-Revenons sur la page d'accueil et cliquons sur `[+]` à droite du lien
-`articles`. Appellons cette nouvelle entité ``Hello World`` et introduisons
-un peut de texte avant de ``Valider``. Vous venez d'ajouter un article
-sans avoir précisé à quel Blog il appartenait. Dans la colonne de gauche
-se trouve une boite intitulé ``actions``, cliquez sur le menu ``modifier``.
-Vous êtes de retour sur le formulaire d'édition de l'article que vous 
-venez de créer, à ceci près que ce formulaire a maintenant une nouvelle
-section intitulée ``ajouter relation``. Choisissez ``entry_of`` dans ce menu,
-cela va faire apparaitre une deuxième menu déroulant dans lequel vous
-allez pouvoir séléctionner le Blog ``MyLife``.
-
-Vous auriez pu aussi, au moment où vous avez crée votre article, sélectionner
-``appliquer`` au lieu de ``valider`` et le menu ``ajouter relation`` serait apparu.
-
-.. image:: images/lax-book.06-add-relation-entryof.en.png
-   :alt: editing a blog entry to add a relation to a blog
-
-Validez vos modifications en cliquant sur ``Valider``. L'entité article
-qui est listée contient maintenant un lien vers le Blog auquel il 
-appartient, ``MyLife``.
-
-.. image:: images/lax-book.07-detail-one-blogentry.en.png
-   :alt: displaying the detailed view of a blogentry
-
-Rappelez-vous que pour le moment, tout a été géré par la plate-forme
-*CubicWeb* et que la seule chose qui a été fournie est le schéma de
-données. D'ailleurs pour obtenir une vue graphique du schéma, exécutez
-la commande ``laxctl genschema blogdemo`` et vous pourrez visualiser
-votre schéma a l'URL suivante : http://localhost:8080/schema
-
-.. image:: images/lax-book.08-schema.en.png
-   :alt: graphical view of the schema (aka data-model)
-
-
-Change view permission
-~~~~~~~~~~~~~~~~~~~~~~
-
-
-
-Conclusion
-----------
-
-Exercise
-~~~~~~~~
-
-Create new blog entries in ``Tech-blog``.
-
-What we learned
-~~~~~~~~~~~~~~~
-
-Creating a simple schema was enough to set up a new application that
-can store blogs and blog entries. 
-
-What is next ?
-~~~~~~~~~~~~~~
-
-Although the application is fully functionnal, its look is very
-basic. In the following section we will learn to create views to
-customize how data is displayed.
-
-
--- a/doc/book/en/_themes/cubicweb/layout.html	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-{% extends "basic/layout.html" %}
-
-{%- block extrahead %}
-<!--[if lte IE 6]>
-<link rel="stylesheet" href="{{ pathto('_static/ie6.css', 1) }}" type="text/css" media="screen" charset="utf-8" />
-<![endif]-->
-{%- if theme_favicon %}
-<link rel="shortcut icon" href="{{ pathto('_static/'+theme_favicon, 1) }}"/>
-{%- endif %}
-
-{%- if theme_canonical_url %}
-<link rel="canonical" href="{{ theme_canonical_url }}{{ pagename }}.html"/>
-{%- endif %}
-{% endblock %}
-
-{% block header %}
-
-{% if theme_in_progress|tobool %}
-    <img style="position: fixed; display: block; width: 165px; height: 165px; bottom: 60px; right: 0; border: 0;" src="{{ pathto('_static/in_progress.png', 1) }}" alt="Documentation in progress" />
-{% endif %}
-
-{% if theme_outdated|tobool %}
-    <div style="bottom: 60px; right: 20px;position: fixed;"><a href="{{ latest_url }}" class="btn btn-large btn-danger"><strong>&gt;</strong> Read the latest version of this page</a></div>
-{% endif %}
-
-<div class="header-small">
-	{%- if theme_logo %}
-	{% set img, ext = theme_logo.split('.', -1) %}
-	<div class="logo-small">
-		<a href="{{ pathto(master_doc) }}">
-      		<img class="logo" src="{{ pathto('_static/%s-small.%s' % (img, ext), 1)}}" alt="Logo"/>
-		</a>
-  	</div>
-  	{%- endif %}
-</div>
-{% endblock %}
-
-{%- macro relbar() %}
-<div class="related">
-	<h3>{{ _('Navigation') }}</h3>
-	<ul>
-		{%- for rellink in rellinks %}
-		<li class="right" {% if loop.first %}style="margin-right: 10px"{% endif %}>
-			<a href="{{ pathto(rellink[0]) }}" title="{{ rellink[1]|striptags|e }}"
-			{{ accesskey(rellink[2]) }}>{{ rellink[3] }}</a>
-			{%- if not loop.first %}{{ reldelim2 }}{% endif %}
-		</li>
-		{%- endfor %}
-    	{%- block rootrellink %}
-    	<li><a href="{{ pathto(master_doc) }}">{{ docstitle|e }}</a>{{ reldelim1 }}</li>
-    	{%- endblock %}
-    	{%- for parent in parents %}
-          <li><a href="{{ parent.link|e }}" {% if loop.last %}{{ accesskey("U") }}{% endif %}>{{ parent.title }}</a>{{ reldelim1 }}</li>
-        {%- endfor %}
-        {%- block relbaritems %} {% endblock %}
-  	</ul>
-</div>
-{%- endmacro %}
-
-{%- block sidebarlogo %}{%- endblock %}
-{%- block sidebarsourcelink %}{%- endblock %}
--- a/doc/book/en/_themes/cubicweb/static/cubicweb.css_t	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-/*
- * cubicweb.css_t
- * ~~~~~~~~~~~~~~
- *
- * Sphinx stylesheet -- cubicweb theme.
- *
- * :copyright: Copyright 2014 by the Cubicweb team, see AUTHORS.
- * :license: LGPL, see LICENSE for details.
- *
- */
- 
-@import url("pyramid.css");
-
-div.header-small {
-  background-image: linear-gradient(white, #e2e2e2);
-  border-bottom: 1px solid #bbb;
-}
-
-div.logo-small {
-  padding: 10px;
-}
-
-img.logo {
-  width: 150px;
-}
-
-div.related a {
-  color: #e6820e;
-}
-
-a, a .pre {
-  color: #e6820e;
-}
--- a/doc/book/en/_themes/cubicweb/static/cubicweb.ico	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-../../../../../../web/data/favicon.ico
\ No newline at end of file
--- a/doc/book/en/_themes/cubicweb/static/logo-cubicweb-small.svg	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-logo-cubicweb.svg
\ No newline at end of file
--- a/doc/book/en/_themes/cubicweb/static/logo-cubicweb.svg	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-../../../../../../web/data/logo-cubicweb.svg
\ No newline at end of file
--- a/doc/book/en/_themes/cubicweb/theme.conf	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-[theme]
-inherit = pyramid
-pygments_style = sphinx.pygments_styles.PyramidStyle
-stylesheet = cubicweb.css
-
-
-[options]
-logo = logo-cubicweb.svg
-favicon = cubicweb.ico
-in_progress = false
-outdated = false
-canonical_url = 
--- a/doc/book/en/additionnal_services/index.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-Additional services
-===================
-
-In this chapter, we introduce services crossing the *web -
-repository - administration* organisation of the first parts of the
-CubicWeb book. Those services can be either proper services (like the
-undo functionality) or mere *topical cross-sections* across CubicWeb.
-
-.. toctree::
-   :maxdepth: 2
-
-   undo
-
-
--- a/doc/book/en/additionnal_services/undo.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,337 +0,0 @@
-Undoing changes in CubicWeb
----------------------------
-
-Many desktop applications offer the possibility for the user to
-undo its last changes : this *undo feature* has now been
-integrated into the CubicWeb framework. This document will
-introduce you to the *undo feature* both from the end-user and the
-application developer point of view.
-
-But because a semantic web application and a common desktop
-application are not the same thing at all, especially as far as
-undoing is concerned, we will first introduce *what* is the *undo
-feature* for now.
-
-What's *undoing* in a CubicWeb application
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-What is an *undo feature* is quite intuitive in the context of a
-desktop application. But it is a bit subtler in the context of a
-Semantic Web application. This section introduces some of the main
-differences between a classical desktop and a Semantic Web
-applications to keep in mind in order to state precisely *what we
-want*.
-
-The notion transactions
-```````````````````````
-
-A CubicWeb application acts upon an *Entity-Relationship* model,
-described by a schema. This allows to ensure some data integrity
-properties. It also implies that changes are made by all-or-none
-groups called *transactions*, such that the data integrity is
-preserved whether the transaction is completely applied *or* none
-of it is applied.
-
-A transaction can thus include more actions than just those
-directly required by the main purpose of the user.  For example,
-when a user *just* writes a new blog entry, the underlying
-*transaction* holds several *actions* as illustrated below :
-
-* By admin on 2012/02/17 15:18 - Created Blog entry : Torototo
-
-  #. Created Blog entry : Torototo
-  #. Added relation : Torototo owned by admin
-  #. Added relation : Torototo blog entry of Undo Blog
-  #. Added relation : Torototo in state draft (draft)
-  #. Added relation : Torototo created by admin
-
-Because of the very nature (all-or-none) of the transactions, the
-"undoable stuff" are the transactions and not the actions !
-
-Public and private actions within a transaction
-```````````````````````````````````````````````
-
-Actually, within the *transaction* "Created Blog entry :
-Torototo", two of those *actions* are said to be *public* and
-the others are said to be *private*. *Public* here means that the
-public actions (1 and 3) were directly requested by the end user ;
-whereas *private* means that the other actions (2, 4, 5) were
-triggered "under the hood" to fulfill various requirements for the
-user operation (ensuring integrity, security, ... ).
-
-And because quite a lot of actions can be triggered by a "simple"
-end-user request, most of which the end-user is not (and does not
-need or wish to be) aware, only the so-called public actions will
-appear [1]_ in the description of the an undoable transaction.
-
-* By admin on 2012/02/17 15:18 - Created Blog entry : Torototo
-
-  #. Created Blog entry : Torototo
-  #. Added relation : Torototo blog entry of Undo Blog
-
-But note that both public and private actions will be undone
-together when the transaction is undone.
-
-(In)dependent transactions : the simple case
-````````````````````````````````````````````
-
-A CubicWeb application can be used *simultaneously* by different users
-(whereas a single user works on an given office document at a
-given time), so that there is not always a single history
-time-line in the CubicWeb case. Moreover CubicWeb provides
-security through the mechanism of *permissions* granted to each
-user. This can lead to some transactions *not* being undoable in
-some contexts.
-
-In the simple case two (unprivileged) users Alice and Bob make
-relatively independent changes : then both Alice and Bob can undo
-their changes. But in some case there is a clean dependency
-between Alice's and Bob's actions or between actions of one of
-them. For example let's suppose that :
-
-- Alice has created a blog,
-- then has published a first post inside,
-- then Bob has published a second post in the same blog,
-- and finally Alice has updated its post contents.
-
-Then it is clear that Alice can undo her contents changes and Bob
-can undo his post creation independently. But Alice can not undo
-her post creation while she has not first undone her changes.
-It is also clear that Bob should *not* have the
-permissions to undo any of Alice's transactions.
-
-
-More complex dependencies between transactions
-``````````````````````````````````````````````
-
-But more surprising things can quickly happen. Going back to the
-previous example, Alice *can* undo the creation of the blog after
-Bob has published its post in it ! But this is possible only
-because the schema does not *require* for a post to be in a
-blog. Would the *blog entry of* relation have been mandatory, then
-Alice could not have undone the blog creation because it would
-have broken integrity constraint for Bob's post.
-
-When a user attempts to undo a transaction the system will check
-whether a later transaction has explicit dependency on the
-would-be-undone transaction. In this case the system will not even
-attempt the undo operation and inform the user.
-
-If no such dependency is detected the system will attempt the undo
-operation but it can fail, typically because of integrity
-constraint violations. In such a case the undo operation is
-completely [3]_ rollbacked.
-
-
-The *undo feature* for CubicWeb end-users
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The exposition of the undo feature to the end-user through a Web
-interface is still quite basic and will be improved toward a
-greater usability. But it is already fully functional.  For now
-there are two ways to access the *undo feature* as long as the it
-has been activated in the instance configuration file with the
-option *undo-support=yes*.
-
-Immediately after having done the change to be canceled through
-the **undo** link in the message. This allows to undo an
-hastily action immediately. For example, just after having
-validated the creation of the blog entry *A second blog entry* we
-get the following message, allowing to undo the creation.
-
-.. image:: /images/undo_mesage_w600.png
-   :width: 600px
-   :alt: Screenshot of the undo link in the message
-   :align: center
-
-At any time we can access the **undo-history view** accessible from the
-start-up page.
-
-.. image:: /images/undo_startup-link_w600.png
-   :width: 600px
-   :alt: Screenshot of the startup menu with access to the history view
-   :align: center
-
-This view will provide inspection of the transaction and their (public)
-actions. Each transaction provides its own **undo** link. Only the
-transactions the user has permissions to see and undo will be shown.
-
-.. image:: /images/undo_history-view_w600.png
-   :width: 600px
-   :alt: Screenshot of the undo history main view
-   :align: center
-
-If the user attempts to undo a transaction which can't be undone or
-whose undoing fails, then a message will explain the situation and
-no partial undoing will be left behind.
-
-This is all for the end-user side of the undo mechanism : this is
-quite simple indeed ! Now, in the following section, we are going
-to introduce the developer side of the undo mechanism.
-
-The *undo feature* for CubicWeb application developers
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-A word of warning : this section is intended for developers,
-already having some knowledge of what's under CubicWeb's hood. If
-it is not *yet* the case, please refer to CubicWeb documentation
-http://docs.cubicweb.org/ .
-
-Overview
-````````
-
-The core of the undo mechanisms is at work in the *native source*,
-beyond the RQL. This does mean that *transactions* and *actions*
-are *no entities*. Instead they are represented at the SQL level
-and exposed through the *DB-API* supported by the repository
-*Connection* objects.
-
-Once the *undo feature* has been activated in the instance
-configuration file with the option *undo-support=yes*, each
-mutating operation (cf. [2]_) will be recorded in some special SQL
-table along with its associated transaction. Transaction are
-identified by a *txuuid* through which the functions of the
-*DB-API* handle them.
-
-On the web side the last commited transaction *txuuid* is
-remembered in the request's data to allow for imediate undoing
-whereas the *undo-history view* relies upon the *DB-API* to list
-the accessible transactions. The actual undoing is performed by
-the *UndoController* accessible at URL of the form
-`www.my.host/my/instance/undo?txuuid=...`
-
-The repository side
-```````````````````
-
-Please refer to the file `cubicweb/server/sources/native.py` and
-`cubicweb/transaction.py` for the details.
-
-The undoing information is mainly stored in three SQL tables:
-
-`transactions`
-    Stores the txuuid, the user eid and the date-and-time of
-    the transaction. This table is referenced by the two others.
-
-`tx_entity_actions`
-    Stores the undo information for actions on entities.
-
-`tx_relation_actions`
-    Stores the undo information for the actions on relations.
-
-When the undo support is activated, entries are added to those
-tables for each mutating operation on the data repository, and are
-deleted on each transaction undoing.
-
-Those table are accessible through the following methods of the
-repository `Connection` object :
-
-`undoable_transactions`
-    Returns a list of `Transaction` objects accessible to the user
-    and according to the specified filter(s) if any.
-
-`tx_info`
-    Returns a `Transaction` object from a `txuuid`
-
-`undo_transaction`
-    Returns the list of `Action` object for the given `txuuid`.
-
-    NB:  By default it only return *public* actions.
-
-The web side
-````````````
-
-The exposure of the *undo feature* to the end-user through the Web
-interface relies on the *DB-API* introduced above. This implies
-that the *transactions* and *actions* are not *entities* linked by
-*relations* on which the usual views can be applied directly.
-
-That's why the file `cubicweb/web/views/undohistory.py` defines
-some dedicated views to access the undo information :
-
-`UndoHistoryView`
-    This is a *StartupView*, the one accessible from the home
-    page of the instance which list all transactions.
-
-`UndoableTransactionView`
-    This view handles the display of a single `Transaction` object.
-
-`UndoableActionBaseView`
-    This (abstract) base class provides private methods to build
-    the display of actions whatever their nature.
-
-`Undoable[Add|Remove|Create|Delete|Update]ActionView`
-    Those views all inherit from `UndoableActionBaseView` and
-    each handles a specific kind of action.
-
-`UndoableActionPredicate`
-    This predicate is used as a *selector* to pick the appropriate
-    view for actions.
-
-Apart from this main *undo-history view* a `txuuid` is stored in
-the request's data `last_undoable_transaction` in order to allow
-immediate undoing of a hastily validated operation. This is
-handled in `cubicweb/web/application.py` in the `main_publish` and
-`add_undo_link_to_msg` methods for the storing and displaying
-respectively.
-
-Once the undo information is accessible, typically through a
-`txuuid` in an *undo* URL, the actual undo operation can be
-performed by the `UndoController` defined in
-`cubicweb/web/views/basecontrollers.py`. This controller basically
-extracts the `txuuid` and performs a call to `undo_transaction` and
-in case of an undo-specific error, lets the top level publisher
-handle it as a validation error.
-
-
-Conclusion
-~~~~~~~~~~
-
-The undo mechanism relies upon a low level recording of the
-mutating operation on the repository. Those records are accessible
-through some method added to the *DB-API* and exposed to the
-end-user either through a whole history view of through an
-immediate undoing link in the message box.
-
-The undo feature is functional but the interface and configuration
-options are still quite reduced. One major improvement would be to
-be able to filter with a finer grain which transactions or actions
-one wants to see in the *undo-history view*. Another critical
-improvement would be to enable the undo feature on a part only of
-the entity-relationship schema to avoid storing too much useless
-data and reduce the underlying overhead.
-
-But both functionality are related to the strong design choice not
-to represent transactions and actions as entities and
-relations. This has huge benefits in terms of safety and conceptual
-simplicity but prevents from using lots of convenient CubicWeb
-features such as *facets* to access undo information.
-
-Before developing further the undo feature or eventually revising
-this design choice, it appears that some return of experience is
-strongly needed. So don't hesitate to try the undo feature in your
-application and send us some feedback.
-
-
-Notes
-~~~~~
-
-.. [1] The end-user Web interface could be improved to enable
-       user to choose whether he wishes to see private actions.
-
-.. [2] There is only five kind of elementary actions (beyond
-       merely accessing data for reading):
-
-       * **C** : creating an entity
-       * **D** : deleting an entity
-       * **U** : updating an entity attributes
-       * **A** : adding a relation
-       * **R** : removing a relation
-
-.. [3] Meaning none of the actions in the transaction is
-       undone. Depending upon the application, it might make sense
-       to enable *partial* undo. That is to say undo in which some
-       actions could not be undo without preventing to undo the
-       others actions in the transaction (as long as it does not
-       break schema integrity). This is not forbidden by the
-       back-end but is deliberately not supported by the front-end
-       (for now at least).
--- a/doc/book/en/admin/additional-tips.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-
-.. _Additional Tips:
-
-Backups (mostly with postgresql)
---------------------------------
-
-It is always a good idea to backup. If your system does not do that,
-you should set it up. Note that whenever you do an upgrade,
-`cubicweb-ctl` offers you to backup your database.  There are a number
-of ways for doing backups.
-
-Using postgresql (and only that)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Before you
-go ahead, make sure the following permissions are correct ::
-
-   # chgrp postgres /var/lib/cubicweb/backup
-   # chmod g+ws /var/lib/cubicweb/backup
-   # chgrp postgres /etc/cubicweb.d/*<instance>*/sources
-   # chmod g+r /etc/cubicweb.d/*<instance>*/sources
-
-Simply use the pg_dump in a cron installed for `postgres` user on the database server::
-
-    # m h  dom mon dow   command
-    0 2 * * * pg_dump -Fc --username=cubicweb --no-owner <instance> > /var/backups/<instance>-$(date '+%Y-%m-%d_%H:%M:%S').dump
-
-Using :command:`cubicweb-ctl db-dump`
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The CubicWeb way is to use the :command:`db-dump` command. For that,
-you have to put your passwords in a user-only-readable file at the
-home directory of root user.  The file is `.pgpass` (`chmod 0600`), in
-this case for a socket run connection to PostgreSQL ::
-
-    /var/run/postgresql:5432:<instance>:<database user>:<database password>
-
-The postgres documentation for the `.pgpass` format can be found `here`_
-
-Then add the following command to the crontab of the user (`crontab -e`)::
-
-    # m h  dom mon dow   command
-    0 2 * * * cubicweb-ctl db-dump <instance>
-
-
-Backup ninja
-~~~~~~~~~~~~
-
-You can use a combination `backup-ninja`_ (which has a postgres script in the
-example directory), `backuppc`)_ (for versionning).
-
-Please note that in the *CubicWeb way* it adds a second location for your
-password which is error-prone.
-
-.. _`here` : http://www.postgresql.org/docs/current/static/libpq-pgpass.html
-.. _`backup-ninja` : https://labs.riseup.net/code/projects/show/backupninja/
-.. _`backuppc` : http://backuppc.sourceforge.net/
-
-.. warning::
-
-  Remember that these indications will fail you whenever you use
-  another database backend than postgres. Also it does properly handle
-  externally managed data such as files (using the Bytes File System
-  Storage).
--- a/doc/book/en/admin/config.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,255 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _ConfigEnv:
-
-Set-up of a *CubicWeb* environment
-==================================
-
-You can `configure the database`_ system of your choice:
-
-  - `PostgreSQL configuration`_
-  - `MySql configuration`_
-  - `SQLServer configuration`_
-  - `SQLite configuration`_
-
-For advanced features, have a look to:
-
-  - `Pyro configuration`_
-  - `Cubicweb resources configuration`_
-
-.. _`configure the database`: DatabaseInstallation_
-.. _`PostgreSQL configuration`: PostgresqlConfiguration_
-.. _`MySql configuration`: MySqlConfiguration_
-.. _`SQLServer configuration`: SQLServerConfiguration_
-.. _`SQLite configuration`: SQLiteConfiguration_
-.. _`Pyro configuration`: PyroConfiguration_
-.. _`Cubicweb resources configuration`: RessourcesConfiguration_
-
-
-
-.. _RessourcesConfiguration:
-
-Cubicweb resources configuration
---------------------------------
-
-.. autodocstring:: cubicweb.cwconfig
-
-
-.. _DatabaseInstallation:
-
-Databases configuration
------------------------
-
-Each instance can be configured with its own database connection information,
-that will be stored in the instance's :file:`sources` file. The database to use
-will be chosen when creating the instance. CubicWeb is known to run with
-Postgresql (recommended), MySQL, SQLServer and SQLite.
-
-Other possible sources of data include CubicWeb, Subversion, LDAP and Mercurial,
-but at least one relational database is required for CubicWeb to work. You do
-not need to install a backend that you do not intend to use for one of your
-instances. SQLite is not fit for production use, but it works well for testing
-and ships with Python, which saves installation time when you want to get
-started quickly.
-
-.. _PostgresqlConfiguration:
-
-PostgreSQL
-~~~~~~~~~~
-
-Many Linux distributions ship with the appropriate PostgreSQL packages.
-Basically, you need to install the following packages:
-
-* `postgresql` and `postgresql-client`, which will pull the respective
-  versioned packages (e.g. `postgresql-9.1` and `postgresql-client-9.1`) and,
-  optionally,
-* a `postgresql-plpython-X.Y` package with a version corresponding to that of
-  the aforementioned packages (e.g. `postgresql-plpython-9.1`).
-
-If you run postgres version prior to 8.3, you'll also need the
-`postgresql-contrib-8.X` package for full-text search extension.
-
-If you run postgres on another host than the |cubicweb| repository, you should
-install the `postgresql-client` package on the |cubicweb| host, and others on the
-database host.
-
-For extra details concerning installation, please refer to the `PostgreSQL
-project online documentation`_.
-
-.. _`PostgreSQL project online documentation`: http://www.postgresql.org/docs
-
-
-Database cluster
-++++++++++++++++
-
-If you already have an existing cluster and PostgreSQL server running, you do
-not need to execute the initilization step of your PostgreSQL database unless
-you want a specific cluster for |cubicweb| databases or if your existing
-cluster doesn't use the UTF8 encoding (see note below).
-
-To initialize a PostgreSQL cluster, use the command ``initdb``::
-
-    $ initdb -E UTF8 -D /path/to/pgsql
-
-Notice the encoding specification. This is necessary since |cubicweb| usually
-want UTF8 encoded database. If you use a cluster with the wrong encoding, you'll
-get error like::
-
-  new encoding (UTF8) is incompatible with the encoding of the template database (SQL_ASCII)
-  HINT:  Use the same encoding as in the template database, or use template0 as template.
-
-Once initialized, start the database server PostgreSQL with the command::
-
-  $ postgres -D /path/to/psql
-
-If you cannot execute this command due to permission issues, please make sure
-that your username has write access on the database.  ::
-
-  $ chown username /path/to/pgsql
-
-Database authentication
-+++++++++++++++++++++++
-
-The database authentication is configured in `pg_hba.conf`. It can be either set
-to `ident sameuser` or `md5`.  If set to `md5`, make sure to use an existing
-user of your database.  If set to `ident sameuser`, make sure that your client's
-operating system user name has a matching user in the database. If not, please
-do as follow to create a user::
-
-  $ su
-  $ su - postgres
-  $ createuser -s -P username
-
-The option `-P` (for password prompt), will encrypt the password with the
-method set in the configuration file :file:`pg_hba.conf`.  If you do not use this
-option `-P`, then the default value will be null and you will need to set it
-with::
-
-  $ su postgres -c "echo ALTER USER username WITH PASSWORD 'userpasswd' | psql"
-
-The above login/password will be requested when you will create an instance with
-`cubicweb-ctl create` to initialize the database of your instance.
-
-Notice that the `cubicweb-ctl db-create` does database initialization that
-may requires a postgres superuser. That's why a login/password is explicitly asked
-at this step, so you can use there a superuser without using this user when running
-the instance. Things that require special privileges at this step:
-
-* database creation, require the 'create database' permission
-* install the plpython extension language (require superuser)
-* install the tsearch extension for postgres version prior to 8.3 (require superuser)
-
-To avoid using a super user each time you create an install, a nice trick is to
-install plpython (and tsearch when needed) on the special `template1` database,
-so they will be installed automatically when cubicweb databases are created
-without even with needs for special access rights. To do so, run ::
-
-  # Installation of plpythonu language by default ::
-  $ createlang -U pgadmin plpythonu template1
-  $ psql -U pgadmin template1
-  template1=# update pg_language set lanpltrusted=TRUE where lanname='plpythonu';
-
-Where `pgadmin` is a postgres superuser. The last command is necessary since by
-default plpython is an 'untrusted' language and as such can't be used by non
-superuser. This update fix that problem by making it trusted.
-
-To install the tsearch plain-text index extension on postgres prior to 8.3, run::
-
-    cat /usr/share/postgresql/8.X/contrib/tsearch2.sql | psql -U username template1
-
-
-.. _MySqlConfiguration:
-
-MySql
-~~~~~
-
-You must add the following lines in ``/etc/mysql/my.cnf`` file::
-
-    transaction-isolation=READ-COMMITTED
-    default-storage-engine=INNODB
-    default-character-set=utf8
-    max_allowed_packet = 128M
-
-.. Note::
-    It is unclear whether mysql supports indexed string of arbitrary length or
-    not.
-
-
-.. _SQLServerConfiguration:
-
-SQLServer
-~~~~~~~~~
-
-As of this writing, support for SQLServer 2005 is functional but incomplete. You
-should be able to connect, create a database and go quite far, but some of the
-SQL generated from RQL queries is still currently not accepted by the
-backend. Porting to SQLServer 2008 is also an item on the backlog.
-
-The `source` configuration file may look like this (specific parts only are
-shown)::
-
-  [system]
-  db-driver=sqlserver2005
-  db-user=someuser
-  # database password not needed
-  #db-password=toto123
-  #db-create/init may ask for a pwd: just say anything
-  db-extra-arguments=Trusted_Connection
-  db-encoding=utf8
-
-
-You need to change the default settings on the database by running::
-
- ALTER DATABASE <databasename> SET READ_COMMITTED_SNAPSHOT ON;
-
-The ALTER DATABASE command above requires some permissions that your
-user may not have. In that case you will have to ask your local DBA to
-run the query for you.
-
-You can check that the setting is correct by running the following
-query which must return '1'::
-
-   SELECT is_read_committed_snapshot_on
-     FROM sys.databases WHERE name='<databasename>';
-
-
-
-.. _SQLiteConfiguration:
-
-SQLite
-~~~~~~
-
-SQLite has the great advantage of requiring almost no configuration. Simply
-use 'sqlite' as db-driver, and set path to the dabase as db-name. Don't specify
-anything for db-user and db-password, they will be ignore anyway.
-
-.. Note::
-  SQLite is great for testing and to play with cubicweb but is not suited for
-  production environments.
-
-
-.. _PyroConfiguration:
-
-Pyro configuration
-------------------
-
-Pyro name server
-~~~~~~~~~~~~~~~~
-
-If you want to use Pyro to access your instance remotely, or to have multi-source
-or distributed configuration, it is required to have a Pyro name server running
-on your network. By default it is detected by a broadcast request, but you can
-specify a location in the instance's configuration file.
-
-To do so, you need to :
-
-* be sure to have installed it (see :ref:`InstallDependencies`)
-
-* launch the pyro name server with `pyro-nsd start` before starting cubicweb
-
-* under debian, edit the file :file:`/etc/default/pyro-nsd` so that the name
-  server pyro will be launched automatically when the machine fire up
-
-Note that you can use the pyro server without a running pyro nameserver.
-Refer to `pyro-ns-host` server configuration option for details.
-
--- a/doc/book/en/admin/create-instance.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-Creation of your first instance
-===============================
-
-Instance creation
------------------
-
-Now that we created a cube, we can create an instance and access it via a web
-browser. We will use a `all-in-one` configuration to simplify things ::
-
-  cubicweb-ctl create -c all-in-one mycube myinstance
-
-.. note::
-  Please note that we created a new cube for a demo purposes but
-  you could have used an existing cube available in our standard library
-  such as blog or person for example.
-
-A series of questions will be prompted to you, the default answer is usually
-sufficient. You can anyway modify the configuration later on by editing
-configuration files. When a login/password are requested to access the database
-please use the credentials you created at the time you configured the database
-(:ref:`PostgresqlConfiguration`).
-
-It is important to distinguish here the user used to access the database and the
-user used to login to the cubicweb instance. When an instance starts, it uses
-the login/password for the database to get the schema and handle low level
-transaction. But, when :command:`cubicweb-ctl create` asks for a manager
-login/psswd of *CubicWeb*, it refers to the user you will use during the
-development to administrate your web instance. It will be possible, later on,
-to use this user to create other users for your final web instance.
-
-
-Instance administration
------------------------
-
-start / stop
-~~~~~~~~~~~~
-
-When this command is completed, the definition of your instance is
-located in :file:`~/etc/cubicweb.d/myinstance/*`. To launch it, you
-just type ::
-
-  cubicweb-ctl start -D myinstance
-
-The option `-D` specifies the *debug mode* : the instance is not
-running in server mode and does not disconnect from the terminal,
-which simplifies debugging in case the instance is not properly
-launched. You can see how it looks by visiting the URL
-`http://localhost:8080` (the port number depends of your
-configuration). To login, please use the cubicweb administrator
-login/password you defined when you created the instance.
-
-To shutdown the instance, Crtl-C in the terminal window is enough.
-If you did not use the option `-D`, then type ::
-
-  cubicweb-ctl stop myinstance
-
-This is it! All is settled down to start developping your data model...
-
-.. note::
-
-  The output of `cubicweb-ctl start -D myinstance` can be
-  overwhelming. It is possible to reduce the log level with the
-  `--loglevel` parameter as in `cubicweb-ctl start -D myinstance -l
-  info` to filter out all logs under `info` gravity.
-
-upgrade
-~~~~~~~
-
-A manual upgrade step is necessary whenever a new version of CubicWeb or
-a cube is installed, in order to synchronise the instance's
-configuration and schema with the new code.  The command is::
-
-  cubicweb-ctl upgrade myinstance
-
-A series of questions will be asked. It always starts with a proposal
-to make a backup of your sources (where it applies). Unless you know
-exactly what you are doing (i.e. typically fiddling in debug mode, but
-definitely NOT migrating a production instance), you should answer YES
-to that.
-
-The remaining questions concern the migration steps of |cubicweb|,
-then of the cubes that form the whole application, in reverse
-dependency order.
-
-In principle, if the migration scripts have been properly written and
-tested, you should answer YES to all questions.
-
-Somtimes, typically while debugging a migration script, something goes
-wrong and the migration fails. Unfortunately the databse may be in an
-incoherent state. You have two options here:
-
-* fix the bug, restore the database and restart the migration process
-  from scratch (quite recommended in a production environement)
-
-* try to replay the migration up to the last successful commit, that
-  is answering NO to all questions up to the step that failed, and
-  finish by answering YES to the remaining questions.
-
--- a/doc/book/en/admin/cubicweb-ctl.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _cubicweb-ctl:
-
-``cubicweb-ctl`` tool
-=====================
-
-`cubicweb-ctl` is the swiss knife to manage *CubicWeb* instances.
-The general syntax is ::
-
-  cubicweb-ctl <command> [options command] <arguments commands>
-
-To view available commands ::
-
-  cubicweb-ctl
-  cubicweb-ctl --help
-
-Please note that the commands available depends on the *CubicWeb* packages
-and cubes that have been installed.
-
-To view the help menu on specific command ::
-
-  cubicweb-ctl <command> --help
-
-Listing available cubes and instance
--------------------------------------
-
-* ``list``, provides a list of the available configuration, cubes
-  and instances.
-
-
-Creation of a new cube
------------------------
-
-Create your new cube cube ::
-
-   cubicweb-ctl newcube
-
-This will create a new cube in
-``/path/to/grshell-cubicweb/cubes/<mycube>`` for a Mercurial
-installation, or in ``/usr/share/cubicweb/cubes`` for a debian
-packages installation.
-
-Create an instance
--------------------
-
-You must ensure `~/etc/cubicweb.d/` exists prior to this. On windows, the
-'~' part will probably expand to 'Documents and Settings/user'.
-
-To create an instance from an existing cube, execute the following
-command ::
-
-   cubicweb-ctl create <cube_name> <instance_name>
-
-This command will create the configuration files of an instance in
-``~/etc/cubicweb.d/<instance_name>``.
-
-The tool ``cubicweb-ctl`` executes the command ``db-create`` and
-``db-init`` when you run ``create`` so that you can complete an
-instance creation in a single command. But of course it is possible
-to issue these separate commands separately, at a later stage.
-
-Command to create/initialize an instance database
--------------------------------------------------
-
-* ``db-create``, creates the system database of an instance (tables and
-  extensions only)
-* ``db-init``, initializes the system database of an instance
-  (schema, groups, users, workflows...)
-
-Commands to control instances
------------------------------
-
-* ``start``, starts one or more or all instances
-
-of special interest::
-
-  start -D
-
-will start in debug mode (under windows, starting without -D will not
-work; you need instead to setup your instance as a service).
-
-* ``stop``, stops one or more or all instances
-* ``restart``, restarts one or more or all instances
-* ``status``, returns the status of the instance(s)
-
-Commands to maintain instances
-------------------------------
-
-* ``upgrade``, launches the existing instances migration when a new version
-  of *CubicWeb* or the cubes installed is available
-* ``shell``, opens a (Python based) migration shell for manual maintenance of the instance
-* ``db-dump``, creates a dump of the system database
-* ``db-restore``, restores a dump of the system database
-* ``db-check``, checks data integrity of an instance. If the automatic correction
-  is activated, it is recommanded to create a dump before this operation.
-* ``schema-sync``, synchronizes the persistent schema of an instance with
-  the instance schema. It is recommanded to create a dump before this operation.
-
-Commands to maintain i18n catalogs
-----------------------------------
-* ``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:`internationalization`.
-
-Other commands
---------------
-* ``delete``, deletes an instance (configuration files and database)
--- a/doc/book/en/admin/index.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _Part3:
-
---------------
-Administration
---------------
-
-This part is for installation and administration of the *CubicWeb* framework and
-instances based on that framework.
-
-.. toctree::
-   :maxdepth: 1
-   :numbered:
-
-   setup
-   setup-windows
-   config
-   cubicweb-ctl
-   create-instance
-   instance-config
-   site-config
-   multisources
-   ldap
-   pyro
-   migration
-   additional-tips
-   rql-logs
-
--- a/doc/book/en/admin/instance-config.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,226 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-
-Configure an instance
-=====================
-
-While creating an instance, a configuration file is generated in::
-
-    $ (CW_INSTANCES_DIR) / <instance> / <configuration name>.conf
-
-For example::
-
-    /etc/cubicweb.d/myblog/all-in-one.conf
-
-It is a simple text file in the INI format
-(http://en.wikipedia.org/wiki/INI_file). In the following description,
-each option name is prefixed with its own section and followed by its
-default value if necessary, e.g. "`<section>.<option>` [value]."
-
-.. _`WebServerConfig`:
-
-Configuring the Web server
---------------------------
-:`web.auth-model` [cookie]:
-    authentication mode, cookie or http
-:`web.realm`:
-    realm of the instance in http authentication mode
-:`web.http-session-time` [0]:
-    period of inactivity of an HTTP session before it closes automatically.
-    Duration in seconds, 0 meaning no expiration (or more exactly at the
-    closing of the browser client)
-
-:`main.anonymous-user`, `main.anonymous-password`:
-    login and password to use to connect to the RQL server with
-    HTTP anonymous connection. CWUser account should exist.
-
-:`main.base-url`:
-    url base site to be used to generate the urls of web pages
-
-Https configuration
-```````````````````
-It is possible to make a site accessible for anonymous http connections
-and https for authenticated users. This requires to
-use apache (for example) for redirection and the variable `main.https-url`
-of configuration file.
-
-For this to work you have to activate the following apache modules :
-
-* rewrite
-* proxy
-* http_proxy
-
-The command on Debian based systems for that is ::
-
-  a2enmod rewrite http_proxy proxy
-  /etc/init.d/apache2 restart
-
-:Example:
-
-   For an apache redirection of a site accessible via `http://localhost/demo`
-   and `https://localhost/demo` and actually running on port 8080, it
-   takes to the http:::
-
-     ProxyPreserveHost On
-     RewriteEngine On
-     RewriteCond %{REQUEST_URI} ^/demo
-     RewriteRule ^/demo$ /demo/
-     RewriteRule ^/demo/(.*) http://127.0.0.1:8080/$1 [L,P]
-
-   and for the https:::
-
-     ProxyPreserveHost On
-     RewriteEngine On
-     RewriteCond %{REQUEST_URI} ^/ demo
-     RewriteRule ^/demo$/demo/
-     RewriteRule ^/demo/(.*) http://127.0.0.1:8080/https/$1 [L,P]
-
-
-   and we will file in the all-in-one.conf of the instance:::
-
-     base-url = http://localhost/demo
-     https-url = https://localhost/demo
-
-Notice that if you simply want a site accessible through https, not *both* http
-and https, simply set `base-url` to the https url and the first section into your
-apache configuration (as you would have to do for an http configuration with an
-apache front-end).
-
-Setting up the web client
--------------------------
-:`web.embed-allowed`:
-    regular expression matching sites which could be "embedded" in
-    the site (controllers 'embed')
-:`web.submit-url`:
-    url where the bugs encountered in the instance can be mailed to
-
-
-RQL server configuration
-------------------------
-:`main.host`:
-    host name if it can not be detected correctly
-:`main.pid-file`:
-    file where will be written the server pid
-:`main.uid`:
-    user account to use for launching the server when it is
-    root launched by init
-:`main.session-time [30*60]`:
-    timeout of a RQL session
-:`main.query-log-file`:
-    file where all requests RQL executed by the server are written
-
-
-Pyro configuration for the instance
------------------------------------
-Web server side:
-
-:`pyro.pyro-instance-id`:
-    pyro identifier of RQL server (e.g. the instance name)
-
-RQL server side:
-
-:`main.pyro-server`:
-    boolean to switch on/off pyro server-side
-
-:`pyro.pyro-host`:
-    pyro host:port number. If no port is specified, it is assigned
-    automatically.
-
-RQL and web servers side:
-
-:`pyro.pyro-ns-host`:
-    hostname hosting pyro server name. If no value is
-    specified, it is located by a request from broadcast
-
-:`pyro.pyro-ns-group`:
-    pyro group in which to save the instance (will default to 'cubicweb')
-
-
-Configuring e-mail
-------------------
-RQL and web server side:
-
-:`email.mangle-mails [no]`:
-    indicates whether the email addresses must be displayed as is or
-    transformed
-
-RQL server side:
-
-:`email.smtp-host [mail]`:
-    hostname hosting the SMTP server to use for outgoing mail
-:`email.smtp-port [25]`:
-    SMTP server port to use for outgoing mail
-:`email.sender-name`:
-    name to use for outgoing mail of the instance
-:`email.sender-addr`:
-    address for outgoing mail of the instance
-:`email.default dest-addrs`:
-    destination addresses by default, if used by the configuration of the
-    dissemination of the model (separated by commas)
-:`email.supervising-addrs`:
-    destination addresses of e-mails of supervision (separated by
-    commas)
-
-
-Configuring logging
--------------------
-:`main.log-threshold`:
-    level of filtering messages (DEBUG, INFO, WARNING, ERROR)
-:`main.log-file`:
-    file to write messages
-
-
-.. _PersistentProperties:
-
-Configuring persistent properties
----------------------------------
-Other configuration settings are in the form of entities `CWProperty`
-in the database. It must be edited via the web interface or by
-RQL queries.
-
-:`ui.encoding`:
-    Character encoding to use for the web
-:`navigation.short-line-size`:
-    number of characters for "short" display
-:`navigation.page-size`:
-    maximum number of entities to show per results page
-:`navigation.related-limit`:
-    number of related entities to show up on primary entity view
-:`navigation.combobox-limit`:
-    number of entities unrelated to show up on the drop-down lists of
-    the sight on an editing entity view
-
-Cross-Origin Resource Sharing
------------------------------
-
-CubicWeb provides some support for the CORS_ protocol. For now, the
-provided implementation only deals with access to a CubicWeb instance
-as a whole. Support for a finer granularity may be considered in the
-future.
-
-Specificities of the provided implementation:
-
-- ``Access-Control-Allow-Credentials`` is always true
-- ``Access-Control-Allow-Origin`` header in response will never be
-  ``*``
-- ``Access-Control-Expose-Headers`` can be configured globally (see below)
-- ``Access-Control-Max-Age`` can be configured globally (see below)
-- ``Access-Control-Allow-Methods`` can be configured globally (see below)
-- ``Access-Control-Allow-Headers`` can be configured globally (see below)
-
-
-A few parameters can be set to configure the CORS_ capabilities of CubicWeb.
-
-.. _CORS: http://www.w3.org/TR/cors/
-
-:`access-control-allow-origin`:
-   comma-separated list of allowed origin domains or "*" for any domain
-:`access-control-allow-methods`:
-   comma-separated list of allowed HTTP methods
-:`access-control-max-age`:
-   maximum age of cross-origin resource sharing (in seconds)
-:`access-control-allow-headers`:
-   comma-separated list of allowed HTTP custom headers (used in simple requests)
-:`access-control-expose-headers`:
-   comma-separated list of allowed HTTP custom headers (used in preflight requests)
-
--- a/doc/book/en/admin/ldap.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,134 +0,0 @@
-.. _LDAP:
-
-LDAP integration
-================
-
-Overview
---------
-
-Using LDAP as a source for user credentials and information is quite
-easy. The most difficult part lies in building an LDAP schema or
-using an existing one.
-
-At cube creation time, one is asked if more sources are wanted. LDAP
-is one possible option at this time. Of course, it is always possible
-to set it up later using the `CWSource` entity type, which we discuss
-there.
-
-It is possible to add as many LDAP sources as wanted, which translates
-in as many `CWSource` entities as needed.
-
-The general principle of the LDAP source is, given a proper
-configuration, to create local users matching the users available in
-the directory and deriving local user attributes from directory users
-attributes. Then a periodic task ensures local user information
-synchronization with the directory.
-
-Users handled by such a source should not be edited directly from
-within the application instance itself. Rather, updates should happen
-at the LDAP server level.
-
-Credential checks are _always_ done against the LDAP server.
-
-.. Note::
-
-  There are currently two ldap source types: the older `ldapuser` and
-  the newer `ldapfeed`. The older will be deprecated anytime soon, as
-  the newer has now gained all the features of the old and does not
-  suffer from some of its illnesses.
-
-  The ldapfeed creates real `CWUser` entities, and then
-  activate/deactivate them depending on their presence/absence in the
-  corresponding LDAP source. Their attribute and state
-  (activated/deactivated) are hence managed by the source mechanism;
-  they should not be altered by other means (as such alterations may
-  be overridden in some subsequent source synchronisation).
-
-
-Configuration of an LDAPfeed source
------------------------------------
-
-Additional sources are created at cube creation time or later through the
-user interface.
-
-Configure an `ldapfeed` source from the user interface under `Manage` then
-`data sources`:
-
-* At this point `type` has been set to `ldapfeed`.
-
-* The `parser` attribute shall be set to `ldapfeed`.
-
-* The `url` attribute shall be set to an URL such as ldap://ldapserver.domain/.
-
-* The `configuration` attribute contains many options. They are described in
-  detail in the next paragraph.
-
-
-Options of an LDAPfeed source
------------------------------
-
-Let us enumerate the options by categories (LDAP server connection,
-LDAP schema mapping information).
-
-LDAP server connection options:
-
-* `auth-mode`, (choices are simple, cram_md5, digest_md5, gssapi, support
-  for the later being partial as of now)
-
-* `auth-realm`, realm to use when using gssapi/kerberos authentication
-
-* `data-cnx-dn`, user dn to use to open data connection to the ldap (eg
-  used to respond to rql queries)
-
-* `data-cnx-password`, password to use to open data connection to the
-  ldap (eg used to respond to rql queries)
-
-If the LDAP server accepts anonymous binds, then it is possible to
-leave data-cnx-dn and data-cnx-password empty. This is, however, quite
-unlikely in practice. Beware that the LDAP server might hide attributes
-such as "userPassword" while the rest of the attributes remain visible
-through an anonymous binding.
-
-LDAP schema mapping options:
-
-* `user-base-dn`, base DN to lookup for users
-
-* `user-scope`, user search scope (valid values: "BASE", "ONELEVEL",
-  "SUBTREE")
-
-* `user-classes`, classes of user (with Active Directory, you want to
-  say "user" here)
-
-* `user-filter`, additional filters to be set in the ldap query to
-  find valid users
-
-* `user-login-attr`, attribute used as login on authentication (with
-  Active Directory, you want to use "sAMAccountName" here)
-
-* `user-default-group`, name of a group in which ldap users will be by
-  default. You can set multiple groups by separating them by a comma
-
-* `user-attrs-map`, map from ldap user attributes to cubicweb
-  attributes (with Active Directory, you want to use
-  sAMAccountName:login,mail:email,givenName:firstname,sn:surname)
-
-
-Other notes
------------
-
-* Cubicweb is able to start if ldap cannot be reached, even on
-  cubicweb-ctl start ... If some source ldap server cannot be used
-  while an instance is running, the corresponding users won't be
-  authenticated but their status will not change (e.g. they will not
-  be deactivated)
-
-* The user-base-dn is a key that helps cubicweb map CWUsers to LDAP
-  users: beware updating it
-
-* When a user is removed from an LDAP source, it is deactivated in the
-  CubicWeb instance; when a deactivated user comes back in the LDAP
-  source, it (automatically) is activated again
-
-* You can use the :class:`CWSourceHostConfig` to have variants for a source
-  configuration according to the host the instance is running on. To do so
-  go on the source's view from the sources management view.
--- a/doc/book/en/admin/migration.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-Migrating cubicweb instances - benefits from a distributed architecture
-=======================================================================
-
-Migrate apache & cubicweb
--------------------------
-
-**Aim** : do the migration for N cubicweb instances hosted on a server to another with no downtime.
-
-**Prerequisites** : have an explicit definition of the database host (not default or localhost). In our case, the database is hosted on another host. You are not migrating your pyro server. You are not using multisource (more documentation on that soon).
-
-**Steps** :
-
-1. *on new machine* : install your environment (*pseudocode*) ::
-
-     apt-get install cubicweb cubicweb-applications apache2
-
-2. *on old machine* : copy your cubicweb and apache configuration to the new machine ::
-
-    scp /etc/cubicweb.d/ newmachine:/etc/cubicweb.d/
-    scp /etc/apache2/sites-available/ newmachine:/etc/apache2/sites-available/
-
-3. *on new machine* : give new ids to pyro registration so the new instances can register ::
-
-     cd /etc/cubicweb.d/ ; sed -i.bck 's/^pyro-instance-id=.*$/\02/' */all-in-one.conf
-
-4. *on new machine* : start your instances ::
-
-     cubicweb start
-
-5. *on new machine* : enable sites and modules for apache and start it, test it using by modifying your /etc/host file.
-
-6. change dns entry from your oldmachine to newmachine
-
-7. shutdown your *old machine* (if it doesn't host other services or your database)
-
-8. That's it.
-
-**Possible enhancements** : use right from the start a pound server behind your apache, that way you can add backends and smoothily migrate by shuting down backends that pound will take into account.
-
-Migrate apache & cubicweb with pyro
------------------------------------
-
-FIXME TODO
-
--- a/doc/book/en/admin/multisources.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-Multiple sources of data
-========================
-
-Data sources include SQL, LDAP, RQL, mercurial and subversion.
-
-.. XXX feed me
--- a/doc/book/en/admin/pyro.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-.. _UsingPyro:
-
-Working with a distributed client (using Pyro)
-==============================================
-
-In some circumstances, it is practical to split the repository and
-web-client parts of the application for load-balancing reasons. Or
-one wants to access the repository from independant scripts to consult
-or update the database.
-
-Prerequisites
--------------
-
-For this to work, several steps have to be taken in order.
-
-You must first ensure that the appropriate software is installed and
-running (see :ref:`ConfigEnv`)::
-
-  pyro-nsd -x -p 6969
-
-Then you have to set appropriate options in your configuration. For
-instance::
-
-  pyro-server=yes
-  pyro-ns-host=localhost:6969
-
-  pyro-instance-id=myinstancename
-
-Connect to the CubicWeb repository from a python script
--------------------------------------------------------
-
-Assuming pyro-nsd is running and your instance is configured with ``pyro-server=yes``,
-you will be able to use :mod:`cubicweb.dbapi` api to initiate the connection.
-
-.. note::
-    Regardless of whether your instance is pyro activated or not, you can still
-    achieve this by using cubicweb-ctl shell scripts in a simpler way, as by default
-    it creates a repository 'in-memory' instead of connecting through pyro. That
-    also means you've to be on the host where the instance is running.
-
-Finally, the client (for instance a python script) must connect specifically
-as in the following example code:
-
-.. sourcecode:: python
-
-    from cubicweb import dbapi
-
-    cnx = dbapi.connect(database='instance-id', user='admin', password='admin')
-    cnx.load_appobjects()
-    cur = cnx.cursor()
-    for name in (u'Personal', u'Professional', u'Computers'):
-        cur.execute('INSERT Tag T: T name %(n)s', {'n': name})
-    cnx.commit()
-
-Calling :meth:`cubicweb.dbapi.load_appobjects`, will populate the
-cubicweb registries (see :ref:`VRegistryIntro`) with the application
-objects installed on the host where the script runs. You'll then be
-allowed to use the ORM goodies and custom entity methods and views. Of
-course this is optional, without it you can still get the repository
-data through the connection but in a roughly way: only RQL cursors
-will be available, e.g. you can't even build entity objects from the
-result set.
--- a/doc/book/en/admin/rql-logs.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-RQL logs
-========
-
-You can configure the *CubicWeb* instance to keep a log
-of the queries executed against your database. To do so,
-edit the configuration file of your instance
-``.../etc/cubicweb.d/myapp/all-in-one.conf`` and uncomment the
-variable ``query-log-file``::
-
-  # web instance query log file
-  query-log-file=/tmp/rql-myapp.log
-
--- a/doc/book/en/admin/setup-windows.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,146 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _SetUpWindowsEnv:
-
-Installing a development environement on Windows
-================================================
-
-Setting up a Windows development environment is not too complicated
-but it requires a series of small steps.
-
-We propose an example of a typical |cubicweb| installation on Windows
-from sources. We assume everything goes into ``C:\\`` and for any
-package, without version specification, "the latest is
-the greatest".
-
-Mind that adjusting the installation drive should be straightforward.
-
-
-
-Install the required elements
------------------------------
-
-|cubicweb| requires some base elements that must be installed to run
-correctly. So, first of all, you must install them :
-
-* python >= 2.6 and < 3
-  (`Download Python <http://www.python.org/download/>`_).
-  You can also consider the Python(x,y) distribution
-  (`Download Python(x,y) <http://code.google.com/p/pythonxy/wiki/Downloads>`_)
-  as it makes things easier for Windows user by wrapping in a single installer
-  python 2.7 plus numerous useful third-party modules and
-  applications (including Eclipse + pydev, which is an arguably good
-  IDE for Python under Windows).
-
-* `Twisted <http://twistedmatrix.com/trac/>`_ is an event-driven
-  networking engine
-  (`Download Twisted <http://twistedmatrix.com/trac/>`_)
-
-* `lxml <http://codespeak.net/lxml/>`_ library
-  (version >=2.2.1) allows working with XML and HTML
-  (`Download lxml <http://pypi.python.org/pypi/lxml/2.2.1>`_)
-
-* `Postgresql <http://www.postgresql.org/>`_,
-  an object-relational database system
-  (`Download Postgresql <http://www.enterprisedb.com/products/pgdownload.do#windows>`_)
-  and its python drivers
-  (`Download psycopg <http://www.stickpeople.com/projects/python/win-psycopg/#Version2>`_)
-
-* A recent version of `gettext`
-  (`Download gettext <http://download.logilab.org/pub/gettext/gettext-0.17-win32-setup.exe>`_).
-
-* `rql <http://www.logilab.org/project/rql>`_,
-  the recent version of the Relationship Query Language parser.
-
-Install optional elements
--------------------------
-
-We recommend you to install the following elements. They are not
-mandatory but they activate very interesting features in |cubicweb|:
-
-* `python-ldap <http://pypi.python.org/pypi/python-ldap>`_
-  provides access to LDAP/Active directory directories
-  (`Download python-ldap <http://www.osuch.org/python-ldap>`_).
-
-* `graphviz <http://www.graphviz.org/>`_
-  which allow schema drawings.
-  (`Download graphviz <http://www.graphviz.org/Download_windows.php>`_).
-  It is quite recommended (albeit not mandatory).
-
-Other elements will activate more features once installed. Take a look
-at :ref:`InstallDependencies`.
-
-Useful tools
-------------
-
-Some additional tools could be useful to develop :ref:`cubes <AvailableCubes>`
-with the framework.
-
-* `mercurial <http://mercurial.selenic.com/>`_ and its standard windows GUI
-  (`TortoiseHG <http://tortoisehg.bitbucket.org/>`_) allow you to get the source
-  code of |cubicweb| from control version repositories. So you will be able to
-  get the latest development version and pre-release bugfixes in an easy way
-  (`Download mercurial <http://bitbucket.org/tortoisehg/stable/wiki/download>`_).
-
-* You can also consider the ssh client `Putty` in order to peruse
-  mercurial over ssh (`Download <http://www.putty.org/>`_).
-
-* If you are an Eclipse user, mercurial can be integrated using the
-  `MercurialEclipse` plugin
-  (`Home page <http://www.vectrace.com/mercurialeclipse/>`_).
-
-Getting the sources
--------------------
-
-There are two ways to get the sources of |cubicweb| and its
-:ref:`cubes <AvailableCubes>`:
-
-* download the latest release (:ref:`SourceInstallation`)
-* get the development version using Mercurial
-  (:ref:`MercurialInstallation`)
-
-Environment variables
----------------------
-
-You will need some convenience environment variables once all is set up. These
-variables are settable through the GUI by getting at the `System properties`
-window (by righ-clicking on `My Computer` -> `properties`).
-
-In the `advanced` tab, there is an `Environment variables` button. Click on
-it. That opens a small window allowing edition of user-related and system-wide
-variables.
-
-We will consider only user variables. First, the ``PATH`` variable. Assuming
-you are logged as user *Jane*, add the following paths, separated by
-semi-colons::
-
-  C:\Documents and Settings\Jane\My Documents\Python\cubicweb\cubicweb\bin
-  C:\Program Files\Graphviz2.24\bin
-
-The ``PYTHONPATH`` variable should also contain::
-
-  C:\Documents and Settings\Jane\My Documents\Python\cubicweb\
-
-From now, on a fresh `cmd` shell, you should be able to type::
-
-  cubicweb-ctl list
-
-... and get a meaningful output.
-
-Running an instance as a service
---------------------------------
-
-This currently assumes that the instances configurations is located at
-``C:\\etc\\cubicweb.d``. For a cube 'my_instance', you will find
-``C:\\etc\\cubicweb.d\\my_instance\\win32svc.py``.
-
-Now, register your instance as a windows service with::
-
-  win32svc install
-
-Then start the service with::
-
-  net start cubicweb-my_instance
-
-In case this does not work, you should be able to see error reports in
-the application log, using the windows event log viewer.
--- a/doc/book/en/admin/setup.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,269 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _SetUpEnv:
-
-Installation of a *CubicWeb* environment
-========================================
-
-Official releases are available from the `CubicWeb.org forge`_ and from
-`PyPI`_. Since CubicWeb is developed using `Agile software development
-<http://en.wikipedia.org/wiki/Agile_software_development>`_ techniques, releases
-happen frequently. In a version numbered X.Y.Z, X changes after a few years when
-the API breaks, Y changes after a few weeks when features are added and Z
-changes after a few days when bugs are fixed.
-
-Depending on your needs, you will chose a different way to install CubicWeb on
-your system:
-
-- `Installation on Debian/Ubuntu`_
-- `Installation on Windows`_
-- `Installation in a virtualenv`_
-- `Installation with pip`_
-- `Installation with easy_install`_
-- `Installation from tarball`_
-
-If you are a power-user and need the very latest features, you will
-
-- `Install from version control`_
-
-Once the software is installed, move on to :ref:`ConfigEnv` for better control
-and advanced features of |cubicweb|.
-
-.. _`Installation on Debian/Ubuntu`: DebianInstallation_
-.. _`Installation on Windows`: WindowsInstallation_
-.. _`Installation in a virtualenv`: VirtualenvInstallation_
-.. _`Installation with pip`: PipInstallation_
-.. _`Installation with easy_install`: EasyInstallInstallation_
-.. _`Installation from tarball`: TarballInstallation_
-.. _`Install from version control`: MercurialInstallation_
-
-
-.. _DebianInstallation:
-
-Debian/Ubuntu install
----------------------
-
-|cubicweb| is packaged for Debian/Ubuntu (and derived
-distributions). Their integrated package-management system make
-installation and upgrade much easier for users since
-dependencies (like databases) are automatically installed.
-
-Depending on the distribution you are using, add the appropriate line to your
-`list of sources` (for example by editing ``/etc/apt/sources.list``).
-
-For Debian 7.0 Wheezy (stable)::
-
-  deb http://download.logilab.org/production/ wheezy/
-
-For Debian Sid (unstable)::
-
-  deb http://download.logilab.org/production/ sid/
-
-For Ubuntu 12.04 Precise Pangolin (Long Term Support) and newer::
-
-  deb http://download.logilab.org/production/ precise/
-
-The repositories are signed with the `Logilab's gnupg key`_. You can download
-and register the key to avoid warnings::
-
-  wget -q http://download.logilab.org/logilab-dists-key.asc -O- | sudo apt-key add -
-
-Update your list of packages and perform the installation::
-
-  apt-get update
-  apt-get install cubicweb cubicweb-dev
-
-``cubicweb`` installs the framework itself, allowing you to create new
-instances. ``cubicweb-dev`` installs the development environment
-allowing you to develop new cubes.
-
-There is also a wide variety of :ref:`cubes <AvailableCubes>`. You can access a
-list of available cubes using ``apt-cache search cubicweb`` or at the
-`CubicWeb.org forge`_.
-
-.. note::
-
-  `cubicweb-dev` will install basic sqlite support. You can easily setup
-  :ref:`cubicweb with other database <DatabaseInstallation>` using the following
-  virtual packages :
-
-  * `cubicweb-postgresql-support` contains the necessary dependencies for
-    using :ref:`cubicweb with postgresql datatabase <PostgresqlConfiguration>`
-
-  * `cubicweb-mysql-support` contains the necessary dependencies for using
-    :ref:`cubicweb with mysql database <MySqlConfiguration>`.
-
-.. _`list of sources`: http://wiki.debian.org/SourcesList
-.. _`Logilab's gnupg key`: http://download.logilab.org/logilab-dists-key.asc
-.. _`CubicWeb.org Forge`: http://www.cubicweb.org/project/
-
-.. _WindowsInstallation:
-
-Windows Install
----------------
-
-You need to have `python`_ version >= 2.5 and < 3 installed.
-
-If you want an automated install, your best option is probably the
-:ref:`EasyInstallInstallation`. EasyInstall is a tool that helps users to
-install python packages along with their dependencies, searching for suitable
-pre-compiled binaries on the `The Python Package Index`_.
-
-If you want better control over the process as well as a suitable development
-environment or if you are having problems with `easy_install`, read on to
-:ref:`SetUpWindowsEnv`.
-
-.. _python:  http://www.python.org/
-.. _`The Python Package Index`: http://pypi.python.org
-
-.. _VirtualenvInstallation:
-
-`Virtualenv` install
---------------------
-
-|cubicweb| can be safely installed, used and contained inside a
-`virtualenv`_. You can use either :ref:`pip <PipInstallation>` or
-:ref:`easy_install <EasyInstallInstallation>` to install |cubicweb|
-inside an activated virtual environment.
-
-.. _PipInstallation:
-
-`pip` install
--------------
-
-`pip <http://pip.openplans.org/>`_ is a python tool that helps downloading,
-building, installing, and managing Python packages and their dependencies. It
-is fully compatible with `virtualenv`_ and installs the packages from sources
-published on the `The Python Package Index`_.
-
-.. _`virtualenv`: http://virtualenv.openplans.org/
-
-A working compilation chain is needed to build the modules that include C
-extensions. If you really do not want to compile anything, installing `lxml <http://lxml.de/>`_,
-`Twisted Web <http://twistedmatrix.com/trac/wiki/Downloads/>`_ and `libgecode
-<http://www.gecode.org/>`_ will help.
-
-For Debian, these minimal dependencies can be obtained by doing::
-
-  apt-get install gcc python-pip python-dev python-lxml
-
-or, if you prefer to get as much as possible from pip::
-
-  apt-get install gcc python-pip python-dev libxslt1-dev libxml2-dev
-
-For Windows, you can install pre-built packages (possible `source
-<http://www.lfd.uci.edu/~gohlke/pythonlibs/>`_). For a minimal setup, install:
-
-- pip http://www.lfd.uci.edu/~gohlke/pythonlibs/#pip
-- setuptools http://www.lfd.uci.edu/~gohlke/pythonlibs/#setuptools
-- libxml-python http://www.lfd.uci.edu/~gohlke/pythonlibs/#libxml-python>
-- lxml http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml and
-- twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
-
-Make sure to choose the correct architecture and version of Python.
-
-Finally, install |cubicweb| and its dependencies, by running::
-
-  pip install cubicweb
-
-Many other :ref:`cubes <AvailableCubes>` are available. A list is available at
-`PyPI <http://pypi.python.org/pypi?%3Aaction=search&term=cubicweb&submit=search>`_
-or at the `CubicWeb.org forge`_.
-
-For example, installing the *blog cube* is achieved by::
-
-  pip install cubicweb-blog
-
-.. _EasyInstallInstallation:
-
-`easy_install` install
-----------------------
-
-.. note::
-
-   If you are not a Windows user and you have a compilation environment, we
-   recommend you to use the PipInstallation_.
-
-`easy_install`_ is a python utility that helps downloading, installing, and
-managing python packages and their dependencies.
-
-Install |cubicweb| and its dependencies, run::
-
-  easy_install cubicweb
-
-There is also a wide variety of :ref:`cubes <AvailableCubes>`. You can access a
-list of available cubes on `PyPI
-<http://pypi.python.org/pypi?%3Aaction=search&term=cubicweb&submit=search>`_
-or at the `CubicWeb.org Forge`_.
-
-For example, installing the *blog cube* is achieved by::
-
-  easy_install cubicweb-blog
-
-.. note::
-
-  If you encounter problem with :ref:`cubes <AvailableCubes>` installation,
-  consider using :ref:`PipInstallation` which is more stable
-  but can not installed pre-compiled binaries.
-
-.. _`easy_install`: http://packages.python.org/distribute/easy_install.html
-
-
-.. _SourceInstallation:
-
-Install from source
--------------------
-
-.. _TarballInstallation:
-
-You can download the archive containing the sources from
-`http://download.logilab.org/pub/cubicweb/ <http://download.logilab.org/pub/cubicweb/>`_.
-
-Make sure you also have all the :ref:`InstallDependencies`.
-
-Once uncompressed, you can install the framework from inside the uncompressed
-folder with::
-
-  python setup.py install
-
-Or you can run |cubicweb| directly from the source directory by
-setting the :ref:`resource mode <RessourcesConfiguration>` to `user`. This will
-ease the development with the framework.
-
-There is also a wide variety of :ref:`cubes <AvailableCubes>`. You can access a
-list of availble cubes at the `CubicWeb.org Forge`_.
-
-
-.. _MercurialInstallation:
-
-Install from version control system
------------------------------------
-
-To keep-up with on-going development, clone the :ref:`Mercurial
-<MercurialPresentation>` repository::
-
-  hg clone -u stable http://hg.logilab.org/cubicweb # stable branch
-  hg clone http://hg.logilab.org/cubicweb # development branch
-
-To get many of CubicWeb's dependencies and a nice set of base cubes, run the
-`clone_deps.py` script located in `cubicweb/bin/`::
-
-  python cubicweb/bin/clone_deps.py
-
-(Windows users should replace slashes with antislashes).
-
-This script will clone a set of mercurial repositories into the
-directory containing the ``cubicweb`` repository, and update them to the
-latest published version tag (if any).
-
-.. note::
-
-  In every cloned repositories, a `hg tags` will display a list of
-  tags in reverse chronological order. One reasonnable option is to go to a
-  tagged version: the latest published version or example, as done by
-  the `clone_deps` script)::
-
-   hg update cubicweb-version-3.12.2
-
-Make sure you also have all the :ref:`InstallDependencies`.
-
--- a/doc/book/en/admin/site-config.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,94 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-User interface for web site configuration
-=========================================
-
-.. image:: ../images/lax-book_03-site-config-panel_en.png
-
-This panel allows you to configure the appearance of your instance site.
-Six menus are available and we will go through each of them to explain how
-to use them.
-
-Navigation
-~~~~~~~~~~
-This menu provides you a way to adjust some navigation options depending on
-your needs, such as the number of entities to display by page of results.
-Follows the detailled list of available options :
-
-* navigation.combobox-limit : maximum number of entities to display in related
-  combo box (sample format: 23)
-* navigation.page-size : maximum number of objects displayed by page of results
-  (sample format: 23)
-* navigation.related-limit : maximum number of related entities to display in
-  the primary view (sample format: 23)
-* navigation.short-line-size : maximum number of characters in short description
-  (sample format: 23)
-
-UI
-~~
-This menu provides you a way to customize the user interface settings such as
-date format or encoding in the produced html.
-Follows the detailled list of available options :
-
-* ui.date-format : how to format date in the ui ("man strftime" for format description)
-* ui.datetime-format : how to format date and time in the ui ("man strftime" for format
-  description)
-* ui.default-text-format : default text format for rich text fields.
-* ui.encoding : user interface encoding
-* ui.fckeditor :should html fields being edited using fckeditor (a HTML WYSIWYG editor).
-  You should also select text/html as default text format to actually get fckeditor.
-* ui.float-format : how to format float numbers in the ui
-* ui.language : language of the user interface
-* ui.main-template : id of main template used to render pages
-* ui.site-title	: site title, which is displayed right next to the logo in the header
-* ui.time-format : how to format time in the ui ("man strftime" for format description)
-
-
-Actions
-~~~~~~~
-This menu provides a way to configure the context in which you expect the actions
-to be displayed to the user and if you want the action to be visible or not.
-You must have notice that when you view a list of entities, an action box is
-available on the left column which display some actions as well as a drop-down
-menu for more actions.
-
-The context available are :
-
-* mainactions : actions listed in the left box
-* moreactions : actions listed in the `more` menu of the left box
-* addrelated : add actions listed in the left box
-* useractions : actions listed in the first section of drop-down menu
-  accessible from the right corner user login link
-* siteactions : actions listed in the second section of drop-down menu
-  accessible from the right corner user login link
-* hidden : select this to hide the specific action
-
-Boxes
-~~~~~
-The instance has already a pre-defined set of boxes you can use right away.
-This configuration section allows you to place those boxes where you want in the
-instance interface to customize it.
-
-The available boxes are :
-
-* actions box : box listing the applicable actions on the displayed data
-
-* boxes_blog_archives_box : box listing the blog archives
-
-* possible views box : box listing the possible views for the displayed data
-
-* rss box : RSS icon to get displayed data as a RSS thread
-
-* search box : search box
-
-* startup views box : box listing the configuration options available for
-  the instance site, such as `Preferences` and `Site Configuration`
-
-Components
-~~~~~~~~~~
-[WRITE ME]
-
-Contextual components
-~~~~~~~~~~~~~~~~~~~~~
-[WRITE ME]
-
--- a/doc/book/en/annexes/depends.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _InstallDependencies:
-
-Installation dependencies
-=========================
-
-When you run CubicWeb from source, either by downloading the tarball or
-cloning the mercurial tree, here is the list of tools and libraries you need
-to have installed in order for CubicWeb to work:
-
-* yapps - http://theory.stanford.edu/~amitp/yapps/ -
-  http://pypi.python.org/pypi/Yapps2
-
-* pygraphviz - http://networkx.lanl.gov/pygraphviz/ -
-  http://pypi.python.org/pypi/pygraphviz
-
-* docutils - http://docutils.sourceforge.net/ - http://pypi.python.org/pypi/docutils
-
-* lxml - http://codespeak.net/lxml - http://pypi.python.org/pypi/lxml
-
-* twisted - http://twistedmatrix.com/ - http://pypi.python.org/pypi/Twisted
-
-* logilab-common - http://www.logilab.org/project/logilab-common -
-  http://pypi.python.org/pypi/logilab-common/
-
-* logilab-database - http://www.logilab.org/project/logilab-database -
-  http://pypi.python.org/pypi/logilab-database/
-
-* logilab-constraint - http://www.logilab.org/project/logilab-constraint -
-  http://pypi.python.org/pypi/constraint/
-
-* logilab-mtconverter - http://www.logilab.org/project/logilab-mtconverter -
-  http://pypi.python.org/pypi/logilab-mtconverter
-
-* rql - http://www.logilab.org/project/rql - http://pypi.python.org/pypi/rql
-
-* yams - http://www.logilab.org/project/yams - http://pypi.python.org/pypi/yams
-
-* indexer - http://www.logilab.org/project/indexer -
-  http://pypi.python.org/pypi/indexer
-
-* passlib - https://code.google.com/p/passlib/ -
-  http://pypi.python.org/pypi/passlib
-
-If you're using a Postgresql database (recommended):
-
-* psycopg2 - http://initd.org/projects/psycopg2 - http://pypi.python.org/pypi/psycopg2
-* plpythonu extension
-
-Other optional packages:
-
-* fyzz - http://www.logilab.org/project/fyzz -
-  http://pypi.python.org/pypi/fyzz *to activate Sparql querying*
-
-
-Any help with the packaging of CubicWeb for more than Debian/Ubuntu (including
-eggs, buildouts, etc) will be greatly appreciated.
--- a/doc/book/en/annexes/docstrings-conventions.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +0,0 @@
-Javascript docstrings
-=====================
-
-Whereas in Python source code we only need to include a module docstrings
-using the directive `.. automodule:: mypythonmodule`, we will have to
-explicitely define Javascript modules and functions in the doctrings since
-there is no native directive to include Javascript files.
-
-Rest generation
----------------
-
-`pyjsrest` is a small utility parsing Javascript doctrings and generating the
-corresponding Restructured file used by Sphinx to generate HTML documentation.
-This script will have the following structure::
-
-  ===========
-  filename.js
-  ===========
-  .. module:: filename.js
-
-We use the `.. module::` directive to register a javascript library
-as a Python module for Sphinx. This provides an entry in the module index.
-
-The contents of the docstring found in the javascript file will be added as is
-following the module declaration. No treatment will be done on the doctring.
-All the documentation structure will be in the docstrings and will comply
-with the following rules.
-
-Docstring structure
--------------------
-
-Basically we document javascript with RestructuredText docstring
-following the same convention as documenting Python code.
-
-The doctring in Javascript files must be contained in standard
-Javascript comment signs, starting with `/**` and ending with `*/`,
-such as::
-
- /**
-  * My comment starts here.
-  * This is the second line prefixed with a `*`.
-  * ...
-  * ...
-  * All the follwing line will be prefixed with a `*` followed by a space.
-  * ...
-  * ...
-  */
-
-
-Comments line prefixed by `//` will be ignored. They are reserved for source
-code comments dedicated to developers.
-
-
-Javscript functions docstring
------------------------------
-
-By default, the `function` directive describes a module-level function.
-
-`function` directive
-~~~~~~~~~~~~~~~~~~~~
-
-Its purpose is to define the function prototype such as::
-
-    .. function:: loadxhtml(url, data, reqtype, mode)
-
-If any namespace is used, we should add it in the prototype for now,
-until we define an appropriate directive::
-
-    .. function:: jQuery.fn.loadxhtml(url, data, reqtype, mode)
-
-Function parameters
-~~~~~~~~~~~~~~~~~~~
-
-We will define function parameters as a bulleted list, where the
-parameter name will be backquoted and followed by its description.
-
-Example of a javascript function docstring::
-
-    .. function:: loadxhtml(url, data, reqtype, mode)
-
-    cubicweb loadxhtml plugin to make jquery handle xhtml response
-
-    fetches `url` and replaces this's content with the result
-
-    Its arguments are:
-
-    * `url`
-
-    * `mode`, 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
-
-
-Optional parameter specification
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Javascript functions handle arguments not listed in the function signature.
-In the javascript code, they will be flagged using `/* ... */`. In the docstring,
-we flag those optional arguments the same way we would define it in
-Python::
-
-    .. function:: asyncRemoteExec(fname, arg1=None, arg2=None)
-
-
--- a/doc/book/en/annexes/faq.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,439 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-Frequently Asked Questions (FAQ)
-================================
-
-
-Generalities
-````````````
-
-Why do you use the LGPL license to prevent me from doing X ?
-------------------------------------------------------------
-
-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 according to the terms of the LGPL. 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
-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.
-
-Why does not CubicWeb have a template language ?
-------------------------------------------------
-
-There are enough template languages out there. You can use your
-preferred template language if you want. [explain how to use a
-template language]
-
-*CubicWeb* does not define its own templating language as this was
-not our goal. Based on our experience, we realized that
-we could gain productivity by letting designers use design tools
-and developpers develop without the use of the templating language
-as an intermediary that could not be anyway efficient for both parties.
-Python is the templating language that we use in *CubicWeb*, but again,
-it does not prevent you from using a templating language.
-
-Moreover, CubicWeb currently supports `simpletal`_ out of the box and
-it is also possible to use the `cwtags`_ library to build html trees
-using the `with statement`_ with more comfort than raw strings.
-
-.. _`simpletal`: http://www.owlfish.com/software/simpleTAL/
-.. _`cwtags`: http://www.cubicweb.org/project/cwtags
-.. _`with statement`: http://www.python.org/dev/peps/pep-0343/
-
-Why do you think using pure python is better than using a template language ?
------------------------------------------------------------------------------
-
-Python is an Object Oriented Programming language and as such it
-already provides a consistent and strong architecture and syntax
-a templating language would not reach.
-
-Using Python instead of a template langage for describing the user interface
-makes it to maintain with real functions/classes/contexts without the need of
-learning a new dialect. By using Python, we use standard OOP techniques and
-this is a key factor in a robust application.
-
-CubicWeb looks pretty recent. Is it stable ?
---------------------------------------------
-
-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.
-
-You can see the roadmap there:
-http://www.cubicweb.org/project/cubicweb?tab=projectroadmap_tab.
-
-
-Why is the RQL query language looking similar to X ?
-----------------------------------------------------
-
-It may remind you of SQL but it is higher level than SQL, more like
-SPARQL. Except that SPARQL did not exist when we started the project.
-With version 3.4, CubicWeb has support for SPARQL.
-
-The RQL language is what is going to make a difference with django-
-like frameworks for several reasons.
-
-1. accessing data is *much* easier with it. One can write complex
-   queries with RQL that would be tedious to define and hard to maintain
-   using an object/filter suite of method calls.
-
-2. it offers an abstraction layer allowing your applications to run
-   on multiple back-ends. That means not only various SQL backends
-   (postgresql, sqlite, mysql), but also multiple databases at the
-   same time, and also non-SQL data stores like LDAP directories and
-   subversion/mercurial repositories (see the `vcsfile`
-   component). Google App Engine is yet another supported target for
-   RQL.
-
-Which ajax library is CubicWeb using ?
---------------------------------------
-
-CubicWeb uses jQuery_ and provides a few helpers on top of that. Additionally,
-some jQuery plugins are provided (some are provided in specific cubes).
-
-.. _jQuery: http://jquery.com
-
-
-Development
-```````````
-
-How to change the instance logo ?
----------------------------------
-
-The logo is managed by css. You must provide a custom css that will contain
-the code below: 
-
-::
-   
-     #logo {
-        background-image: url("logo.jpg");
-     }
-
-
-``logo.jpg`` is in ``mycube/data`` directory.
-
-How to create an anonymous user ?
----------------------------------
-
-This allows to browse the site without being authenticated. In the
-``all-in-one.conf`` file of your instance, define the anonymous user
-as follows ::
-
-  # login of the CubicWeb user account to use for anonymous user (if you want to
-  # allow anonymous)
-  anonymous-user=anon
-
-  # password of the CubicWeb user account matching login
-  anonymous-password=anon
-
-You also must ensure that this `anon` user is a registered user of
-the DB backend. If not, you can create through the administation
-interface of your instance by adding a user with in the group `guests`.
-
-.. note::
-    While creating a new instance, you can decide to allow access
-    to anonymous user, which will automatically execute what is
-    decribed above.
-
-How to load data from a python script ?
----------------------------------------
-Please, refer to :ref:`UsingPyro`.
-
-
-How to format an entity date attribute ?
-----------------------------------------
-
-If your schema has an attribute of type `Date` or `Datetime`, you usually want to
-format it when displaying it. First, you should define your preferred format
-using the site configuration panel
-``http://appurl/view?vid=systempropertiesform`` and then set ``ui.date`` and/or
-``ui.datetime``.  Then in the view code, use:
-
-.. sourcecode:: python
-
-    entity.printable_value(date_attribute)
-
-which will always return a string whatever the attribute's type (so it's
-recommended also for other attribute types). By default it expects to generate
-HTML, so it deals with rich text formating, xml escaping...
-
-How to update a database after a schema modification ?
-------------------------------------------------------
-
-It depends on what has been modified in the schema.
-
-* update the permissions and properties of an entity or a relation:
-  ``sync_schema_props_perms('MyEntityOrRelation')``.
-
-* add an attribute: ``add_attribute('MyEntityType', 'myattr')``.
-
-* add a relation: ``add_relation_definition('SubjRelation', 'MyRelation', 'ObjRelation')``.
-
-I get `NoSelectableObject` exceptions, how do I debug selectors ?
------------------------------------------------------------------
-
-You just need to put the appropriate context manager around view/component
-selection. One standard place for components is in cubicweb/vregistry.py: 
-
-.. sourcecode:: python
-
-    def possible_objects(self, *args, **kwargs):
-        """return an iterator on possible objects in this registry for the given
-        context
-        """
-        from logilab.common.registry import traced_selection
-        with traced_selection():
-            for appobjects in self.itervalues():
-                try:
-                    yield self._select_best(appobjects, *args, **kwargs)
-                except NoSelectableObject:
-                    continue
-
-This will yield additional WARNINGs, like this::
-
-    2009-01-09 16:43:52 - (cubicweb.selectors) WARNING: selector one_line_rset returned 0 for <class 'cubicweb.web.views.basecomponents.WFHistoryVComponent'>
-
-For views, you can put this context in `cubicweb/web/views/basecontrollers.py` in
-the `ViewController`:
-
-.. sourcecode:: python
-
-    def _select_view_and_rset(self, rset):
-        ...
-        try:
-            from logilab.common.registry import traced_selection
-            with traced_selection():
-                view = self._cw.vreg['views'].select(vid, req, rset=rset)
-        except ObjectNotFound:
-            self.warning("the view %s could not be found", vid)
-            req.set_message(req._("The view %s could not be found") % vid)
-            vid = vid_from_rset(req, rset, self._cw.vreg.schema)
-            view = self._cw.vreg['views'].select(vid, req, rset=rset)
-        ...
-
-I get "database is locked" when executing tests
------------------------------------------------
-
-If you have "database is locked" as error when you are executing security tests,
-it is usually because commit or rollback are missing before login() calls.
-
-You can also use a context manager, to avoid such errors, as described
-here: :ref:`securitytest`.
-
-
-What are hooks used for ?
--------------------------
-
-Hooks are executed around (actually before or after) events.  The most common
-events are data creation, update and deletion.  They permit additional constraint
-checking (those not expressible at the schema level), pre and post computations
-depending on data movements.
-
-As such, they are a vital part of the framework.
-
-Other kinds of hooks, called Operations, are available
-for execution just before commit.
-
-For more information, read :ref:`hooks` section.
-
-
-Configuration
-`````````````
-
-How to configure a LDAP source ?
---------------------------------
-
-See :ref:`LDAP`.
-
-How to import LDAP users in |cubicweb| ?
-----------------------------------------
-
-  Here is a useful script which enables you to import LDAP users
-  into your *CubicWeb* instance by running the following:
-
-.. sourcecode:: python
-
-    import os
-    import pwd
-    import sys
-
-    from logilab.database import get_connection
-
-    def getlogin():
-        """avoid using os.getlogin() because of strange tty/stdin problems
-        (man 3 getlogin)
-        Another solution would be to use $LOGNAME, $USER or $USERNAME
-        """
-        return pwd.getpwuid(os.getuid())[0]
-
-
-    try:
-        database = sys.argv[1]
-    except IndexError:
-        print 'USAGE: python ldap2system.py <database>'
-        sys.exit(1)
-
-    if raw_input('update %s db ? [y/n]: ' % database).strip().lower().startswith('y'):
-        cnx = get_connection(user=getlogin(), database=database)
-        cursor = cnx.cursor()
-
-        insert = ('INSERT INTO euser (creation_date, eid, modification_date, login, '
-                  ' firstname, surname, last_login_time, upassword) '
-                  "VALUES (%(mtime)s, %(eid)s, %(mtime)s, %(login)s, %(firstname)s, "
-                  "%(surname)s, %(mtime)s, './fqEz5LeZnT6');")
-        update = "UPDATE entities SET source='system' WHERE eid=%(eid)s;"
-        cursor.execute("SELECT eid,type,source,extid,mtime FROM entities WHERE source!='system'")
-        for eid, type, source, extid, mtime in cursor.fetchall():
-            if type != 'CWUser':
-                print "don't know what to do with entity type", type
-                continue
-            if source != 'ldapuser':
-                print "don't know what to do with source type", source
-                continue
-            ldapinfos = dict(x.strip().split('=') for x in extid.split(','))
-            login = ldapinfos['uid']
-            firstname = ldapinfos['uid'][0].upper()
-            surname = ldapinfos['uid'][1:].capitalize()
-            if login != 'jcuissinat':
-                args = dict(eid=eid, type=type, source=source, login=login,
-                            firstname=firstname, surname=surname, mtime=mtime)
-                print args
-                cursor.execute(insert, args)
-                cursor.execute(update, args)
-
-        cnx.commit()
-        cnx.close()
-
-
-Security
-````````
-
-How to reset the password for user joe ?
-----------------------------------------
-
-If you want to reset the admin password for ``myinstance``, do::
-
-    $ cubicweb-ctl reset-admin-pwd myinstance
-
-You need to generate a new encrypted password::
-
-    $ python
-    >>> from cubicweb.server.utils import crypt_password
-    >>> crypt_password('joepass')
-    'qHO8282QN5Utg'
-    >>>
-
-and paste it in the database::
-
-    $ psql mydb
-    mydb=> update cw_cwuser set cw_upassword='qHO8282QN5Utg' where cw_login='joe';
-    UPDATE 1
-
-if you're running over SQL Server, you need to use the CONVERT
-function to convert the string to varbinary(255). The SQL query is
-therefore::
-
-    update cw_cwuser set cw_upassword=CONVERT(varbinary(255), 'qHO8282QN5Utg') where cw_login='joe';
-
-Be careful, the encryption algorithm is different on Windows and on
-Unix. You cannot therefore use a hash generated on Unix to fill in a
-Windows database, nor the other way round.
-
-
-You can prefer use a migration script similar to this shell invocation instead::
-
-    $ cubicweb-ctl shell <instance>
-    >>> from cubicweb import Binary
-    >>> from cubicweb.server.utils import crypt_password
-    >>> crypted = crypt_password('joepass')
-    >>> rset = rql('Any U WHERE U is CWUser, U login "joe"')
-    >>> joe = rset.get_entity(0,0)
-    >>> joe.cw_set(upassword=Binary(crypted))
-
-Please, refer to the script example is provided in the `misc/examples/chpasswd.py` file.
-
-The more experimented people would use RQL request directly::
-
-    >>> rql('SET X upassword %(a)s WHERE X is CWUser, X login "joe"',
-    ...     {'a': crypted})
-
-I've just created a user in a group and it doesn't work !
----------------------------------------------------------
-
-You are probably getting errors such as ::
-
-  remove {'PR': 'Project', 'C': 'CWUser'} from solutions since your_user has no read access to cost
-
-This is because you have to put your user in the "users" group. The user has to
-be in both groups.
-
-How is security implemented ?
-------------------------------
-
-The basis for security is a mapping from operations to groups or
-arbitrary RQL expressions. These mappings are scoped to entities and
-relations.
-
-This is an example for an Entity Type definition:
-
-.. sourcecode:: python
-
-    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' or to users in group G, if G is linked by a permission
-entity named "add_version" to the version's project.
-
-An example for a Relation Definition (RelationType both defines a
-relation type and implicitly one relation definition, on which the
-permissions actually apply):
-
-.. sourcecode:: python
-
-    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'),) }
-
-The main difference lies in the basic available operations (there is
-no 'update' operation) and the usage of an RRQLExpression (rql
-expression for a relation) instead of an ERQLExpression (rql
-expression for an entity).
-
-You can find additional information in the section :ref:`securitymodel`.
-
-Is it possible to bypass security from the UI (web front) part ?
-----------------------------------------------------------------
-
-No. Only Hooks/Operations can do that.
-
-Can PostgreSQL and CubicWeb authentication work with kerberos ?
-----------------------------------------------------------------
-
-If you have PostgreSQL set up to accept kerberos authentication, you can set
-the db-host, db-name and db-user parameters in the `sources` configuration
-file while leaving the password blank. It should be enough for your
-instance to connect to postgresql with a kerberos ticket.
-
-
--- a/doc/book/en/annexes/index.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _Part4:
-
-----------
-Appendixes
-----------
-
-The following chapters are reference material.
-
-.. toctree::
-   :maxdepth: 1
-   :numbered:
-
-   faq
-   rql/index
-   mercurial
-   depends
-   docstrings-conventions
--- a/doc/book/en/annexes/mercurial.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _MercurialPresentation:
-
-Introducing Mercurial
-=====================
-
-Introduction
-````````````
-Mercurial_ manages a distributed repository containing revisions
-trees (each revision indicates the changes required to obtain the
-next, and so on). Locally, we have a repository containing revisions
-tree, and a working directory. It is possible
-to put in its working directory, one of the versions of its local repository,
-modify and then push it in its repository.
-It is also possible to get revisions from another repository or to export
-its own revisions from the local repository to another repository.
-
-.. _Mercurial: http://www.selenic.com/mercurial/
-
-In contrast to CVS/Subversion, we usually create a repository per
-project to manage.
-
-In a collaborative development, we usually create a central repository
-accessible to all developers of the project. These central repository is used
-as a reference. According to their needs, everyone can have a local repository,
-that they will have to synchronize with the central repository from time to time.
-
-
-Major commands
-``````````````
-* Create a local repository::
-
-     hg clone ssh://myhost//home/src/repo
-
-* See the contents of the local repository (graphical tool in Qt)::
-
-     hgview
-
-* Add a sub-directory or file in the current directory::
-
-     hg add subdir
-
-* Move to the working directory a specific revision (or last
-  revision) from the local repository::
-
-     hg update [identifier-revision]
-     hg up [identifier-revision]
-
-* Get in its local repository, the tree of revisions contained in a
-  remote repository (this does not change the local directory)::
-
-     hg pull ssh://myhost//home/src/repo
-     hg pull -u ssh://myhost//home/src/repo # equivalent to pull + update
-
-* See what are the heads of branches of the local repository if a `pull`
-  returned a new branch::
-
-     hg heads
-
-* Submit the working directory in the local repository (and create a new
-  revision)::
-
-     hg commit
-     hg ci
-
-* Merge with the mother revision of local directory, another revision from
-  the local respository (the new revision will be then two mothers
-  revisions)::
-
-     hg merge identifier-revision
-
-* Export to a remote repository, the tree of revisions in its content
-  local respository (this does not change the local directory)::
-
-     hg push ssh://myhost//home/src/repo
-
-* See what local revisions are not in another repository::
-
-     hg outgoing ssh://myhost//home/src/repo
-
-* See what are the revisions of a repository not found locally::
-
-     hg incoming ssh://myhost//home/src/repo
-
-* See what is the revision of the local repository which has been taken out
-  from the working directory and amended::
-
-     hg parent
-
-* See the differences between the working directory and the mother revision
-  of the local repository, possibly to submit them in the local repository::
-
-     hg diff
-     hg commit-tool
-     hg ct
-
-
-Best Practices
-``````````````
-* Remember to `hg pull -u` regularly, and particularly before
-   a `hg commit`.
-
-* Remember to `hg push` when your repository contains a version
-  relatively stable of your changes.
-
-* If a `hg pull -u` created a new branch head:
-
-   1. find its identifier with `hg head`
-   2. merge with `hg merge`
-   3. `hg ci`
-   4. `hg push`
-
-Installation of the guestrepo extension
-```````````````````````````````````````
-
-Set up the guestrepo extension by getting a copy of the sources
-from https://bitbucket.org/selinc/guestrepo and adding the following
-lines to your ``~/.hgrc``: ::
-
-   [extensions]
-   guestrepo=/path/to/guestrepo/guestrepo
-
-
-More information
-````````````````
-
-For more information about Mercurial, please refer to the Mercurial project online documentation_.
-
-.. _documentation: http://www.selenic.com/mercurial/wiki/
-
Binary file doc/book/en/annexes/rql/Graph-ex.gif has changed
--- a/doc/book/en/annexes/rql/debugging.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _DEBUGGING:
-
-Debugging RQL
--------------
-
-Available levels
-~~~~~~~~~~~~~~~~
-
-Server debugging flags. They may be combined using binary operators.
-
-.. autodata:: cubicweb.server.DBG_NONE
-.. autodata:: cubicweb.server.DBG_RQL
-.. autodata:: cubicweb.server.DBG_SQL
-.. autodata:: cubicweb.server.DBG_REPO
-.. autodata:: cubicweb.server.DBG_MS
-.. autodata:: cubicweb.server.DBG_HOOKS
-.. autodata:: cubicweb.server.DBG_OPS
-.. autodata:: cubicweb.server.DBG_MORE
-.. autodata:: cubicweb.server.DBG_ALL
-
-
-Enable verbose output
-~~~~~~~~~~~~~~~~~~~~~
-
-To debug your RQL statements, it can be useful to enable a verbose output:
-
-.. sourcecode:: python
-
-    from cubicweb import server
-    server.set_debug(server.DBG_RQL|server.DBG_SQL|server.DBG_ALL)
-
-.. autofunction:: cubicweb.server.set_debug
-
-Another example showing how to debug hooks at a specific code site:
-
-.. sourcecode:: python
-
-    from cubicweb.server import debugged, DBG_HOOKS
-    with debugged(DBG_HOOKS):
-        person.cw_set(works_for=company)
-
-
-Detect largest RQL queries
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-See `Profiling and performance` chapter (see :ref:`PROFILING`).
-
-
-API
-~~~
-
-.. autoclass:: cubicweb.server.debugged
-
--- a/doc/book/en/annexes/rql/implementation.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,159 +0,0 @@
-
-
-Implementation
---------------
-
-BNF grammar
-~~~~~~~~~~~
-
-The terminal elements are in capital letters, non-terminal in lowercase.
-The value of the terminal elements (between quotes) is a Python regular
-expression.
-::
-
-     statement ::= (select | delete | insert | update) ';'
-
-
-     # select specific rules
-     select      ::= 'DISTINCT'? E_TYPE selected_terms restriction? group? sort?
-
-     selected_terms ::= expression ( ',' expression)*
-
-     group       ::= 'GROUPBY' VARIABLE ( ',' VARIABLE)*
-
-     sort        ::= 'ORDERBY' sort_term ( ',' sort_term)*
-
-     sort_term   ::=  VARIABLE sort_method =?
-
-     sort_method ::= 'ASC' | 'DESC'
-
-
-     # delete specific rules
-     delete ::= 'DELETE' (variables_declaration | relations_declaration) restriction?
-
-
-     # insert specific rules
-     insert ::= 'INSERT' variables_declaration ( ':' relations_declaration)? restriction?
-
-
-     # update specific rules
-     update ::= 'SET' relations_declaration restriction
-
-
-     # common rules
-     variables_declaration ::= E_TYPE VARIABLE (',' E_TYPE VARIABLE)*
-
-     relations_declaration ::= simple_relation (',' simple_relation)*
-
-     simple_relation ::= VARIABLE R_TYPE expression
-
-     restriction ::= 'WHERE' relations
-
-     relations   ::= relation (LOGIC_OP relation)*
-                   | '(' relations')'
-
-     relation    ::= 'NOT'? VARIABLE R_TYPE COMP_OP? expression
-                   | 'NOT'? R_TYPE VARIABLE 'IN' '(' expression (',' expression)* ')'
-
-     expression  ::= var_or_func_or_const (MATH_OP var_or_func_or_const) *
-                   | '(' expression ')'
-
-     var_or_func_or_const ::= VARIABLE | function | constant
-
-     function    ::= FUNCTION '(' expression ( ',' expression) * ')'
-
-     constant    ::= KEYWORD | STRING | FLOAT | INT
-
-     # tokens
-     LOGIC_OP ::= ',' | 'OR' | 'AND'
-     MATH_OP  ::= '+' | '-' | '/' | '*'
-     COMP_OP  ::= '>' | '>=' | '=' | '<=' | '<' | '~=' | 'LIKE'
-
-     FUNCTION ::= 'MIN' | 'MAX' | 'SUM' | 'AVG' | 'COUNT' | 'UPPER' | 'LOWER'
-
-     VARIABLE ::= '[A-Z][A-Z0-9]*'
-     E_TYPE   ::= '[A-Z]\w*'
-     R_TYPE   ::= '[a-z_]+'
-
-     KEYWORD  ::= 'TRUE' | 'FALSE' | 'NULL' | 'TODAY' | 'NOW'
-     STRING   ::= "'([^'\]|\\.)*'" |'"([^\"]|\\.)*\"'
-     FLOAT    ::= '\d+\.\d*'
-     INT      ::= '\d+'
-
-
-Internal representation (syntactic tree)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The tree research does not contain the selected variables
-(e.g. there is only what follows "WHERE").
-
-The insertion tree does not contain the variables inserted or relations
-defined on these variables (e.g. there is only what follows "WHERE").
-
-The removal tree does not contain the deleted variables and relations
-(e.g. there is only what follows the "WHERE").
-
-The update tree does not contain the variables and relations updated
-(e.g. there is only what follows the "WHERE").
-
-::
-
-     Select         ((Relationship | And | Or)?, Group?, Sort?)
-     Insert         (Relations | And | Or)?
-     Delete         (Relationship | And | Or)?
-     Update         (Relations | And | Or)?
-
-     And            ((Relationship | And | Or), (Relationship | And | Or))
-     Or             ((Relationship | And | Or), (Relationship | And | Or))
-
-     Relationship   ((VariableRef, Comparison))
-
-     Comparison     ((Function | MathExpression | Keyword | Constant | VariableRef) +)
-
-     Function       (())
-     MathExpression ((MathExpression | Keyword | Constant | VariableRef), (MathExpression | Keyword | Constant | VariableRef))
-
-     Group          (VariableRef +)
-     Sort           (SortTerm +)
-     SortTerm       (VariableRef +)
-
-     VariableRef    ()
-     Variable       ()
-     Keyword        ()
-     Constant       ()
-
-
-Known limitations
-~~~~~~~~~~~~~~~~~
-
-- The current implementation does not support linking two relations of type 'is'
-  with an OR. I do not think that the negation is supported on this type of
-  relation (XXX to be confirmed).
-
-- missing COALESCE and certainly other things...
-
-- writing an rql query requires knowledge of the used schema (with real relation
-  names and entities, not those viewed in the user interface). On the other
-  hand, we cannot really bypass that, and it is the job of a user interface to
-  hide the RQL.
-
-
-Topics
-~~~~~~
-
-It would be convenient to express the schema matching
-relations (non-recursive rules)::
-
-     Document class Type <-> Document occurence_of Fiche class Type
-     Sheet class Type    <-> Form collection Collection class Type
-
-Therefore 1. becomes::
-
-     Document X where
-     X class C, C name 'Cartoon'
-     X owned_by U, U login 'syt'
-     X available true
-
-I'm not sure that we should handle this at RQL level ...
-
-There should also be a special relation 'anonymous'.
--- a/doc/book/en/annexes/rql/index.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-.. _RQLChapter:
-
-Relation Query Language (RQL)
-=============================
-
-This chapter describes the Relation Query Language syntax and its implementation in CubicWeb.
-
-.. toctree::
-   :maxdepth: 2
-
-   intro
-   language
-   debugging
-   implementation
--- a/doc/book/en/annexes/rql/intro.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,162 +0,0 @@
-
-.. _rql_intro:
-
-Introduction
-------------
-
-Goals of RQL
-~~~~~~~~~~~~
-
-The goal is to have a semantic language in order to:
-
-- query relations in a clear syntax
-- empowers access to data repository manipulation
-- making attributes/relations browsing easy
-
-As such, attributes will be regarded as cases of special relations (in
-terms of usage, the user should see no syntactic difference between an
-attribute and a relation).
-
-Comparison with existing languages
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-SQL
-```
-
-RQL may remind of SQL but works at a higher abstraction level (the *CubicWeb*
-framework generates SQL from RQL to fetch data from relation databases). RQL is
-focused on browsing relations. The user needs only to know about the *CubicWeb*
-data model he is querying, but not about the underlying SQL model.
-
-Sparql
-``````
-
-The query language most similar to RQL is SPARQL_, defined by the W3C to serve
-for the semantic web.
-
-Versa
-`````
-
-We should look in more detail, but here are already some ideas for the moment
-... Versa_ is the language most similar to what we wanted to do, but the model
-underlying data being RDF, there are some things such as namespaces or
-handling of the RDF types which does not interest us. On the functionality
-level, Versa_ is very comprehensive including through many functions of
-conversion and basic types manipulation, which we may want to look at one time
-or another.  Finally, the syntax is a little esoteric.
-
-Datalog
-```````
-
-Datalog_ is a prolog derived query langage which applies to relational
-databases. It is more expressive than RQL in that it accepts either
-extensional_ and intensional_ predicates (or relations). As of now,
-RQL only deals with intensional relations.
-
-The different types of queries
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Search (`Any`)
-   Extract entities and attributes of entities.
-
-Insert entities (`INSERT`)
-   Insert new entities or relations in the database.
-   It can also directly create relationships for the newly created entities.
-
-Update entities, create relations (`SET`)
-   Update existing entities in the database,
-   or create relations between existing entities.
-
-Delete entities or relationship (`DELETE`)
-   Remove entities or relations existing in the database.
-
-
-RQL relation expressions
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-RQL expressions apply to a live database defined by a
-:ref:`datamodel_definition`. Apart from the main type, or head, of the
-expression (search, insert, etc.) the most common constituent of an
-RQL expression is a (set of) relation expression(s).
-
-An RQL relation expression contains three components:
-
-* the subject, which is an entity type
-* the predicate, which is a relation definition (an arc of the schema)
-* the object, which is either an attribute or a relation to another entity
-
-.. image:: Graph-ex.gif
-    :alt: <subject> <predicate> <object>
-    :align: center
-
-.. warning::
-
- A relation is always expressed in the order: ``subject``,
- ``predicate``, ``object``.
-
- It is important to determine if the entity type is subject or object
- to construct a valid expression. Inverting the subject/object is an
- error since the relation cannot be found in the schema.
-
- If one does not have access to the code, one can find the order by
- looking at the schema image in manager views (the subject is located
- at the beginning of the arrow).
-
-An example of two related relation expressions::
-
-  P works_for C, P name N
-
-RQL variables represent typed entities. The type of entities is
-either automatically inferred (by looking at the possible relation
-definitions, see :ref:`RelationDefinition`) or explicitely constrained
-using the ``is`` meta relation.
-
-In the example above, we barely need to look at the schema. If
-variable names (in the RQL expression) and relation type names (in the
-schema) are expresssively designed, the human reader can infer as much
-as the |cubicweb| querier.
-
-The ``P`` variable is used twice but it always represent the same set
-of entities. Hence ``P works_for C`` and ``P name N`` must be
-compatible in the sense that all the Ps (which *can* refer to
-different entity types) must accept the ``works_for`` and ``name``
-relation types. This does restrict the set of possible values of P.
-
-Adding another relation expression::
-
-  P works_for C, P name N, C name "logilab"
-
-This further restricts the possible values of P through an indirect
-constraint on the possible values of ``C``. The RQL-level unification_
-happening there is translated to one (or several) joins_ at the
-database level.
-
-.. note::
-
- In |cubicweb|, the term `relation` is often found without ambiguity
- instead of `predicate`.  This predicate is also known as the
- `property` of the triple in `RDF concepts`_
-
-
-RQL Operators
-~~~~~~~~~~~~~
-
-An RQL expression's head can be completed using various operators such
-as ``ORDERBY``, ``GROUPBY``, ``HAVING``, ``LIMIT`` etc.
-
-RQL relation expressions can be grouped with ``UNION`` or
-``WITH``. Predicate oriented keywords such as ``EXISTS``, ``OR``,
-``NOT`` are available.
-
-The complete zoo of RQL operators is described extensively in the
-following chapter (:ref:`RQL`).
-
-.. _RDF concepts: http://www.w3.org/TR/rdf-concepts/
-.. _Versa: http://wiki.xml3k.org/Versa
-.. _SPARQL: http://www.w3.org/TR/rdf-sparql-query/
-.. _unification: http://en.wikipedia.org/wiki/Unification_(computing)
-.. _joins: http://en.wikipedia.org/wiki/Join_(SQL)
-.. _Datalog: http://en.wikipedia.org/wiki/Datalog
-.. _intensional: http://en.wikipedia.org/wiki/Intensional_definition
-.. _extensional: http://en.wikipedia.org/wiki/Extension_(predicate_logic)
-
--- a/doc/book/en/annexes/rql/language.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,804 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _RQL:
-
-RQL syntax
-----------
-
-.. _RQLKeywords:
-
-Reserved keywords
-~~~~~~~~~~~~~~~~~
-
-::
-
-  AND, ASC, BEING, DELETE, DESC, DISTINCT, EXISTS, FALSE, GROUPBY,
-  HAVING, ILIKE, INSERT, LIKE, LIMIT, NOT, NOW, NULL, OFFSET,
-  OR, ORDERBY, SET, TODAY, TRUE, UNION, WHERE, WITH
-
-The keywords are not case sensitive. You should not use them when defining your
-schema, or as RQL variable names.
-
-
-.. _RQLCase:
-
-Case
-~~~~
-
-* Variables should be all upper-cased.
-
-* Relation should be all lower-cased and match exactly names of relations defined
-  in the schema.
-
-* Entity types should start with an upper cased letter and be followed by at least
-  a lower cased latter.
-
-
-.. _RQLVariables:
-
-Variables and typing
-~~~~~~~~~~~~~~~~~~~~
-
-Entities and values to browse and/or select are represented in the query by
-*variables* that must be written in capital letters.
-
-With RQL, we do not distinguish between entities and attributes. The value of an
-attribute is considered as an entity of a particular type (see below), linked to
-one (real) entity by a relation called the name of the attribute, where the
-entity is the subject and the attribute the object.
-
-The possible type(s) for each variable is derived from the schema according to
-the constraints expressed above and thanks to the relations between each
-variable.
-
-We can restrict the possible types for a variable using the special relation
-**is** in the restrictions.
-
-
-.. _VirtualRelations:
-
-Virtual relations
-~~~~~~~~~~~~~~~~~
-
-Those relations may only be used in RQL query but are not actual attributes of
-your entities.
-
-* `has_text`: relation to use to query the full text index (only for entities
-  having fulltextindexed attributes).
-
-* `identity`: relation to use to tell that a RQL variable is the same as another
-  when you've to use two different variables for querying purpose. On the
-  opposite it's also useful together with the ``NOT`` operator to tell that two
-  variables should not identify the same entity
-
-
-.. _RQLLiterals:
-
-Literal expressions
-~~~~~~~~~~~~~~~~~~~
-
-Bases types supported by RQL are those supported by yams schema. Literal values
-are expressed as explained below:
-
-* string should be between double or single quotes. If the value contains a
-  quote, it should be preceded by a backslash '\\'
-
-* floats separator is dot '.'
-
-* boolean values are ``TRUE`` and ``FALSE`` keywords
-
-* date and time should be expressed as a string with ISO notation : YYYY/MM/DD
-  [hh:mm], or using keywords ``TODAY`` and ``NOW``
-
-You may also use the ``NULL`` keyword, meaning 'unspecified'.
-
-
-.. _RQLOperators:
-
-Operators
-~~~~~~~~~
-
-.. _RQLLogicalOperators:
-
-Logical operators
-`````````````````
-::
-
-     AND, OR, NOT, ','
-
-',' is equivalent to 'AND' but with the smallest among the priority of logical
-operators (see :ref:`RQLOperatorsPriority`).
-
-.. _RQLMathematicalOperators:
-
-Mathematical operators
-``````````````````````
-
-+----------+---------------------+-----------+--------+
-| Operator |    Description      | Example   | Result |
-+==========+=====================+===========+========+
-|  `+`     | addition            | 2 + 3     | 5      |
-+----------+---------------------+-----------+--------+
-|  `-`     | subtraction         | 2 - 3     | -1     |
-+----------+---------------------+-----------+--------+
-|  `*`     | multiplication      | 2 * 3     | 6      |
-+----------+---------------------+-----------+--------+
-|  /       | division            | 4 / 2     | 2      |
-+----------+---------------------+-----------+--------+
-|  %       | modulo (remainder)  | 5 % 4     | 1      |
-+----------+---------------------+-----------+--------+
-|  ^       | exponentiation      | 2.0 ^ 3.0 | 8      |
-+----------+---------------------+-----------+--------+
-|  &       | bitwise AND         | 91 & 15   | 11     |
-+----------+---------------------+-----------+--------+
-|  `|`     | bitwise OR          | 32 | 3    | 35     |
-+----------+---------------------+-----------+--------+
-|  #       | bitwise XOR         | 17 # 5    | 20     |
-+----------+---------------------+-----------+--------+
-|  ~       | bitwise NOT         | ~1        | -2     |
-+----------+---------------------+-----------+--------+
-|  <<      | bitwise shift left  | 1 << 4    | 16     |
-+----------+---------------------+-----------+--------+
-|  >>      | bitwise shift right | 8 >> 2    | 2      |
-+----------+---------------------+-----------+--------+
-
-
-Notice integer division truncates results depending on the backend behaviour. For
-instance, postgresql does.
-
-
-.. _RQLComparisonOperators:
-
-Comparison operators
-````````````````````
- ::
-
-     =, !=, <, <=, >=, >, IN
-
-
-The syntax to use comparison operators is:
-
-    `VARIABLE attribute <operator> VALUE`
-
-The `=` operator is the default operator and can be omitted, i.e. :
-
-    `VARIABLE attribute = VALUE`
-
-is equivalent to
-
-    `VARIABLE attribute VALUE`
-
-
-The operator `IN` provides a list of possible values:
-
-.. sourcecode:: sql
-
-    Any X WHERE X name IN ('chauvat', 'fayolle', 'di mascio', 'thenault')
-
-
-.. _RQLStringOperators:
-
-String operators
-````````````````
-::
-
-  LIKE, ILIKE, ~=, REGEXP
-
-The ``LIKE`` string operator can be used with the special character `%` in
-a string as wild-card:
-
-.. sourcecode:: sql
-
-     -- match every entity whose name starts with 'Th'
-     Any X WHERE X name ~= 'Th%'
-     -- match every entity whose name endswith 'lt'
-     Any X WHERE X name LIKE '%lt'
-     -- match every entity whose name contains a 'l' and a 't'
-     Any X WHERE X name LIKE '%l%t%'
-
-``ILIKE`` is the case insensitive version of ``LIKE``. It's not
-available on all backend (e.g. sqlite doesn't support it). If not available for
-your backend, ``ILIKE`` will behave like ``LIKE``.
-
-`~=` is a shortcut version of ``ILIKE``, or of ``LIKE`` when the
-former is not available on the back-end.
-
-
-The ``REGEXP`` is an alternative to ``LIKE`` that supports POSIX
-regular expressions:
-
-.. sourcecode:: sql
-
-   -- match entities whose title starts with a digit
-   Any X WHERE X title REGEXP "^[0-9].*"
-
-
-The underlying SQL operator used is back-end-dependent :
-
-- the ``~`` operator is used for postgresql,
-- the ``REGEXP`` operator for mysql and sqlite.
-
-Other back-ends are not supported yet.
-
-
-.. _RQLOperatorsPriority:
-
-Operators priority
-``````````````````
-
-#. `(`, `)`
-#. `^`, `<<`, `>>`
-#. `*`, `/`, `%`, `&`
-#. `+`, `-`, `|`, `#`
-#. `NOT`
-#. `AND`
-#. `OR`
-#. `,`
-
-
-.. _RQLSearchQuery:
-
-Search Query
-~~~~~~~~~~~~
-
-Simplified grammar of search query: ::
-
-   [ `DISTINCT`] `Any` V1 (, V2) \*
-   [ `GROUPBY` V1 (, V2) \*] [ `ORDERBY` <orderterms>]
-   [ `LIMIT` <value>] [ `OFFSET` <value>]
-   [ `WHERE` <triplet restrictions>]
-   [ `WITH` V1 (, V2)\* BEING (<query>)]
-   [ `HAVING` <other restrictions>]
-   [ `UNION` <query>]
-
-Selection
-`````````
-
-The fist occuring clause is the selection of terms that should be in the result
-set.  Terms may be variable, literals, function calls, arithmetic, etc. and each
-term is separated by a comma.
-
-There will be as much column in the result set as term in this clause, respecting
-order.
-
-Syntax for function call is somewhat intuitive, for instance:
-
-.. sourcecode:: sql
-
-    Any UPPER(N) WHERE P firstname N
-
-
-Grouping and aggregating
-````````````````````````
-
-The ``GROUPBY`` keyword is followed by a list of terms on which results
-should be grouped. They are usually used with aggregate functions, responsible to
-aggregate values for each group (see :ref:`RQLAggregateFunctions`).
-
-For grouped queries, all selected variables must be either aggregated (i.e. used
-by an aggregate function) or grouped (i.e. listed in the ``GROUPBY``
-clause).
-
-
-Sorting
-```````
-
-The ``ORDERBY`` keyword if followed by the definition of the selection
-order: variable or column number followed by sorting method (``ASC``,
-``DESC``), ``ASC`` being the default. If the sorting method is not
-specified, then the sorting is ascendant (`ASC`).
-
-
-Pagination
-``````````
-
-The ``LIMIT`` and ``OFFSET`` keywords may be respectively used to
-limit the number of results and to tell from which result line to start (for
-instance, use `LIMIT 20` to get the first 20 results, then `LIMIT 20 OFFSET 20`
-to get the next 20.
-
-
-Restrictions
-````````````
-
-The ``WHERE`` keyword introduce one of the "main" part of the query, where
-you "define" variables and add some restrictions telling what you're interested
-in.
-
-It's a list of triplets "subject relation object", e.g. `V1 relation
-(V2 | <static value>)`. Triplets are separated using :ref:`RQLLogicalOperators`.
-
-.. note::
-
-  About the negation operator (``NOT``):
-
-  * ``NOT X relation Y`` is equivalent to ``NOT EXISTS(X relation Y)``
-
-  * ``Any X WHERE NOT X owned_by U`` means "entities that have no relation
-    ``owned_by``".
-
-  * ``Any X WHERE NOT X owned_by U, U login "syt"`` means "the entity have no
-     relation ``owned_by`` with the user syt". They may have a relation "owned_by"
-     with another user.
-
-In this clause, you can also use ``EXISTS`` when you want to know if some
-expression is true and do not need the complete set of elements that make it
-true. Testing for existence is much faster than fetching the complete set of
-results, especially when you think about using ``OR`` against several expressions. For instance
-if you want to retrieve versions which are in state "ready" or tagged by
-"priority", you should write :
-
-.. sourcecode:: sql
-
-    Any X ORDERBY PN,N
-    WHERE X num N, X version_of P, P name PN,
-          EXISTS(X in_state S, S name "ready")
-          OR EXISTS(T tags X, T name "priority")
-
-not
-
-.. sourcecode:: sql
-
-    Any X ORDERBY PN,N
-    WHERE X num N, X version_of P, P name PN,
-          (X in_state S, S name "ready")
-          OR (T tags X, T name "priority")
-
-Both queries aren't at all equivalent :
-
-* the former will retrieve all versions, then check for each one which are in the
-  matching state of or tagged by the expected tag,
-
-* the later will retrieve all versions, state and tags (cartesian product!),
-  compute join and then exclude each row which are in the matching state or
-  tagged by the expected tag. This implies that you won't get any result if the
-  in_state or tag tables are empty (ie there is no such relation in the
-  application). This is usually NOT what you want.
-
-Another common case where you may want to use ``EXISTS`` is when you
-find yourself using ``DISTINCT`` at the beginning of your query to
-remove duplicate results. The typical case is when you have a
-multivalued relation such as Version version_of Project and you want
-to retrieve projects which have a version:
-
-.. sourcecode:: sql
-
-  Any P WHERE V version_of P
-
-will return each project number of versions times. So you may be
-tempted to use:
-
-.. sourcecode:: sql
-
-  DISTINCT Any P WHERE V version_of P
-
-This will work, but is not efficient, as it will use the ``SELECT
-DISTINCT`` SQL predicate, which needs to retrieve all projects, then
-sort them and discard duplicates, which can have a very high cost for
-large result sets. So the best way to write this is:
-
-.. sourcecode:: sql
-
-  Any P WHERE EXISTS(V version_of P)
-
-
-You can also use the question mark (`?`) to mark optional relations. This allows
-you to select entities related **or not** to another. It is a similar concept
-to `Left outer join`_:
-
-    the result of a left outer join (or simply left join) for table A and B
-    always contains all records of the "left" table (A), even if the
-    join-condition does not find any matching record in the "right" table (B).
-
-You must use the `?` behind a variable to specify that the relation to
-that variable is optional. For instance:
-
-- Bugs of a project attached or not to a version
-
-   .. sourcecode:: sql
-
-       Any X, V WHERE X concerns P, P eid 42, X corrected_in V?
-
-  You will get a result set containing all the project's tickets, with either the
-  version in which it's fixed or None for tickets not related to a version.
-
-
-- All cards and the project they document if any
-
-  .. sourcecode:: sql
-
-       Any C, P WHERE C is Card, P? documented_by C
-
-Notice you may also use outer join:
-
-- on the RHS of attribute relation, e.g.
-
-  .. sourcecode:: sql
-
-       Any X WHERE X ref XR, Y name XR?
-
-  so that Y is outer joined on X by ref/name attributes comparison
-
-
-- on any side of an ``HAVING`` expression, e.g.
-
-  .. sourcecode:: sql
-
-       Any X WHERE X creation_date XC, Y creation_date YC
-       HAVING YEAR(XC)=YEAR(YC)?
-
-  so that Y is outer joined on X by comparison of the year extracted from their
-  creation date.
-
-  .. sourcecode:: sql
-
-       Any X WHERE X creation_date XC, Y creation_date YC
-       HAVING YEAR(XC)?=YEAR(YC)
-
-  would outer join X on Y instead.
-
-
-Having restrictions
-```````````````````
-
-The ``HAVING`` clause, as in SQL, may be used to restrict a query
-according to value returned by an aggregate function, e.g.
-
-.. sourcecode:: sql
-
-    Any X GROUPBY X WHERE X relation Y HAVING COUNT(Y) > 10
-
-It may however be used for something else: In the ``WHERE`` clause, we are
-limited to triplet expressions, so some things may not be expressed there. Let's
-take an example : if you want to get people whose upper-cased first name equals to
-another person upper-cased first name. There is no proper way to express this
-using triplet, so you should use something like:
-
-.. sourcecode:: sql
-
-    Any X WHERE X firstname XFN, Y firstname YFN, NOT X identity Y HAVING UPPER(XFN) = UPPER(YFN)
-
-Another example: imagine you want person born in 2000:
-
-.. sourcecode:: sql
-
-    Any X WHERE X birthday XB HAVING YEAR(XB) = 2000
-
-Notice that while we would like this to work without the HAVING clause, this
-can't be currently be done because it introduces an ambiguity in RQL's grammar
-that can't be handled by Yapps_, the parser's generator we're using.
-
-
-Sub-queries
-```````````
-
-The ``WITH`` keyword introduce sub-queries clause. Each sub-query has the
-form:
-
-  V1(,V2) BEING (rql query)
-
-Variables at the left of the ``BEING`` keyword defines into which
-variables results from the sub-query will be mapped to into the outer query.
-Sub-queries are separated from each other using a comma.
-
-Let's say we want to retrieve for each project its number of versions and its
-number of tickets. Due to the nature of relational algebra behind the scene, this
-can't be achieved using a single query. You have to write something along the
-line of:
-
-.. sourcecode:: sql
-
-  Any X, VC, TC WHERE X identity XX
-  WITH X, VC BEING (Any X, COUNT(V) GROUPBY X WHERE V version_of X),
-       XX, TC BEING (Any X, COUNT(T) GROUPBY X WHERE T ticket_of X)
-
-Notice that we can't reuse a same variable name as alias for two different
-sub-queries, hence the usage of 'X' and 'XX' in this example, which are then
-unified using the special `identity` relation (see :ref:`VirtualRelations`).
-
-.. warning::
-
-  Sub-queries define a new variable scope, so even if a variable has the same name
-  in the outer query and in the sub-query, they technically **aren't** the same
-  variable. So:
-
-  .. sourcecode:: sql
-
-     Any W, REF WITH W, REF BEING
-         (Any W, REF WHERE W is Workcase, W ref REF,
-                           W concerned_by D, D name "Logilab")
-
-  could be written:
-
-  .. sourcecode:: sql
-
-     Any W, REF WITH W, REF BEING
-        (Any W1, REF1 WHERE W1 is Workcase, W1 ref REF1,
-                            W1 concerned_by D, D name "Logilab")
-
-  Also, when a variable is coming from a sub-query, you currently can't reference
-  its attribute or inlined relations in the outer query, you've to fetch them in
-  the sub-query. For instance, let's say we want to sort by project name in our
-  first example, we would have to write:
-
-  .. sourcecode:: sql
-
-
-    Any X, VC, TC ORDERBY XN WHERE X identity XX
-    WITH X, XN, VC BEING (Any X, COUNT(V) GROUPBY X,XN WHERE V version_of X, X name XN),
-         XX, TC BEING (Any X, COUNT(T) GROUPBY X WHERE T ticket_of X)
-
-  instead of:
-
-  .. sourcecode:: sql
-
-    Any X, VC, TC ORDERBY XN WHERE X identity XX, X name XN,
-    WITH X, XN, VC BEING (Any X, COUNT(V) GROUPBY X WHERE V version_of X),
-         XX, TC BEING (Any X, COUNT(T) GROUPBY X WHERE T ticket_of X)
-
-  which would result in a SQL execution error.
-
-
-Union
-`````
-
-You may get a result set containing the concatenation of several queries using
-the ``UNION``. The selection of each query should have the same number of
-columns.
-
-.. sourcecode:: sql
-
-    (Any X, XN WHERE X is Person, X surname XN) UNION (Any X,XN WHERE X is Company, X name XN)
-
-
-.. _RQLFunctions:
-
-Available functions
-~~~~~~~~~~~~~~~~~~~
-
-Below is the list of aggregate and transformation functions that are supported
-nativly by the framework. Notice that cubes may define additional functions.
-
-.. _RQLAggregateFunctions:
-
-Aggregate functions
-```````````````````
-+------------------------+----------------------------------------------------------+
-| ``COUNT(Any)``         | return the number of rows                                |
-+------------------------+----------------------------------------------------------+
-| ``MIN(Any)``           | return the minimum value                                 |
-+------------------------+----------------------------------------------------------+
-| ``MAX(Any)``           | return the maximum value                                 |
-+------------------------+----------------------------------------------------------+
-| ``AVG(Any)``           | return the average value                                 |
-+------------------------+----------------------------------------------------------+
-| ``SUM(Any)``           | return the sum of values                                 |
-+------------------------+----------------------------------------------------------+
-| ``COMMA_JOIN(String)`` | return each value separated by a comma (for string only) |
-+------------------------+----------------------------------------------------------+
-
-All aggregate functions above take a single argument. Take care some aggregate
-functions (e.g. ``MAX``, ``MIN``) may return `None` if there is no
-result row.
-
-.. _RQLStringFunctions:
-
-String transformation functions
-```````````````````````````````
-
-+-----------------------------------------------+-----------------------------------------------------------------+
-| ``UPPER(String)``                             | upper case the string                                           |
-+-----------------------------------------------+-----------------------------------------------------------------+
-| ``LOWER(String)``                             | lower case the string                                           |
-+-----------------------------------------------+-----------------------------------------------------------------+
-| ``LENGTH(String)``                            | return the length of the string                                 |
-+-----------------------------------------------+-----------------------------------------------------------------+
-| ``SUBSTRING(String, start, length)``          | extract from the string a string starting at given index and of |
-|                                               | given length                                                    |
-+-----------------------------------------------+-----------------------------------------------------------------+
-| ``LIMIT_SIZE(String, max size)``              | if the length of the string is greater than given max size,     |
-|                                               | strip it and add ellipsis ("..."). The resulting string will    |
-|                                               | hence have max size + 3 characters                              |
-+-----------------------------------------------+-----------------------------------------------------------------+
-| ``TEXT_LIMIT_SIZE(String, format, max size)`` | similar to the above, but allow to specify the MIME type of the |
-|                                               | text contained by the string. Supported formats are text/html,  |
-|                                               | text/xhtml and text/xml. All others will be considered as plain |
-|                                               | text. For non plain text format, sgml tags will be first removed|
-|                                               | before limiting the string.                                     |
-+-----------------------------------------------+-----------------------------------------------------------------+
-
-.. _RQLDateFunctions:
-
-Date extraction functions
-`````````````````````````
-
-+----------------------+----------------------------------------+
-| ``YEAR(Date)``       | return the year of a date or datetime  |
-+----------------------+----------------------------------------+
-| ``MONTH(Date)``      | return the month of a date or datetime |
-+----------------------+----------------------------------------+
-| ``DAY(Date)``        | return the day of a date or datetime   |
-+----------------------+----------------------------------------+
-| ``HOUR(Datetime)``   | return the hours of a datetime         |
-+----------------------+----------------------------------------+
-| ``MINUTE(Datetime)`` | return the minutes of a datetime       |
-+----------------------+----------------------------------------+
-| ``SECOND(Datetime)`` | return the seconds of a datetime       |
-+----------------------+----------------------------------------+
-| ``WEEKDAY(Date)``    | return the day of week of a date or    |
-|                      | datetime.  Sunday == 1, Saturday == 7. |
-+----------------------+----------------------------------------+
-
-.. _RQLOtherFunctions:
-
-Other functions
-```````````````
-+-------------------+--------------------------------------------------------------------+
-| ``ABS(num)``      |  return the absolute value of a number                             |
-+-------------------+--------------------------------------------------------------------+
-| ``RANDOM()``      | return a pseudo-random value from 0.0 to 1.0                       |
-+-------------------+--------------------------------------------------------------------+
-| ``FSPATH(X)``     | expect X to be an attribute whose value is stored in a             |
-|                   | :class:`BFSStorage` and return its path on the file system         |
-+-------------------+--------------------------------------------------------------------+
-| ``FTIRANK(X)``    | expect X to be an entity used in a has_text relation, and return a |
-|                   | number corresponding to the rank order of each resulting entity    |
-+-------------------+--------------------------------------------------------------------+
-| ``CAST(Type, X)`` | expect X to be an attribute and return it casted into the given    |
-|                   | final type                                                         |
-+-------------------+--------------------------------------------------------------------+
-
-
-.. _RQLExamples:
-
-Examples
-~~~~~~~~
-
-- *Search for the object of identifier 53*
-
-  .. sourcecode:: sql
-
-        Any X WHERE X eid 53
-
-- *Search material such as comics, owned by syt and available*
-
-  .. sourcecode:: sql
-
-        Any X WHERE X is Document,
-                    X occurence_of F, F class C, C name 'Comics',
-                    X owned_by U, U login 'syt',
-                    X available TRUE
-
-- *Looking for people working for eurocopter interested in training*
-
-  .. sourcecode:: sql
-
-        Any P WHERE P is Person, P work_for S, S name 'Eurocopter',
-                    P interested_by T, T name 'training'
-
-- *Search note less than 10 days old written by jphc or ocy*
-
-  .. sourcecode:: sql
-
-        Any N WHERE N is Note, N written_on D, D day> (today -10),
-                    N written_by P, P name 'jphc' or P name 'ocy'
-
-- *Looking for people interested in training or living in Paris*
-
-  .. sourcecode:: sql
-
-        Any P WHERE P is Person, EXISTS(P interested_by T, T name 'training') OR
-                    (P city 'Paris')
-
-- *The surname and firstname of all people*
-
-  .. sourcecode:: sql
-
-        Any N, P WHERE X is Person, X name N, X firstname P
-
-  Note that the selection of several entities generally force
-  the use of "Any" because the type specification applies otherwise
-  to all the selected variables. We could write here
-
-  .. sourcecode:: sql
-
-        String N, P WHERE X is Person, X name N, X first_name P
-
-
-  Note: You can not specify several types with * ... where X is FirstType or X is SecondType*.
-  To specify several types explicitly, you have to do
-
-
-  .. sourcecode:: sql
-
-        Any X WHERE X is IN (FirstType, SecondType)
-
-
-.. _RQLInsertQuery:
-
-Insertion query
-~~~~~~~~~~~~~~~
-
-    `INSERT` <entity type> V1 (, <entity type> V2) \ * `:` <assignments>
-    [ `WHERE` <restriction>]
-
-:assignments:
-   list of relations to assign in the form `V1 relationship V2 | <static value>`
-
-The restriction can define variables used in assignments.
-
-Caution, if a restriction is specified, the insertion is done for
-*each line result returned by the restriction*.
-
-- *Insert a new person named 'foo'*
-
-  .. sourcecode:: sql
-
-        INSERT Person X: X name 'foo'
-
-- *Insert a new person named 'foo', another called 'nice' and a 'friend' relation
-  between them*
-
-  .. sourcecode:: sql
-
-        INSERT Person X, Person Y: X name 'foo', Y name 'nice', X friend Y
-
-- *Insert a new person named 'foo' and a 'friend' relation with an existing
-  person called 'nice'*
-
-  .. sourcecode:: sql
-
-        INSERT Person X: X name 'foo', X friend  Y WHERE Y name 'nice'
-
-.. _RQLSetQuery:
-
-Update and relation creation queries
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    `SET` <assignements>
-    [ `WHERE` <restriction>]
-
-Caution, if a restriction is specified, the update is done *for
-each result line returned by the restriction*.
-
-- *Renaming of the person named 'foo' to 'bar' with the first name changed*
-
-  .. sourcecode:: sql
-
-        SET X name 'bar', X firstname 'original' WHERE X is Person, X name 'foo'
-
-- *Insert a relation of type 'know' between objects linked by
-  the relation of type 'friend'*
-
-  .. sourcecode:: sql
-
-        SET X know Y  WHERE X friend Y
-
-
-.. _RQLDeleteQuery:
-
-Deletion query
-~~~~~~~~~~~~~~
-
-    `DELETE` (<entity type> V) | (V1 relation v2 ),...
-    [ `WHERE` <restriction>]
-
-Caution, if a restriction is specified, the deletion is made *for
-each line result returned by the restriction*.
-
-- *Deletion of the person named 'foo'*
-
-  .. sourcecode:: sql
-
-        DELETE Person X WHERE X name 'foo'
-
-- *Removal of all relations of type 'friend' from the person named 'foo'*
-
-  .. sourcecode:: sql
-
-        DELETE X friend Y WHERE X is Person, X name 'foo'
-
-
-.. _Yapps: http://theory.stanford.edu/~amitp/yapps/
-.. _Left outer join: http://en.wikipedia.org/wiki/Join_(SQL)#Left_outer_join
-
--- a/doc/book/en/conf.py	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,225 +0,0 @@
-# -*- coding: utf-8 -*-
-# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of CubicWeb.
-#
-# CubicWeb is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""
-
-"""
-#
-# Cubicweb documentation build configuration file, created by
-# sphinx-quickstart on Fri Oct 31 09:10:36 2008.
-#
-# This file is execfile()d with the current directory set to its containing dir.
-#
-# The contents of this file are pickled, so don't put values in the namespace
-# that aren't pickleable (module imports are okay, they're removed automatically).
-#
-# All configuration values have a default value; values that are commented out
-# serve to show the default value.
-
-from os import path as osp
-
-path = __file__
-path = osp.dirname(path) #./doc/book/en
-path = osp.dirname(path) #./doc/book/
-path = osp.dirname(path) #./doc/
-path = osp.dirname(path) #./
-path = osp.join(path,'__pkginfo__.py') #./__pkginfo__.py
-cw = {}
-execfile(path,{},cw)
-
-# If your extensions are in another directory, add it here. If the directory
-# is relative to the documentation root, use os.path.abspath to make it
-# absolute, like shown here.
-#sys.path.append(os.path.abspath('some/directory'))
-
-# General configuration
-# ---------------------
-
-# Add any Sphinx extension module names here, as strings. They can be extensions
-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = [
-  'sphinx.ext.autodoc', 
-  'sphinx.ext.viewcode',
-  'logilab.common.sphinx_ext',
-  ]
-
-autoclass_content = 'both'
-
-# Add any paths that contain templates here, relative to this directory.
-#templates_path = []
-
-# The suffix of source filenames.
-source_suffix = '.rst'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General substitutions.
-project = 'CubicWeb'
-copyright = '2001-2014, Logilab'
-
-# The default replacements for |version| and |release|, also used in various
-# other places throughout the built documents.
-#
-# The short X.Y version.
-version = '.'.join(str(n) for n in cw['numversion'][:2])
-# The full version, including alpha/beta/rc tags.
-release = cw['version']
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-today_fmt = '%B %d, %Y'
-
-# List of documents that shouldn't be included in the build.
-unused_docs = []
-
-# List of directories, relative to source directories, that shouldn't be searched
-# for source files.
-#exclude_dirs = []
-
-# The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
-
-
-# Options for HTML output
-# -----------------------
-
-# The style sheet to use for HTML and HTML Help pages. A file of that name
-# must exist either in Sphinx' static/ path, or in one of the custom paths
-# given in html_static_path.
-#html_style = 'sphinx-default.css'
-
-# The name for this set of Sphinx documents.  If None, it defaults to
-# "<project> v<release> documentation".
-html_title = '%s %s' % (project, release)
-
-html_theme_path = ['_themes']
-html_theme = 'cubicweb'
-
-# A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (within the static path) to place at the top of
-# the sidebar.
-#html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-#html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['.static']
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-html_use_modindex = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, the reST sources are included in the HTML build as _sources/<name>.
-#html_copy_source = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it.  The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
-html_file_suffix = '.html'
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'Cubicwebdoc'
-
-
-# Options for LaTeX output
-# ------------------------
-
-# The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
-
-# The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, document class [howto/manual]).
-latex_documents = [
-  ('index', 'Cubicweb.tex', 'Cubicweb Documentation',
-   'Logilab', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_use_modindex = True
-
-#aafig_format = dict(latex='pdf', html='svg', text=None)
-
-rst_epilog = """
-.. |cubicweb| replace:: *CubicWeb*
-.. |yams| replace:: *Yams*
-.. |rql| replace:: *RQL*
-"""
--- a/doc/book/en/devrepo/cubes/available-cubes.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-.. _AvailableCubes:
-
-Available cubes
----------------
-
-An instance is made of several basic cubes. In the set of available
-basic cubes we can find for example:
-
-Base entity types
-~~~~~~~~~~~~~~~~~
-* addressbook_: PhoneNumber and PostalAddress
-* card_: Card, generic documenting card
-* event_: Event (define events, display them in calendars)
-* file_: File (to allow users to upload and store binary or text files)
-* link_: Link (to collect links to web resources)
-* mailinglist_: MailingList (to reference a mailing-list and the URLs
-  for its archives and its admin interface)
-* person_: Person (easily mixed with addressbook)
-* task_: Task (something to be done between start and stop date)
-* zone_: Zone (to define places within larger places, for example a
-  city in a state in a country)
-
-
-Classification
-~~~~~~~~~~~~~~
-* folder_: Folder (to organize things by grouping them in folders)
-* keyword_: Keyword (to define classification schemes)
-* tag_: Tag (to tag anything)
-
-Other features
-~~~~~~~~~~~~~~
-* basket_: Basket (like a shopping cart)
-* blog_: a blogging system using Blog and BlogEntry entity types
-* comment_: system to attach comment threads to entities)
-* email_: archiving management for emails (`Email`, `Emailpart`,
-  `Emailthread`), trigger action in cubicweb through email
-
-
-
-
-
-.. _addressbook: http://www.cubicweb.org/project/cubicweb-addressbook
-.. _basket: http://www.cubicweb.org/project/cubicweb-basket
-.. _card: http://www.cubicweb.org/project/cubicweb-card
-.. _blog: http://www.cubicweb.org/project/cubicweb-blog
-.. _comment: http://www.cubicweb.org/project/cubicweb-comment
-.. _email: http://www.cubicweb.org/project/cubicweb-email
-.. _event: http://www.cubicweb.org/project/cubicweb-event
-.. _file: http://www.cubicweb.org/project/cubicweb-file
-.. _folder: http://www.cubicweb.org/project/cubicweb-folder
-.. _keyword: http://www.cubicweb.org/project/cubicweb-keyword
-.. _link: http://www.cubicweb.org/project/cubicweb-link
-.. _mailinglist: http://www.cubicweb.org/project/cubicweb-mailinglist
-.. _person: http://www.cubicweb.org/project/cubicweb-person
-.. _tag: http://www.cubicweb.org/project/cubicweb-tag
-.. _task: http://www.cubicweb.org/project/cubicweb-task
-.. _zone: http://www.cubicweb.org/project/cubicweb-zone
-
-To declare the use of a cube, once installed, add the name of the cube
-and its dependency relation in the `__depends_cubes__` dictionary
-defined in the file `__pkginfo__.py` of your own component.
-
-.. note::
-  The listed cubes above are available as debian-packages on `CubicWeb's forge`_.
-
-.. _`CubicWeb's forge`: http://www.cubicweb.org/project?vtitle=All%20cubicweb%20projects
--- a/doc/book/en/devrepo/cubes/cc-newcube.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-Creating a new cube from scratch
---------------------------------
-
-Let's start by creating the cube environment in which we will develop ::
-
-  cd ~/cubes
-  # use cubicweb-ctl to generate a template for the cube
-  # will ask some questions, most with nice default
-  cubicweb-ctl newcube mycube
-  # makes the cube source code managed by mercurial
-  cd mycube
-  hg init
-  hg add .
-  hg ci
-
-If all went well, you should see the cube you just created in the list
-returned by ``cubicweb-ctl list`` in the  *Available cubes* section.
-If not, please refer to :ref:`ConfigurationEnv`.
-
-To reuse an existing cube, add it to the list named
-``__depends_cubes__`` which is defined in :file:`__pkginfo__.py`.
-This variable is used for the instance packaging (dependencies handled
-by system utility tools such as APT) and to find used cubes when the
-database for the instance is created (import_erschema('MyCube') will
-not properly work otherwise).
-
-On a Unix system, the available cubes are usually stored in the
-directory :file:`/usr/share/cubicweb/cubes`. If you are using the
-cubicweb mercurial repository (:ref:`SourceInstallation`), the cubes
-are searched in the directory
-:file:`/path/to/cubicweb_toplevel/cubes`. In this configuration
-cubicweb itself ought to be located at
-:file:`/path/to/cubicweb_toplevel/cubicweb`.
-
-.. note::
-
-    Please note that if you do not wish to use default directory for your cubes
-    library, you should set the :envvar:`CW_CUBES_PATH` environment variable to
-    add extra directories where cubes will be search, and you'll then have to use
-    the option `--directory` to specify where you would like to place the source
-    code of your cube:
-
-    ``cubicweb-ctl newcube --directory=/path/to/cubes/library mycube``
-
-
-.. XXX resurrect once live-server is back
-.. Usage of :command:`cubicweb-ctl liveserver`
-.. -------------------------------------------
-
-.. To quickly test a new cube, you can also use the `liveserver` command for cubicweb-ctl
-.. which allows to create an instance in memory (using an SQLite database by
-.. default) and make it accessible through a web server ::
-
-..   cubicweb-ctl live-server mycube
-
-.. or by using an existing database (SQLite or Postgres)::
-
-..   cubicweb-ctl live-server -s myfile_sources mycube
--- a/doc/book/en/devrepo/cubes/index.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-Cubes
-=====
-
-This chapter describes how to define your own cubes and reuse already available cubes.
-
-.. toctree::
-   :maxdepth: 1
-
-   layout.rst
-   cc-newcube.rst
-   available-cubes.rst
--- a/doc/book/en/devrepo/cubes/layout.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,153 +0,0 @@
-
-.. _foundationsCube:
-
-.. _cubelayout:
-
-Standard structure for a cube
------------------------------
-
-A cube is structured as follows:
-
-::
-
-  mycube/
-  |
-  |-- data/
-  |   |-- cubes.mycube.css
-  |   |-- cubes.mycube.js
-  |   `-- external_resources
-  |
-  |-- debian/
-  |   |-- changelog
-  |   |-- compat
-  |   |-- control
-  |   |-- copyright
-  |   |-- cubicweb-mycube.prerm
-  |   `-- rules
-  |
-  |-- entities.py
-  |
-  |-- i18n/
-  |   |-- en.po
-  |   |-- es.po
-  |   `-- fr.po
-  |
-  |-- __init__.py
-  |
-  |-- MANIFEST.in
-  |
-  |-- migration/
-  |   |-- postcreate.py
-  |   `-- precreate.py
-  |
-  |-- __pkginfo__.py
-  |
-  |-- schema.py
-  |
-  |-- setup.py
-  |
-  |-- site_cubicweb.py
-  |
-  |-- hooks.py
-  |
-  |-- test/
-  |   |-- data/
-  |   |   `-- bootstrap_cubes
-  |   |-- pytestconf.py
-  |   |-- realdb_test_mycube.py
-  |   `-- test_mycube.py
-  |
-  `-- views.py
-
-
-We can use subpackages instead of python modules for ``views.py``, ``entities.py``,
-``schema.py`` or ``hooks.py``. For example, we could have:
-
-::
-
-  mycube/
-  |
-  |-- entities.py
-  |-- hooks.py
-  `-- views/
-      |-- __init__.py
-      |-- forms.py
-      |-- primary.py
-      `-- widgets.py
-
-
-where :
-
-* ``schema`` contains the schema definition (server side only)
-* ``entities`` contains the entity definitions (server side and web interface)
-* ``hooks`` contains hooks and/or views notifications (server side only)
-* ``views`` contains the web interface components (web interface only)
-* ``test`` contains tests related to the cube (not installed)
-* ``i18n`` contains message catalogs for supported languages (server side and
-  web interface)
-* ``data`` contains data files for static content (images, css,
-  javascript code)...(web interface only)
-* ``migration`` contains initialization files for new instances (``postcreate.py``)
-  and a file containing dependencies of the component depending on the version
-  (``depends.map``)
-* ``debian`` contains all the files managing debian packaging (you will find
-  the usual files ``control``, ``rules``, ``changelog``... not installed)
-* file ``__pkginfo__.py`` provides component meta-data, especially the distribution
-  and the current version (server side and web interface) or sub-cubes used by
-  the cube.
-
-
-At least you should have the file ``__pkginfo__.py``.
-
-
-The :file:`__init__.py` and :file:`site_cubicweb.py` files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. XXX WRITEME
-
-The :file:`__pkginfo__.py` file
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-It contains metadata describing your cube, mostly useful for packaging.
-
-Two important attributes of this module are __depends__ and __recommends__
-dictionaries that indicates what should be installed (and each version if
-necessary) for the cube to work.
-
-Dependency on other cubes are expected to be of the form 'cubicweb-<cubename>'.
-
-When an instance is created, dependencies are automatically installed, while
-recommends are not.
-
-Recommends may be seen as a kind of 'weak dependency'. Eg, the most important
-effect of recommending a cube is that, if cube A recommends cube B, the cube B
-will be loaded before the cube A (same thing happend when A depends on B).
-
-Having this behaviour is sometime desired: on schema creation, you may rely on
-something defined in the other's schema; on database creation, on something
-created by the other's postcreate, and so on.
-
-
-:file:`migration/precreate.py` and :file:`migration/postcreate.py`
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. XXX detail steps of instance creation
-
-
-External resources such as image, javascript and css files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. XXX naming convention external_resources file
-
-
-Out-of the box testing
-~~~~~~~~~~~~~~~~~~~~~~
-
-.. XXX MANIFEST.in, __pkginfo__.include_dirs, debian
-
-
-Packaging and distribution
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. XXX MANIFEST.in, __pkginfo__.include_dirs, debian
-
--- a/doc/book/en/devrepo/dataimport.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-. -*- coding: utf-8 -*-
-
-.. _dataimport:
-
-Dataimport
-==========
-
-*CubicWeb* is designed to manipulate huge of amount of data, and provides helper functions to do so.
-These functions insert data within different levels of the *CubicWeb* API,
-allowing different speed/security tradeoffs. Those keeping all the *CubicWeb* hooks
-and security will be slower but the possible errors in insertion
-(bad data types, integrity error, ...) will be raised.
-
-These dataimport function are provided in the file `dataimport.py`.
-
-All the stores have the following API::
-
-    >>> store = ObjectStore()
-    >>> user = store.create_entity('CWUser', login=u'johndoe')
-    >>> group = store.create_entity('CWUser', name=u'unknown')
-    >>> store.relate(user.eid, 'in_group', group.eid)
-
-
-ObjectStore
------------
-
-This store keeps objects in memory for *faster* validation. It may be useful
-in development mode. However, as it will not enforce the constraints of the schema,
-it may miss some problems.
-
-
-
-RQLObjectStore
---------------
-
-This store works with an actual RQL repository, and it may be used in production mode.
-
-
-NoHookRQLObjectStore
---------------------
-
-This store works similarly to the *RQLObjectStore* but bypasses some *CubicWeb* hooks to be faster.
-
-
-SQLGenObjectStore
------------------
-
-This store relies on *COPY FROM*/execute many sql commands to directly push data using SQL commands
-rather than using the whole *CubicWeb* API. For now, **it only works with PostgresSQL** as it requires
-the *COPY FROM* command.
-
-The API is similar to the other stores, but **it requires a flush** after some imports to copy data
-in the database (these flushes may be multiples through the processes, or be done only once at the
-end if there is no memory issue)::
-
-    >>> store = SQLGenObjectStore(session)
-    >>> store.create_entity('Person', ...)
-    >>> store.flush()
--- a/doc/book/en/devrepo/datamodel/baseschema.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-.. _pre_defined_entity_types:
-
-Pre-defined entities in the library
------------------------------------
-
-The library defines a set of entity schemas that are required by the system
-or commonly used in *CubicWeb* instances.
-
-
-Entity types used to store the schema
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-* _`CWEType`, entity type
-* _`CWRType`, relation type
-* _`CWRelation`, relation definition
-* _`CWAttribute`, attribute relation definition
-* _`CWConstraint`,  `CWConstraintType`, `RQLExpression`
-
-Entity types used to manage users and permissions
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-* _`CWUser`, system users
-* _`CWGroup`, users groups
-
-Entity types used to manage workflows
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-* :ref:`Workflow <Workflow>`, workflow entity, linked to some entity types which may use this workflow
-* _`State`, workflow state
-* _`Transition`, workflow transition
-* _`TrInfo`, record of a transition trafic for an entity
-
-Other entity types
-~~~~~~~~~~~~~~~~~~
-* _`CWCache`, cache entities used to improve performances
-* _`CWProperty`, used to configure the instance
-
-* _`EmailAddress`, email address, used by the system to send notifications
-  to the users and also used by others optionnals schemas
-
-* _`Bookmark`, an entity type used to allow a user to customize his links within
-  the instance
-
-* _`ExternalUri`, used for semantic web site to indicate that an entity is the
-  same as another from an external site
--- a/doc/book/en/devrepo/datamodel/define-workflows.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,160 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _Workflow:
-
-Defining a Workflow
-===================
-
-General
--------
-
-A workflow describes how certain entities have to evolve between different
-states. Hence we have a set of states, and a "transition graph", i.e. a set of
-possible transitions from one state to another state.
-
-We will define a simple workflow for a blog, with only the following two states:
-`submitted` and `published`. You may want to take a look at :ref:`TutosBase` if
-you want to quickly setup an instance running a blog.
-
-Setting up a workflow
----------------------
-
-We want to create a workflow to control the quality of the BlogEntry
-submitted on the instance. When a BlogEntry is created by a user
-its state should be `submitted`. To be visible to all, it has to
-be in the state `published`. To move it from `submitted` to `published`,
-we need a transition that we can call `approve_blogentry`.
-
-A BlogEntry state should not be modifiable by every user.
-So we have to define a group of users, `moderators`, and
-this group will have appropriate permissions to publish a BlogEntry.
-
-There are two ways to create a workflow: from the user interface, or
-by defining it in ``migration/postcreate.py``. This script is executed
-each time a new ``cubicweb-ctl db-init`` is done.  We strongly
-recommend to create the workflow in ``migration/postcreate.py`` and we
-will now show you how. Read `Two bits of warning`_ to understand why.
-
-The state of an entity is managed by the `in_state` attribute which
-can be added to your entity schema by inheriting from
-`cubicweb.schema.WorkflowableEntityType`.
-
-
-About our example of BlogEntry, we must have:
-
-.. sourcecode:: python
-
-  from cubicweb.schema import WorkflowableEntityType
-
-  class BlogEntry(WorkflowableEntityType):
-      ...
-
-
-Creating states, transitions and group permissions
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The :mod:`postcreate` script is executed in a special environment,
-adding several |cubicweb| primitives that can be used.
-
-They are all defined in the :class:`ServerMigrationHelper` class.
-We will only discuss the methods we use to create a workflow in this example.
-
-A workflow is a collection of entities of type ``State`` and of type
-``Transition`` which are standard *CubicWeb* entity types.
-
-To define a workflow for BlogDemo, please add the following lines
-to ``migration/postcreate.py``:
-
-.. sourcecode:: python
-
-  _ = unicode
-
-  moderators = add_entity('CWGroup', name=u"moderators")
-
-This adds the `moderators` user group.
-
-.. sourcecode:: python
-
-  wf = add_workflow(u'blog publication workflow', 'BlogEntry')
-
-At first, instanciate a new workflow object with a gentle description
-and the concerned entity types (this one can be a tuple for multiple
-value).
-
-.. sourcecode:: python
-
-  submitted = wf.add_state(_('submitted'), initial=True)
-  published = wf.add_state(_('published'))
-
-This will create two entities of type ``State``, one with name
-'submitted', and the other with name 'published'.
-
-``add_state`` expects as first argument the name of the state you want
-to create and an optional argument to say if it is supposed to be the
-initial state of the entity type.
-
-.. sourcecode:: python
-
-  wf.add_transition(_('approve_blogentry'), (submitted,), published, ('moderators', 'managers'),)
-
-This will create an entity of type ``Transition`` with name
-`approve_blogentry` which will be linked to the ``State`` entities
-created before.
-
-``add_transition`` expects
-
-  * as the first argument: the name of the transition
-  * then the list of states on which the transition can be triggered,
-  * the target state of the transition,
-  * and the permissions
-    (e.g. a list of user groups who can apply the transition; the user
-    has to belong to at least one of the listed group to perform the action).
-
-.. sourcecode:: python
-
-  checkpoint()
-
-.. note::
-  Do not forget to add the `_()` in front of all states and
-  transitions names while creating a workflow so that they will be
-  identified by the i18n catalog scripts.
-
-In addition to the user groups (one of which the user needs to belong
-to), we could have added a RQL condition.  In this case, the user can
-only perform the action if the two conditions are satisfied.
-
-If we use an RQL condition on a transition, we can use the following variables:
-
-* `X`, the entity on which we may pass the transition
-* `U`, the user executing that may pass the transition
-
-
-.. image:: ../../images/03-transitions-view_en.png
-
-You can notice that in the action box of a BlogEntry, the state is now
-listed as well as the possible transitions for the current state
-defined by the workflow.
-
-The transitions will only be displayed for users having the right permissions.
-In our example, the transition `approve_blogentry` will only be displayed
-for the users belonging to the group `moderators` or `managers`.
-
-
-Two bits of warning
-~~~~~~~~~~~~~~~~~~~
-
-We could perfectly use the administration interface to do these
-operations. It is a convenient thing to do at times (when doing
-development, to quick-check things). But it is not recommended beyond
-that because it is a bit complicated to do it right and it will be
-only local to your instance (or, said a bit differently, such a
-workflow only exists in an instance database). Furthermore, you cannot
-write unit tests against deployed instances, and experience shows it
-is mandatory to have tests for any mildly complicated workflow
-setup.
-
-Indeed, if you create the states and transitions through the user
-interface, next time you initialize the database you will have to
-re-create all the workflow entities. The user interface should only be
-a reference for you to view the states and transitions, but is not the
-appropriate interface to define your application workflow.
--- a/doc/book/en/devrepo/datamodel/definition.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,912 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _datamodel_definition:
-
-Yams *schema*
--------------
-
-The **schema** is the core piece of a *CubicWeb* instance as it
-defines and handles the data model. It is based on entity types that
-are either already defined in `Yams`_ and the *CubicWeb* standard
-library; or more specific types defined in cubes. The schema for a
-cube is defined in a `schema` python module or package.
-
-.. _`Yams`: http://www.logilab.org/project/yams
-
-.. _datamodel_overview:
-
-Overview
-~~~~~~~~
-
-The core idea of the yams schema is not far from the classical
-`Entity-relationship`_ model. But while an E/R model (or `logical
-model`) traditionally has to be manually translated to a lower-level
-data description language (such as the SQL `create table`
-sublanguage), also often described as the `physical model`, no such
-step is required with |yams| and |cubicweb|.
-
-.. _`Entity-relationship`: http://en.wikipedia.org/wiki/Entity-relationship_model
-
-This is because in addition to high-level, logical |yams| models, one
-uses the |rql| data manipulation language to query, insert, update and
-delete data. |rql| abstracts as much of the underlying SQL database as
-a |yams| schema abstracts from the physical layout. The vagaries of
-SQL are avoided.
-
-As a bonus point, such abstraction make it quite comfortable to build
-or use different backends to which |rql| queries apply.
-
-So, as in the E/R formalism, the building blocks are ``entities``
-(:ref:`EntityType`), ``relationships`` (:ref:`RelationType`,
-:ref:`RelationDefinition`) and ``attributes`` (handled like relation
-with |yams|).
-
-Let us detail a little the divergences between E/R and |yams|:
-
-* all relationship are binary which means that to represent a
-  non-binary relationship, one has to use an entity,
-* relationships do not support attributes (yet, see:
-  http://www.cubicweb.org/ticket/341318), hence the need to reify it
-  as an entity if need arises,
-* all entities have an `eid` attribute (an integer) that is its
-  primary key (but it is possible to declare uniqueness on other
-  attributes)
-
-Also |yams| supports the notions of:
-
-* entity inheritance (quite experimental yet, and completely
-  undocumented),
-* relation type: that is, relationships can be established over a set
-  of couple of entity types (henre the distinction made between
-  `RelationType` and `RelationDefinition` below)
-
-Finally |yams| has a few concepts of its own:
-
-* relationships being oriented and binary, we call the left hand
-  entity type the `subject` and the right hand entity type the
-  `object`
-
-.. note::
-
-   The |yams| schema is available at run time through the .schema
-   attribute of the `vregistry`.  It's an instance of
-   :class:`cubicweb.schema.Schema`, which extends
-   :class:`yams.schema.Schema`.
-
-.. _EntityType:
-
-Entity type
-~~~~~~~~~~~
-
-An entity type is an instance of :class:`yams.schema.EntitySchema`. Each entity type has
-a set of attributes and relations, and some permissions which define who can add, read,
-update or delete entities of this type.
-
-The following built-in types are available: ``String``,
-``Int``, ``BigInt``, ``Float``, ``Decimal``, ``Boolean``,
-``Date``, ``Datetime``, ``Time``, ``Interval``, ``Byte`` and
-``Password``. They can only be used as attributes of an other entity
-type.
-
-There is also a `RichString` kindof type:
-
- .. autoclass:: yams.buildobjs.RichString
-
-The ``__unique_together__`` class attribute is a list of tuples of names of
-attributes or inlined relations.  For each tuple, CubicWeb ensures the unicity
-of the combination.  For example:
-
-.. sourcecode:: python
-
-  class State(EntityType):
-      __unique_together__ = [('name', 'state_of')]
-
-      name = String(required=True)
-      state_of = SubjectRelation('Workflow', cardinality='1*',
-                                 composite='object', inlined=True)
-
-
-You can find more base entity types in
-:ref:`pre_defined_entity_types`.
-
-.. XXX yams inheritance
-
-.. _RelationType:
-
-Relation type
-~~~~~~~~~~~~~
-
-A relation type is an instance of
-:class:`yams.schema.RelationSchema`. A relation type is simply a
-semantic definition of a kind of relationship that may occur in an
-application.
-
-It may be referenced by zero, one or more relation definitions.
-
-It is important to choose a good name, at least to avoid conflicts
-with some semantically different relation defined in other cubes
-(since there's only a shared name space for these names).
-
-A relation type holds the following properties (which are hence shared
-between all relation definitions of that type):
-
-* `inlined`: boolean handling the physical optimization for archiving
-  the relation in the subject entity table, instead of creating a specific
-  table for the relation. This applies to relations where cardinality
-  of subject->relation->object is 0..1 (`?`) or 1..1 (`1`) for *all* its relation
-  definitions.
-
-* `symmetric`: boolean indicating that the relation is symmetrical, which
-  means that `X relation Y` implies `Y relation X`.
-
-.. _RelationDefinition:
-
-Relation definition
-~~~~~~~~~~~~~~~~~~~
-
-A relation definition is an instance of
-:class:`yams.schema.RelationDefinition`. It is a complete triplet
-"<subject entity type> <relation type> <object entity type>".
-
-When creating a new instance of that class, the corresponding
-:class:`RelationType` instance is created on the fly if necessary.
-
-Properties
-``````````
-
-The available properties for relation definitions are enumerated
-here. There are several kind of properties, as some relation
-definitions are actually attribute definitions, and other are not.
-
-Some properties may be completely optional, other may have a default
-value.
-
-Common properties for attributes and relations:
-
-* `description`: an unicode string describing an attribute or a
-  relation. By default this string will be used in the editing form of
-  the entity, which means that it is supposed to help the end-user and
-  should be flagged by the function `_` to be properly
-  internationalized.
-
-* `constraints`: a list of conditions/constraints that the relation has to
-  satisfy (c.f. `Constraints`_)
-
-* `cardinality`: a two character string specifying the cardinality of
-  the relation. The first character defines the cardinality of the
-  relation on the subject, and the second on the object. When a
-  relation can have multiple subjects or objects, the cardinality
-  applies to all, not on a one-to-one basis (so it must be
-  consistent...). Default value is '**'. The possible values are
-  inspired from regular expression syntax:
-
-    * `1`: 1..1
-    * `?`: 0..1
-    * `+`: 1..n
-    * `*`: 0..n
-
-Attributes properties:
-
-* `unique`: boolean indicating if the value of the attribute has to be
-  unique or not within all entities of the same type (false by
-  default)
-
-* `indexed`: boolean indicating if an index needs to be created for
-  this attribute in the database (false by default). This is useful
-  only if you know that you will have to run numerous searches on the
-  value of this attribute.
-
-* `default`: default value of the attribute. In case of date types, the values
-  which could be used correspond to the RQL keywords `TODAY` and `NOW`.
-
-* `metadata`: Is also accepted as an argument of the attribute contructor. It is
-  not really an attribute property. see `Metadata`_ for details.
-
-Properties for `String` attributes:
-
-* `fulltextindexed`: boolean indicating if the attribute is part of
-  the full text index (false by default) (*applicable on the type
-  `Byte` as well*)
-
-* `internationalizable`: boolean indicating if the value of the
-  attribute is internationalizable (false by default)
-
-Relation properties:
-
-* `composite`: string indicating that the subject (composite ==
-  'subject') is composed of the objects of the relations. For the
-  opposite case (when the object is composed of the subjects of the
-  relation), we just set 'object' as value. The composition implies
-  that when the relation is deleted (so when the composite is deleted,
-  at least), the composed are also deleted.
-
-* `fulltext_container`: string indicating if the value if the full
-  text indexation of the entity on one end of the relation should be
-  used to find the entity on the other end. The possible values are
-  'subject' or 'object'. For instance the use_email relation has that
-  property set to 'subject', since when performing a full text search
-  people want to find the entity using an email address, and not the
-  entity representing the email address.
-
-Constraints
-```````````
-
-By default, the available constraint types are:
-
-General Constraints
-......................
-
-* `SizeConstraint`: allows to specify a minimum and/or maximum size on
-  string (generic case of `maxsize`)
-
-* `BoundaryConstraint`: allows to specify a minimum and/or maximum value
-  on numeric types and date
-
-.. sourcecode:: python
-
-   from yams.constraints import BoundaryConstraint, TODAY, NOW, Attribute
-
-   class DatedEntity(EntityType):
-      start = Date(constraints=[BoundaryConstraint('>=', TODAY())])
-      end = Date(constraints=[BoundaryConstraint('>=', Attribute('start'))])
-
-   class Before(EntityType);
-      last_time = DateTime(constraints=[BoundaryConstraint('<=', NOW())])
-
-* `IntervalBoundConstraint`: allows to specify an interval with
-  included values
-
-.. sourcecode:: python
-
-     class Node(EntityType):
-         latitude = Float(constraints=[IntervalBoundConstraint(-90, +90)])
-
-* `UniqueConstraint`: identical to "unique=True"
-
-* `StaticVocabularyConstraint`: identical to "vocabulary=(...)"
-
-Constraints can be dependent on a fixed value (90, Date(2015,3,23)) or a variable.
-In this second case, yams can handle :
-
-* `Attribute`: compare to the value of another attribute.
-* `TODAY`: compare to the current Date.
-* `NOW`: compare to the current Datetime.
-
-RQL Based Constraints
-......................
-
-RQL based constraints may take three arguments. The first one is the ``WHERE``
-clause of a RQL query used by the constraint. The second argument ``mainvars``
-is the ``Any`` clause of the query. By default this include `S` reserved for the
-subject of the relation and `O` for the object. Additional variables could be
-specified using ``mainvars``. The argument expects a single string with all
-variable's name separated by spaces. The last one, ``msg``, is the error message
-displayed when the constraint fails. As RQLVocabularyConstraint never fails the
-third argument is not available.
-
-* `RQLConstraint`: allows to specify a RQL query that has to be satisfied
-  by the subject and/or the object of relation. In this query the variables
-  `S` and `O` are reserved for the relation subject and object entities.
-
-* `RQLVocabularyConstraint`: similar to the previous type of constraint except
-  that it does not express a "strong" constraint, which means it is only used to
-  restrict the values listed in the drop-down menu of editing form, but it does
-  not prevent another entity to be selected.
-
-* `RQLUniqueConstraint`: allows to the specify a RQL query that ensure that an
-  attribute is unique in a specific context. The Query must **never** return more
-  than a single result to be satisfied. In this query the variables `S` is
-  reserved for the relation subject entity. The other variables should be
-  specified with the second constructor argument (mainvars). This constraint type
-  should be used when __unique_together__ doesn't fit.
-
-.. XXX note about how to add new constraint
-
-.. _securitymodel:
-
-The security model
-~~~~~~~~~~~~~~~~~~
-
-The security model of `CubicWeb` is based on `Access Control List`.
-The main principles are:
-
-* users and groups of users
-* a user belongs to at least one group of user
-* permissions (`read`, `update`, `create`, `delete`)
-* permissions are assigned to groups (and not to users)
-
-For *CubicWeb* in particular:
-
-* we associate rights at the entities/relations schema level
-
-* the default groups are: `managers`, `users` and `guests`
-
-* users belong to the `users` group
-
-* there is a virtual group called `owners` to which we can associate only
-  `delete` and `update` permissions
-
-  * we can not add users to the `owners` group, they are implicitly added to it
-    according to the context of the objects they own
-
-  * the permissions of this group are only checked on `update`/`delete` actions
-    if all the other groups the user belongs to do not provide those permissions
-
-Setting permissions is done with the class attribute `__permissions__`
-of entity types and relation definitions. The value of this attribute
-is a dictionary where the keys are the access types (action), and the
-values are the authorized groups or rql expressions.
-
-For an entity type, the possible actions are `read`, `add`, `update` and
-`delete`.
-
-For a relation, the possible actions are `read`, `add`, and `delete`.
-
-For an attribute, the possible actions are `read`, `add` and `update`,
-and they are a refinement of an entity type permission.
-
-.. note::
-
-   By default, the permissions of an entity type attributes are
-   equivalent to the permissions of the entity type itself.
-
-   It is possible to provide custom attribute permissions which are
-   stronger than, or are more lenient than the entity type
-   permissions.
-
-   In a situation where all attributes were given custom permissions,
-   the entity type permissions would not be checked, except for the
-   `delete` action.
-
-For each access type, a tuple indicates the name of the authorized groups and/or
-one or multiple RQL expressions to satisfy to grant access. The access is
-provided if the user is in one of the listed groups or if one of the RQL condition
-is satisfied.
-
-Default permissions
-```````````````````
-
-The default permissions for ``EntityType`` are:
-
-.. sourcecode:: python
-
-   __permissions__ = {
-        'read': ('managers', 'users', 'guests',),
-        'update': ('managers', 'owners',),
-        'delete': ('managers', 'owners'),
-        'add': ('managers', 'users',)
-        }
-
-The default permissions for relations are:
-
-.. sourcecode:: python
-
-   __permissions__ = {'read': ('managers', 'users', 'guests',),
-                    'delete': ('managers', 'users'),
-                    'add': ('managers', 'users',)}
-
-The default permissions for attributes are:
-
-.. sourcecode:: python
-
-   __permissions__ = {'read': ('managers', 'users', 'guests',),
-                      'add': ('managers', ERQLExpression('U has_add_permission X'),
-                      'update': ('managers', ERQLExpression('U has_update_permission X')),}
-
-.. note::
-
-   The default permissions for attributes are not syntactically
-   equivalent to the default permissions of the entity types, but the
-   rql expressions work by delegating to the entity type permissions.
-
-
-The standard user groups
-````````````````````````
-
-* `guests`
-
-* `users`
-
-* `managers`
-
-* `owners`: virtual group corresponding to the entity's owner.
-  This can only be used for the actions `update` and `delete` of an entity
-  type.
-
-It is also possible to use specific groups if they are defined in the precreate
-script of the cube (``migration/precreate.py``). Defining groups in postcreate
-script or later makes them unavailable for security purposes (in this case, an
-`sync_schema_props_perms` command has to be issued in a CubicWeb shell).
-
-
-Use of RQL expression for write permissions
-```````````````````````````````````````````
-
-It is possible to define RQL expression to provide update permission (`add`,
-`delete` and `update`) on entity type / relation definitions. An rql expression
-is a piece of query (corresponds to the WHERE statement of an RQL query), and the
-expression will be considered as satisfied if it returns some results. They can
-not be used in `read` permission.
-
-To use RQL expression in entity type permission:
-
-* you have to use the class :class:`~cubicweb.schema.ERQLExpression`
-
-* in this expression, the variables `X` and `U` are pre-defined references
-  respectively on the current entity (on which the action is verified) and on the
-  user who send the request
-
-For RQL expressions on a relation type, the principles are the same except for
-the following:
-
-* you have to use the class :class:`~cubicweb.schema.RRQLExpression` instead of
-  :class:`~cubicweb.schema.ERQLExpression`
-
-* in the expression, the variables `S`, `O` and `U` are pre-defined references to
-  respectively the subject and the object of the current relation (on which the
-  action is being verified) and the user who executed the query
-
-To define security for attributes of an entity (non-final relation), you have to
-use the class :class:`~cubicweb.schema.ERQLExpression` in which `X` represents
-the entity the attribute belongs to.
-
-It is possible to use in those expression a special relation
-`has_<ACTION>_permission` where the subject is the user (eg 'U') and the object
-is any variable representing an entity (usually 'X' in
-:class:`~cubicweb.schema.ERQLExpression`, 'S' or 'O' in
-:class:`~cubicweb.schema.RRQLExpression`), meaning that the user needs to have
-permission to execute the action <ACTION> on the entities represented by this
-variable. It's recommanded to use this feature whenever possible since it
-simplify greatly complex security definition and upgrade.
-
-
-.. sourcecode:: python
-
-  class my_relation(RelationDefinition):
-    __permissions__ = {'read': ('managers', 'users'),
-                       'add': ('managers', RRQLExpression('U has_update_permission S')),
-                       'delete': ('managers', RRQLExpression('U has_update_permission S'))
-		       }
-
-In the above example, user will be allowed to add/delete `my_relation` if he has
-the `update` permission on the subject of the relation.
-
-.. note::
-
-  Potentially, the `use of an RQL expression to add an entity or a relation` can
-  cause problems for the user interface, because if the expression uses the
-  entity or the relation to create, we are not able to verify the permissions
-  before we actually added the entity (please note that this is not a problem for
-  the RQL server at all, because the permissions checks are done after the
-  creation). In such case, the permission check methods
-  (CubicWebEntitySchema.check_perm and has_perm) can indicate that the user is
-  not allowed to create this entity while it would obtain the permission.  To
-  compensate this problem, it is usually necessary in such case to use an action
-  that reflects the schema permissions but which check properly the permissions
-  so that it would show up only if possible.
-
-
-Use of RQL expression for reading rights
-````````````````````````````````````````
-
-The principles are the same but with the following restrictions:
-
-* you can not use rql expression for the `read` permission of relations and
-  attributes,
-
-* you can not use special `has_<ACTION>_permission` relation in the rql
-  expression.
-
-
-Important notes about write permissions checking
-````````````````````````````````````````````````
-
-Write permissions (e.g. 'add', 'update', 'delete') are checked in core hooks.
-
-When a permission is checked slightly vary according to if it's an entity or
-relation, and if the relation is an attribute relation or not). It's important to
-understand that since according to when a permission is checked, values returned
-by rql expressions may changes, hence the permission being granted or not.
-
-Here are the current rules:
-
-1. permission to add/update entity and its attributes are checked on
-   commit
-
-2. permission to delete an entity is checked in 'before_delete_entity' hook
-
-3. permission to add a relation is checked either:
-
-   - in 'before_add_relation' hook if the relation type is in the
-     `BEFORE_ADD_RELATIONS` set
-
-   - else at commit time if the relation type is in the `ON_COMMIT_ADD_RELATIONS`
-     set
-
-   - else in 'after_add_relation' hook (the default)
-
-4. permission to delete a relation is checked in 'before_delete_relation' hook
-
-Last but not least, remember queries issued from hooks and operation are by
-default 'unsafe', eg there are no read or write security checks.
-
-See :mod:`cubicweb.hooks.security` for more details.
-
-
-.. _yams_example:
-
-
-Derived attributes and relations
---------------------------------
-
-.. note:: **TODO** Check organisation of the whole chapter of the documentation
-
-Cubicweb offers the possibility to *query* data using so called
-*computed* relations and attributes. Those are *seen* by RQL requests
-as normal attributes and relations but are actually derived from other
-attributes and relations. In a first section we'll informally review
-two typical use cases. Then we see how to use computed attributes and
-relations in your schema. Last we will consider various significant
-aspects of their implementation and the impact on their usage.
-
-Motivating use cases
-~~~~~~~~~~~~~~~~~~~~
-
-Computed (or reified) relations
-```````````````````````````````
-
-It often arises that one must represent a ternary relation, or a
-family of relations. For example, in the context of an exhibition
-catalog you might want to link all *contributors* to the *work* they
-contributed to, but this contribution can be as *illustrator*,
-*author*, *performer*, ...
-
-The classical way to describe this kind of information within an
-entity-relationship schema is to *reify* the relation, that is turn
-the relation into a entity. In our example the schema will have a
-*Contribution* entity type used to represent the family of the
-contribution relations.
-
-
-.. sourcecode:: python
-
-    class ArtWork(EntityType):
-        name = String()
-        ...
-
-    class Person(EntityType):
-        name = String()
-        ...
-
-    class Contribution(EntityType):
-        contributor = SubjectRelation('Person', cardinality='1*', inlined=True)
-        manifestation = SubjectRelation('ArtWork')
-        role = SubjectRelation('Role')
-
-    class Role(EntityType):
-        name = String()
-
-But then, in order to query the illustrator(s) ``I`` of a work ``W``,
-one has to write::
-
-    Any I, W WHERE C is Contribution, C contributor I, C manifestation W,
-                   C role R, R name 'illustrator'
-
-whereas we would like to be able to simply write::
-
-    Any I, W WHERE I illustrator_of W
-
-This is precisely what the computed relations allow.
-
-
-Computed (or synthesized) attribute
-```````````````````````````````````
-
-Assuming a trivial schema for describing employees in companies, one
-can be interested in the total of salaries payed by a company for
-all its employees. One has to write::
-
-    Any C, SUM(SA) GROUPBY S WHERE E works_for C, E salary SA
-
-whereas it would be most convenient to simply write::
-
-    Any C, TS WHERE C total_salary TS
-
-And this is again what computed attributes provide.
-
-
-Using computed attributes and relations
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Computed (or reified) relations
-```````````````````````````````
-
-In the above case we would define the *computed relation*
-``illustrator_of`` in the schema by:
-
-.. sourcecode:: python
-
-    class illustrator_of(ComputedRelation):
-        rule  = ('C is Contribution, C contributor S, C manifestation O,'
-                 'C role R, R name "illustrator"')
-
-You will note that:
-
-* the ``S`` and ``O`` RQL variables implicitly identify the subject and
-  object of the defined computed relation, akin to what happens in
-  RRQLExpression
-* the possible subject and object entity types are inferred from the rule;
-* computed relation definitions always have empty *add* and *delete* permissions
-* *read* permissions can be defined, permissions from the relations used in the
-  rewrite rule **are not considered** ;
-* nothing else may be defined on the `ComputedRelation` subclass beside
-  description, permissions and rule (e.g. no cardinality, composite, etc.,).
-  `BadSchemaDefinition` is raised on attempt to specify other attributes;
-* computed relations can not be used in 'SET' and 'DELETE' rql queries
-  (`BadQuery` exception raised).
-
-
-NB: The fact that the *add* and *delete* permissions are *empty* even
-for managers is expected to make the automatic UI not attempt to edit
-them.
-
-Computed (or synthesized) attributes
-````````````````````````````````````
-
-In the above case we would define the *computed attribute*
-``total_salary`` on the ``Company`` entity type in the schema by:
-
-.. sourcecode:: python
-
-    class Company(EntityType):
-        name = String()
-        total_salary = Int(formula='Any SUM(SA) GROUPBY E WHERE P works_for X, E salary SA')
-
-* the ``X`` RQL variable implicitly identifies the entity holding the
-  computed attribute, akin to what happens in ERQLExpression;
-* the type inferred from the formula is checked against the declared type, and
-  `BadSchemaDefinition` is raised if they don't match;
-* the computed attributes always have empty *update* permissions
-* `BadSchemaDefinition` is raised on attempt to set 'update' permissions;
-* 'read' permissions can be defined, permissions regarding the formula
-  **are not considered**;
-* other attribute's property (inlined, ...) can be defined as for normal attributes;
-* Similarly to computed relation, computed attribute can't be used in 'SET' and
-  'DELETE' rql queries (`BadQuery` exception raised).
-
-
-API and implementation
-~~~~~~~~~~~~~~~~~~~~~~
-
-Representation in the data backend
-``````````````````````````````````
-
-Computed relations have no direct representation at the SQL table
-level.  Instead, each time a query is issued the query is rewritten to
-replace the computed relation by its equivalent definition and the
-resulting rewritten query is performed in the usual way.
-
-On the contrary, computed attributes are represented as a column in the
-table for their host entity type, just like normal attributes. Their
-value is kept up-to-date with respect to their defintion by a system
-of hooks (also called triggers in most RDBMS) which recomputes them
-when the relations and attributes they depend on are modified.
-
-Yams API
-````````
-
-When accessing the schema through the *yams API* (not when defining a
-schema in a ``schema.py`` file) the computed attributes and relations
-are represented as follows:
-
-relations
-    The ``yams.RelationSchema`` class has a new ``rule`` attribute
-    holding the rule as a string. If this attribute is set all others
-    must not be set.
-attributes
-    A new property ``formula`` is added on class
-    ``yams.RelationDefinitionSchema`` alomng with a new keyword
-    argument ``formula`` on the initializer.
-
-Migration
-`````````
-
-The migrations are to be handled as summarized in the array below.
-
-+------------+---------------------------------------------------+---------------------------------------+
-|            | Computed rtype                                    | Computed attribute                    |
-+============+===================================================+=======================================+
-| add        | * add_relation_type                               | * add_attribute                       |
-|            | * add_relation_definition should trigger an error | * add_relation_definition             |
-+------------+---------------------------------------------------+---------------------------------------+
-| modify     | * sync_schema_prop_perms:                         | * sync_schema_prop_perms:             |
-|            |   checks the rule is                              |                                       |
-| (rule or   |   synchronized with the database                  |   - empty the cache,                  |
-| formula)   |                                                   |   - check formula,                    |
-|            |                                                   |   - make sure all the values get      |
-|            |                                                   |     updated                           |
-+------------+---------------------------------------------------+---------------------------------------+
-| del        | * drop_relation_type                              | * drop_attribute                      |
-|            | * drop_relation_definition should trigger an error| * drop_relation_definition            |
-+------------+---------------------------------------------------+---------------------------------------+
-
-
-Defining your schema using yams
--------------------------------
-
-Entity type definition
-~~~~~~~~~~~~~~~~~~~~~~
-
-An entity type is defined by a Python class which inherits from
-:class:`yams.buildobjs.EntityType`.  The class definition contains the
-description of attributes and relations for the defined entity type.
-The class name corresponds to the entity type name. It is expected to
-be defined in the module ``mycube.schema``.
-
-:Note on schema definition:
-
- The code in ``mycube.schema`` is not meant to be executed. The class
- EntityType mentioned above is different from the EntitySchema class
- described in the previous chapter. EntityType is a helper class to
- make Entity definition easier. Yams will process EntityType classes
- and create EntitySchema instances from these class definitions. Similar
- manipulation happen for relations.
-
-When defining a schema using python files, you may use the following shortcuts:
-
-- `required`: boolean indicating if the attribute is required, ed subject cardinality is '1'
-
-- `vocabulary`: specify static possible values of an attribute
-
-- `maxsize`: integer providing the maximum size of a string (no limit by default)
-
-For example:
-
-.. sourcecode:: python
-
-  class Person(EntityType):
-    """A person with the properties and the relations necessary for my
-    application"""
-
-    last_name = String(required=True, fulltextindexed=True)
-    first_name = String(required=True, fulltextindexed=True)
-    title = String(vocabulary=('Mr', 'Mrs', 'Miss'))
-    date_of_birth = Date()
-    works_for = SubjectRelation('Company', cardinality='?*')
-
-
-The entity described above defines three attributes of type String,
-last_name, first_name and title, an attribute of type Date for the date of
-birth and a relation that connects a `Person` to another entity of type
-`Company` through the semantic `works_for`.
-
-
-
-:Naming convention:
-
- Entity class names must start with an uppercase letter. The common
- usage is to use ``CamelCase`` names.
-
- Attribute and relation names must start with a lowercase letter. The
- common usage is to use ``underscore_separated_words``. Attribute and
- relation names starting with a single underscore are permitted, to
- denote a somewhat "protected" or "private" attribute.
-
- In any case, identifiers starting with "CW" or "cw" are reserved for
- internal use by the framework.
-
- .. _Metadata:
-
- Some attribute using the name of another attribute as prefix are considered
- metadata.  For example, if an EntityType have both a ``data`` and
- ``data_format`` attribute, ``data_format`` is view as the ``format`` metadata
- of ``data``. Later the :meth:`cw_attr_metadata` method will allow you to fetch
- metadata related to an attribute. There are only three valid metadata names:
- ``format``, ``encoding`` and ``name``.
-
-
-The name of the Python attribute corresponds to the name of the attribute
-or the relation in *CubicWeb* application.
-
-An attribute is defined in the schema as follows::
-
-    attr_name = AttrType(*properties, metadata={})
-
-where
-
-* `AttrType`: is one of the type listed in EntityType_,
-
-* `properties`: is a list of the attribute needs to satisfy (see `Properties`_
-  for more details),
-
-* `metadata`: is a dictionary of meta attributes related to ``attr_name``.
-  Dictionary keys are the name of the meta attribute. Dictionary values
-  attributes objects (like the content of ``AttrType``). For each entry of the
-  metadata dictionary a ``<attr_name>_<key> = <value>`` attribute is
-  automaticaly added to the EntityType.  see `Metadata`_ section for details
-  about valid key.
-
-
- ---
-
-While building your schema
-
-* it is possible to use the attribute `meta` to flag an entity type as a `meta`
-  (e.g. used to describe/categorize other entities)
-
-.. XXX the paragraph below needs clarification and / or moving out in
-.. another place
-
-*Note*: if you end up with an `if` in the definition of your entity, this probably
-means that you need two separate entities that implement the `ITree` interface and
-get the result from `.children()` which ever entity is concerned.
-
-.. Inheritance
-.. ```````````
-.. XXX feed me
-
-
-Definition of relations
-~~~~~~~~~~~~~~~~~~~~~~~
-
-.. XXX add note about defining relation type / definition
-
-A relation is defined by a Python class heriting `RelationType`. The name
-of the class corresponds to the name of the type. The class then contains
-a description of the properties of this type of relation, and could as well
-contain a string for the subject and a string for the object. This allows to create
-new definition of associated relations, (so that the class can have the
-definition properties from the relation) for example ::
-
-  class locked_by(RelationType):
-    """relation on all entities indicating that they are locked"""
-    inlined = True
-    cardinality = '?*'
-    subject = '*'
-    object = 'CWUser'
-
-If provided, the `subject` and `object` attributes denote the subject
-and object of the various relation definitions related to the relation
-type. Allowed values for these attributes are:
-
-* a string corresponding to an entity type
-* a tuple of string corresponding to multiple entity types
-* the '*' special string, meaning all types of entities
-
-When a relation is not inlined and not symmetrical, and it does not require
-specific permissions, it can be defined using a `SubjectRelation`
-attribute in the EntityType class. The first argument of `SubjectRelation` gives
-the entity type for the object of the relation.
-
-:Naming convention:
-
- Although this way of defining relations uses a Python class, the
- naming convention defined earlier prevails over the PEP8 conventions
- used in the framework: relation type class names use
- ``underscore_separated_words``.
-
-:Historical note:
-
-   It has been historically possible to use `ObjectRelation` which
-   defines a relation in the opposite direction. This feature is
-   deprecated and therefore should not be used in newly written code.
-
-:Future deprecation note:
-
-  In an even more remote future, it is quite possible that the
-  SubjectRelation shortcut will become deprecated, in favor of the
-  RelationType declaration which offers some advantages in the context
-  of reusable cubes.
-
-
-
-
-Handling schema changes
-~~~~~~~~~~~~~~~~~~~~~~~
-
-Also, it should be clear that to properly handle data migration, an
-instance's schema is stored in the database, so the python schema file
-used to defined it is only read when the instance is created or
-upgraded.
-
-.. XXX complete me
--- a/doc/book/en/devrepo/datamodel/index.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-Data model
-==========
-
-This chapter describes how you define a schema and how to make it evolves as the time goes.
-
-.. toctree::
-   :maxdepth: 1
-
-   definition
-   metadata
-   baseschema
-   define-workflows
--- a/doc/book/en/devrepo/datamodel/metadata.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-
-Metadata
---------
-
-.. index::
-   schema: meta-data;
-   schema: eid; creation_date; modification_data; cwuri
-   schema: created_by; owned_by; is; is_instance;
-
-Each entity type in |cubicweb| has at least the following meta-data attributes and relations:
-
-`eid`
-  entity's identifier which is unique in an instance. We usually call this identifier `eid` for historical reason.
-
-`creation_date`
-  Date and time of the creation of the entity.
-
-`modification_date`
-  Date and time of the latest modification of an entity.
-
-`cwuri`
-  Reference URL of the entity, which is not expected to change.
-
-`created_by`
-  Relation to the :ref:`users <CWUser>` who has created the entity
-
-`owned_by`
-  Relation to :ref:`users <CWUser>` whom the entity belongs; usually the creator but not
-  necessary, and it could have multiple owners notably for permission control
-
-`is`
-  Relation to the :ref:`entity type <CWEType>` of which type the entity is.
-
-`is_instance`
-  Relation to the :ref:`entity types <CWEType>` of which type the
-  entity is an instance of.
-
--- a/doc/book/en/devrepo/devcore/dbapi.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +0,0 @@
-.. _dbapi:
-
-Python/RQL API
-~~~~~~~~~~~~~~
-
-The Python API developped to interface with RQL is inspired from the standard db-api,
-with a Connection object having the methods cursor, rollback and commit essentially.
-The most important method is the `execute` method of a cursor.
-
-.. sourcecode:: python
-
-   execute(rqlstring, args=None, build_descr=True)
-
-:rqlstring: the RQL query to execute (unicode)
-:args: if the query contains substitutions, a dictionary containing the values to use
-
-The `Connection` object owns the methods `commit` and `rollback`. You
-*should never need to use them* during the development of the web
-interface based on the *CubicWeb* framework as it determines the end
-of the transaction depending on the query execution success. They are
-however useful in other contexts such as tests or custom controllers.
-
-.. note::
-
-  If a query generates an error related to security (:exc:`Unauthorized`) or to
-  integrity (:exc:`ValidationError`), the transaction can still continue but you
-  won't be able to commit it, a rollback will be necessary to start a new
-  transaction.
-
-  Also, a rollback is automatically done if an error occurs during commit.
-
-.. note::
-
-   A :exc:`ValidationError` has a `entity` attribute. In CubicWeb,
-   this atttribute is set to the entity's eid (not a reference to the
-   entity itself).
-
-Executing RQL queries from a view or a hook
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-When you're within code of the web interface, the db-api like connexion is
-handled by the request object. You should not have to access it directly, but
-use the `execute` method directly available on the request, eg:
-
-.. sourcecode:: python
-
-   rset = self._cw.execute(rqlstring, kwargs)
-
-Similarly, on the server side (eg in hooks), there is no db-api connexion (since
-you're directly inside the data-server), so you'll have to use the execute method
-of the session object.
-
-
-Proper usage of `.execute`
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Let's say you want to get T which is in configuration C, this translates to:
-
-.. sourcecode:: python
-
-   self._cw.execute('Any T WHERE T in_conf C, C eid %s' % entity.eid)
-
-But it must be written in a syntax that will benefit from the use
-of a cache on the RQL server side:
-
-.. sourcecode:: python
-
-   self._cw.execute('Any T WHERE T in_conf C, C eid %(x)s', {'x': entity.eid})
-
-The syntax tree is built once for the "generic" RQL and can be re-used
-with a number of different eids. There rql IN operator is an exception
-to this rule.
-
-.. sourcecode:: python
-
-   self._cw.execute('Any T WHERE T in_conf C, C name IN (%s)'
-                    % ','.join(['foo', 'bar']))
-
-Alternativelly, some of the common data related to an entity can be
-obtained from the `entity.related()` method (which is used under the
-hood by the orm when you use attribute access notation on an entity to
-get a relation. The initial request would then be translated to:
-
-.. sourcecode:: python
-
-   entity.related('in_conf', 'object')
-
-Additionnaly this benefits from the fetch_attrs policy (see
-:ref:`FetchAttrs`) eventually defined on the class element, which says
-which attributes must be also loaded when the entity is loaded through
-the orm.
-
-
-.. _resultset:
-
-The `ResultSet` API
-~~~~~~~~~~~~~~~~~~~
-
-ResultSet instances are a very commonly manipulated object. They have
-a rich API as seen below, but we would like to highlight a bunch of
-methods that are quite useful in day-to-day practice:
-
-* `__str__()` (applied by `print`) gives a very useful overview of both
-  the underlying RQL expression and the data inside; unavoidable for
-  debugging purposes
-
-* `printable_rql()` produces back a well formed RQL expression as a
-  string; it is very useful to build views
-
-* `entities()` returns a generator on all entities of the result set
-
-* `get_entity(row, col)` gets the entity at row, col coordinates; one
-  of the most used result set method
-
-.. autoclass:: cubicweb.rset.ResultSet
-   :members:
-
-
-The `Cursor` and `Connection` API
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The whole cursor API is developped below.
-
-.. note::
-
-  In practice you'll usually use the `.execute` method on the _cw object of
-  appobjects. Usage of other methods is quite rare.
-
-.. autoclass:: cubicweb.dbapi.Cursor
-   :members:
-
-.. autoclass:: cubicweb.dbapi.Connection
-   :members:
--- a/doc/book/en/devrepo/devcore/index.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-Core APIs
-=========
-
-.. toctree::
-   :maxdepth: 1
-
-   dbapi.rst
-   reqbase.rst
-
--- a/doc/book/en/devrepo/devcore/reqbase.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-Request and ResultSet methods
------------------------------
-
-Those are methods you'll find on both request objects and on
-repository session.
-
-Request methods
-~~~~~~~~~~~~~~~
-
-`URL handling`:
-
-* `build_url(*args, **kwargs)`, returns an absolute URL based on the
-  given arguments. The *controller* supposed to handle the response,
-  can be specified through the first positional parameter (the
-  connection is theoretically done automatically :).
-
-`Data formatting`:
-
-* `format_date(date, date_format=None, time=False)` returns a string for a
-  date time according to instance's configuration
-
-* `format_time(time)` returns a string for a date time according to
-  instance's configuration
-
-`And more...`:
-
-* `tal_render(template, variables)`, renders a precompiled page template with
-  variables in the given dictionary as context
-
-
-Result set methods
-~~~~~~~~~~~~~~~~~~
-
-* `get_entity(row, col)`, returns the entity corresponding to the data position
-  in the *result set*
-
-* `complete_entity(row, col, skip_bytes=True)`, is equivalent to `get_entity` but
-  also call the method `complete()` on the entity before returning it
-
-
--- a/doc/book/en/devrepo/entityclasses/adapters.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,167 +0,0 @@
-.. _adapters:
-
-Interfaces and Adapters
------------------------
-
-Interfaces are the same thing as object-oriented programming `interfaces`_.
-Adapter refers to a well-known `adapter`_ design pattern that helps separating
-concerns in object oriented applications.
-
-.. _`interfaces`: http://java.sun.com/docs/books/tutorial/java/concepts/interface.html
-.. _`adapter`: http://en.wikipedia.org/wiki/Adapter_pattern
-
-In |cubicweb| adapters provide logical functionalities to entity types.
-
-Definition of an adapter is quite trivial. An excerpt from cubicweb
-itself (found in :mod:`cubicweb.entities.adapters`):
-
-.. sourcecode:: python
-
-
-    class ITreeAdapter(EntityAdapter):
-        """This adapter has to be overriden to be configured using the
-        tree_relation, child_role and parent_role class attributes to
-        benefit from this default implementation
-        """
-        __regid__ = 'ITree'
-
-        child_role = 'subject'
-        parent_role = 'object'
-
-        def children_rql(self):
-            """returns RQL to get children """
-            return self.entity.cw_related_rql(self.tree_relation, self.parent_role)
-
-The adapter object has ``self.entity`` attribute which represents the
-entity being adapted.
-
-.. Note::
-
-   Adapters came with the notion of service identified by the registry identifier
-   of an adapters, hence dropping the need for explicit interface and the
-   :class:`cubicweb.predicates.implements` selector. You should instead use
-   :class:`cubicweb.predicates.is_instance` when you want to select on an entity
-   type, or :class:`cubicweb.predicates.adaptable` when you want to select on a
-   service.
-
-
-Specializing and binding an adapter
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. sourcecode:: python
-
-  from cubicweb.entities.adapters import ITreeAdapter
-
-  class MyEntityITreeAdapter(ITreeAdapter):
-      __select__ = is_instance('MyEntity')
-      tree_relation = 'filed_under'
-
-The ITreeAdapter here provides a default implementation. The
-tree_relation class attribute is actually used by this implementation
-to help implement correct behaviour.
-
-Here we provide a specific implementation which will be bound for
-``MyEntity`` entity type (the `adaptee`).
-
-
-.. _interfaces_to_adapters:
-
-Converting code from Interfaces/Mixins to Adapters
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Here we go with a small example. Before:
-
-.. sourcecode:: python
-
-    from cubicweb.predicates import implements
-    from cubicweb.interfaces import ITree
-    from cubicweb.mixins import ITreeMixIn
-
-    class MyEntity(ITreeMixIn, AnyEntity):
-        __implements__ = AnyEntity.__implements__ + (ITree,)
-
-
-    class ITreeView(EntityView):
-        __select__ = implements('ITree')
-        def cell_call(self, row, col):
-            entity = self.cw_rset.get_entity(row, col)
-            children = entity.children()
-
-After:
-
-.. sourcecode:: python
-
-    from cubicweb.predicates import adaptable, is_instance
-    from cubicweb.entities.adapters import ITreeAdapter
-
-    class MyEntityITreeAdapter(ITreeAdapter):
-        __select__ = is_instance('MyEntity')
-
-    class ITreeView(EntityView):
-        __select__ = adaptable('ITree')
-        def cell_call(self, row, col):
-            entity = self.cw_rset.get_entity(row, col)
-            itree = entity.cw_adapt_to('ITree')
-            children = itree.children()
-
-As we can see, the interface/mixin duality disappears and the entity
-class itself is completely freed from these concerns. When you want
-to use the ITree interface of an entity, call its `cw_adapt_to` method
-to get an adapter for this interface, then access to members of the
-interface on the adapter
-
-Let's look at an example where we defined everything ourselves. We
-start from:
-
-.. sourcecode:: python
-
-    class IFoo(Interface):
-        def bar(self, *args):
-            raise NotImplementedError
-
-    class MyEntity(AnyEntity):
-        __regid__ = 'MyEntity'
-        __implements__ = AnyEntity.__implements__ + (IFoo,)
-
-        def bar(self, *args):
-            return sum(captain.age for captain in self.captains)
-
-    class FooView(EntityView):
-        __regid__ = 'mycube.fooview'
-        __select__ = implements('IFoo')
-
-        def cell_call(self, row, col):
-            entity = self.cw_rset.get_entity(row, col)
-            self.w('bar: %s' % entity.bar())
-
-Converting to:
-
-.. sourcecode:: python
-
-   class IFooAdapter(EntityAdapter):
-       __regid__ = 'IFoo'
-       __select__ = is_instance('MyEntity')
-
-       def bar(self, *args):
-           return sum(captain.age for captain in self.entity.captains)
-
-   class FooView(EntityView):
-        __regid__ = 'mycube.fooview'
-        __select__ = adaptable('IFoo')
-
-        def cell_call(self, row, col):
-            entity = self.cw_rset.get_entity(row, col)
-            self.w('bar: %s' % entity.cw_adapt_to('IFoo').bar())
-
-.. note::
-
-   When migrating an entity method to an adapter, the code can be moved as is
-   except for the `self` of the entity class, which in the adapter must become `self.entity`.
-
-Adapters defined in the library
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. automodule:: cubicweb.entities.adapters
-   :members:
-
-More are defined in web/views.
--- a/doc/book/en/devrepo/entityclasses/application-logic.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,177 +0,0 @@
-How to use entities objects and adapters
-----------------------------------------
-
-The previous chapters detailed the classes and methods available to
-the developer at the so-called `ORM`_ level. However they say little
-about the common patterns of usage of these objects.
-
-.. _`ORM`: http://en.wikipedia.org/wiki/Object-relational_mapping
-
-Entities objects (and their adapters) are used in the repository and
-web sides of CubicWeb. On the repository side of things, one should
-manipulate them in Hooks and Operations.
-
-Hooks and Operations provide support for the implementation of rules
-such as computed attributes, coherency invariants, etc (they play the
-same role as database triggers, but in a way that is independent of
-the actual data sources).
-
-So a lot of an application's business rules will be written in Hooks
-(or Operations).
-
-On the web side, views also typically operate using entity
-objects. Obvious entity methods for use in views are the Dublin Core
-methods like ``dc_title``. For separation of concerns reasons, one
-should ensure no ui logic pervades the entities level, and also no
-business logic should creep into the views.
-
-In the duration of a transaction, entities objects can be instantiated
-many times, in views and hooks, even for the same database entity. For
-instance, in a classic CubicWeb deployment setup, the repository and
-the web front-end are separated process communicating over the
-wire. There is no way state can be shared between these processes
-(there is a specific API for that). Hence, it is not possible to use
-entity objects as messengers between these components of an
-application. It means that an attribute set as in ``obj.x = 42``,
-whether or not x is actually an entity schema attribute, has a short
-life span, limited to the hook, operation or view within which the
-object was built.
-
-Setting an attribute or relation value can be done in the context of a
-Hook/Operation, using the ``obj.cw_set(x=42)`` notation or a plain
-RQL ``SET`` expression.
-
-In views, it would be preferable to encapsulate the necessary logic in
-a method of an adapter for the concerned entity class(es). But of
-course, this advice is also reasonable for Hooks/Operations, though
-the separation of concerns here is less stringent than in the case of
-views.
-
-This leads to the practical role of objects adapters: it's where an
-important part of the application logic lies (the other part being
-located in the Hook/Operations).
-
-Anatomy of an entity class
---------------------------
-
-We can look now at a real life example coming from the `tracker`_
-cube. Let us begin to study the ``entities/project.py`` content.
-
-.. sourcecode:: python
-
-    from cubicweb.entities.adapters import ITreeAdapter
-
-    class ProjectAdapter(ITreeAdapter):
-        __select__ = is_instance('Project')
-        tree_relation = 'subproject_of'
-
-    class Project(AnyEntity):
-        __regid__ = 'Project'
-        fetch_attrs, cw_fetch_order = fetch_config(('name', 'description',
-                                                    'description_format', 'summary'))
-
-        TICKET_DEFAULT_STATE_RESTR = 'S name IN ("created","identified","released","scheduled")'
-
-        def dc_title(self):
-            return self.name
-
-The fact that the `Project` entity type implements an ``ITree``
-interface is materialized by the ``ProjectAdapter`` class (inheriting
-the pre-defined ``ITreeAdapter`` whose ``__regid__`` is of course
-``ITree``), which will be selected on `Project` entity types because
-of its selector. On this adapter, we redefine the ``tree_relation``
-attribute of the ``ITreeAdapter`` class.
-
-This is typically used in views concerned with the representation of
-tree-like structures (CubicWeb provides several such views).
-
-It is important that the views themselves try not to implement this
-logic, not only because such views would be hardly applyable to other
-tree-like relations, but also because it is perfectly fine and useful
-to use such an interface in Hooks.
-
-In fact, Tree nature is a property of the data model that cannot be
-fully and portably expressed at the level of database entities (think
-about the transitive closure of the child relation). This is a further
-argument to implement it at entity class level.
-
-``fetch_attrs`` configures which attributes should be pre-fetched when using ORM
-methods retrieving entity of this type. In a same manner, the ``cw_fetch_order`` is
-a class method allowing to control sort order. More on this in :ref:`FetchAttrs`.
-
-We can observe the big ``TICKET_DEFAULT_STATE_RESTR`` is a pure
-application domain piece of data. There is, of course, no limitation
-to the amount of class attributes of this kind.
-
-The ``dc_title`` method provides a (unicode string) value likely to be
-consumed by views, but note that here we do not care about output
-encodings. We care about providing data in the most universal format
-possible, because the data could be used by a web view (which would be
-responsible of ensuring XHTML compliance), or a console or file
-oriented output (which would have the necessary context about the
-needed byte stream encoding).
-
-.. note::
-
-  The Dublin Core `dc_xxx` methods are not moved to an adapter as they
-  are extremely prevalent in CubicWeb and assorted cubes and should be
-  available for all entity types.
-
-Let us now dig into more substantial pieces of code, continuing the
-Project class.
-
-.. sourcecode:: python
-
-    def latest_version(self, states=('published',), reverse=None):
-        """returns the latest version(s) for the project in one of the given
-        states.
-
-        when no states specified, returns the latest published version.
-        """
-        order = 'DESC'
-        if reverse is not None:
-            warn('reverse argument is deprecated',
-                 DeprecationWarning, stacklevel=1)
-            if reverse:
-                order = 'ASC'
-        rset = self.versions_in_state(states, order, True)
-        if rset:
-            return rset.get_entity(0, 0)
-        return None
-
-    def versions_in_state(self, states, order='ASC', limit=False):
-        """returns version(s) for the project in one of the given states, sorted
-        by version number.
-
-        If limit is true, limit result to one version.
-        If reverse, versions are returned from the smallest to the greatest.
-        """
-        if limit:
-            order += ' LIMIT 1'
-        rql = 'Any V,N ORDERBY version_sort_value(N) %s ' \
-              'WHERE V num N, V in_state S, S name IN (%s), ' \
-              'V version_of P, P eid %%(p)s' % (order, ','.join(repr(s) for s in states))
-        return self._cw.execute(rql, {'p': self.eid})
-
-.. _`tracker`: http://www.cubicweb.org/project/cubicweb-tracker/
-
-These few lines exhibit the important properties we want to outline:
-
-* entity code is concerned with the application domain
-
-* it is NOT concerned with database consistency (this is the realm of
-  Hooks/Operations); in other words, it assumes a consistent world
-
-* it is NOT (directly) concerned with end-user interfaces
-
-* however it can be used in both contexts
-
-* it does not create or manipulate the internal object's state
-
-* it plays freely with RQL expression as needed
-
-* it is not concerned with internationalization
-
-* it does not raise exceptions
-
-
--- a/doc/book/en/devrepo/entityclasses/data-as-objects.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,142 +0,0 @@
-Access to persistent data
---------------------------
-
-Python-level access to persistent data is provided by the
-:class:`Entity <cubicweb.entity>` class.
-
-.. XXX this part is not clear. refactor it.
-
-An entity class is bound to a schema entity type. Descriptors are added when
-classes are registered in order to initialize the class according to its schema:
-
-* the attributes defined in the schema appear as attributes of these classes
-
-* the relations defined in the schema appear as attributes of these classes,
-  but are lists of instances
-
-`Formatting and output generation`:
-
-* :meth:`view(__vid, __registry='views', **kwargs)`, applies the given view to the entity
-  (and returns an unicode string)
-
-* :meth:`absolute_url(*args, **kwargs)`, returns an absolute URL including the base-url
-
-* :meth:`rest_path()`, returns a relative REST URL to get the entity
-
-* :meth:`printable_value(attr, value=_marker, attrtype=None, format='text/html', displaytime=True)`,
-  returns a string enabling the display of an attribute value in a given format
-  (the value is automatically recovered if necessary)
-
-`Data handling`:
-
-* :meth:`as_rset()`, converts the entity into an equivalent result set simulating the
-  request `Any X WHERE X eid _eid_`
-
-* :meth:`complete(skip_bytes=True)`, executes a request that recovers at
-  once all the missing attributes of an entity
-
-* :meth:`get_value(name)`, returns the value associated to the attribute name given
-  in parameter
-
-* :meth:`related(rtype, role='subject', limit=None, entities=False)`,
-  returns a list of entities related to the current entity by the
-  relation given in parameter
-
-* :meth:`unrelated(rtype, targettype, role='subject', limit=None)`,
-  returns a result set corresponding to the entities not (yet)
-  related to the current entity by the relation given in parameter
-  and satisfying its constraints
-
-* :meth:`cw_set(**kwargs)`, updates entity's attributes and/or relation with the
-  corresponding values given named parameters. To set a relation where this
-  entity is the object of the relation, use `reverse_<relation>` as argument
-  name.  Values may be an entity, a list of entities, or None (meaning that all
-  relations of the given type from or to this object should be deleted).
-
-* :meth:`copy_relations(ceid)`, copies the relations of the entities having the eid
-  given in the parameters on the current entity
-
-* :meth:`cw_delete()` allows to delete the entity
-
-
-The :class:`AnyEntity` class
-----------------------------
-
-To provide a specific behavior for each entity, we can define a class
-inheriting from `cubicweb.entities.AnyEntity`. In general, we define this class
-in `mycube.entities` module (or in a submodule if we want to split code among
-multiple files) so that it will be available on both server and client side.
-
-The class `AnyEntity` is a sub-class of Entity that add methods to it,
-and helps specializing (by further subclassing) the handling of a
-given entity type.
-
-Most methods defined for `AnyEntity`, in addition to `Entity`, add
-support for the `Dublin Core`_ metadata.
-
-.. _`Dublin Core`: http://dublincore.org/
-
-`Standard meta-data (Dublin Core)`:
-
-* :meth:`dc_title()`, returns a unicode string corresponding to the
-  meta-data `Title` (used by default is the first non-meta attribute
-  of the entity schema)
-
-* :meth:`dc_long_title()`, same as dc_title but can return a more
-  detailed title
-
-* :meth:`dc_description(format='text/plain')`, returns a unicode string
-  corresponding to the meta-data `Description` (looks for a
-  description attribute by default)
-
-* :meth:`dc_authors()`, returns a unicode string corresponding to the meta-data
-  `Authors` (owners by default)
-
-* :meth:`dc_creator()`, returns a unicode string corresponding to the
-  creator of the entity
-
-* :meth:`dc_date(date_format=None)`, returns a unicode string corresponding to
-  the meta-data `Date` (update date by default)
-
-* :meth:`dc_type(form='')`, returns a string to display the entity type by
-  specifying the preferred form (`plural` for a plural form)
-
-* :meth:`dc_language()`, returns the language used by the entity
-
-Inheritance
------------
-
-When describing a data model, entities can inherit from other entities as is
-common in object-oriented programming.
-
-You have the possibility to redefine whatever pleases you, as follow:
-
-.. sourcecode:: python
-
-    from cubes.OTHER_CUBE import entities
-
-    class EntityExample(entities.EntityExample):
-
-        def dc_long_title(self):
-            return '%s (%s)' % (self.name, self.description)
-
-The most specific entity definition will always the one used by the
-ORM. For instance, the new EntityExample above in mycube replaces the
-one in OTHER_CUBE. These types are stored in the `etype` section of
-the `vregistry`.
-
-Notice this is different than yams schema inheritance, which is an
-experimental undocumented feature.
-
-
-Application logic
------------------
-
-While a lot of custom behaviour and application logic can be
-implemented using entity classes, the programmer must be aware that
-adding new attributes and method on an entity class adds may shadow
-schema-level attribute or relation definitions.
-
-To keep entities clean (mostly data structures plus a few universal
-methods such as listed above), one should use `adapters` (see
-:ref:`adapters`).
--- a/doc/book/en/devrepo/entityclasses/index.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-Data as objects
-===============
-
-In this chapter, we will introduce the objects that are used to handle
-the logic associated to the data stored in the database.
-
-.. toctree::
-   :maxdepth: 1
-
-   data-as-objects
-   load-sort
-   adapters
-   application-logic
--- a/doc/book/en/devrepo/entityclasses/load-sort.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-
-.. _FetchAttrs:
-
-Loaded attributes and default sorting management
-````````````````````````````````````````````````
-
-* The class attribute `fetch_attrs` allows to define in an entity class a list of
-  names of attributes that should be automatically loaded when entities of this
-  type are fetched from the database using ORM methods retrieving entity of this
-  type (such as :meth:`related` and :meth:`unrelated`). You can also put relation
-  names in there, but we are limited to *subject relations of cardinality `?` or
-  `1`*.
-
-* The :meth:`cw_fetch_order` and :meth:`cw_fetch_unrelated_order` class methods
-  are respectively responsible to control how entities will be sorted when:
-
-  - retrieving all entities of a given type, or entities related to another
-
-  - retrieving a list of entities for use in drop-down lists enabling relations
-    creation in the editing view of an entity
-
-By default entities will be listed on their modification date descending,
-i.e. you'll get entities recently modified first. While this is usually a good
-default in drop-down list, you'll probably want to change `cw_fetch_order`.
-
-This may easily be done using the :func:`~cubicweb.entities.fetch_config`
-function, which simplifies the definition of attributes to load and sorting by
-returning a list of attributes to pre-load (considering automatically the
-attributes of `AnyEntity`) and a sorting function as described below:
-
-.. autofunction:: cubicweb.entities.fetch_config
-
-In you want something else (such as sorting on the result of a registered
-procedure), here is the prototype of those methods:
-
-
-.. automethod:: cubicweb.entity.Entity.cw_fetch_order
-
-.. automethod:: cubicweb.entity.Entity.cw_fetch_unrelated_order
-
--- a/doc/book/en/devrepo/fti.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,159 +0,0 @@
-.. _fti:
-
-Full Text Indexing in CubicWeb
-------------------------------
-
-When an attribute is tagged as *fulltext-indexable* in the datamodel,
-CubicWeb will automatically trigger hooks to update the internal
-fulltext index (i.e the ``appears`` SQL table) each time this attribute
-is modified.
-
-CubicWeb also provides a ``db-rebuild-fti`` command to rebuild the whole
-fulltext on demand:
-
-.. sourcecode:: bash
-
-   cubicweb@esope~$ cubicweb db-rebuild-fti my_tracker_instance
-
-You can also rebuild the fulltext index for a given set of entity types:
-
-.. sourcecode:: bash
-
-   cubicweb@esope~$ cubicweb db-rebuild-fti my_tracker_instance Ticket Version
-
-In the above example, only fulltext index of entity types ``Ticket`` and ``Version``
-will be rebuilt.
-
-
-Standard FTI process
-~~~~~~~~~~~~~~~~~~~~
-
-Considering an entity type ``ET``, the default *fti* process is to :
-
-1. fetch all entities of type ``ET``
-
-2. for each entity, adapt it to ``IFTIndexable`` (see
-   :class:`~cubicweb.entities.adapters.IFTIndexableAdapter`)
-
-3. call
-   :meth:`~cubicweb.entities.adapters.IFTIndexableAdapter.get_words` on
-   the adapter which is supposed to return a dictionary *weight* ->
-   *list of words* as expected by
-   :meth:`~logilab.database.fti.FTIndexerMixIn.index_object`. The
-   tokenization of each attribute value is done by
-   :meth:`~logilab.database.fti.tokenize`.
-
-
-See :class:`~cubicweb.entities.adapters.IFTIndexableAdapter` for more documentation.
-
-
-Yams and ``fulltext_container``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-It is possible in the datamodel to indicate that fulltext-indexed
-attributes defined for an entity type will be used to index not the
-entity itself but a related entity. This is especially useful for
-composite entities. Let's take a look at (a simplified version of)
-the base schema defined in CubicWeb (see :mod:`cubicweb.schemas.base`):
-
-.. sourcecode:: python
-
-  class CWUser(WorkflowableEntityType):
-      login     = String(required=True, unique=True, maxsize=64)
-      upassword = Password(required=True)
-
-  class EmailAddress(EntityType):
-      address = String(required=True,  fulltextindexed=True,
-                       indexed=True, unique=True, maxsize=128)
-
-
-  class use_email_relation(RelationDefinition):
-      name = 'use_email'
-      subject = 'CWUser'
-      object = 'EmailAddress'
-      cardinality = '*?'
-      composite = 'subject'
-
-
-The schema above states that there is a relation between ``CWUser`` and ``EmailAddress``
-and that the ``address`` field of ``EmailAddress`` is fulltext indexed. Therefore,
-in your application, if you use fulltext search to look for an email address, CubicWeb
-will return the ``EmailAddress`` itself. But the objects we'd like to index
-are more likely to be the associated ``CWUser`` than the ``EmailAddress`` itself.
-
-The simplest way to achieve that is to tag the ``use_email`` relation in
-the datamodel:
-
-.. sourcecode:: python
-
-  class use_email(RelationType):
-      fulltext_container = 'subject'
-
-
-Customizing how entities are fetched during ``db-rebuild-fti``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-``db-rebuild-fti`` will call the
-:meth:`~cubicweb.entities.AnyEntity.cw_fti_index_rql_queries` class
-method on your entity type.
-
-.. automethod:: cubicweb.entities.AnyEntity.cw_fti_index_rql_queries
-
-Now, suppose you've got a _huge_ table to index, you probably don't want to
-get all entities at once. So here's a simple customized example that will
-process block of 10000 entities:
-
-.. sourcecode:: python
-
-
-    class MyEntityClass(AnyEntity):
-        __regid__ = 'MyEntityClass'
-
-    @classmethod
-    def cw_fti_index_rql_queries(cls, req):
-        # get the default RQL method and insert LIMIT / OFFSET instructions
-        base_rql = super(SearchIndex, cls).cw_fti_index_rql_queries(req)[0]
-        selected, restrictions = base_rql.split(' WHERE ')
-        rql_template = '%s ORDERBY X LIMIT %%(limit)s OFFSET %%(offset)s WHERE %s' % (
-            selected, restrictions)
-        # count how many entities you'll have to index
-        count = req.execute('Any COUNT(X) WHERE X is MyEntityClass')[0][0]
-        # iterate by blocks of 10000 entities
-        chunksize = 10000
-        for offset in xrange(0, count, chunksize):
-            print 'SENDING', rql_template % {'limit': chunksize, 'offset': offset}
-            yield rql_template % {'limit': chunksize, 'offset': offset}
-
-Since you have access to ``req``, you can more or less fetch whatever you want.
-
-
-Customizing :meth:`~cubicweb.entities.adapters.IFTIndexableAdapter.get_words`
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-You can also customize the FTI process by providing your own ``get_words()``
-implementation:
-
-.. sourcecode:: python
-
-    from cubicweb.entities.adapters import IFTIndexableAdapter
-
-    class SearchIndexAdapter(IFTIndexableAdapter):
-        __regid__ = 'IFTIndexable'
-        __select__ = is_instance('MyEntityClass')
-
-        def fti_containers(self, _done=None):
-            """this should yield any entity that must be considered to
-            fulltext-index self.entity
-
-            CubicWeb's default implementation will look for yams'
-            ``fulltex_container`` property.
-            """
-            yield self.entity
-            yield self.entity.some_related_entity
-
-
-        def get_words(self):
-            # implement any logic here
-            # see http://www.postgresql.org/docs/9.1/static/textsearch-controls.html
-            # for the actual signification of 'C'
-            return {'C': ['any', 'word', 'I', 'want']}
--- a/doc/book/en/devrepo/index.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-.. _Part2:
-
-----------------------
-Repository development
-----------------------
-
-This part is about developing applications with the *CubicWeb*
-framework. It is not concerned with the web system, which is a
-separate layer and has its own whole chapter.
-
-.. toctree::
-   :maxdepth: 2
-   :numbered:
-
-   cubes/index
-   vreg.rst
-   datamodel/index
-   entityclasses/index
-   devcore/index
-   repo/index
-   testing.rst
-   migration.rst
-   profiling.rst
-   fti.rst
-   dataimport
--- a/doc/book/en/devrepo/migration.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,250 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _migration:
-
-Migration
-=========
-
-One of the main design goals of *CubicWeb* was to support iterative and agile
-development. For this purpose, multiple actions are provided to facilitate the
-improvement of an instance, and in particular to handle the changes to be
-applied to the data model, without loosing existing data.
-
-The current version of a cube (and of cubicweb itself) is provided in the file
-`__pkginfo__.py` as a tuple of 3 integers.
-
-Migration scripts management
-----------------------------
-
-Migration scripts has to be located in the directory `migration` of your
-cube and named accordingly:
-
-::
-
-  <version n° X.Y.Z>[_<description>]_<mode>.py
-
-in which :
-
-* X.Y.Z is the model version number to which the script enables to migrate.
-
-* *mode* (between the last "_" and the extension ".py") is used for
-  distributed installation. It indicates to which part
-  of the application (RQL server, web server) the script applies.
-  Its value could be :
-
-  * `common`, applies to the RQL server as well as the web server and updates
-    files on the hard drive (configuration files migration for example).
-
-  * `web`, applies only to the web server and updates files on the hard drive.
-
-  * `repository`, applies only to the RQL server and updates files on the
-    hard drive.
-
-  * `Any`, applies only to the RQL server and updates data in the database
-    (schema and data migration for example).
-
-Again in the directory `migration`, the file `depends.map` allows to indicate
-that for the migration to a particular model version, you always have to first
-migrate to a particular *CubicWeb* version. This file can contain comments (lines
-starting with `#`) and a dependency is listed as follows: ::
-
-  <model version n° X.Y.Z> : <cubicweb version n° X.Y.Z>
-
-For example: ::
-
-  0.12.0: 2.26.0
-  0.13.0: 2.27.0
-  # 0.14 works with 2.27 <= cubicweb <= 2.28 at least
-  0.15.0: 2.28.0
-
-Base context
-------------
-
-The following identifiers are pre-defined in migration scripts:
-
-* `config`, instance configuration
-
-* `interactive_mode`, boolean indicating that the script is executed in
-  an interactive mode or not
-
-* `versions_map`, dictionary of migrated versions  (key are cubes
-  names, including 'cubicweb', values are (from version, to version)
-
-* `confirm(question)`, function asking the user and returning true
-  if the user answers yes, false otherwise (always returns true in
-  non-interactive mode)
-
-* `_()` is equivalent to `unicode` allowing to flag the strings to
-  internationalize in the migration scripts.
-
-In the `repository` scripts, the following identifiers are also defined:
-
-* `commit(ask_confirm=True)`, request confirming and executing a "commit"
-
-* `schema`, instance schema (readen from the database)
-
-* `fsschema`, installed schema on the file system (e.g. schema of
-  the updated model and cubicweb)
-
-* `repo`, repository object
-
-* `session`, repository session object
-
-
-New cube dependencies
----------------------
-
-If your code depends on some new cubes, you have to add them in a migration
-script by using:
-
-* `add_cube(cube, update_database=True)`, add a cube.
-* `add_cubes(cubes, update_database=True)`, add a list of cubes.
-
-The `update_database` parameter is telling if the database schema
-should be updated or if only the relevant persistent property should be
-inserted (for the case where a new cube has been extracted from an
-existing one, so the new cube schema is actually already in there).
-
-If some of the added cubes are already used by an instance, they'll simply be
-silently skipped.
-
-
-Schema migration
-----------------
-The following functions for schema migration are available in `repository`
-scripts:
-
-* `add_attribute(etype, attrname, attrtype=None, commit=True)`, adds a new
-  attribute to an existing entity type. If the attribute type is not specified,
-  then it is extracted from the updated schema.
-
-* `drop_attribute(etype, attrname, commit=True)`, removes an attribute from an
-  existing entity type.
-
-* `rename_attribute(etype, oldname, newname, commit=True)`, renames an attribute
-
-* `add_entity_type(etype, auto=True, commit=True)`, adds a new entity type.
-  If `auto` is True, all the relations using this entity type and having a known
-  entity type on the other hand will automatically be added.
-
-* `drop_entity_type(etype, commit=True)`, removes an entity type and all the
-  relations using it.
-
-* `rename_entity_type(oldname, newname, commit=True)`, renames an entity type
-
-* `add_relation_type(rtype, addrdef=True, commit=True)`, adds a new relation
-  type. If `addrdef` is True, all the relations definitions of this type will
-  be added.
-
-* `drop_relation_type(rtype, commit=True)`, removes a relation type and all the
-  definitions of this type.
-
-* `rename_relation_type(oldname, newname, commit=True)`, renames a relation type.
-
-* `add_relation_definition(subjtype, rtype, objtype, commit=True)`, adds a new
-  relation definition.
-
-* `drop_relation_definition(subjtype, rtype, objtype, commit=True)`, removes
-  a relation definition.
-
-* `sync_schema_props_perms(ertype=None, syncperms=True, syncprops=True, syncrdefs=True, commit=True)`,
-  synchronizes properties and/or permissions on:
-  - the whole schema if ertype is None
-  - an entity or relation type schema if ertype is a string
-  - a relation definition  if ertype is a 3-uple (subject, relation, object)
-
-* `change_relation_props(subjtype, rtype, objtype, commit=True, **kwargs)`, changes
-  properties of a relation definition by using the named parameters of the properties
-  to change.
-
-* `set_widget(etype, rtype, widget, commit=True)`, changes the widget used for the
-  relation <rtype> of entity type <etype>.
-
-* `set_size_constraint(etype, rtype, size, commit=True)`, changes the size constraints
-  for the relation <rtype> of entity type <etype>.
-
-Data migration
---------------
-The following functions for data migration are available in `repository` scripts:
-
-* `rql(rql, kwargs=None, cachekey=None, ask_confirm=True)`, executes an arbitrary RQL
-  query, either to interrogate or update. A result set object is returned.
-
-* `add_entity(etype, *args, **kwargs)`, adds a new entity of the given type.
-  The attribute and relation values are specified as named positional
-  arguments.
-
-Workflow creation
------------------
-
-The following functions for workflow creation are available in `repository`
-scripts:
-
-* `add_workflow(label, workflowof, initial=False, commit=False, **kwargs)`, adds a new workflow
-  for a given type(s)
-
-You can find more details about workflows in the chapter :ref:`Workflow` .
-
-Configuration migration
------------------------
-
-The following functions for configuration migration are available in all
-scripts:
-
-* `option_renamed(oldname, newname)`, indicates that an option has been renamed
-
-* `option_group_change(option, oldgroup, newgroup)`, indicates that an option does not
-  belong anymore to the same group.
-
-* `option_added(oldname, newname)`, indicates that an option has been added.
-
-* `option_removed(oldname, newname)`, indicates that an option has been deleted.
-
-The `config` variable is an object which can be used to access the
-configuration values, for reading and updating, with a dictionary-like
-syntax. 
-
-Example 1: migration script changing the variable 'sender-addr' in
-all-in-one.conf. The script also checks that in that the instance is
-configured with a known value for that variable, and only updates the
-value in that case.
-
-.. sourcecode:: python
-
- wrong_addr = 'cubicweb@loiglab.fr' # known wrong address
- fixed_addr = 'cubicweb@logilab.fr'
- configured_addr = config.get('sender-addr')
- # check that the address has not been hand fixed by a sysadmin
- if configured_addr == wrong_addr: 
-     config['sender-addr'] = fixed-addr
-     config.save()
-
-Example 2: checking the value of the database backend driver, which
-can be useful in case you need to issue backend-dependent raw SQL
-queries in a migration script.
-
-.. sourcecode:: python
-
- dbdriver  = config.sources()['system']['db-driver']
- if dbdriver == "sqlserver2005":
-     # this is now correctly handled by CW :-)
-     sql('ALTER TABLE cw_Xxxx ALTER COLUMN cw_name varchar(64) NOT NULL;')
-     commit()
- else: # postgresql
-     sync_schema_props_perms(ertype=('Xxxx', 'name', 'String'),
-     syncperms=False)
-
-
-Others migration functions
---------------------------
-Those functions are only used for low level operations that could not be
-accomplished otherwise or to repair damaged databases during interactive
-session. They are available in `repository` scripts:
-
-* `sql(sql, args=None, ask_confirm=True)`, executes an arbitrary SQL query on the system source
-* `add_entity_type_table(etype, commit=True)`
-* `add_relation_type_table(rtype, commit=True)`
-* `uninline_relation(rtype, commit=True)`
-
-
-[FIXME] Add explanation on how to use cubicweb-ctl shell
--- a/doc/book/en/devrepo/profiling.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-.. _PROFILING:
-
-Profiling and performance
-=========================
-
-If you feel that one of your pages takes more time than it should to be
-generated, chances are that you're making too many RQL queries.  Obviously,
-there are other reasons but experience tends to show this is the first thing to
-track down. Luckily, CubicWeb provides a configuration option to log RQL
-queries. In your ``all-in-one.conf`` file, set the **query-log-file** option::
-
-    # web application query log file
-    query-log-file=/home/user/myapp-rql.log
-
-Then restart your application, reload your page and stop your application.
-The file ``myapp-rql.log`` now contains the list of RQL queries that were
-executed during your test. It's a simple text file containing lines such as::
-
-    Any A WHERE X eid %(x)s, X lastname A {'x': 448} -- (0.002 sec, 0.010 CPU sec)
-    Any A WHERE X eid %(x)s, X firstname A {'x': 447} -- (0.002 sec, 0.000 CPU sec)
-
-The structure of each line is::
-
-    <RQL QUERY> <QUERY ARGS IF ANY> -- <TIME SPENT>
-
-CubicWeb also provides the **exlog** command to examine and summarize data found
-in such a file:
-
-.. sourcecode:: sh
-
-    $ cubicweb-ctl exlog /home/user/myapp-rql.log
-    0.07 50 Any A WHERE X eid %(x)s, X firstname A {}
-    0.05 50 Any A WHERE X eid %(x)s, X lastname A {}
-    0.01 1 Any X,AA ORDERBY AA DESC WHERE E eid %(x)s, E employees X, X modification_date AA {}
-    0.01 1 Any X WHERE X eid %(x)s, X owned_by U, U eid %(u)s {, }
-    0.01 1 Any B,T,P ORDERBY lower(T) WHERE B is Bookmark,B title T, B path P, B bookmarked_by U, U eid %(x)s {}
-    0.01 1 Any A,B,C,D WHERE A eid %(x)s,A name B,A creation_date C,A modification_date D {}
-
-This command sorts and uniquifies queries so that it's easy to see where
-is the hot spot that needs optimization.
-
-Do not neglect to set the **fetch_attrs** attribute you can define in your
-entity classes because it can greatly reduce the number of queries executed (see
-:ref:`FetchAttrs`).
-
-You should also know about the **profile** option in the ``all-in-on.conf``. If
-set, this option will make your application run in an `hotshot`_ session and
-store the results in the specified file.
-
-.. _hotshot: http://docs.python.org/library/hotshot.html#module-hotshot
-
-Last but no least, if you're using the PostgreSQL database backend, VACUUMing
-your database can significantly improve the performance of the queries (by
-updating the statistics used by the query optimizer). Nowadays, this is done
-automatically from time to time, but if you've just imported a large amount of
-data in your db, you will want to vacuum it (with the analyse option on). Read
-the documentation of your database for more information.
--- a/doc/book/en/devrepo/repo/hooks.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,279 +0,0 @@
-.. -*- coding: utf-8 -*-
-.. _hooks:
-
-Hooks and Operations
-====================
-
-.. autodocstring:: cubicweb.server.hook
-
-
-Example using dataflow hooks
-----------------------------
-
-We will use a very simple example to show hooks usage. Let us start with the
-following schema.
-
-.. sourcecode:: python
-
-   class Person(EntityType):
-       age = Int(required=True)
-
-We would like to add a range constraint over a person's age. Let's write an hook
-(supposing yams can not handle this nativly, which is wrong). It shall be placed
-into `mycube/hooks.py`. If this file were to grow too much, we can easily have a
-`mycube/hooks/... package` containing hooks in various modules.
-
-.. sourcecode:: python
-
-   from cubicweb import ValidationError
-   from cubicweb.predicates import is_instance
-   from cubicweb.server.hook import Hook
-
-   class PersonAgeRange(Hook):
-        __regid__ = 'person_age_range'
-        __select__ = Hook.__select__ & is_instance('Person')
-        events = ('before_add_entity', 'before_update_entity')
-
-        def __call__(self):
-	    if 'age' in self.entity.cw_edited:
-                if 0 <= self.entity.age <= 120:
-                   return
-		msg = self._cw._('age must be between 0 and 120')
-		raise ValidationError(self.entity.eid, {'age': msg})
-
-In our example the base `__select__` is augmented with an `is_instance` selector
-matching the desired entity type.
-
-The `events` tuple is used specify that our hook should be called before the
-entity is added or updated.
-
-Then in the hook's `__call__` method, we:
-
-* check if the 'age' attribute is edited
-* if so, check the value is in the range
-* if not, raise a validation error properly
-
-Now Let's augment our schema with new `Company` entity type with some relation to
-`Person` (in 'mycube/schema.py').
-
-.. sourcecode:: python
-
-   class Company(EntityType):
-        name = String(required=True)
-        boss = SubjectRelation('Person', cardinality='1*')
-        subsidiary_of = SubjectRelation('Company', cardinality='*?')
-
-
-We would like to constrain the company's bosses to have a minimum (legal)
-age. Let's write an hook for this, which will be fired when the `boss` relation
-is established (still supposing we could not specify that kind of thing in the
-schema).
-
-.. sourcecode:: python
-
-   class CompanyBossLegalAge(Hook):
-        __regid__ = 'company_boss_legal_age'
-        __select__ = Hook.__select__ & match_rtype('boss')
-        events = ('before_add_relation',)
-
-        def __call__(self):
-            boss = self._cw.entity_from_eid(self.eidto)
-            if boss.age < 18:
-                msg = self._cw._('the minimum age for a boss is 18')
-                raise ValidationError(self.eidfrom, {'boss': msg})
-
-.. Note::
-
-    We use the :class:`~cubicweb.server.hook.match_rtype` selector to select the
-    proper relation type.
-
-    The essential difference with respect to an entity hook is that there is no
-    self.entity, but `self.eidfrom` and `self.eidto` hook attributes which
-    represent the subject and object **eid** of the relation.
-
-Suppose we want to check that there is no cycle by the `subsidiary_of`
-relation. This is best achieved in an operation since all relations are likely to
-be set at commit time.
-
-.. sourcecode:: python
-
-    from cubicweb.server.hook import Hook, DataOperationMixIn, Operation, match_rtype
-
-    def check_cycle(self, session, eid, rtype, role='subject'):
-        parents = set([eid])
-        parent = session.entity_from_eid(eid)
-        while parent.related(rtype, role):
-            parent = parent.related(rtype, role)[0]
-            if parent.eid in parents:
-                msg = session._('detected %s cycle' % rtype)
-                raise ValidationError(eid, {rtype: msg})
-            parents.add(parent.eid)
-
-
-    class CheckSubsidiaryCycleOp(Operation):
-
-        def precommit_event(self):
-            check_cycle(self.session, self.eidto, 'subsidiary_of')
-
-
-    class CheckSubsidiaryCycleHook(Hook):
-        __regid__ = 'check_no_subsidiary_cycle'
-        __select__ = Hook.__select__ & match_rtype('subsidiary_of')
-        events = ('after_add_relation',)
-
-        def __call__(self):
-            CheckSubsidiaryCycleOp(self._cw, eidto=self.eidto)
-
-
-Like in hooks, :exc:`~cubicweb.ValidationError` can be raised in operations. Other
-exceptions are usually programming errors.
-
-In the above example, our hook will instantiate an operation each time the hook
-is called, i.e. each time the `subsidiary_of` relation is set. There is an
-alternative method to schedule an operation from a hook, using the
-:func:`get_instance` class method.
-
-.. sourcecode:: python
-
-   from cubicweb.server.hook import set_operation
-
-   class CheckSubsidiaryCycleHook(Hook):
-       __regid__ = 'check_no_subsidiary_cycle'
-       events = ('after_add_relation',)
-       __select__ = Hook.__select__ & match_rtype('subsidiary_of')
-
-       def __call__(self):
-           CheckSubsidiaryCycleOp.get_instance(self._cw).add_data(self.eidto)
-
-   class CheckSubsidiaryCycleOp(DataOperationMixIn, Operation):
-
-       def precommit_event(self):
-           for eid in self.get_data():
-               check_cycle(self.session, eid, self.rtype)
-
-
-Here, we call :func:`set_operation` so that we will simply accumulate eids of
-entities to check at the end in a single `CheckSubsidiaryCycleOp`
-operation. Value are stored in a set associated to the
-'subsidiary_cycle_detection' transaction data key. The set initialization and
-operation creation are handled nicely by :func:`set_operation`.
-
-A more realistic example can be found in the advanced tutorial chapter
-:ref:`adv_tuto_security_propagation`.
-
-
-Inter-instance communication
-----------------------------
-
-If your application consists of several instances, you may need some means to
-communicate between them.  Cubicweb provides a publish/subscribe mechanism
-using ØMQ_.  In order to use it, use
-:meth:`~cubicweb.server.cwzmq.ZMQComm.add_subscription` on the
-`repo.app_instances_bus` object.  The `callback` will get the message (as a
-list).  A message can be sent by calling
-:meth:`~cubicweb.server.cwzmq.ZMQComm.publish` on `repo.app_instances_bus`.
-The first element of the message is the topic which is used for filtering and
-dispatching messages.
-
-.. _ØMQ: http://www.zeromq.org/
-
-.. sourcecode:: python
-
-  class FooHook(hook.Hook):
-      events = ('server_startup',)
-      __regid__ = 'foo_startup'
-
-      def __call__(self):
-          def callback(msg):
-              self.info('received message: %s', ' '.join(msg))
-          self.repo.app_instances_bus.add_subscription('hello', callback)
-
-.. sourcecode:: python
-
-  def do_foo(self):
-      actually_do_foo()
-      self._cw.repo.app_instances_bus.publish(['hello', 'world'])
-
-The `zmq-address-pub` configuration variable contains the address used
-by the instance for sending messages, e.g. `tcp://*:1234`.  The
-`zmq-address-sub` variable contains a comma-separated list of addresses
-to listen on, e.g. `tcp://localhost:1234, tcp://192.168.1.1:2345`.
-
-
-Hooks writing tips
-------------------
-
-Reminder
-~~~~~~~~
-
-You should never use the `entity.foo = 42` notation to update an entity. It will
-not do what you expect (updating the database). Instead, use the
-:meth:`~cubicweb.entity.Entity.cw_set` method or direct access to entity's
-:attr:`cw_edited` attribute if you're writing a hook for 'before_add_entity' or
-'before_update_entity' event.
-
-
-How to choose between a before and an after event ?
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-`before_*` hooks give you access to the old attribute (or relation)
-values. You can also intercept and update edited values in the case of
-entity modification before they reach the database.
-
-Else the question is: should I need to do things before or after the actual
-modification ? If the answer is "it doesn't matter", use an 'after' event.
-
-
-Validation Errors
-~~~~~~~~~~~~~~~~~
-
-When a hook which is responsible to maintain the consistency of the
-data model detects an error, it must use a specific exception named
-:exc:`~cubicweb.ValidationError`. Raising anything but a (subclass of)
-:exc:`~cubicweb.ValidationError` is a programming error. Raising it
-entails aborting the current transaction.
-
-This exception is used to convey enough information up to the user
-interface. Hence its constructor is different from the default Exception
-constructor. It accepts, positionally:
-
-* an entity eid (**not the entity itself**),
-
-* a dict whose keys represent attribute (or relation) names and values
-  an end-user facing message (hence properly translated) relating the
-  problem.
-
-.. sourcecode:: python
-
-  raise ValidationError(earth.eid, {'sea_level': self._cw._('too high'),
-                                    'temperature': self._cw._('too hot')})
-
-
-Checking for object created/deleted in the current transaction
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In hooks, you can use the
-:meth:`~cubicweb.server.session.Session.added_in_transaction` or
-:meth:`~cubicweb.server.session.Session.deleted_in_transaction` of the session
-object to check if an eid has been created or deleted during the hook's
-transaction.
-
-This is useful to enable or disable some stuff if some entity is being added or
-deleted.
-
-.. sourcecode:: python
-
-   if self._cw.deleted_in_transaction(self.eidto):
-      return
-
-
-Peculiarities of inlined relations
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Relations which are defined in the schema as `inlined` (see :ref:`RelationType`
-for details) are inserted in the database at the same time as entity attributes.
-
-This may have some side effect, for instance when creating an entity
-and setting an inlined relation in the same rql query, then at
-`before_add_relation` time, the relation will already exist in the
-database (it is otherwise not the case).
--- a/doc/book/en/devrepo/repo/index.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-Repository customization
-++++++++++++++++++++++++
-.. toctree::
-   :maxdepth: 1
-
-   sessions
-   hooks
-   notifications
-   tasks
-
-
--- a/doc/book/en/devrepo/repo/notifications.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-Notifications management
-========================
-
-CubicWeb provides a machinery to ease notifications handling. To use it for a
-notification:
-
-* write a view inheriting from
-  :class:`~cubicweb.sobjects.notification.NotificationView`.  The usual view api
-  is used to generated the email (plain text) content, and additional
-  :meth:`~cubicweb.sobjects.notification.NotificationView.subject` and
-  :meth:`~cubicweb.sobjects.notification.NotificationView.recipients` methods
-  are used to build the email's subject and
-  recipients. :class:`NotificationView` provides default implementation for both
-  methods.
-
-* write a hook for event that should trigger this notification, select the view
-  (without rendering it), and give it to
-  :func:`cubicweb.hooks.notification.notify_on_commit` so that the notification
-  will be sent if the transaction succeed.
-
-
-.. XXX explain recipient finder and provide example
-
-API details
-~~~~~~~~~~~
-.. autoclass:: cubicweb.sobjects.notification.NotificationView
-.. autofunction:: cubicweb.hooks.notification.notify_on_commit
--- a/doc/book/en/devrepo/repo/sessions.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,204 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-Sessions
-========
-
-Sessions are objects linked to an authenticated user.  The `Session.new_cnx`
-method returns a new Connection linked to that session.
-
-Connections
-===========
-
-Connections provide the `.execute` method to query the data sources.
-
-Kinds of connections
---------------------
-
-There are two kinds of connections.
-
-* `normal connections` are the most common: they are related to users and
-  carry security checks coming with user credentials
-
-* `internal connections` have all the powers; they are also used in only a
-  few situations where you don't already have an adequate session at
-  hand, like: user authentication, data synchronisation in
-  multi-source contexts
-
-Normal connections are typically named `_cw` in most appobjects or
-sometimes just `session`.
-
-Internal connections are available from the `Repository` object and are
-to be used like this:
-
-.. sourcecode:: python
-
-   with self.repo.internal_cnx() as cnx:
-       do_stuff_with(cnx)
-       cnx.commit()
-
-Connections should always be used as context managers, to avoid leaks.
-
-Authentication and management of sessions
------------------------------------------
-
-The authentication process is a ballet involving a few dancers:
-
-* through its `get_session` method the top-level application object (the
-  `CubicWebPublisher`) will open a session whenever a web request
-  comes in; it asks the `session manager` to open a session (giving
-  the web request object as context) using `open_session`
-
-  * the session manager asks its authentication manager (which is a
-    `component`) to authenticate the request (using `authenticate`)
-
-    * the authentication manager asks, in order, to its authentication
-      information retrievers, a login and an opaque object containing
-      other credentials elements (calling `authentication_information`),
-      giving the request object each time
-
-      * the default retriever (named `LoginPasswordRetriever`)
-        will in turn defer login and password fetching to the request
-        object (which, depending on the authentication mode (`cookie`
-        or `http`), will do the appropriate things and return a login
-        and a password)
-
-    * the authentication manager, on success, asks the `Repository`
-      object to connect with the found credentials (using `connect`)
-
-      * the repository object asks authentication to all of its
-        sources which support the `CWUser` entity with the given
-        credentials; when successful it can build the cwuser entity,
-        from which a regular `Session` object is made; it returns the
-        session id
-
-        * the source in turn will delegate work to an authentifier
-          class that defines the ultimate `authenticate` method (for
-          instance the native source will query the database against
-          the provided credentials)
-
-    * the authentication manager, on success, will call back _all_
-      retrievers with `authenticated` and return its authentication
-      data (on failure, it will try the anonymous login or, if the
-      configuration forbids it, raise an `AuthenticationError`)
-
-Writing authentication plugins
-------------------------------
-
-Sometimes CubicWeb's out-of-the-box authentication schemes (cookie and
-http) are not sufficient. Nowadays there is a plethora of such schemes
-and the framework cannot provide them all, but as the sequence above
-shows, it is extensible.
-
-Two levels have to be considered when writing an authentication
-plugin: the web client and the repository.
-
-We invented a scenario where it makes sense to have a new plugin in
-each side: some middleware will do pre-authentication and under the
-right circumstances add a new HTTP `x-foo-user` header to the query
-before it reaches the CubicWeb instance. For a concrete example of
-this, see the `trustedauth`_ cube.
-
-.. _`trustedauth`: http://www.cubicweb.org/project/cubicweb-trustedauth
-
-Repository authentication plugins
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-On the repository side, it is possible to register a source
-authentifier using the following kind of code:
-
-.. sourcecode:: python
-
- from cubicweb.server.sources import native
-
- class FooAuthentifier(native.LoginPasswordAuthentifier):
-     """ a source authentifier plugin
-     if 'foo' in authentication information, no need to check
-     password
-     """
-     auth_rql = 'Any X WHERE X is CWUser, X login %(login)s'
-
-     def authenticate(self, session, login, **kwargs):
-         """return CWUser eid for the given login
-         if this account is defined in this source,
-         else raise `AuthenticationError`
-         """
-         session.debug('authentication by %s', self.__class__.__name__)
-         if 'foo' not in kwargs:
-             return super(FooAuthentifier, self).authenticate(session, login, **kwargs)
-         try:
-             rset = session.execute(self.auth_rql, {'login': login})
-             return rset[0][0]
-         except Exception, exc:
-             session.debug('authentication failure (%s)', exc)
-         raise AuthenticationError('foo user is unknown to us')
-
-Since repository authentifiers are not appobjects, we have to register
-them through a `server_startup` hook.
-
-.. sourcecode:: python
-
- class ServerStartupHook(hook.Hook):
-     """ register the foo authenticator """
-     __regid__ = 'fooauthenticatorregisterer'
-     events = ('server_startup',)
-
-     def __call__(self):
-         self.debug('registering foo authentifier')
-         self.repo.system_source.add_authentifier(FooAuthentifier())
-
-Web authentication plugins
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. sourcecode:: python
-
- class XFooUserRetriever(authentication.LoginPasswordRetriever):
-     """ authenticate by the x-foo-user http header
-     or just do normal login/password authentication
-     """
-     __regid__ = 'x-foo-user'
-     order = 0
-
-     def authentication_information(self, req):
-         """retrieve authentication information from the given request, raise
-         NoAuthInfo if expected information is not found
-         """
-         self.debug('web authenticator building auth info')
-         try:
-            login = req.get_header('x-foo-user')
-            if login:
-                return login, {'foo': True}
-            else:
-                return super(XFooUserRetriever, self).authentication_information(self, req)
-         except Exception, exc:
-            self.debug('web authenticator failed (%s)', exc)
-         raise authentication.NoAuthInfo()
-
-     def authenticated(self, retriever, req, cnx, login, authinfo):
-         """callback when return authentication information have opened a
-         repository connection successfully. Take care req has no session
-         attached yet, hence req.execute isn't available.
-
-         Here we set a flag on the request to indicate that the user is
-         foo-authenticated. Can be used by a selector
-         """
-         self.debug('web authenticator running post authentication callback')
-         cnx.foo_user = authinfo.get('foo')
-
-In the `authenticated` method we add (in an admitedly slightly hackish
-way) an attribute to the connection object. This, in turn, can be used
-to build a selector dispatching on the fact that the user was
-preauthenticated or not.
-
-.. sourcecode:: python
-
- @objectify_selector
- def foo_authenticated(cls, req, rset=None, **kwargs):
-     if hasattr(req.cnx, 'foo_user') and req.foo_user:
-         return 1
-     return 0
-
-Full Session and Connection API
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. autoclass:: cubicweb.server.session.Session
-.. autoclass:: cubicweb.server.session.Connection
--- a/doc/book/en/devrepo/repo/tasks.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-Tasks
-=========
-
-[WRITE ME]
-
-* repository tasks
-
--- a/doc/book/en/devrepo/testing.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,559 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-Tests
-=====
-
-Unit tests
-----------
-
-The *CubicWeb* framework provides the
-:class:`cubicweb.devtools.testlib.CubicWebTC` test base class .
-
-Tests shall be put into the mycube/test directory. Additional test
-data shall go into mycube/test/data.
-
-It is much advised to write tests concerning entities methods,
-actions, hooks and operations, security. The
-:class:`~cubicweb.devtools.testlib.CubicWebTC` base class has
-convenience methods to help test all of this.
-
-In the realm of views, automatic tests check that views are valid
-XHTML. See :ref:`automatic_views_tests` for details.
-
-Most unit tests need a live database to work against. This is achieved
-by CubicWeb using automatically sqlite (bundled with Python, see
-http://docs.python.org/library/sqlite3.html) as a backend.
-
-The database is stored in the mycube/test/tmpdb,
-mycube/test/tmpdb-template files. If it does not (yet) exist, it will
-be built automatically when the test suite starts.
-
-.. warning::
-
-  Whenever the schema changes (new entities, attributes, relations)
-  one must delete these two files. Changes concerned only with entity
-  or relation type properties (constraints, cardinalities,
-  permissions) and generally dealt with using the
-  `sync_schema_props_perms()` function of the migration environment do
-  not need a database regeneration step.
-
-.. _hook_test:
-
-Unit test by example
-````````````````````
-
-We start with an example extracted from the keyword cube (available
-from http://www.cubicweb.org/project/cubicweb-keyword).
-
-.. sourcecode:: python
-
-    from cubicweb.devtools.testlib import CubicWebTC
-    from cubicweb import ValidationError
-
-    class ClassificationHooksTC(CubicWebTC):
-
-        def setup_database(self):
-            with self.admin_access.repo_cnx() as cnx:
-                group_etype = cnx.find('CWEType', name='CWGroup').one()
-                c1 = cnx.create_entity('Classification', name=u'classif1',
-                                       classifies=group_etype)
-                user_etype = cnx.find('CWEType', name='CWUser').one()
-                c2 = cnx.create_entity('Classification', name=u'classif2',
-                                       classifies=user_etype)
-                self.kw1eid = cnx.create_entity('Keyword', name=u'kwgroup', included_in=c1).eid
-                cnx.commit()
-
-        def test_cannot_create_cycles(self):
-            with self.admin_access.repo_cnx() as cnx:
-                kw1 = cnx.entity_from_eid(self.kw1eid)
-                # direct obvious cycle
-                with self.assertRaises(ValidationError):
-                    kw1.cw_set(subkeyword_of=kw1)
-                cnx.rollback()
-                # testing indirect cycles
-                kw3 = cnx.execute('INSERT Keyword SK: SK name "kwgroup2", SK included_in C, '
-                                  'SK subkeyword_of K WHERE C name "classif1", K eid %(k)s'
-                                  {'k': kw1}).get_entity(0,0)
-                kw3.cw_set(reverse_subkeyword_of=kw1)
-                self.assertRaises(ValidationError, cnx.commit)
-
-The test class defines a :meth:`setup_database` method which populates the
-database with initial data. Each test of the class runs with this
-pre-populated database.
-
-The test case itself checks that an Operation does its job of
-preventing cycles amongst Keyword entities.
-
-The `create_entity` method of connection (or request) objects allows
-to create an entity. You can link this entity to other entities, by
-specifying as argument, the relation name, and the entity to link, as
-value. In the above example, the `Classification` entity is linked to
-a `CWEtype` via the relation `classifies`. Conversely, if you are
-creating a `CWEtype` entity, you can link it to a `Classification`
-entity, by adding `reverse_classifies` as argument.
-
-.. note::
-
-   the :meth:`commit` method is not called automatically. You have to
-   call it explicitly if needed (notably to test operations). It is a
-   good practice to regenerate entities with :meth:`entity_from_eid`
-   after a commit to avoid request cache effects.
-
-You can see an example of security tests in the
-:ref:`adv_tuto_security`.
-
-It is possible to have these tests run continuously using `apycot`_.
-
-.. _apycot: http://www.cubicweb.org/project/apycot
-
-.. _securitytest:
-
-Managing connections or users
-+++++++++++++++++++++++++++++
-
-Since unit tests are done with the SQLITE backend and this does not
-support multiple connections at a time, you must be careful when
-simulating security, changing users.
-
-By default, tests run with a user with admin privileges. Connections
-using these credentials are accessible through the `admin_access` object
-of the test classes.
-
-The `repo_cnx()` method returns a connection object that can be used as a
-context manager:
-
-.. sourcecode:: python
-
-   # admin_access is a pre-cooked session wrapping object
-   # it is built with:
-   # self.admin_access = self.new_access('admin')
-   with self.admin_access.repo_cnx() as cnx:
-       cnx.execute(...)
-       self.create_user(cnx, login='user1')
-       cnx.commit()
-
-   user1access = self.new_access('user1')
-   with user1access.web_request() as req:
-       req.execute(...)
-       req.cnx.commit()
-
-On exit of the context manager, a rollback is issued, which releases
-the connection. Don't forget to issue the `cnx.commit()` calls!
-
-.. warning::
-
-   Do not use references kept to the entities created with a
-   connection from another one!
-
-Email notifications tests
-`````````````````````````
-
-When running tests, potentially generated e-mails are not really sent
-but are found in the list `MAILBOX` of module
-:mod:`cubicweb.devtools.testlib`.
-
-You can test your notifications by analyzing the contents of this list, which
-contains objects with two attributes:
-
-* `recipients`, the list of recipients
-* `msg`, email.Message object
-
-Let us look at a simple example from the ``blog`` cube.
-
-.. sourcecode:: python
-
-    from cubicweb.devtools.testlib import CubicWebTC, MAILBOX
-
-    class BlogTestsCubicWebTC(CubicWebTC):
-        """test blog specific behaviours"""
-
-        def test_notifications(self):
-            with self.admin_access.web_request() as req:
-                cubicweb_blog = req.create_entity('Blog', title=u'cubicweb',
-                                    description=u'cubicweb is beautiful')
-                blog_entry_1 = req.create_entity('BlogEntry', title=u'hop',
-                                                 content=u'cubicweb hop')
-                blog_entry_1.cw_set(entry_of=cubicweb_blog)
-                blog_entry_2 = req.create_entity('BlogEntry', title=u'yes',
-                                                 content=u'cubicweb yes')
-                blog_entry_2.cw_set(entry_of=cubicweb_blog)
-                self.assertEqual(len(MAILBOX), 0)
-                req.cnx.commit()
-                self.assertEqual(len(MAILBOX), 2)
-                mail = MAILBOX[0]
-                self.assertEqual(mail.subject, '[data] hop')
-                mail = MAILBOX[1]
-                self.assertEqual(mail.subject, '[data] yes')
-
-Visible actions tests
-`````````````````````
-
-It is easy to write unit tests to test actions which are visible to
-a user or to a category of users. Let's take an example in the
-`conference cube`_.
-
-.. _`conference cube`: http://www.cubicweb.org/project/cubicweb-conference
-.. sourcecode:: python
-
-    class ConferenceActionsTC(CubicWebTC):
-
-        def setup_database(self):
-            with self.admin_access.repo_cnx() as cnx:
-                self.confeid = cnx.create_entity('Conference',
-                                                 title=u'my conf',
-                                                 url_id=u'conf',
-                                                 start_on=date(2010, 1, 27),
-                                                 end_on = date(2010, 1, 29),
-                                                 call_open=True,
-                                                 reverse_is_chair_at=chair,
-                                                 reverse_is_reviewer_at=reviewer).eid
-
-        def test_admin(self):
-            with self.admin_access.web_request() as req:
-                rset = req.find('Conference').one()
-                self.assertListEqual(self.pactions(req, rset),
-                                      [('workflow', workflow.WorkflowActions),
-                                       ('edit', confactions.ModifyAction),
-                                       ('managepermission', actions.ManagePermissionsAction),
-                                       ('addrelated', actions.AddRelatedActions),
-                                       ('delete', actions.DeleteAction),
-                                       ('generate_badge_action', badges.GenerateBadgeAction),
-                                       ('addtalkinconf', confactions.AddTalkInConferenceAction)
-                                       ])
-                self.assertListEqual(self.action_submenu(req, rset, 'addrelated'),
-                                      [(u'add Track in_conf Conference object',
-                                        u'http://testing.fr/cubicweb/add/Track'
-                                        u'?__linkto=in_conf%%3A%(conf)s%%3Asubject&'
-                                        u'__redirectpath=conference%%2Fconf&'
-                                        u'__redirectvid=' % {'conf': self.confeid}),
-                                       ])
-
-You just have to execute a rql query corresponding to the view you want to test,
-and to compare the result of
-:meth:`~cubicweb.devtools.testlib.CubicWebTC.pactions` with the list of actions
-that must be visible in the interface. This is a list of tuples. The first
-element is the action's `__regid__`, the second the action's class.
-
-To test actions in a submenu, you just have to test the result of
-:meth:`~cubicweb.devtools.testlib.CubicWebTC.action_submenu` method. The last
-parameter of the method is the action's category. The result is a list of
-tuples. The first element is the action's title, and the second element the
-action's url.
-
-
-.. _automatic_views_tests:
-
-Automatic views testing
------------------------
-
-This is done automatically with the :class:`cubicweb.devtools.testlib.AutomaticWebTest`
-class. At cube creation time, the mycube/test/test_mycube.py file
-contains such a test. The code here has to be uncommented to be
-usable, without further modification.
-
-The ``auto_populate`` method uses a smart algorithm to create
-pseudo-random data in the database, thus enabling the views to be
-invoked and tested.
-
-Depending on the schema, hooks and operations constraints, it is not
-always possible for the automatic auto_populate to proceed.
-
-It is possible of course to completely redefine auto_populate. A
-lighter solution is to give hints (fill some class attributes) about
-what entities and relations have to be skipped by the auto_populate
-mechanism. These are:
-
-* `no_auto_populate`, may contain a list of entity types to skip
-* `ignored_relations`, may contain a list of relation types to skip
-* `application_rql`, may contain a list of rql expressions that
-  auto_populate cannot guess by itself; these must yield resultsets
-  against which views may be selected.
-
-.. warning::
-
-  Take care to not let the imported `AutomaticWebTest` in your test module
-  namespace, else both your subclass *and* this parent class will be run.
-
-Cache heavy database setup
--------------------------------
-
-Some test suites require a complex setup of the database that takes
-seconds (or even minutes) to complete. Doing the whole setup for each
-individual test makes the whole run very slow. The ``CubicWebTC``
-class offer a simple way to prepare a specific database once for
-multiple tests. The `test_db_id` class attribute of your
-``CubicWebTC`` subclass must be set to a unique identifier and the
-:meth:`pre_setup_database` class method must build the cached content. As
-the :meth:`pre_setup_database` method is not garanteed to be called
-every time a test method is run, you must not set any class attribute
-to be used during test *there*. Databases for each `test_db_id` are
-automatically created if not already in cache. Clearing the cache is
-up to the user. Cache files are found in the :file:`data/database`
-subdirectory of your test directory.
-
-.. warning::
-
-  Take care to always have the same :meth:`pre_setup_database`
-  function for all classes with a given `test_db_id` otherwise your
-  tests will have unpredictable results depending on the first
-  encountered one.
-
-
-Testing on a real-life database
--------------------------------
-
-The ``CubicWebTC`` class uses the `cubicweb.devtools.ApptestConfiguration`
-configuration class to setup its testing environment (database driver,
-user password, application home, and so on). The `cubicweb.devtools`
-module also provides a `RealDatabaseConfiguration`
-class that will read a regular cubicweb sources file to fetch all
-this information but will also prevent the database to be initalized
-and reset between tests.
-
-For a test class to use a specific configuration, you have to set
-the `_config` class attribute on the class as in:
-
-.. sourcecode:: python
-
-    from cubicweb.devtools import RealDatabaseConfiguration
-    from cubicweb.devtools.testlib import CubicWebTC
-
-    class BlogRealDatabaseTC(CubicWebTC):
-        _config = RealDatabaseConfiguration('blog',
-                                            sourcefile='/path/to/realdb_sources')
-
-        def test_blog_rss(self):
-            with self.admin_access.web_request() as req:
-            rset = req.execute('Any B ORDERBY D DESC WHERE B is BlogEntry, '
-                               'B created_by U, U login "logilab", B creation_date D')
-            self.view('rss', rset, req=req)
-
-
-Testing with other cubes
-------------------------
-
-Sometimes a small component cannot be tested all by itself, so one
-needs to specify other cubes to be used as part of the the unit test
-suite. This is handled by the ``bootstrap_cubes`` file located under
-``mycube/test/data``. One example from the `preview` cube::
-
- card, file, preview
-
-The format is:
-
-* possibly several empy lines or lines starting with ``#`` (comment lines)
-* one line containing a comma-separated list of cube names.
-
-It is also possible to add a ``schema.py`` file in
-``mycube/test/data``, which will be used by the testing framework,
-therefore making new entity types and relations available to the
-tests. 
-
-Literate programming
---------------------
-
-CubicWeb provides some literate programming capabilities. The :ref:`cubicweb-ctl`
-`shell` command accepts different file formats. If your file ends with `.txt`
-or `.rst`, the file will be parsed by :mod:`doctest.testfile` with CubicWeb's
-:ref:`migration` API enabled in it.
-
-Create a `scenario.txt` file in the `test/` directory and fill with some content.
-Refer to the :mod:`doctest.testfile` `documentation`_.
-
-.. _documentation: http://docs.python.org/library/doctest.html
-
-Then, you can run it directly by::
-
-    $ cubicweb-ctl shell <cube_instance> test/scenario.txt
-
-When your scenario file is ready, put it in a new test case to be able to run
-it automatically.
-
-.. sourcecode:: python
-
-      from os.path import dirname, join
-      from logilab.common.testlib import unittest_main
-      from cubicweb.devtools.testlib import CubicWebTC
-
-      class AcceptanceTC(CubicWebTC):
-
-              def test_scenario(self):
-                      self.assertDocTestFile(join(dirname(__file__), 'scenario.txt'))
-
-      if __name__ == '__main__':
-              unittest_main()
-
-Skipping a scenario
-```````````````````
-
-If you want to set up initial conditions that you can't put in your unit test
-case, you have to use a :exc:`KeyboardInterrupt` exception only because of the
-way :mod:`doctest` module will catch all the exceptions internally.
-
-    >>> if condition_not_met:
-    ...     raise KeyboardInterrupt('please, check your fixture.')
-
-Passing paramaters
-``````````````````
-Using extra arguments to parametrize your scenario is possible by prepending them
-by double dashes.
-
-Please refer to the `cubicweb-ctl shell --help` usage.
-
-.. important::
-    Your scenario file must be utf-8 encoded.
-
-Test APIS
----------
-
-Using Pytest
-````````````
-
-The `pytest` utility (shipping with `logilab-common`_, which is a
-mandatory dependency of CubicWeb) extends the Python unittest
-functionality and is the preferred way to run the CubicWeb test
-suites. Bare unittests also work the usual way.
-
-.. _logilab-common: http://www.logilab.org/project/logilab-common
-
-To use it, you may:
-
-* just launch `pytest` in your cube to execute all tests (it will
-  discover them automatically)
-* launch `pytest unittest_foo.py` to execute one test file
-* launch `pytest unittest_foo.py bar` to execute all test methods and
-  all test cases whose name contains `bar`
-
-Additionally, the `-x` option tells pytest to exit at the first error
-or failure. The `-i` option tells pytest to drop into pdb whenever an
-exception occurs in a test.
-
-When the `-x` option has been used and the run stopped on a test, it
-is possible, after having fixed the test, to relaunch pytest with the
-`-R` option to tell it to start testing again from where it previously
-failed.
-
-Using the `TestCase` base class
-```````````````````````````````
-
-The base class of CubicWebTC is logilab.common.testlib.TestCase, which
-provides a lot of convenient assertion methods.
-
-.. autoclass:: logilab.common.testlib.TestCase
-   :members:
-
-CubicWebTC API
-``````````````
-.. autoclass:: cubicweb.devtools.testlib.CubicWebTC
-   :members:
-
-
-What you need to know about request and session
------------------------------------------------
-
-.. image:: ../images/request_session.png
-
-First, remember to think that some code run on a client side, some
-other on the repository side. More precisely:
-
-* client side: web interface, raw repoapi connection (cubicweb-ctl shell for
-  instance);
-
-* repository side: RQL query execution, that may trigger hooks and operation.
-
-The client interacts with the repository through a repoapi connection.
-
-
-.. note::
-
-   These distinctions are going to disappear in cubicweb 3.21 (if not
-   before).
-
-A repoapi connection is tied to a session in the repository. The connection and
-request objects are inaccessible from repository code / the session object is
-inaccessible from client code (theoretically at least).
-
-The web interface provides a request class.  That `request` object provides
-access to all cubicweb resources, eg:
-
-* the registry (which itself provides access to the schema and the
-  configuration);
-
-* an underlying repoapi connection (when using req.execute, you actually call the
-  repoapi);
-
-* other specific resources depending on the client type (url generation according
-  to base url, form parameters, etc.).
-
-
-A `session` provides an api similar to a request regarding RQL execution and
-access to global resources (registry and all), but also has the following
-responsibilities:
-
-* handle transaction data, that will live during the time of a single
-  transaction. This includes the database connections that will be used to
-  execute RQL queries.
-
-* handle persistent data that may be used across different (web) requests
-
-* security and hooks control (not possible through a request)
-
-
-The `_cw` attribute
-```````````````````
-The `_cw` attribute available on every application object provides access to all
-cubicweb resources, i.e.:
-
-- For code running on the client side (eg web interface view), `_cw` is a request
-  instance.
-
-- For code running on the repository side (hooks and operation), `_cw` is a
-  Connection or Session instance.
-
-
-Beware some views may be called with a session (e.g. notifications) or with a
-request.
-
-
-Request, session and transaction
-````````````````````````````````
-
-In the web interface, an HTTP request is handled by a single request, which will
-be thrown away once the response is sent.
-
-The web publisher handles the transaction:
-
-* commit / rollback is done automatically
-
-* you should not commit / rollback explicitly, except if you really
-  need it
-
-Let's detail the process:
-
-1. an incoming RQL query comes from a client to the web stack
-
-2. the web stack opens an authenticated database connection for the
-   request, which is associated to a user session
-
-3. the query is executed (through the repository connection)
-
-4. this query may trigger hooks. Hooks and operations may execute some rql queries
-   through `cnx.execute`.
-
-5. the repository gets the result of the query in 1. If it was a RQL read query,
-   the database connection is released. If it was a write query, the connection
-   is then tied to the session until the transaction is commited or rolled back.
-
-6. results are sent back to the client
-
-This implies several things:
-
-* when using a request, or code executed in hooks, this database
-  connection handling is totally transparent
-
-* however, take care when writing tests: you are usually faking /
-  testing both the server and the client side, so you have to decide
-  when to use RepoAccess.client_cnx or RepoAccess.repo_cnx. Ask
-  yourself "where will the code I want to test be running, client or
-  repository side?". The response is usually: use a repo (since the
-  "client connection" concept is going away in a couple of releases).
--- a/doc/book/en/devrepo/vreg.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-The Registry, selectors and application objects
-===============================================
-
-This chapter deals with some of the  core concepts of the |cubicweb| framework
-which make it different from other frameworks (and maybe not easy to
-grasp at a first glance). To be able to do advanced development with
-|cubicweb| you need a good understanding of what is explained below.
-
-This chapter goes deep into details. You don't have to remember them
-all but keep it in mind so you can go back there later.
-
-An overview of AppObjects, the VRegistry and Selectors is given in the
-:ref:`VRegistryIntro` chapter.
-
-.. autodocstring:: cubicweb.cwvreg
-.. autodocstring:: cubicweb.predicates
-.. automodule:: cubicweb.appobject
-
-Base predicates
----------------
-
-Predicates are scoring functions that are called by the registry to tell whenever
-an appobject can be selected in a given context. Predicates may be chained
-together using operators to build a selector. A selector is the glue that tie
-views to the data model or whatever input context. Using them appropriately is an
-essential part of the construction of well behaved cubes.
-
-Of course you may have to write your own set of predicates as your needs grows
-and you get familiar with the framework (see :ref:`CustomPredicates`).
-
-Here is a description of generic predicates provided by CubicWeb that should suit
-most of your needs.
-
-Bare predicates
-~~~~~~~~~~~~~~~
-Those predicates are somewhat dumb, which doesn't mean they're not (very) useful.
-
-.. autoclass:: cubicweb.appobject.yes
-.. autoclass:: cubicweb.predicates.match_kwargs
-.. autoclass:: cubicweb.predicates.appobject_selectable
-.. autoclass:: cubicweb.predicates.adaptable
-.. autoclass:: cubicweb.predicates.configuration_values
-
-
-Result set predicates
-~~~~~~~~~~~~~~~~~~~~~
-Those predicates are looking for a result set in the context ('rset' argument or
-the input context) and match or not according to its shape. Some of these
-predicates have different behaviour if a particular cell of the result set is
-specified using 'row' and 'col' arguments of the input context or not.
-
-.. autoclass:: cubicweb.predicates.none_rset
-.. autoclass:: cubicweb.predicates.any_rset
-.. autoclass:: cubicweb.predicates.nonempty_rset
-.. autoclass:: cubicweb.predicates.empty_rset
-.. autoclass:: cubicweb.predicates.one_line_rset
-.. autoclass:: cubicweb.predicates.multi_lines_rset
-.. autoclass:: cubicweb.predicates.multi_columns_rset
-.. autoclass:: cubicweb.predicates.paginated_rset
-.. autoclass:: cubicweb.predicates.sorted_rset
-.. autoclass:: cubicweb.predicates.one_etype_rset
-.. autoclass:: cubicweb.predicates.multi_etypes_rset
-
-
-Entity predicates
-~~~~~~~~~~~~~~~~~
-Those predicates are looking for either an `entity` argument in the input context,
-or entity found in the result set ('rset' argument or the input context) and
-match or not according to entity's (instance or class) properties.
-
-.. autoclass:: cubicweb.predicates.non_final_entity
-.. autoclass:: cubicweb.predicates.is_instance
-.. autoclass:: cubicweb.predicates.score_entity
-.. autoclass:: cubicweb.predicates.rql_condition
-.. autoclass:: cubicweb.predicates.relation_possible
-.. autoclass:: cubicweb.predicates.partial_relation_possible
-.. autoclass:: cubicweb.predicates.has_related_entities
-.. autoclass:: cubicweb.predicates.partial_has_related_entities
-.. autoclass:: cubicweb.predicates.has_permission
-.. autoclass:: cubicweb.predicates.has_add_permission
-.. autoclass:: cubicweb.predicates.has_mimetype
-.. autoclass:: cubicweb.predicates.is_in_state
-.. autofunction:: cubicweb.predicates.on_fire_transition
-
-
-Logged user predicates
-~~~~~~~~~~~~~~~~~~~~~~
-Those predicates are looking for properties of the user issuing the request.
-
-.. autoclass:: cubicweb.predicates.match_user_groups
-
-
-Web request predicates
-~~~~~~~~~~~~~~~~~~~~~~
-Those predicates are looking for properties of *web* request, they can not be
-used on the data repository side.
-
-.. autoclass:: cubicweb.predicates.no_cnx
-.. autoclass:: cubicweb.predicates.anonymous_user
-.. autoclass:: cubicweb.predicates.authenticated_user
-.. autoclass:: cubicweb.predicates.match_form_params
-.. autoclass:: cubicweb.predicates.match_search_state
-.. autoclass:: cubicweb.predicates.match_context_prop
-.. autoclass:: cubicweb.predicates.match_context
-.. autoclass:: cubicweb.predicates.match_view
-.. autoclass:: cubicweb.predicates.primary_view
-.. autoclass:: cubicweb.predicates.contextual
-.. autoclass:: cubicweb.predicates.specified_etype_implements
-.. autoclass:: cubicweb.predicates.attribute_edited
-.. autoclass:: cubicweb.predicates.match_transition
-
-
-Other predicates
-~~~~~~~~~~~~~~~~
-.. autoclass:: cubicweb.predicates.match_exception
-.. autoclass:: cubicweb.predicates.debug_mode
-
-You'll also find some other (very) specific predicates hidden in other modules
-than :mod:`cubicweb.predicates`.
--- a/doc/book/en/devweb/ajax.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-.. _ajax:
-
-Ajax
-----
-
-CubicWeb provides a few helpers to facilitate *javascript <-> python* communications.
-
-You can, for instance, register some python functions that will become
-callable from javascript through ajax calls. All the ajax URLs are handled
-by the :class:`cubicweb.web.views.ajaxcontroller.AjaxController` controller.
-
-.. automodule:: cubicweb.web.views.ajaxcontroller
--- a/doc/book/en/devweb/controllers.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-.. _controllers:
-
-Controllers
------------
-
-Overview
-++++++++
-
-Controllers are responsible for taking action upon user requests
-(loosely following the terminology of the MVC meta pattern).
-
-The following controllers are provided out-of-the box in CubicWeb. We
-list them by category. They are all defined in
-(:mod:`cubicweb.web.views.basecontrollers`).
-
-`Browsing`:
-
-* the View controller is associated with most browsing actions within a
-  CubicWeb application: it always instantiates a
-  :ref:`the_main_template_layout` and lets the ResultSet/Views dispatch system
-  build up the whole content; it handles :exc:`ObjectNotFound` and
-  :exc:`NoSelectableObject` errors that may bubble up to its entry point, in an
-  end-user-friendly way (but other programming errors will slip through)
-
-* the JSonpController is a wrapper around the ``ViewController`` that
-  provides jsonp_ services. Padding can be specified with the
-  ``callback`` request parameter. Only *jsonexport* / *ejsonexport*
-  views can be used. If another ``vid`` is specified, it will be
-  ignored and replaced by *jsonexport*. Request is anonymized
-  to avoid returning sensitive data and reduce the risks of CSRF attacks;
-
-* the Login/Logout controllers make effective user login or logout
-  requests
-
-
-.. _jsonp: http://en.wikipedia.org/wiki/JSONP
-
-`Edition`:
-
-* the Edit controller (see :ref:`edit_controller`) handles CRUD
-  operations in response to a form being submitted; it works in close
-  association with the Forms, to which it delegates some of the work
-
-* the ``Form validator controller`` provides form validation from Ajax
-  context, using the Edit controller, to implement the classic form
-  handling loop (user edits, hits `submit/apply`, validation occurs
-  server-side by way of the Form validator controller, and the UI is
-  decorated with failure information, either global or per-field ,
-  until it is valid)
-
-`Other`:
-
-* the ``SendMail controller`` (web/views/basecontrollers.py) is reponsible
-  for outgoing email notifications
-
-* the MailBugReport controller (web/views/basecontrollers.py) allows
-  to quickly have a `reportbug` feature in one's application
-
-* the :class:`cubicweb.web.views.ajaxcontroller.AjaxController`
-  (:mod:`cubicweb.web.views.ajaxcontroller`) provides
-  services for Ajax calls, typically using JSON as a serialization format
-  for input, and sometimes using either JSON or XML for output. See
-  :ref:`ajax` chapter for more information.
-
-
-Registration
-++++++++++++
-
-All controllers (should) live in the 'controllers' namespace within
-the global registry.
-
-Concrete controllers
-++++++++++++++++++++
-
-Most API details should be resolved by source code inspection, as the
-various controllers have differing goals. See for instance the
-:ref:`edit_controller` chapter.
-
-:mod:`cubicweb.web.controller` contains the top-level abstract
-Controller class and its unimplemented entry point
-`publish(rset=None)` method.
-
-A handful of helpers are also provided there:
-
-* process_rql builds a result set from an rql query typically issued
-  from the browser (and available through _cw.form['rql'])
-
-* validate_cache will force cache validation handling with respect to
-  the HTTP Cache directives (that were typically originally issued
-  from a previous server -> client response); concrete Controller
-  implementations dealing with HTTP (thus, for instance, not the
-  SendMail controller) may very well call this in their publication
-  process.
--- a/doc/book/en/devweb/css.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-CSS Stylesheet
----------------
-Conventions
-~~~~~~~~~~~
-
-.. XXX external_resources variable
-..    naming convention
-..    request.add_css
-
-
-Extending / overriding existing styles
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-We cannot modify the order in which the application is reading the CSS. In
-the case we want to create new CSS style, the best is to define it a in a new
-CSS located under ``myapp/data/`` and use those new styles while writing
-customized views and templates.
-
-If you want to modify an existing CSS styling property, you will have to use
-``!important`` declaration to override the existing property. The application
-apply a higher priority on the default CSS and you can not change that.
-Customized CSS will not be read first.
-
-
-CubicWeb stylesheets
-~~~~~~~~~~~~~~~~~~~~
-
-.. XXX explain diffenrent files and main classes
--- a/doc/book/en/devweb/edition/dissection.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,338 +0,0 @@
-
-.. _form_dissection:
-
-Dissection of an entity form
-----------------------------
-
-This is done (again) with a vanilla instance of the `tracker`_
-cube. We will populate the database with a bunch of entities and see
-what kind of job the automatic entity form does.
-
-.. _`tracker`: http://www.cubicweb.org/project/cubicweb-tracker
-
-Populating the database
-~~~~~~~~~~~~~~~~~~~~~~~
-
-We should start by setting up a bit of context: a project with two
-unpublished versions, and a ticket linked to the project and the first
-version.
-
-.. sourcecode:: python
-
- >>> p = rql('INSERT Project P: P name "cubicweb"')
- >>> for num in ('0.1.0', '0.2.0'):
- ...  rql('INSERT Version V: V num "%s", V version_of P WHERE P eid %%(p)s' % num, {'p': p[0][0]})
- ...
- <resultset 'INSERT Version V: V num "0.1.0", V version_of P WHERE P eid %(p)s' (1 rows): [765L] (('Version',))>
- <resultset 'INSERT Version V: V num "0.2.0", V version_of P WHERE P eid %(p)s' (1 rows): [766L] (('Version',))>
- >>> t = rql('INSERT Ticket T: T title "let us write more doc", T done_in V, '
-             'T concerns P WHERE V num "0.1.0"', P eid %(p)s', {'p': p[0][0]})
- >>> commit()
-
-Now let's see what the edition form builds for us.
-
-.. sourcecode:: python
-
- >>> cnx.use_web_compatible_requests('http://fakeurl.com')
- >>> req = cnx.request()
- >>> form = req.vreg['forms'].select('edition', req, rset=rql('Ticket T'))
- >>> html = form.render()
-
-.. note::
-
-  In order to play interactively with web side application objects, we have to
-  cheat a bit to have request object that will looks like HTTP request object, by
-  calling :meth:`use_web_compatible_requests()` on the connection.
-
-This creates an automatic entity form. The ``.render()`` call yields
-an html (unicode) string. The html output is shown below (with
-internal fieldset omitted).
-
-Looking at the html output
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The form enveloppe
-''''''''''''''''''
-
-.. sourcecode:: html
-
- <div class="iformTitle"><span>main informations</span></div>
- <div class="formBody">
-  <form action="http://crater:9999/validateform" method="post" enctype="application/x-www-form-urlencoded"
-        id="entityForm" onsubmit="return freezeFormButtons(&#39;entityForm&#39;);"
-        class="entityForm" cubicweb:target="eformframe">
-    <div id="progress">validating...</div>
-    <fieldset>
-      <input name="__form_id" type="hidden" value="edition" />
-      <input name="__errorurl" type="hidden" value="http://perdu.com#entityForm" />
-      <input name="__domid" type="hidden" value="entityForm" />
-      <input name="__type:763" type="hidden" value="Ticket" />
-      <input name="eid" type="hidden" value="763" />
-      <input name="__maineid" type="hidden" value="763" />
-      <input name="_cw_edited_fields:763" type="hidden"
-             value="concerns-subject,done_in-subject,priority-subject,type-subject,title-subject,description-subject,__type,_cw_generic_field" />
-      ...
-    </fieldset>
-   </form>
- </div>
-
-The main fieldset encloses a set of hidden fields containing various
-metadata, that will be used by the `edit controller` to process it
-back correctly.
-
-The `freezeFormButtons(...)` javascript callback defined on the
-``onlick`` event of the form element prevents accidental multiple
-clicks in a row.
-
-The ``action`` of the form is mapped to the ``validateform`` controller
-(situated in :mod:`cubicweb.web.views.basecontrollers`).
-
-A full explanation of the validation loop is given in
-:ref:`validation_process`.
-
-.. _attributes_section:
-
-The attributes section
-''''''''''''''''''''''
-
-We can have a look at some of the inner nodes of the form. Some fields
-are omitted as they are redundant for our purposes.
-
-.. sourcecode:: html
-
-      <fieldset class="default">
-        <table class="attributeForm">
-          <tr class="title_subject_row">
-            <th class="labelCol"><label class="required" for="title-subject:763">title</label></th>
-            <td>
-              <input id="title-subject:763" maxlength="128" name="title-subject:763" size="45"
-                     tabindex="1" type="text" value="let us write more doc" />
-            </td>
-          </tr>
-          ... (description field omitted) ...
-          <tr class="priority_subject_row">
-            <th class="labelCol"><label class="required" for="priority-subject:763">priority</label></th>
-            <td>
-              <select id="priority-subject:763" name="priority-subject:763" size="1" tabindex="4">
-                <option value="important">important</option>
-                <option selected="selected" value="normal">normal</option>
-                <option value="minor">minor</option>
-              </select>
-              <div class="helper">importance</div>
-            </td>
-          </tr>
-          ... (type field omitted) ...
-          <tr class="concerns_subject_row">
-            <th class="labelCol"><label class="required" for="concerns-subject:763">concerns</label></th>
-            <td>
-              <select id="concerns-subject:763" name="concerns-subject:763" size="1" tabindex="6">
-                <option selected="selected" value="760">Foo</option>
-              </select>
-            </td>
-          </tr>
-          <tr class="done_in_subject_row">
-            <th class="labelCol"><label for="done_in-subject:763">done in</label></th>
-            <td>
-              <select id="done_in-subject:763" name="done_in-subject:763" size="1" tabindex="7">
-                <option value="__cubicweb_internal_field__"></option>
-                <option selected="selected" value="761">Foo 0.1.0</option>
-                <option value="762">Foo 0.2.0</option>
-              </select>
-              <div class="helper">version in which this ticket will be / has been  done</div>
-            </td>
-          </tr>
-        </table>
-      </fieldset>
-
-
-Note that the whole form layout has been computed by the form
-renderer. It is the renderer which produces the table
-structure. Otherwise, the fields html structure is emitted by their
-associated widget.
-
-While it is called the `attributes` section of the form, it actually
-contains attributes and *mandatory relations*. For each field, we
-observe:
-
-* a dedicated row with a specific class, such as ``title_subject_row``
-  (responsability of the form renderer)
-
-* an html widget (input, select, ...) with:
-
-  * an id built from the ``rtype-role:eid`` pattern
-
-  * a name built from the same pattern
-
-  * possible values or preselected options
-
-The relations section
-'''''''''''''''''''''
-
-.. sourcecode:: html
-
-      <fieldset class="This ticket :">
-        <legend>This ticket :</legend>
-        <table class="attributeForm">
-          <tr class="_cw_generic_field_None_row">
-            <td colspan="2">
-              <table id="relatedEntities">
-                <tr><th>&#160;</th><td>&#160;</td></tr>
-                <tr id="relationSelectorRow_763" class="separator">
-                  <th class="labelCol">
-                    <select id="relationSelector_763" tabindex="8"
-                            onchange="javascript:showMatchingSelect(this.options[this.selectedIndex].value,763);">
-                      <option value="">select a relation</option>
-                      <option value="appeared_in_subject">appeared in</option>
-                      <option value="custom_workflow_subject">custom workflow</option>
-                      <option value="depends_on_object">dependency of</option>
-                      <option value="depends_on_subject">depends on</option>
-                      <option value="identical_to_subject">identical to</option>
-                      <option value="see_also_subject">see also</option>
-                    </select>
-                  </th>
-                  <td id="unrelatedDivs_763"></td>
-                </tr>
-              </table>
-            </td>
-          </tr>
-        </table>
-      </fieldset>
-
-The optional relations are grouped into a drop-down combo
-box. Selection of an item triggers a javascript function which will:
-
-* show already related entities in the div of id `relatedentities`
-  using a two-colown layout, with an action to allow deletion of
-  individual relations (there are none in this example)
-
-* provide a relation selector in the div of id `relationSelector_EID`
-  to allow the user to set up relations and trigger dynamic action on
-  the last div
-
-* fill the div of id `unrelatedDivs_EID` with a dynamically computed
-  selection widget allowing direct selection of an unrelated (but
-  relatable) entity or a switch towards the `search mode` of
-  |cubicweb| which allows full browsing and selection of an entity
-  using a dedicated action situated in the left column boxes.
-
-
-The buttons zone
-''''''''''''''''
-
-Finally comes the buttons zone.
-
-.. sourcecode:: html
-
-      <table width="100%">
-        <tbody>
-          <tr>
-            <td align="center">
-              <button class="validateButton" tabindex="9" type="submit" value="validate">
-                <img alt="OK_ICON" src="http://myapp/datafd8b5d92771209ede1018a8d5da46a37/ok.png" />
-                validate
-              </button>
-            </td>
-            <td style="align: right; width: 50%;">
-              <button class="validateButton"
-                      onclick="postForm(&#39;__action_apply&#39;, &#39;button_apply&#39;, &#39;entityForm&#39;)"
-                      tabindex="10" type="button" value="apply">
-                <img alt="APPLY_ICON" src="http://myapp/datafd8b5d92771209ede1018a8d5da46a37/plus.png" />
-                apply
-              </button>
-              <button class="validateButton"
-                      onclick="postForm(&#39;__action_cancel&#39;, &#39;button_cancel&#39;, &#39;entityForm&#39;)"
-                      tabindex="11" type="button" value="cancel">
-                <img alt="CANCEL_ICON" src="http://myapp/datafd8b5d92771209ede1018a8d5da46a37/cancel.png" />
-                cancel
-              </button>
-            </td>
-          </tr>
-        </tbody>
-      </table>
-
-The most notable artifacts here are the ``postForm(...)`` calls
-defined on click events on these buttons. This function basically
-submits the form.
-
-.. _validation_process:
-
-The form validation process
----------------------------
-
-Preparation
-~~~~~~~~~~~
-
-After the (html) document is loaded, the ``setFormsTarget`` javascript
-function dynamically transforms the DOM as follows. For all forms of
-the DOM, it:
-
-* sets the ``target`` attribute where there is a ``cubicweb:target``
-  attribute (with the same value)
-
-* appends an empty `IFRAME` element at the end
-
-Let us have a look again at the form element. We have omitted some
-irrelevant attributes.
-
-.. sourcecode::html
-
-  <form action="http://crater:9999/validateform" method="post"
-        enctype="application/x-www-form-urlencoded"
-        id="entityForm" cubicweb:target="eformframe"
-        target="eformframe">
-  ...
-  </form>
-
-Validation loop
-~~~~~~~~~~~~~~~
-
-On form submission, the form.action is invoked. Basically, the
-``validateform`` controller is called and its output lands in the
-specified ``target``, the iframe that was previously prepared.
-
-Hence, the main page is not replaced, only the iframe contents. The
-``validateform`` controller only outputs a tiny javascript fragment
-which is then immediately executed.
-
-.. sourcecode:: html
-
- <iframe width="0px" height="0px" name="eformframe" id="eformframe" src="javascript: void(0)">
-   <script type="text/javascript">
-     window.parent.handleFormValidationResponse('entityForm', null, null,
-                                                [false, [2164, {"name-subject": "required field"}], null],
-                                                null);
-   </script>
- </iframe>
-
-The ``window.parent`` part ensures the javascript function is called
-on the right context (that is: the form element). We will describe its
-parameters:
-
-* first comes the form id (`entityForm`)
-
-* then two optional callbacks for the success and failure case
-
-* an array containing:
-
-  * a boolean which indicates status (success or failure), and then, on error:
-
-    * an array structured as ``[eid, {'rtype-role': 'error msg'}, ...]``
-
-  * on success:
-
-    * an url (string) representing the next thing to jump to
-
-Given the array structure described above, it is quite simple to
-manipulate the DOM to show the errors at appropriate places.
-
-Explanation
-~~~~~~~~~~~
-
-This mecanism may seem a bit overcomplicated but we have to deal with
-two realities:
-
-* in the (strict) XHTML world, there are no iframes (hence the dynamic
-  inclusion, tolerated by Firefox)
-
-* no (or not all) browser(s) support file input field handling through
-  ajax.
--- a/doc/book/en/devweb/edition/editcontroller.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,113 +0,0 @@
-.. _edit_controller:
-
-The `edit controller`
----------------------
-
-It can be found in (:mod:`cubicweb.web.views.editcontroller`). This
-controller processes data received from an html form to create or
-update entities.
-
-Edition handling
-~~~~~~~~~~~~~~~~
-
-The parameters related to entities to edit are specified as follows
-(first seen in :ref:`attributes_section`)::
-
-  <rtype-role>:<entity eid>
-
-where entity eid could be a letter in case of an entity to create. We
-name those parameters as *qualified*.
-
-* Retrieval of entities to edit is done by using the forms parameters
-  `eid` and `__type`
-
-* For all the attributes and the relations of an entity to edit
-  (attributes and relations are handled a bit differently but these
-  details are not much relevant here) :
-
-   * using the ``rtype``, ``role`` and ``__type`` information, fetch
-     an appropriate field instance
-
-   * check if the field has been modified (if not, proceed to the next
-     relation)
-
-   * build an rql expression to update the entity
-
-At the end, all rql expressions are executed.
-
-* For each entity to edit:
-
-   * if a qualified parameter `__linkto` is specified, its value has
-     to be a string (or a list of strings) such as: ::
-
-        <relation type>:<eids>:<target>
-
-     where <target> is either `subject` or `object` and each eid could
-     be separated from the others by a `_`. Target specifies if the
-     *edited entity* is subject or object of the relation and each
-     relation specified will be inserted.
-
-    * if a qualified parameter `__clone_eid` is specified for an entity, the
-      relations of the specified entity passed as value of this parameter are
-      copied on the edited entity.
-
-    * if a qualified parameter `__delete` is specified, its value must be
-      a string or a list of string such as follows: ::
-
-          <subjects eids>:<relation type>:<objects eids>
-
-      where each eid subject or object can be seperated from the other
-      by `_`. Each specified relation will be deleted.
-
-
-* If no entity is edited but the form contains the parameters `__linkto`
-  and `eid`, this one is interpreted by using the value specified for `eid`
-  to designate the entity on which to add the relations.
-
-.. note::
-
-   * if the parameter `__action_delete` is found, all the entities specified
-     as to be edited will be deleted.
-
-   * if the parameter `__action_cancel` is found, no action is completed.
-
-   * if the parameter `__action_apply` is found, the editing is
-     applied normally but the redirection is done on the form (see
-     :ref:`RedirectionControl`).
-
-   * if no entity is found to be edited and if there is no parameter
-     `__action_delete`, `__action_cancel`, `__linkto`, `__delete` or
-     `__insert`, an error is raised.
-
-   * using the parameter `__message` in the form will allow to use its value
-     as a message to provide the user once the editing is completed.
-
-
-.. _RedirectionControl:
-
-Redirection control
-~~~~~~~~~~~~~~~~~~~
-Once editing is completed, there is still an issue left: where should we go
-now? If nothing is specified, the controller will do his job but it does not
-mean we will be happy with the result. We can control that by using the
-following parameters:
-
-* `__redirectpath`: path of the URL (relative to the root URL of the site,
-  no form parameters
-
-* `__redirectparams`: forms parameters to add to the path
-
-* `__redirectrql`: redirection RQL request
-
-* `__redirectvid`: redirection view identifier
-
-* `__errorurl`: initial form URL, used for redirecting in case a validation
-  error is raised during editing. If this one is not specified, an error page
-  is displayed instead of going back to the form (which is, if necessary,
-  responsible for displaying the errors)
-
-* `__form_id`: initial view form identifier, used if `__action_apply` is
-  found
-
-In general we use either `__redirectpath` and `__redirectparams` or
-`__redirectrql` and `__redirectvid`.
--- a/doc/book/en/devweb/edition/examples.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,232 +0,0 @@
-Examples
---------
-
-(Automatic) Entity form
-~~~~~~~~~~~~~~~~~~~~~~~
-
-Looking at some cubes available on the `cubicweb forge`_ we find some
-with form manipulation. The following example comes from the the
-`conference`_ cube. It extends the change state form for the case
-where a ``Talk`` entity is getting into ``submitted`` state. The goal
-is to select reviewers for the submitted talk.
-
-.. _`cubicweb forge`: http://www.cubicweb.org/view?rql=Any+P+ORDERBY+N+WHERE+P+name+LIKE+%22cubicweb-%25%22%2C+P+is+Project%2C+P+name+N
-.. _`conference`: http://www.cubicweb.org/project/cubicweb-conference
-
-.. sourcecode:: python
-
- from cubicweb.web import formfields as ff, formwidgets as fwdgs
- class SendToReviewerStatusChangeView(ChangeStateFormView):
-     __select__ = (ChangeStateFormView.__select__ &
-                   is_instance('Talk') &
-                   rql_condition('X in_state S, S name "submitted"'))
-
-     def get_form(self, entity, transition, **kwargs):
-         form = super(SendToReviewerStatusChangeView, self).get_form(entity, transition, **kwargs)
-         relation = ff.RelationField(name='reviews', role='object',
-                                     eidparam=True,
-                                     label=_('select reviewers'),
-                                     widget=fwdgs.Select(multiple=True))
-         form.append_field(relation)
-         return form
-
-Simple extension of a form can be done from within the `FormView`
-wrapping the form. FormView instances have a handy ``get_form`` method
-that returns the form to be rendered. Here we add a ``RelationField``
-to the base state change form.
-
-One notable point is the ``eidparam`` argument: it tells both the
-field and the ``edit controller`` that the field is linked to a
-specific entity.
-
-It is hence entirely possible to add ad-hoc fields that will be
-processed by some specialized instance of the edit controller.
-
-
-Ad-hoc fields form
-~~~~~~~~~~~~~~~~~~
-
-We want to define a form doing something else than editing an entity. The idea is
-to propose a form to send an email to entities in a resultset which implements
-:class:`IEmailable`.  Let's take a simplified version of what you'll find in
-:mod:`cubicweb.web.views.massmailing`.
-
-Here is the source code:
-
-.. sourcecode:: python
-
-    def sender_value(form, field):
-	return '%s <%s>' % (form._cw.user.dc_title(), form._cw.user.get_email())
-
-    def recipient_choices(form, field):
-	return [(e.get_email(), e.eid)
-                 for e in form.cw_rset.entities()
-		 if e.get_email()]
-
-    def recipient_value(form, field):
-	return [e.eid for e in form.cw_rset.entities()
-                if e.get_email()]
-
-    class MassMailingForm(forms.FieldsForm):
-	__regid__ = 'massmailing'
-
-	needs_js = ('cubicweb.widgets.js',)
-	domid = 'sendmail'
-	action = 'sendmail'
-
-	sender = ff.StringField(widget=TextInput({'disabled': 'disabled'}),
-				label=_('From:'),
-				value=sender_value)
-
-	recipient = ff.StringField(widget=CheckBox(),
-	                           label=_('Recipients:'),
-				   choices=recipient_choices,
-				   value=recipients_value)
-
-	subject = ff.StringField(label=_('Subject:'), max_length=256)
-
-	mailbody = ff.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')]
-
-Let's detail what's going on up there. Our form will hold four fields:
-
-* a sender field, which is disabled and will simply contains the user's name and
-  email
-
-* a recipients field, which will be displayed as a list of users in the context
-  result set with checkboxes so user can still choose who will receive his mailing
-  by checking or not the checkboxes. By default all of them will be checked since
-  field's value return a list containing same eids as those returned by the
-  vocabulary function.
-
-* a subject field, limited to 256 characters (hence we know a
-  :class:`~cubicweb.web.formwidgets.TextInput` will be used, as explained in
-  :class:`~cubicweb.web.formfields.StringField`)
-
-* a mailbody field. This field use an ajax widget, defined in `cubicweb.widgets.js`,
-  and whose definition won't be shown here. Notice though that we tell this form
-  need this javascript file by using `needs_js`
-
-Last but not least, we add two buttons control: one to post the form using
-javascript (`$('#sendmail')` being the jQuery call to get the element with DOM id
-set to 'sendmail', which is our form DOM id as specified by its `domid`
-attribute), another to cancel the form which will go back to the previous page
-using another javascript call. Also we specify an image to use as button icon as a
-resource identifier (see :ref:`uiprops`) given as last argument to
-:class:`cubicweb.web.formwidgets.ImgButton`.
-
-To see this form, we still have to wrap it in a view. This is pretty simple:
-
-.. sourcecode:: python
-
-    class MassMailingFormView(form.FormViewMixIn, EntityView):
-	__regid__ = 'massmailing'
-	__select__ = is_instance(IEmailable) & authenticated_user()
-
-	def call(self):
-	    form = self._cw.vreg['forms'].select('massmailing', self._cw,
-	                                         rset=self.cw_rset)
-	    form.render(w=self.w)
-
-As you see, we simply define a view with proper selector so it only apply to a
-result set containing :class:`IEmailable` entities, and so that only users in the
-managers or users group can use it. Then in the `call()` method for this view we
-simply select the above form and call its `.render()` method with our output
-stream as argument.
-
-When this form is submitted, a controller with id 'sendmail' will be called (as
-specified using `action`). This controller will be responsible to actually send
-the mail to specified recipients.
-
-Here is what it looks like:
-
-.. sourcecode:: python
-
-   class SendMailController(Controller):
-       __regid__ = 'sendmail'
-       __select__ = (authenticated_user() &
-                     match_form_params('recipient', 'mailbody', 'subject'))
-
-       def publish(self, rset=None):
-           body = self._cw.form['mailbody']
-           subject = self._cw.form['subject']
-           eids = self._cw.form['recipient']
-           # eids may be a string if only one recipient was specified
-           if isinstance(eids, basestring):
-               rset = self._cw.execute('Any X WHERE X eid %(x)s', {'x': eids})
-           else:
-               rset = self._cw.execute('Any X WHERE X eid in (%s)' % (','.join(eids)))
-           recipients = list(rset.entities())
-           msg = format_mail({'email' : self._cw.user.get_email(),
-                              'name' : self._cw.user.dc_title()},
-                             recipients, body, subject)
-           if not self._cw.vreg.config.sendmails([(msg, recipients)]):
-               msg = self._cw._('could not connect to the SMTP server')
-           else:
-               msg = self._cw._('emails successfully sent')
-           raise Redirect(self._cw.build_url(__message=msg))
-
-
-The entry point of a controller is the publish method. In that case we simply get
-back post values in request's `form` attribute, get user instances according
-to eids found in the 'recipient' form value, and send email after calling
-:func:`format_mail` to get a proper email message. If we can't send email or
-if we successfully sent email, we redirect to the index page with proper message
-to inform the user.
-
-Also notice that our controller has a selector that deny access to it
-to anonymous users (we don't want our instance to be used as a spam
-relay), but also checks if the expected parameters are specified in
-forms. That avoids later defensive programming (though it's not enough
-to handle all possible error cases).
-
-To conclude our example, suppose we wish a different form layout and that existent
-renderers are not satisfying (we would check that first of course :). We would then
-have to define our own renderer:
-
-.. sourcecode:: python
-
-    class MassMailingFormRenderer(formrenderers.FormRenderer):
-        __regid__ = 'massmailing'
-
-        def _render_fields(self, fields, w, form):
-            w(u'<table class="headersform">')
-            for field in fields:
-                if field.name == 'mailbody':
-                    w(u'</table>')
-                    w(u'<div id="toolbar">')
-                    w(u'<ul>')
-                    for button in form.form_buttons:
-                        w(u'<li>%s</li>' % button.render(form))
-                    w(u'</ul>')
-                    w(u'</div>')
-                    w(u'<div>')
-                    w(field.render(form, self))
-                    w(u'</div>')
-                else:
-                    w(u'<tr>')
-                    w(u'<td class="hlabel">%s</td>' %
-                      self.render_label(form, field))
-                    w(u'<td class="hvalue">')
-                    w(field.render(form, self))
-                    w(u'</td></tr>')
-
-        def render_buttons(self, w, form):
-            pass
-
-We simply override the `_render_fields` and `render_buttons` method of the base form renderer
-to arrange fields as we desire it: here we'll have first a two columns table with label and
-value of the sender, recipients and subject field (form order respected), then form controls,
-then a div containing the textarea for the email's content.
-
-To bind this renderer to our form, we should add to our form definition above:
-
-.. sourcecode:: python
-
-    form_renderer_id = 'massmailing'
-
--- a/doc/book/en/devweb/edition/form.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,377 +0,0 @@
-.. _webform:
-
-HTML form construction
-----------------------
-
-CubicWeb provides the somewhat usual form / field / widget / renderer abstraction
-to provide generic building blocks which will greatly help you in building forms
-properly integrated with CubicWeb (coherent display, error handling, etc...),
-while keeping things as flexible as possible.
-
-A ``form`` basically only holds a set of ``fields``, and has te be bound to a
-``renderer`` which is responsible to layout them. Each field is bound to a
-``widget`` that will be used to fill in value(s) for that field (at form
-generation time) and 'decode' (fetch and give a proper Python type to) values
-sent back by the browser.
-
-The ``field`` should be used according to the type of what you want to edit.
-E.g. if you want to edit some date, you'll have to use the
-:class:`cubicweb.web.formfields.DateField`. Then you can choose among multiple
-widgets to edit it, for instance :class:`cubicweb.web.formwidgets.TextInput` (a
-bare text field), :class:`~cubicweb.web.formwidgets.DateTimePicker` (a simple
-calendar) or even :class:`~cubicweb.web.formwidgets.JQueryDatePicker` (the JQuery
-calendar).  You can of course also write your own widget.
-
-Exploring the available forms
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-A small excursion into a |cubicweb| shell is the quickest way to
-discover available forms (or application objects in general).
-
-.. sourcecode:: python
-
- >>> from pprint import pprint
- >>> pprint( session.vreg['forms'] )
- {'base': [<class 'cubicweb.web.views.forms.FieldsForm'>,
-           <class 'cubicweb.web.views.forms.EntityFieldsForm'>],
-  'changestate': [<class 'cubicweb.web.views.workflow.ChangeStateForm'>,
-                  <class 'cubes.tracker.views.forms.VersionChangeStateForm'>],
-  'composite': [<class 'cubicweb.web.views.forms.CompositeForm'>,
-                <class 'cubicweb.web.views.forms.CompositeEntityForm'>],
-  'deleteconf': [<class 'cubicweb.web.views.editforms.DeleteConfForm'>],
-  'edition': [<class 'cubicweb.web.views.autoform.AutomaticEntityForm'>,
-              <class 'cubicweb.web.views.workflow.TransitionEditionForm'>,
-              <class 'cubicweb.web.views.workflow.StateEditionForm'>],
-  'logform': [<class 'cubicweb.web.views.basetemplates.LogForm'>],
-  'massmailing': [<class 'cubicweb.web.views.massmailing.MassMailingForm'>],
-  'muledit': [<class 'cubicweb.web.views.editforms.TableEditForm'>],
-  'sparql': [<class 'cubicweb.web.views.sparql.SparqlForm'>]}
-
-
-The two most important form families here (for all practical purposes) are `base`
-and `edition`. Most of the time one wants alterations of the
-:class:`AutomaticEntityForm` to generate custom forms to handle edition of an
-entity.
-
-The Automatic Entity Form
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. automodule:: cubicweb.web.views.autoform
-
-Anatomy of a choices function
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Let's have a look at the `ticket_done_in_choices` function given to
-the `choices` parameter of the relation tag that is applied to the
-('Ticket', 'done_in', '*') relation definition, as it is both typical
-and sophisticated enough. This is a code snippet from the `tracker`_
-cube.
-
-.. _`tracker`: http://www.cubicweb.org/project/cubicweb-tracker
-
-The ``Ticket`` entity type can be related to a ``Project`` and a
-``Version``, respectively through the ``concerns`` and ``done_in``
-relations. When a user is about to edit a ticket, we want to fill the
-combo box for the ``done_in`` relation with values pertinent with
-respect to the context. The important context here is:
-
-* creation or modification (we cannot fetch values the same way in
-  either case)
-
-* ``__linkto`` url parameter given in a creation context
-
-.. sourcecode:: python
-
-    from cubicweb.web import formfields
-
-    def ticket_done_in_choices(form, field):
-        entity = form.edited_entity
-        # first see if its specified by __linkto form parameters
-        linkedto = form.linked_to[('done_in', 'subject')]
-        if linkedto:
-            return linkedto
-        # it isn't, get initial values
-        vocab = field.relvoc_init(form)
-        veid = None
-        # try to fetch the (already or pending) related version and project
-        if not entity.has_eid():
-            peids = form.linked_to[('concerns', 'subject')]
-            peid = peids and peids[0]
-        else:
-            peid = entity.project.eid
-            veid = entity.done_in and entity.done_in[0].eid
-        if peid:
-            # we can complete the vocabulary with relevant values
-            rschema = form._cw.vreg.schema['done_in'].rdef('Ticket', 'Version')
-            rset = form._cw.execute(
-                'Any V, VN ORDERBY version_sort_value(VN) '
-                'WHERE V version_of P, P eid %(p)s, V num VN, '
-                'V in_state ST, NOT ST name "published"', {'p': peid}, 'p')
-            vocab += [(v.view('combobox'), v.eid) for v in rset.entities()
-                      if rschema.has_perm(form._cw, 'add', toeid=v.eid)
-                      and v.eid != veid]
-        return vocab
-
-The first thing we have to do is fetch potential values from the ``__linkto`` url
-parameter that is often found in entity creation contexts (the creation action
-provides such a parameter with a predetermined value; for instance in this case,
-ticket creation could occur in the context of a `Version` entity). The
-:class:`~cubicweb.web.formfields.RelationField` field class provides a
-:meth:`~cubicweb.web.formfields.RelationField.relvoc_linkedto` method that gets a
-list suitably filled with vocabulary values.
-
-.. sourcecode:: python
-
-        linkedto = field.relvoc_linkedto(form)
-        if linkedto:
-            return linkedto
-
-Then, if no ``__linkto`` argument was given, we must prepare the vocabulary with
-an initial empty value (because `done_in` is not mandatory, we must allow the
-user to not select a verson) and already linked values. This is done with the
-:meth:`~cubicweb.web.formfields.RelationField.relvoc_init` method.
-
-.. sourcecode:: python
-
-        vocab = field.relvoc_init(form)
-
-But then, we have to give more: if the ticket is related to a project,
-we should provide all the non published versions of this project
-(`Version` and `Project` can be related through the `version_of`
-relation). Conversely, if we do not know yet the project, it would not
-make sense to propose all existing versions as it could potentially
-lead to incoherences. Even if these will be caught by some
-RQLConstraint, it is wise not to tempt the user with error-inducing
-candidate values.
-
-The "ticket is related to a project" part must be decomposed as:
-
-* this is a new ticket which is created is the context of a project
-
-* this is an already existing ticket, linked to a project (through the
-  `concerns` relation)
-
-* there is no related project (quite unlikely given the cardinality of
-  the `concerns` relation, so it can only mean that we are creating a
-  new ticket, and a project is about to be selected but there is no
-  ``__linkto`` argument)
-
-.. note::
-
-   the last situation could happen in several ways, but of course in a
-   polished application, the paths to ticket creation should be
-   controlled so as to avoid a suboptimal end-user experience
-
-Hence, we try to fetch the related project.
-
-.. sourcecode:: python
-
-        veid = None
-        if not entity.has_eid():
-            peids = form.linked_to[('concerns', 'subject')]
-            peid = peids and peids[0]
-        else:
-            peid = entity.project.eid
-            veid = entity.done_in and entity.done_in[0].eid
-
-We distinguish between entity creation and entity modification using
-the ``Entity.has_eid()`` method, which returns `False` on creation. At
-creation time the only way to get a project is through the
-``__linkto`` parameter. Notice that we fetch the version in which the
-ticket is `done_in` if any, for later.
-
-.. note::
-
-  the implementation above assumes that if there is a ``__linkto``
-  parameter, it is only about a project. While it makes sense most of
-  the time, it is not an absolute. Depending on how an entity creation
-  action action url is built, several outcomes could be possible
-  there
-
-If the ticket is already linked to a project, fetching it is
-trivial. Then we add the relevant version to the initial vocabulary.
-
-.. sourcecode:: python
-
-        if peid:
-            rschema = form._cw.vreg.schema['done_in'].rdef('Ticket', 'Version')
-            rset = form._cw.execute(
-                'Any V, VN ORDERBY version_sort_value(VN) '
-                'WHERE V version_of P, P eid %(p)s, V num VN, '
-                'V in_state ST, NOT ST name "published"', {'p': peid})
-            vocab += [(v.view('combobox'), v.eid) for v in rset.entities()
-                      if rschema.has_perm(form._cw, 'add', toeid=v.eid)
-                      and v.eid != veid]
-
-.. warning::
-
-   we have to defend ourselves against lack of a project eid. Given
-   the cardinality of the `concerns` relation, there *must* be a
-   project, but this rule can only be enforced at validation time,
-   which will happen of course only after form subsmission
-
-Here, given a project eid, we complete the vocabulary with all
-unpublished versions defined in the project (sorted by number) for
-which the current user is allowed to establish the relation.
-
-
-Building self-posted form with custom fields/widgets
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Sometimes you want a form that is not related to entity edition. For those,
-you'll have to handle form posting by yourself. Here is a complete example on how
-to achieve this (and more).
-
-Imagine you want a form that selects a month period. There are no proper
-field/widget to handle this in CubicWeb, so let's start by defining them:
-
-.. sourcecode:: python
-
-    # let's have the whole import list at the beginning, even those necessary for
-    # subsequent snippets
-    from logilab.common import date
-    from logilab.mtconverter import xml_escape
-    from cubicweb.view import View
-    from cubicweb.predicates import match_kwargs
-    from cubicweb.web import RequestError, ProcessFormError
-    from cubicweb.web import formfields as fields, formwidgets as wdgs
-    from cubicweb.web.views import forms, calendar
-
-    class MonthSelect(wdgs.Select):
-        """Custom widget to display month and year. Expect value to be given as a
-        date instance.
-        """
-
-        def format_value(self, form, field, value):
-            return u'%s/%s' % (value.year, value.month)
-
-        def process_field_data(self, form, field):
-            val = super(MonthSelect, self).process_field_data(form, field)
-            try:
-                year, month = val.split('/')
-                year = int(year)
-                month = int(month)
-                return date.date(year, month, 1)
-            except ValueError:
-                raise ProcessFormError(
-                    form._cw._('badly formated date string %s') % val)
-
-
-    class MonthPeriodField(fields.CompoundField):
-        """custom field composed of two subfields, 'begin_month' and 'end_month'.
-
-        It expects to be used on form that has 'mindate' and 'maxdate' in its
-        extra arguments, telling the range of month to display.
-        """
-
-        def __init__(self, *args, **kwargs):
-            kwargs.setdefault('widget', wdgs.IntervalWidget())
-            super(MonthPeriodField, self).__init__(
-                [fields.StringField(name='begin_month',
-                                    choices=self.get_range, sort=False,
-                                    value=self.get_mindate,
-                                    widget=MonthSelect()),
-                 fields.StringField(name='end_month',
-                                    choices=self.get_range, sort=False,
-                                    value=self.get_maxdate,
-                                    widget=MonthSelect())], *args, **kwargs)
-
-        @staticmethod
-        def get_range(form, field):
-            mindate = date.todate(form.cw_extra_kwargs['mindate'])
-            maxdate = date.todate(form.cw_extra_kwargs['maxdate'])
-            assert mindate <= maxdate
-            _ = form._cw._
-            months = []
-            while mindate <= maxdate:
-                label = '%s %s' % (_(calendar.MONTHNAMES[mindate.month - 1]),
-                                   mindate.year)
-                value = field.widget.format_value(form, field, mindate)
-                months.append( (label, value) )
-                mindate = date.next_month(mindate)
-            return months
-
-        @staticmethod
-        def get_mindate(form, field):
-            return form.cw_extra_kwargs['mindate']
-
-        @staticmethod
-        def get_maxdate(form, field):
-            return form.cw_extra_kwargs['maxdate']
-
-        def process_posted(self, form):
-            for field, value in super(MonthPeriodField, self).process_posted(form):
-                if field.name == 'end_month':
-                    value = date.last_day(value)
-                yield field, value
-
-
-Here we first define a widget that will be used to select the beginning and the
-end of the period, displaying months like '<month> YYYY' but using 'YYYY/mm' as
-actual value.
-
-We then define a field that will actually hold two fields, one for the beginning
-and another for the end of the period. Each subfield uses the widget we defined
-earlier, and the outer field itself uses the standard
-:class:`IntervalWidget`. The field adds some logic:
-
-* a vocabulary generation function `get_range`, used to populate each sub-field
-
-* two 'value' functions `get_mindate` and `get_maxdate`, used to tell to
-  subfields which value they should consider on form initialization
-
-* overriding of `process_posted`, called when the form is being posted, so that
-  the end of the period is properly set to the last day of the month.
-
-Now, we can define a very simple form:
-
-.. sourcecode:: python
-
-    class MonthPeriodSelectorForm(forms.FieldsForm):
-        __regid__ = 'myform'
-        __select__ = match_kwargs('mindate', 'maxdate')
-
-        form_buttons = [wdgs.SubmitButton()]
-        form_renderer_id = 'onerowtable'
-        period = MonthPeriodField()
-
-
-where we simply add our field, set a submit button and use a very simple renderer
-(try others!). Also we specify a selector that ensures form will have arguments
-necessary to our field.
-
-Now, we need a view that will wrap the form and handle post when it occurs,
-simply displaying posted values in the page:
-
-.. sourcecode:: python
-
-    class SelfPostingForm(View):
-        __regid__ = 'myformview'
-
-        def call(self):
-            mindate, maxdate = date.date(2010, 1, 1), date.date(2012, 1, 1)
-            form = self._cw.vreg['forms'].select(
-                'myform', self._cw, mindate=mindate, maxdate=maxdate, action='')
-            try:
-                posted = form.process_posted()
-                self.w(u'<p>posted values %s</p>' % xml_escape(repr(posted)))
-            except RequestError: # no specified period asked
-                pass
-            form.render(w=self.w, formvalues=self._cw.form)
-
-
-Notice usage of the :meth:`process_posted` method, that will return a dictionary
-of typed values (because they have been processed by the field). In our case, when
-the form is posted you should see a dictionary with 'begin_month' and 'end_month'
-as keys with the selected dates as value (as a python `date` object).
-
-
-APIs
-~~~~
-
-.. automodule:: cubicweb.web.formfields
-.. automodule:: cubicweb.web.formwidgets
-.. automodule:: cubicweb.web.views.forms
-.. automodule:: cubicweb.web.views.formrenderers
-
-
--- a/doc/book/en/devweb/edition/index.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-Edition control
-===============
-
-This chapter covers the editing capabilities of |cubicweb|. It
-explains html Form construction, the Edit Controller and their
-interactions.
-
-
-.. toctree::
-   :maxdepth: 2
-
-   form
-   dissection
-   editcontroller
-   examples
--- a/doc/book/en/devweb/facets.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-The facets system
------------------
-
-Facets allow to restrict searches according to some user friendly criterias.
-CubicWeb has a builtin `facet`_ system to define restrictions `filters`_ really
-as easily as possible.
-
-Here is an exemple of the facets rendering picked from our
-http://www.cubicweb.org web site:
-
-.. image:: ../images/facet_overview.png
-
-Facets will appear on each page presenting more than one entity that may be
-filtered according to some known criteria.
-
-Base classes for facets
-~~~~~~~~~~~~~~~~~~~~~~~
-.. automodule:: cubicweb.web.facet
-
-
-.. _facet: http://en.wikipedia.org/wiki/Faceted_browser
-.. _filters: http://www.cubicweb.org/blogentry/154152
-
--- a/doc/book/en/devweb/httpcaching.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-HTTP cache management
-=====================
-
-.. automodule:: cubicweb.web.httpcache
-
-Cache policies
---------------
-.. autoclass:: cubicweb.web.httpcache.NoHTTPCacheManager
-.. autoclass:: cubicweb.web.httpcache.MaxAgeHTTPCacheManager
-.. autoclass:: cubicweb.web.httpcache.EtagHTTPCacheManager
-.. autoclass:: cubicweb.web.httpcache.EntityHTTPCacheManager
-
-Exception
----------
-.. autoexception:: cubicweb.web.httpcache.NoEtag
-
-Helper functions
-----------------
-.. autofunction:: cubicweb.web.httpcache.set_http_cache_headers
-
-.. NOT YET AVAILABLE IN STABLE autofunction:: cubicweb.web.httpcache.lastmodified
--- a/doc/book/en/devweb/index.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-Web side development
-====================
-
-In this chapter, we will describe the core APIs for web development in
-the *CubicWeb* framework.
-
-.. toctree::
-   :maxdepth: 2
-
-   publisher
-   controllers
-   request
-   searchbar
-   views/index
-   rtags
-   ajax
-   js
-   css
-   edition/index
-   facets
-   internationalization
-   property
-   httpcaching
-   resource
--- a/doc/book/en/devweb/internationalization.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,229 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _internationalization:
-
-Internationalization
----------------------
-
-Cubicweb fully supports the internalization of its content and interface.
-
-Cubicweb's interface internationalization is based on the translation project `GNU gettext`_.
-
-.. _`GNU gettext`: http://www.gnu.org/software/gettext/
-
-Cubicweb' internalization involves two steps:
-
-* in your Python code and cubicweb-tal templates : mark translatable strings
-
-* in your instance : handle the translation catalog, edit translations
-
-String internationalization
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-User defined string
-```````````````````
-
-In the Python code and cubicweb-tal templates translatable strings can be
-marked in one of the following ways :
-
- * by using the *built-in* function `_`:
-
-   .. sourcecode:: python
-
-     class PrimaryView(EntityView):
-         """the full view of an non final entity"""
-         __regid__ = 'primary'
-         title = _('primary')
-
-  OR
-
- * by using the equivalent request's method:
-
-   .. sourcecode:: python
-
-     class NoResultView(View):
-         """default view when no result has been found"""
-         __regid__ = 'noresult'
-
-         def call(self, **kwargs):
-             self.w(u'<div class="searchMessage"><strong>%s</strong></div>\n'
-                 % self._cw._('No result matching query'))
-
-The goal of the *built-in* function `_` is only **to mark the
-translatable strings**, it will only return the string to translate
-itself, but not its translation (it's actually another name for the
-`unicode` builtin).
-
-In the other hand the request's method `self._cw._` is also meant to
-retrieve the proper translation of translation strings in the
-requested language.
-
-Finally you can also use the `__` attribute of request object to get a
-translation for a string *which should not itself added to the catalog*,
-usually in case where the actual msgid is created by string interpolation ::
-
-  self._cw.__('This %s' % etype)
-
-In this example ._cw.__` is used instead of ._cw._` so we don't have 'This %s' in
-messages catalogs.
-
-Translations in cubicweb-tal template can also be done with TAL tags
-`i18n:content` and `i18n:replace`.
-
-If you need to add messages on top of those that can be found in the source,
-you can create a file named `i18n/static-messages.pot`.
-
-You could put there messages not found in the python sources or
-overrides for some messages of used cubes.
-
-Generated string
-````````````````
-
-We do not need to mark the translation strings of entities/relations used by a
-particular instance's schema as they are generated automatically. String for
-various actions are also generated.
-
-For exemple the following schema:
-
-.. sourcecode:: python
-
-
-  class EntityA(EntityType):
-      relation_a2b = SubjectRelation('EntityB')
-
-  class EntityB(EntityType):
-      pass
-
-May generate the following message ::
-
-  add EntityA relation_a2b EntityB subject
-
-This message will be used in views of ``EntityA`` for creation of a new
-``EntityB`` with a preset relation ``relation_a2b`` between the current
-``EntityA`` and the new ``EntityB``. The opposite message ::
-
-  add EntityA relation_a2b EntityB object
-
-Is used for similar creation of an ``EntityA`` from a view of ``EntityB``. The
-title of they respective creation form will be ::
-
-  creating EntityB (EntityA %(linkto)s relation_a2b EntityB)
-
-  creating EntityA (EntityA relation_a2b %(linkto)s EntityA)
-
-In the translated string you can use ``%(linkto)s`` for reference to the source
-``entity``.
-
-Handling the translation catalog
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Once the internationalization is done in your code, you need to populate and
-update the translation catalog. Cubicweb provides the following commands for this
-purpose:
-
-
-* `i18ncubicweb` updates Cubicweb framework's translation
-  catalogs. Unless you actually work on the framework itself, you
-  don't need to use this command.
-
-* `i18ncube` updates the translation catalogs of *one particular cube*
-  (or of all cubes). After this command is executed you must update
-  the translation files *.po* in the "i18n" directory of your
-  cube. This command will of course not remove existing translations
-  still in use. It will mark unused translation but not remove them.
-
-* `i18ninstance` recompiles the translation catalogs of *one particular
-  instance* (or of all instances) after the translation catalogs of
-  its cubes have been updated. This command is automatically
-  called every time you create or update your instance. The compiled
-  catalogs (*.mo*) are stored in the i18n/<lang>/LC_MESSAGES of
-  instance where `lang` is the language identifier ('en' or 'fr'
-  for exemple).
-
-
-Example
-```````
-
-You have added and/or modified some translation strings in your cube
-(after creating a new view or modifying the cube's schema for exemple).
-To update the translation catalogs you need to do:
-
-1. `cubicweb-ctl i18ncube <cube>`
-2. Edit the <cube>/i18n/xxx.po  files and add missing translations (empty `msgstr`)
-3. `hg ci -m "updated i18n catalogs"`
-4. `cubicweb-ctl i18ninstance <myinstance>`
-
-Editing po files
-~~~~~~~~~~~~~~~~
-
-Using a PO aware editor
-````````````````````````
-
-Many tools exist to help maintain .po (PO) files. Common editors or
-development environment provides modes for these. One can also find
-dedicated PO files editor, such as `poedit`_.
-
-.. _`poedit`:  http://www.poedit.net/
-
-While usage of such a tool is commendable, PO files are perfectly
-editable with a (unicode aware) plain text editor. It is also useful
-to know their structure for troubleshooting purposes.
-
-Structure of a PO file
-``````````````````````
-
-In this section, we selectively quote passages of the `GNU gettext`_
-manual chapter on PO files, available there::
-
- http://www.gnu.org/software/hello/manual/gettext/PO-Files.html
-
-One PO file entry has the following schematic structure::
-
-     white-space
-     #  translator-comments
-     #. extracted-comments
-     #: reference...
-     #, flag...
-     #| msgid previous-untranslated-string
-     msgid untranslated-string
-     msgstr translated-string
-
-
-A simple entry can look like this::
-
-     #: lib/error.c:116
-     msgid "Unknown system error"
-     msgstr "Error desconegut del sistema"
-
-It is also possible to have entries with a context specifier. They
-look like this::
-
-     white-space
-     #  translator-comments
-     #. extracted-comments
-     #: reference...
-     #, flag...
-     #| msgctxt previous-context
-     #| msgid previous-untranslated-string
-     msgctxt context
-     msgid untranslated-string
-     msgstr translated-string
-
-
-The context serves to disambiguate messages with the same
-untranslated-string. It is possible to have several entries with the
-same untranslated-string in a PO file, provided that they each have a
-different context. Note that an empty context string and an absent
-msgctxt line do not mean the same thing.
-
-Contexts and CubicWeb
-`````````````````````
-
-CubicWeb PO files have both non-contextual and contextual msgids.
-
-Contextual entries are automatically used in some cases. For instance,
-entity.dc_type(), eschema.display_name(req) or display_name(etype,
-req, form, context) methods/function calls will use them.
-
-It is also possible to explicitly use the with _cw.pgettext(context,
-msgid).
--- a/doc/book/en/devweb/js.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,394 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-Javascript
-----------
-
-*CubicWeb* uses quite a bit of javascript in its user interface and
-ships with jquery (1.3.x) and parts of the jquery UI library, plus a
-number of homegrown files and also other third party libraries.
-
-All javascript files are stored in cubicweb/web/data/. There are
-around thirty js files there. In a cube it goes to data/.
-
-Obviously one does not want javascript pieces to be loaded all at
-once, hence the framework provides a number of mechanisms and
-conventions to deal with javascript resources.
-
-Conventions
-~~~~~~~~~~~
-
-It is good practice to name cube specific js files after the name of
-the cube, like this : 'cube.mycube.js', so as to avoid name clashes.
-
-.. XXX external_resources variable (which needs love)
-
-Server-side Javascript API
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Javascript resources are typically loaded on demand, from views. The
-request object (available as self._cw from most application objects,
-for instance views and entities objects) has a few methods to do that:
-
-* `add_js(self, jsfiles, localfile=True)` which takes a sequence of
-  javascript files and writes proper entries into the HTML header
-  section. The localfile parameter allows to declare resources which
-  are not from web/data (for instance, residing on a content delivery
-  network).
-
-* `add_onload(self, jscode)` which adds one raw javascript code
-  snippet inline in the html headers. This is quite useful for setting
-  up early jQuery(document).ready(...) initialisations.
-
-Javascript events
-~~~~~~~~~~~~~~~~~
-
-* ``server-response``: this event is triggered on HTTP responses (both
-  standard and ajax). The two following extra parameters are passed
-  to callbacks :
-
-  - ``ajax``: a boolean that says if the reponse was issued by an
-    ajax request
-
-  - ``node``: the DOM node returned by the server in case of an
-    ajax request, otherwise the document itself for standard HTTP
-    requests.
-
-Important javascript AJAX APIS
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-* `asyncRemoteExec` and `remoteExec` are the base building blocks for
-  doing arbitrary async (resp. sync) communications with the server
-
-* `reloadComponent` is a convenience function to replace a DOM node
-  with server supplied content coming from a specific registry (this
-  is quite handy to refresh the content of some boxes for instances)
-
-* `jQuery.fn.loadxhtml` is an important extension to jQuery which
-  allows proper loading and in-place DOM update of xhtml views. It is
-  suitably augmented to trigger necessary events, and process CubicWeb
-  specific elements such as the facet system, fckeditor, etc.
-
-
-A simple example with asyncRemoteExec
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-On the python side, we have to define an
-:class:`cubicweb.web.views.ajaxcontroller.AjaxFunction` object. The
-simplest way to do that is to use the
-:func:`cubicweb.web.views.ajaxcontroller.ajaxfunc` decorator (for more
-details on this, refer to :ref:`ajax`).
-
-.. sourcecode: python
-
-    from cubicweb.web.views.ajaxcontroller import ajaxfunc
-
-    # serialize output to json to get it back easily on the javascript side
-    @ajaxfunc(output_type='json')
-    def js_say_hello(self, name):
-        return u'hello %s' % name
-
-On the javascript side, we do the asynchronous call. Notice how it
-creates a `deferred` object. Proper treatment of the return value or
-error handling has to be done through the addCallback and addErrback
-methods.
-
-.. sourcecode: javascript
-
-    function asyncHello(name) {
-        var deferred = asyncRemoteExec('say_hello', name);
-        deferred.addCallback(function (response) {
-            alert(response);
-        });
-        deferred.addErrback(function (error) {
-            alert('something fishy happened');
-        });
-     }
-
-     function syncHello(name) {
-         alert( remoteExec('say_hello', name) );
-     }
-
-Anatomy of a reloadComponent call
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-`reloadComponent` allows to dynamically replace some DOM node with new
-elements. It has the following signature:
-
-* `compid` (mandatory) is the name of the component to be reloaded
-
-* `rql` (optional) will be used to generate a result set given as
-  argument to the selected component
-
-* `registry` (optional) defaults to 'components' but can be any other
-  valid registry name
-
-* `nodeid` (optional) defaults to compid + 'Component' but can be any
-  explicitly specified DOM node id
-
-* `extraargs` (optional) should be a dictionary of values that will be
-  given to the cell_call method of the component
-
-A simple reloadComponent example
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The server side implementation of `reloadComponent` is the
-:func:`cubicweb.web.views.ajaxcontroller.component` *AjaxFunction* appobject.
-
-The following function implements a two-steps method to delete a
-standard bookmark and refresh the UI, while keeping the UI responsive.
-
-.. sourcecode:: javascript
-
-    function removeBookmark(beid) {
-        d = asyncRemoteExec('delete_bookmark', beid);
-        d.addCallback(function(boxcontent) {
-	    reloadComponent('bookmarks_box', '', 'boxes', 'bookmarks_box');
-            document.location.hash = '#header';
-            updateMessage(_("bookmark has been removed"));
-         });
-    }
-
-`reloadComponent` is called with the id of the bookmark box as
-argument, no rql expression (because the bookmarks display is actually
-independant of any dataset context), a reference to the 'boxes'
-registry (which hosts all left, right and contextual boxes) and
-finally an explicit 'bookmarks_box' nodeid argument that stipulates
-the target DOM node.
-
-Anatomy of a loadxhtml call
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-`jQuery.fn.loadxhtml` is an important extension to jQuery which allows
-proper loading and in-place DOM update of xhtml views. The existing
-`jQuery.load`_ function does not handle xhtml, hence the addition. The
-API of loadxhtml is roughly similar to that of `jQuery.load`_.
-
-.. _`jQuery.load`: http://api.jquery.com/load/
-
-
-* `url` (mandatory) should be a complete url (typically referencing
-  the :class:`cubicweb.web.views.ajaxcontroller.AjaxController`,
-  but this is not strictly mandatory)
-
-* `data` (optional) is a dictionary of values given to the
-  controller specified through an `url` argument; some keys may have a
-  special meaning depending on the choosen controller (such as `fname`
-  for the JSonController); the `callback` key, if present, must refer
-  to a function to be called at the end of loadxhtml (more on this
-  below)
-
-* `reqtype` (optional) specifies the request method to be used (get or
-  post); if the argument is 'post', then the post method is used,
-  otherwise the get method is used
-
-* `mode` (optional) is one of `replace` (the default) which means the
-  loaded node will replace the current node content, `swap` to replace
-  the current node with the loaded node, and `append` which will
-  append the loaded node to the current node content
-
-About the `callback` option:
-
-* it is called with two parameters: the current node, and a list
-  containing the loaded (and post-processed node)
-
-* whenever it returns another function, this function is called in
-  turn with the same parameters as above
-
-This mechanism allows callback chaining.
-
-
-A simple example with loadxhtml
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Here we are concerned with the retrieval of a specific view to be
-injected in the live DOM. The view will be of course selected
-server-side using an entity eid provided by the client side.
-
-.. sourcecode:: python
-
-    from cubicweb.web.views.ajaxcontroller import ajaxfunc
-
-    @ajaxfunc(output_type='xhtml')
-    def frob_status(self, eid, frobname):
-        entity = self._cw.entity_from_eid(eid)
-        return entity.view('frob', name=frobname)
-
-.. sourcecode:: javascript
-
-    function updateSomeDiv(divid, eid, frobname) {
-        var params = {fname:'frob_status', eid: eid, frobname:frobname};
-        jQuery('#'+divid).loadxhtml(JSON_BASE_URL, params, 'post');
-     }
-
-In this example, the url argument is the base json url of a cube
-instance (it should contain something like
-`http://myinstance/ajax?`). The actual AjaxController method name is
-encoded in the `params` dictionary using the `fname` key.
-
-A more real-life example
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-A frequent need of Web 2 applications is the delayed (or demand
-driven) loading of pieces of the DOM. This is typically achieved using
-some preparation of the initial DOM nodes, jQuery event handling and
-proper use of loadxhtml.
-
-We present here a skeletal version of the mecanism used in CubicWeb
-and available in web/views/tabs.py, in the `LazyViewMixin` class.
-
-.. sourcecode:: python
-
-    def lazyview(self, vid, rql=None):
-        """ a lazy version of wview """
-        w = self.w
-        self._cw.add_js('cubicweb.lazy.js')
-        urlparams = {'vid' : vid, 'fname' : 'view'}
-        if rql is not None:
-            urlparams['rql'] = rql
-        w(u'<div id="lazy-%s" cubicweb:loadurl="%s">' % (
-            vid, xml_escape(self._cw.build_url('json', **urlparams))))
-        w(u'</div>')
-        self._cw.add_onload(u"""
-            jQuery('#lazy-%(vid)s').bind('%(event)s', function() {
-                   loadNow('#lazy-%(vid)s');});"""
-            % {'event': 'load_%s' % vid, 'vid': vid})
-
-This creates a `div` with a specific event associated to it.
-
-The full version deals with:
-
-* optional parameters such as an entity eid, an rset
-
-* the ability to further reload the fragment
-
-* the ability to display a spinning wheel while the fragment is still
-  not loaded
-
-* handling of browsers that do not support ajax (search engines,
-  text-based browsers such as lynx, etc.)
-
-The javascript side is quite simple, due to loadxhtml awesomeness.
-
-.. sourcecode:: javascript
-
-    function loadNow(eltsel) {
-        var lazydiv = jQuery(eltsel);
-        lazydiv.loadxhtml(lazydiv.attr('cubicweb:loadurl'));
-    }
-
-This is all significantly different of the previous `simple example`
-(albeit this example actually comes from real-life code).
-
-Notice how the `cubicweb:loadurl` is used to convey the url
-information. The base of this url is similar to the global javascript
-JSON_BASE_URL. According to the pattern described earlier,
-the `fname` parameter refers to the standard `js_view` method of the
-JSonController. This method renders an arbitrary view provided a view
-id (or `vid`) is provided, and most likely an rql expression yielding
-a result set against which a proper view instance will be selected.
-
-The `cubicweb:loadurl` is one of the 29 attributes extensions to XHTML
-in a specific cubicweb namespace. It is a means to pass information
-without breaking HTML nor XHTML compliance and without resorting to
-ungodly hacks.
-
-Given all this, it is easy to add a small nevertheless useful feature
-to force the loading of a lazy view (for instance, a very
-computation-intensive web page could be scinded into one fast-loading
-part and a delayed part).
-
-On the server side, a simple call to a javascript function is
-sufficient.
-
-.. sourcecode:: python
-
-    def forceview(self, vid):
-        """trigger an event that will force immediate loading of the view
-        on dom readyness
-        """
-        self._cw.add_onload("triggerLoad('%s');" % vid)
-
-The browser-side definition follows.
-
-.. sourcecode:: javascript
-
-    function triggerLoad(divid) {
-        jQuery('#lazy-' + divd).trigger('load_' + divid);
-    }
-
-
-Javascript library: overview
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-* jquery.* : jquery and jquery UI library
-
-* cubicweb.ajax.js : concentrates all ajax related facilities (it
-  extends jQuery with the loahxhtml function, provides a handfull of
-  high-level ajaxy operations like asyncRemoteExec, reloadComponent,
-  replacePageChunk, getDomFromResponse)
-
-* cubicweb.python.js : adds a number of practical extension to stdanrd
-  javascript objects (on Date, Array, String, some list and dictionary
-  operations), and a pythonesque way to build classes. Defines a
-  CubicWeb namespace.
-
-* cubicweb.htmlhelpers.js : a small bag of convenience functions used
-  in various other cubicweb javascript resources (baseuri, progress
-  cursor handling, popup login box, html2dom function, etc.)
-
-* cubicweb.widgets.js : provides a widget namespace and constructors
-  and helpers for various widgets (mainly facets and timeline)
-
-* cubicweb.edition.js : used by edition forms
-
-* cubicweb.preferences.js : used by the preference form
-
-* cubicweb.facets.js : used by the facets mechanism
-
-There is also javascript support for massmailing, gmap (google maps),
-fckcwconfig (fck editor), timeline, calendar, goa (CubicWeb over
-AppEngine), flot (charts drawing), tabs and bookmarks.
-
-API
-~~~
-
-.. toctree::
-    :maxdepth: 1
-
-    js_api/index
-
-
-Testing javascript
-~~~~~~~~~~~~~~~~~~
-
-You with the ``cubicweb.qunit.QUnitTestCase`` can include standard Qunit tests
-inside the python unittest run . You simply have to define a new class that
-inherit from ``QUnitTestCase`` and register your javascript test file in the
-``all_js_tests`` lclass attribut. This  ``all_js_tests`` is a sequence a
-3-tuple (<test_file, [<dependencies> ,] [<data_files>]):
-
-The <test_file> should contains the qunit test. <dependencies> defines the list
-of javascript file that must be imported before the test script.  Dependencies
-are included their definition order. <data_files> are additional files copied in the
-test directory. both <dependencies> and <data_files> are optionnal.
-``jquery.js`` is preincluded in for all test.
-
-.. sourcecode:: python
-
-    from cubicweb.qunit import QUnitTestCase
-
-    class MyQUnitTest(QUnitTestCase):
-
-        all_js_tests = (
-            ("relative/path/to/my_simple_testcase.js",)
-            ("relative/path/to/my_qunit_testcase.js",(
-                "rel/path/to/dependency_1.js",
-                "rel/path/to/dependency_2.js",)),
-            ("relative/path/to/my_complexe_qunit_testcase.js",(
-                 "rel/path/to/dependency_1.js",
-                 "rel/path/to/dependency_2.js",
-               ),(
-                 "rel/path/file_dependency.html",
-                 "path/file_dependency.json")
-                ),
-            )
--- a/doc/book/en/devweb/property.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-.. _cwprops:
-
-The property mecanism
----------------------
-
-.. XXX CWProperty and co
-
-
-Property API
-~~~~~~~~~~~~
-.. XXX feed me
-
-Registering and using your own property
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.. XXX feed me
--- a/doc/book/en/devweb/publisher.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-.. _publisher:
-
-Publisher
----------
-
-What happens when an HTTP request is issued ?
-
-The story begins with the ``CubicWebPublisher.main_publish``
-method. We do not get upper in the bootstrap process because it is
-dependant on the used HTTP library. With `twisted`_ however,
-``cubicweb.etwist.server.CubicWebRootResource.render_request`` is the
-real entry point.
-
-.. _`twisted`: http://twistedmatrix.com/trac/
-
-What main_publish does:
-
-* get a controller id and a result set from the path (this is actually
-  delegated to the `urlpublisher` component)
-
-* the controller is then selected (if not, this is considered an
-  authorization failure and signaled as such) and called
-
-* then either a proper result is returned, in which case the
-  request/connection object issues a ``commit`` and returns the result
-
-* or error handling must happen:
-
-  * ``ValidationErrors`` pop up there and may lead to a redirect to a
-    previously arranged url or standard error handling applies
-  * an HTTP 500 error (`Internal Server Error`) is issued
-
-
-Now, let's turn to the controller. There are many of them in
-:mod:`cubicweb.web.views.basecontrollers`. We can just follow the
-default `view` controller that is selected on a `view` path. See the
-:ref:`controllers` chapter for more information on controllers.
-
-The `View` controller's entry point is the `publish` method. It does
-the following:
-
-* compute the `main` view to be applied, using either the given result
-  set or building one from a user provided rql string (`rql` and `vid`
-  can be forced from the url GET parameters), that is:
-
-    * compute the `vid` using the result set and the schema (see
-      `cubicweb.web.views.vid_from_rset`)
-    * handle all error cases that could happen in this phase
-
-* do some cache management chores
-
-* select a main template (typically `TheMainTemplate`, see chapter
-  :ref:`templates`)
-
-* call it with the result set and the computed view.
-
-What happens next actually depends on the template and the view, but
-in general this is the rendering phase.
-
-
-CubicWebPublisher API
-`````````````````````
-
-.. autoclass:: cubicweb.web.application.CubicWebPublisher
-   :members:
--- a/doc/book/en/devweb/request.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-The `Request` class (`cubicweb.web.request`)
---------------------------------------------
-
-Overview
-````````
-
-A request instance is created when an HTTP request is sent to the web
-server.  It contains informations such as form parameters,
-authenticated user, etc. It is a very prevalent object and is used
-throughout all of the framework and applications, as you'll access to
-almost every resources through it.
-
-**A request represents a user query, either through HTTP or not (we
-also talk about RQL queries on the server side for example).**
-
-Here is a non-exhaustive list of attributes and methods available on
-request objects (grouped by category):
-
-* `Browser control`:
-
-  * `ie_browser`: tells if the browser belong to the Internet Explorer
-    family
-
-* `User and identification`:
-
-  * `user`, instance of `cubicweb.entities.authobjs.CWUser` corresponding to the
-    authenticated user
-
-* `Session data handling`
-
-  * `session.data` is the dictionary of the session data; it can be
-    manipulated like an ordinary Python dictionary
-
-* `Edition` (utilities for edition control):
-
-  * `cancel_edition`: resets error url and cleans up pending operations
-  * `create_entity`: utility to create an entity (from an etype,
-    attributes and relation values)
-  * `datadir_url`: returns the url to the merged external resources
-    (|cubicweb|'s `web/data` directory plus all `data` directories of
-    used cubes)
-  * `edited_eids`: returns the list of eids of entities that are
-    edited under the current http request
-  * `eid_rset(eid)`: utility which returns a result set from an eid
-  * `entity_from_eid(eid)`: returns an entity instance from the given eid
-  * `encoding`: returns the encoding of the current HTTP request
-  * `ensure_ro_rql(rql)`: ensure some rql query is a data request
-  * etype_rset
-  * `form`, dictionary containing the values of a web form
-  * `encoding`, character encoding to use in the response
-  * `next_tabindex()`: returns a monotonically growing integer used to
-    build the html tab index of forms
-
-* `HTTP`
-
-  * `authmode`: returns a string describing the authentication mode
-    (http, cookie, ...)
-  * `lang`: returns the user agents/browser's language as carried by
-    the http request
-  * `demote_to_html()`: in the context of an XHTML compliant browser,
-    this will force emission of the response as an HTML document
-    (using the http content negociation)
-
-*  `Cookies handling`
-
-  * `get_cookie()`, returns a dictionary containing the value of the header
-    HTTP 'Cookie'
-  * `set_cookie(cookie, key, maxage=300)`, adds a header HTTP `Set-Cookie`,
-    with a minimal 5 minutes length of duration by default (`maxage` = None
-    returns a *session* cookie which will expire when the user closes the browser
-    window)
-  * `remove_cookie(cookie, key)`, forces a value to expire
-
-* `URL handling`
-
-  * `build_url(__vid, *args, **kwargs)`: return an absolute URL using
-    params dictionary key/values as URL parameters. Values are
-    automatically URL quoted, and the publishing method to use may be
-    specified or will be guessed.
-  * `build_url_params(**kwargs)`: returns a properly prepared (quoted,
-    separators, ...) string from the given parameters
-  * `url()`, returns the full URL of the HTTP request
-  * `base_url()`, returns the root URL of the web application
-  * `relative_path()`, returns the relative path of the request
-
-* `Web resource (.css, .js files, etc.) handling`:
-
-  * `add_css(cssfiles)`: adds the given list of css resources to the current
-    html headers
-  * `add_js(jsfiles)`: adds the given list of javascript resources to the
-    current html headers
-  * `add_onload(jscode)`: inject the given jscode fragment (an unicode
-    string) into the current html headers, wrapped inside a
-    document.ready(...) or another ajax-friendly one-time trigger event
-  * `add_header(header, values)`: adds the header/value pair to the
-    current html headers
-  * `status_out`: control the HTTP status of the response
-
-* `And more...`
-
-  * `set_content_type(content_type, filename=None)`, adds the header HTTP
-    'Content-Type'
-  * `get_header(header)`, returns the value associated to an arbitrary header
-    of the HTTP request
-  * `set_header(header, value)`, adds an arbitrary header in the response
-  * `execute(*args, **kwargs)`, executes an RQL query and return the result set
-  * `property_value(key)`, properties management (`CWProperty`)
-  * dictionary `data` to store data to share informations between components
-    *while a request is executed*
-
-Please note that this class is abstract and that a concrete implementation
-will be provided by the *frontend* web used (in particular *twisted* as of
-today). For the views or others that are executed on the server side,
-most of the interface of `Request` is defined in the session associated
-to the client.
-
-API
-```
-
-The elements we gave in overview for above are built in three layers,
-from ``cubicweb.req.RequestSessionBase``, ``cubicweb.repoapi.ClientConnection`` and
-``cubicweb.web.ConnectionCubicWebRequestBase``.
-
-.. autoclass:: cubicweb.req.RequestSessionBase
-   :members:
-
-.. autoclass:: cubicweb.repoapi.ClientConnection
-   :members:
-
-.. autoclass:: cubicweb.web.request.ConnectionCubicWebRequestBase
-   :members:
--- a/doc/book/en/devweb/resource.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-.. _resources:
-
-Locate resources
-----------------
-
-.. automethod:: cubicweb.web.webconfig.WebConfiguration.locate_resource
-.. automethod:: cubicweb.web.webconfig.WebConfiguration.locate_doc_file
-.. automethod:: cubicweb.web.webconfig.WebConfiguration.locate_all_files
-
-Static files handling
----------------------
-
-.. autoattribute:: cubicweb.web.webconfig.WebConfiguration.static_directory
-.. automethod:: cubicweb.web.webconfig.WebConfiguration.static_file_exists
-.. automethod:: cubicweb.web.webconfig.WebConfiguration.static_file_open
-.. automethod:: cubicweb.web.webconfig.WebConfiguration.static_file_add
-.. automethod:: cubicweb.web.webconfig.WebConfiguration.static_file_del
-
--- a/doc/book/en/devweb/rtags.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-Configuring the user interface
-------------------------------
-
-.. _relation_tags:
-
-Relation tags
-~~~~~~~~~~~~~
-.. automodule:: cubicweb.rtags
-
-.. _uicfg:
-
-The uicfg module
-~~~~~~~~~~~~~~~~
-
-.. note::
-
- The part of uicfg that deals with primary views is in the
- :ref:`primary_view_configuration` chapter.
-
-.. automodule:: cubicweb.web.views.uicfg
-
-
-The uihelper module
-~~~~~~~~~~~~~~~~~~~
-
-.. automodule:: cubicweb.web.uihelper
-
--- a/doc/book/en/devweb/searchbar.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-.. _searchbar:
-
-RQL search bar
---------------
-
-The RQL search bar is a visual component, hidden by default, the tiny *search*
-input being enough for common use cases.
-
-An autocompletion helper is provided to help you type valid queries, both
-in terms of syntax and in terms of schema validity.
-
-.. autoclass:: cubicweb.web.views.magicsearch.RQLSuggestionsBuilder
-
-
-How search is performed
-+++++++++++++++++++++++
-
-You can use the *rql search bar* to either type RQL queries, plain text queries
-or standard shortcuts such as *<EntityType>* or *<EntityType> <attrname> <value>*.
-
-Ultimately, all queries are translated to rql since it's the only
-language understood on the server (data) side. To transform the user
-query into RQL, CubicWeb uses the so-called *magicsearch component*,
-defined in :mod:`cubicweb.web.views.magicsearch`, which in turn
-delegates to a number of query preprocessor that are responsible of
-interpreting the user query and generating corresponding RQL.
-
-The code of the main processor loop is easy to understand:
-
-.. sourcecode:: python
-
-  for proc in self.processors:
-      try:
-          return proc.process_query(uquery, req)
-      except (RQLSyntaxError, BadRQLQuery):
-          pass
-
-The idea is simple: for each query processor, try to translate the
-query. If it fails, try with the next processor, if it succeeds,
-we're done and the RQL query will be executed.
-
--- a/doc/book/en/devweb/views/basetemplates.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,161 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _templates:
-
-Templates
-=========
-
-Templates are the entry point for the |cubicweb| view system. As seen
-in :ref:`views_base_class`, there are two kinds of views: the
-templatable and non-templatable.
-
-
-Non-templatable views
----------------------
-
-Non-templatable views are standalone. They are responsible for all the details
-such as setting a proper content type (or mime type), the proper document
-headers, namespaces, etc. Examples are pure xml views such as RSS or Semantic Web
-views (`SIOC`_, `DOAP`_, `FOAF`_, `Linked Data`_, etc.), and views which generate
-binary files (pdf, excel files, etc.)
-
-.. _`SIOC`: http://sioc-project.org/
-.. _`DOAP`: http://trac.usefulinc.com/doap
-.. _`FOAF`: http://www.foaf-project.org/
-.. _`Linked Data`: http://linkeddata.org/
-
-
-To notice that a view is not templatable, you just have to set the
-view's class attribute `templatable` to `False`. In this case, it
-should set the `content_type` class attribute to the correct MIME
-type. By default, it is text/xhtml. Additionally, if your view
-generate a binary file, you have to set the view's class attribute
-`binary` to `True` too.
-
-
-Templatable views
------------------
-
-Templatable views are not concerned with such pesky details. They
-leave it to the template. Conversely, the template's main job is to:
-
-* set up the proper document header and content type
-* define the general layout of a document
-* invoke adequate views in the various sections of the document
-
-
-Look at :mod:`cubicweb.web.views.basetemplates` and you will find the base
-templates used to generate (X)HTML for your application. The most important
-template there is :class:`~cubicweb.web.views.basetemplates.TheMainTemplate`.
-
-.. _the_main_template_layout:
-
-TheMainTemplate
-~~~~~~~~~~~~~~~
-
-.. _the_main_template_sections:
-
-Layout and sections
-```````````````````
-
-A page is composed as indicated on the schema below :
-
-.. image:: ../../images/main_template.png
-
-The sections dispatches specific views:
-
-* `header`: the rendering of the header is delegated to the
-  `htmlheader` view, whose default implementation can be found in
-  ``basetemplates.py`` and which does the following things:
-
-    * inject the favicon if there is one
-    * inject the global style sheets and javascript resources
-    * call and display a link to an rss component if there is one available
-
-  it also sets up the page title, and fills the actual
-  `header` section with top-level components, using the `header` view, which:
-
-    * tries to display a logo, the name of the application and the `breadcrumbs`
-    * provides a login status area
-    * provides a login box (hiden by default)
-
-* `left column`: this is filled with all selectable boxes matching the
-  `left` context (there is also a right column but nowadays it is
-  seldom used due to bad usability)
-
-* `contentcol`: this is the central column; it is filled with:
-
-    * the `rqlinput` view (hidden by default)
-    * the `applmessages` component
-    * the `contentheader` view which in turns dispatches all available
-      content navigation components having the `navtop` context (this
-      is used to navigate through entities implementing the IPrevNext
-      interface)
-    * the view that was given as input to the template's `call`
-      method, also dealing with pagination concerns
-    * the `contentfooter`
-
-* `footer`: adds all footer actions
-
-.. note::
-
-  How and why a view object is given to the main template is explained
-  in the :ref:`publisher` chapter.
-
-Configure the main template
-```````````````````````````
-
-You can overload some methods of the
-:class:`~cubicweb.web.views.basetemplates.TheMainTemplate`, in order to fulfil
-your needs. There are also some attributes and methods which can be defined on a
-view to modify the base template behaviour:
-
-* `paginable`: if the result set is bigger than a configurable size, your result
-  page will be paginated by default. You can set this attribute to `False` to
-  avoid this.
-
-* `binary`: boolean flag telling if the view generates some text or a binary
-  stream.  Default to False. When view generates text argument given to `self.w`
-  **must be an unicode string**, encoded string otherwise.
-
-* `content_type`, view's content type, default to 'text/xhtml'
-
-* `templatable`, boolean flag telling if the view's content should be returned
-  directly (when `False`) or included in the main template layout (including
-  header, boxes and so on).
-
-* `page_title()`, method that should return a title that will be set as page
-  title in the html headers.
-
-* `html_headers()`, method that should return a list of HTML headers to be
-  included the html headers.
-
-
-You can also modify certain aspects of the main template of a page
-when building an url or setting these parameters in the req.form:
-
-* `__notemplate`, if present (whatever the value assigned), only the content view
-  is returned
-
-* `__force_display`, if present and its value is not null, no pagination whatever
-  the number of entities to display (e.g. similar effect as view's `paginable`
-  attribute described above.
-
-* `__method`, if the result set to render contains only one entity and this
-  parameter is set, it refers to a method to call on the entity by passing it the
-  dictionary of the forms parameters, before going the classic way (through step
-  1 and 2 described juste above)
-
-* `vtitle`, a title to be set as <h1> of the content
-
-Other templates
-~~~~~~~~~~~~~~~
-
-There are also the following other standard templates:
-
-* :class:`cubicweb.web.views.basetemplates.LogInTemplate`
-* :class:`cubicweb.web.views.basetemplates.LogOutTemplate`
-* :class:`cubicweb.web.views.basetemplates.ErrorTemplate` specializes
-  :class:`~cubicweb.web.views.basetemplates.TheMainTemplate` to do
-  proper end-user output if an error occurs during the computation of
-  TheMainTemplate (it is a fallback view).
--- a/doc/book/en/devweb/views/baseviews.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-Base views
-----------
-
-|cubicweb| provides a lot of standard views, that can be found in
-:mod:`cubicweb.web.views` sub-modules.
-
-A certain number of views are used to build the web interface, which apply to one
-or more entities. As other appobjects, their identifier is what distinguish them
-from each others. The most generic ones, found in
-:mod:`cubicweb.web.views.baseviews`, are described below.
-
-You'll probably want to customize one or more of the described views which are
-default, generic, implementations.
-
-
-.. automodule:: cubicweb.web.views.baseviews
-
-You will also find modules providing some specific services:
-
-.. automodule:: cubicweb.web.views.navigation
-
--- a/doc/book/en/devweb/views/boxes.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-Boxes
------
-
-(:mod:`cubicweb.web.views.boxes`)
-
-*sidebox*
-  This view displays usually a side box of some related entities
-  in a primary view.
-
-The action box
-~~~~~~~~~~~~~~~
-
-The ``add_related`` is an automatic menu in the action box that allows to create
-an entity automatically related to the initial entity (context in
-which the box is displayed). By default, the links generated in this
-box are computed from the schema properties of the displayed entity,
-but it is possible to explicitly specify them thanks to the
-`cubicweb.web.views.uicfg.rmode` *relation tag*:
-
-* `link`, indicates that a relation is in general created pointing
-  to an existing entity and that we should not to display a link
-  for this relation
-
-* `create`, indicates that a relation is in general created pointing
-  to new entities and that we should display a link to create a new
-  entity and link to it automatically
-
-
-If necessary, it is possible to overwrite the method
-`relation_mode(rtype, targettype, x='subject')` to dynamically
-compute a relation creation category.
-
-Please note that if at least one action belongs to the `addrelated` category,
-the automatic behavior is desactivated in favor of an explicit behavior
-(e.g. display of `addrelated` category actions only).
-
--- a/doc/book/en/devweb/views/breadcrumbs.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-Breadcrumbs
------------
-
-Breadcrumbs are a navigation component to help the user locate himself
-along a path of entities.
-
-Display
-~~~~~~~
-
-Breadcrumbs are displayed by default in the header section (see
-:ref:`the_main_template_sections`).  With the default main template,
-the header section is composed by the logo, the application name,
-breadcrumbs and, at the most right, the login box. Breadcrumbs are
-displayed just next to the application name, thus they begin with a
-separator.
-
-Here is the header section of the CubicWeb's forge:
-
-.. image:: ../../images/breadcrumbs_header.png
-
-There are three breadcrumbs components defined in
-:mod:`cubicweb.web.views.ibreadcrumbs`:
-
-- `BreadCrumbEntityVComponent`: displayed for a result set with one line
-  if the entity is adaptable to ``IBreadCrumbsAdapter``.
-- `BreadCrumbETypeVComponent`: displayed for a result set with more than
-  one line, but with all entities of the same type which can adapt to
-  ``IBreadCrumbsAdapter``.
-- `BreadCrumbAnyRSetVComponent`: displayed for any other result set.
-
-Building breadcrumbs
-~~~~~~~~~~~~~~~~~~~~
-
-The ``IBreadCrumbsAdapter`` adapter is defined in the
-:mod:`cubicweb.web.views.ibreadcrumbs` module. It specifies that an
-entity which implements this interface must have a ``breadcrumbs`` and
-a ``parent_entity`` method. A default implementation for each is
-provided. This implementation expoits the ITreeAdapter.
-
-.. note::
-
-   Redefining the breadcrumbs is the hammer way to do it. Another way
-   is to define an `ITreeAdapter` adapter on an entity type. If
-   available, it will be used to compute breadcrumbs.
-
-Here is the API of the ``IBreadCrumbsAdapter`` class:
-
-.. automethod:: cubicweb.web.views.ibreadcrumbs.IBreadCrumbsAdapter.parent_entity
-.. automethod:: cubicweb.web.views.ibreadcrumbs.IBreadCrumbsAdapter.breadcrumbs
-
-If the breadcrumbs method return a list of entities, the
-``cubicweb.web.views.ibreadcrumbs.BreadCrumbView`` is used to display
-the elements.
-
-By default, for any entity, if recurs=True, breadcrumbs method returns
-a list of entities, else a list of a simple string.
-
-In order to see a hierarchical breadcrumbs, entities must have a
-``parent`` method which returns the parent entity. By default this
-method doesn't exist on entity, given that it can not be guessed.
--- a/doc/book/en/devweb/views/idownloadable.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-The 'download' views
-====================
-
-.. automodule:: cubicweb.web.views.idownloadable
-
-Components
-----------
-
-.. autoclass:: cubicweb.web.views.idownloadable.DownloadBox
-
-Download views
---------------
-
-.. autoclass:: cubicweb.web.views.idownloadable.DownloadView
-.. autoclass:: cubicweb.web.views.idownloadable.DownloadLinkView
-.. autoclass:: cubicweb.web.views.idownloadable.IDownloadablePrimaryView
-
-Embedded views
---------------
-
-.. autoclass:: cubicweb.web.views.idownloadable.ImageView
-.. autoclass:: cubicweb.web.views.idownloadable.EHTMLView
--- a/doc/book/en/devweb/views/index.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-The View system
-===============
-
-This chapter aims to describe the concept of a `view` used all along
-the development of a web application and how it has been implemented
-in |cubicweb|.
-
-
-.. toctree::
-   :maxdepth: 3
-
-   views
-   basetemplates
-   primary
-   reledit
-   baseviews
-   startup
-   boxes
-   table
-   xmlrss
-   urlpublish
-   breadcrumbs
-   idownloadable
-   wdoc
-
-..   editforms
-..   embedding
-
--- a/doc/book/en/devweb/views/primary.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,276 +0,0 @@
-.. _primary_view:
-
-The Primary View
------------------
-
-By default, *CubicWeb* provides a view that fits every available
-entity type. This is the first view you might be interested in
-modifying. It is also one of the richest and most complex.
-
-It is automatically selected on a one line result set containing an
-entity.
-
-It lives in the :mod:`cubicweb.web.views.primary` module.
-
-The *primary* view is supposed to render a maximum of informations about the
-entity.
-
-.. _primary_view_layout:
-
-Layout
-``````
-
-The primary view has the following layout.
-
-.. image:: ../../images/primaryview_template.png
-
-.. _primary_view_configuration:
-
-Primary view configuration
-``````````````````````````
-
-If you want to customize the primary view of an entity, overriding the primary
-view class may not be necessary. For simple adjustments (attributes or relations
-display locations and styles), a much simpler way is to use uicfg.
-
-Attributes/relations display location
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-In the primary view, there are three sections where attributes and
-relations can be displayed (represented in pink in the image above):
-
-* 'attributes'
-* 'relations'
-* 'sideboxes'
-
-**Attributes** can only be displayed in the attributes section (default
-  behavior). They can also be hidden. By default, attributes of type `Password`
-  and `Bytes` are hidden.
-
-For instance, to hide the ``title`` attribute of the ``Blog`` entity:
-
-.. sourcecode:: python
-
-   from cubicweb.web.views import uicfg
-   uicfg.primaryview_section.tag_attribute(('Blog', 'title'), 'hidden')
-
-**Relations** can be either displayed in one of the three sections or hidden.
-
-For relations, there are two methods:
-
-* ``tag_object_of`` for modifying the primary view of the object
-* ``tag_subject_of`` for modifying the primary view of the subject
-
-These two methods take two arguments:
-
-* a triplet ``(subject, relation_name, object)``, where subject or object can be replaced with ``'*'``
-* the section name or ``hidden``
-
-.. sourcecode:: python
-
-   pv_section = uicfg.primaryview_section
-   # hide every relation `entry_of` in the `Blog` primary view
-   pv_section.tag_object_of(('*', 'entry_of', 'Blog'), 'hidden')
-
-   # display `entry_of` relations in the `relations`
-   # section in the `BlogEntry` primary view
-   pv_section.tag_subject_of(('BlogEntry', 'entry_of', '*'), 'relations')
-
-
-Display content
-^^^^^^^^^^^^^^^
-
-You can use ``primaryview_display_ctrl`` to customize the display of attributes
-or relations. Values of ``primaryview_display_ctrl`` are dictionaries.
-
-
-Common keys for attributes and relations are:
-
-* ``vid``: specifies the regid of the view for displaying the attribute or the relation.
-
-  If ``vid`` is not specified, the default value depends on the section:
-    * ``attributes`` section: 'reledit' view
-    * ``relations`` section: 'autolimited' view
-    * ``sideboxes`` section: 'sidebox' view
-
-* ``order``: int used to control order within a section. When not specified,
-  automatically set according to order in which tags are added.
-
-* ``label``: label for the relations section or side box
-
-* ``showlabel``: boolean telling whether the label is displayed
-
-.. sourcecode:: python
-
-   # let us remind the schema of a blog entry
-   class BlogEntry(EntityType):
-       title = String(required=True, fulltextindexed=True, maxsize=256)
-       publish_date = Date(default='TODAY')
-       content = String(required=True, fulltextindexed=True)
-       entry_of = SubjectRelation('Blog', cardinality='?*')
-
-   # now, we want to show attributes
-   # with an order different from that in the schema definition
-   view_ctrl = uicfg.primaryview_display_ctrl
-   for index, attr in enumerate('title', 'content', 'publish_date'):
-       view_ctrl.tag_attribute(('BlogEntry', attr), {'order': index})
-
-By default, relations displayed in the 'relations' section are being displayed by
-the 'autolimited' view. This view will use comma separated values, or list view
-and/or limit your rset if there is too much items in it (and generate the "view
-all" link in this case).
-
-You can control this view by setting the following values in the
-`primaryview_display_ctrl` relation tag:
-
-* `limit`, maximum number of entities to display. The value of the
-  'navigation.related-limit'  cwproperty is used by default (which is 8 by default).
-  If None, no limit.
-
-* `use_list_limit`, number of entities until which they should be display as a list
-  (eg using the 'list' view). Below that limit, the 'csv' view is used. If None,
-  display using 'csv' anyway.
-
-* `subvid`, the subview identifier (eg view that should be used of each item in the
-  list)
-
-Notice you can also use the `filter` key to set up a callback taking the related
-result set as argument and returning it filtered, to do some arbitrary filtering
-that can't be done using rql for instance.
-
-
-.. sourcecode:: python
-
-   pv_section = uicfg.primaryview_section
-   # in `CWUser` primary view, display `created_by`
-   # relations in relations section
-   pv_section.tag_object_of(('*', 'created_by', 'CWUser'), 'relations')
-
-   # display this relation as a list, sets the label,
-   # limit the number of results and filters on comments
-   def filter_comment(rset):
-       return rset.filtered_rset(lambda x: x.e_schema == 'Comment')
-   pv_ctrl = uicfg.primaryview_display_ctrl
-   pv_ctrl.tag_object_of(('*', 'created_by', 'CWUser'),
-                         {'vid': 'list', 'label': _('latest comment(s):'),
-                          'limit': True,
-                          'filter': filter_comment})
-
-.. warning:: with the ``primaryview_display_ctrl`` rtag, the subject or the
-   object of the relation is ignored for respectively ``tag_object_of`` or
-   ``tag_subject_of``. To avoid warnings during execution, they should be set to
-   ``'*'``.
-
-
-.. automodule:: cubicweb.web.views.primary
-
-
-Example of customization and creation
-`````````````````````````````````````
-
-We'll show you now an example of a ``primary`` view and how to customize it.
-
-If you want to change the way a ``BlogEntry`` is displayed, just
-override the method ``cell_call()`` of the view ``primary`` in
-``BlogDemo/views.py``.
-
-.. sourcecode:: python
-
-   from cubicweb.predicates import is_instance
-   from cubicweb.web.views.primary import Primaryview
-
-   class BlogEntryPrimaryView(PrimaryView):
-       __select__ = PrimaryView.__select__ & is_instance('BlogEntry')
-
-       def render_entity_attributes(self, entity):
-           self.w(u'<p>published on %s</p>' %
-                  entity.publish_date.strftime('%Y-%m-%d'))
-           super(BlogEntryPrimaryView, self).render_entity_attributes(entity)
-
-
-The above source code defines a new primary view for
-``BlogEntry``. The `__reid__` class attribute is not repeated there since it
-is inherited through the `primary.PrimaryView` class.
-
-The selector for this view chains the selector of the inherited class
-with its own specific criterion.
-
-The view method ``self.w()`` is used to output data. Here `lines
-08-09` output HTML for the publication date of the entry.
-
-.. image:: ../../images/lax-book_09-new-view-blogentry_en.png
-   :alt: blog entries now look much nicer
-
-Let us now improve the primary view of a blog
-
-.. sourcecode:: python
-
- from logilab.mtconverter import xml_escape
- from cubicweb.predicates import is_instance, one_line_rset
- from cubicweb.web.views.primary import Primaryview
-
- class BlogPrimaryView(PrimaryView):
-     __regid__ = 'primary'
-     __select__ = PrimaryView.__select__ & is_instance('Blog')
-     rql = 'Any BE ORDERBY D DESC WHERE BE entry_of B, BE publish_date D, B eid %(b)s'
-
-     def render_entity_relations(self, entity):
-         rset = self._cw.execute(self.rql, {'b' : entity.eid})
-         for entry in rset.entities():
-             self.w(u'<p>%s</p>' % entry.view('inblogcontext'))
-
- class BlogEntryInBlogView(EntityView):
-     __regid__ = 'inblogcontext'
-     __select__ = is_instance('BlogEntry')
-
-     def cell_call(self, row, col):
-         entity = self.cw_rset.get_entity(row, col)
-         self.w(u'<a href="%s" title="%s">%s</a>' %
-                entity.absolute_url(),
-                xml_escape(entity.content[:50]),
-                xml_escape(entity.description))
-
-This happens in two places. First we override the
-render_entity_relations method of a Blog's primary view. Here we want
-to display our blog entries in a custom way.
-
-At `line 10`, a simple request is made to build a result set with all
-the entities linked to the current ``Blog`` entity by the relationship
-``entry_of``. The part of the framework handling the request knows
-about the schema and infers that such entities have to be of the
-``BlogEntry`` kind and retrieves them (in the prescribed publish_date
-order).
-
-The request returns a selection of data called a result set. Result
-set objects have an .entities() method returning a generator on
-requested entities (going transparently through the `ORM` layer).
-
-At `line 13` the view 'inblogcontext' is applied to each blog entry to
-output HTML. (Note that the 'inblogcontext' view is not defined
-whatsoever in *CubicWeb*. You are absolutely free to define whole view
-families.) We juste arrange to wrap each blogentry output in a 'p'
-html element.
-
-Next, we define the 'inblogcontext' view. This is NOT a primary view,
-with its well-defined sections (title, metadata, attribtues,
-relations/boxes). All a basic view has to define is cell_call.
-
-Since views are applied to result sets which can be tables of data, we
-have to recover the entity from its (row,col)-coordinates (`line
-20`). Then we can spit some HTML.
-
-.. warning::
-
-  Be careful: all strings manipulated in *CubicWeb* are actually
-  unicode strings. While web browsers are usually tolerant to
-  incoherent encodings they are being served, we should not abuse
-  it. Hence we have to properly escape our data. The xml_escape()
-  function has to be used to safely fill (X)HTML elements from Python
-  unicode strings.
-
-Assuming we added entries to the blog titled `MyLife`, displaying it
-now allows to read its description and all its entries.
-
-.. image:: ../../images/lax-book_10-blog-with-two-entries_en.png
-   :alt: a blog and all its entries
-
--- a/doc/book/en/devweb/views/reledit.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,149 +0,0 @@
-.. _reledit:
-
-The "Click and Edit" (also `reledit`) View
-------------------------------------------
-
-The principal way to update data through the Web UI is through the
-`modify` action on entities, which brings a full form. This is
-described in the :ref:`webform` chapter.
-
-There is however another way to perform piecewise edition of entities
-and relations, using a specific `reledit` (for *relation edition*)
-view from the :mod:`cubicweb.web.views.reledit` module.
-
-This is typically applied from the default Primary View (see
-:ref:`primary_view`) on the attributes and relation section. It makes
-small editions more convenient.
-
-Of course, this can be used customely in any other view. Here come
-some explanation about its capabilities and instructions on the way to
-use it.
-
-Using `reledit`
-***************
-
-Let's start again with a simple example:
-
-.. sourcecode:: python
-
-   class Company(EntityType):
-        name = String(required=True, unique=True)
-        boss = SubjectRelation('Person', cardinality='1*')
-        status = SubjectRelation('File', cardinality='?*', composite='subject')
-
-In some view code we might want to show these attributes/relations and
-allow the user to edit each of them in turn without having to leave
-the current page. We would write code as below:
-
-.. sourcecode:: python
-
-   company.view('reledit', rtype='name', default_value='<name>') # editable name attribute
-   company.view('reledit', rtype='boss') # editable boss relation
-   company.view('reledit', rtype='status') # editable attribute-like relation
-
-If one wanted to edit the company from a boss's point of view, one
-would have to indicate the proper relation's role. By default the role
-is `subject`.
-
-.. sourcecode:: python
-
-   person.view('reledit', rtype='boss', role='object')
-
-Each of these will provide with a different editing widget. The `name`
-attribute will obviously get a text input field. The `boss` relation
-will be edited through a selection box, allowing to pick another
-`Person` as boss. The `status` relation, given that it defines Company
-as a composite entity with one file inside, will provide additional actions
-
-* to `add` a `File` when there is one
-* to `delete` the `File` (if the cardinality allows it)
-
-Moreover, editing the relation or using the `add` action leads to an
-embedded edition/creation form allowing edition of the target entity
-(which is `File` in our example) instead of merely allowing to choose
-amongst existing files.
-
-The `reledit_ctrl` rtag
-***********************
-
-The behaviour of reledited attributes/relations can be finely
-controlled using the reledit_ctrl rtag, defined in
-:mod:`cubicweb.web.views.uicfg`.
-
-This rtag provides four control variables:
-
-* ``default_value``: alternative default value
-   The default value is what is shown when there is no value.
-* ``reload``: boolean, eid (to reload to) or function taking subject
-   and returning bool/eid This is useful when editing a relation (or
-   attribute) that impacts the url or another parts of the current
-   displayed page. Defaults to false.
-* ``rvid``: alternative view id (as str) for relation or composite
-   edition Default is 'incontext' or 'csv' depending on the
-   cardinality. They can also be statically changed by subclassing
-   ClickAndEditFormView and redefining _one_rvid (resp. _many_rvid).
-* ``edit_target``: 'rtype' (to edit the relation) or 'related' (to
-   edit the related entity) This controls whether to edit the relation
-   or the target entity of the relation.  Currently only one-to-one
-   relations support target entity edition. By default, the 'related'
-   option is taken whenever the relation is composite and one-to-one.
-
-Let's see how to use these controls.
-
-.. sourcecode:: python
-
-    from logilab.mtconverter import xml_escape
-    from cubicweb.web.views.uicfg import reledit_ctrl
-    reledit_ctrl.tag_attribute(('Company', 'name'),
-                               {'reload': lambda x:x.eid,
-                                'default_value': xml_escape(u'<logilab tastes better>')})
-    reledit_ctrl.tag_object_of(('*', 'boss', 'Person'), {'edit_target': 'related'})
-
-The `default_value` needs to be an xml escaped unicode string.
-
-The `edit_target` tag on the `boss` relation being set to `related` will
-ensure edition of the `Person` entity instead (using a standard
-automatic form) of the association of Company and Person.
-
-Finally, the `reload` key accepts either a boolean, an eid or an
-unicode string representing an url. If an eid is provided, it will be
-internally transformed into an url. The eid/url case helps when one
-needs to reload and the current url is inappropriate. A common case is
-edition of a key attribute, which is part of the current url. If one
-user changed the Company's name from `lozilab` to `logilab`, reloading
-on http://myapp/company/lozilab would fail. Providing the entity's
-eid, then, forces to reload on something like http://myapp/company/42,
-which always work.
-
-
-Disable `reledit`
-*****************
-
-By default, `reledit` is available on attributes and relations displayed in
-the 'attribute' section of the default primary view.  If you want to disable
-it for some attribute or relation, you have use `uicfg`:
-
-.. sourcecode:: python
-
-    from cubicweb.web.views.uicfg import primaryview_display_ctrl as _pvdc
-    _pvdc.tag_attribute(('Company', 'name'), {'vid': 'incontext'})
-
-To deactivate it everywhere it's used automatically, you may use the code snippet
-below somewhere in your cube's views:
-
-.. sourcecode:: python
-
-    from cubicweb.web.views import reledit
-
-    class DeactivatedAutoClickAndEditFormView(reledit.AutoClickAndEditFormView):
-	def _should_edit_attribute(self, rschema):
-	    return False
-
-	def _should_edit_attribute(self, rschema, role):
-	    return False
-
-    def registration_callback(vreg):
-	vreg.register_and_replace(DeactivatedAutoClickAndEditFormView,
-				  reledit.AutoClickAndEditFormView)
-
-
--- a/doc/book/en/devweb/views/startup.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-Startup views
--------------
-
-Startup views are views requiring no context, from which you usually start
-browsing (for instance the index page). The usual selectors are
-:class:`~cubicweb.predicates.none_rset` or :class:`~logilab.common.registry.yes`.
-
-You'll find here a description of startup views provided by the framework.
-
-.. automodule:: cubicweb.web.views.startup
-
-
-Other startup views:
-
-*schema*
-    A view dedicated to the display of the schema of the instance
-
-.. XXX to be continued
\ No newline at end of file
--- a/doc/book/en/devweb/views/table.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,145 +0,0 @@
-Table views
------------
-
-.. automodule:: cubicweb.web.views.tableview
-
-Example
-```````
-
-Let us take an example from the timesheet cube:
-
-.. sourcecode:: python
-
-    class ActivityResourcesTable(EntityView):
-        __regid__ = 'activity.resources.table'
-        __select__ = is_instance('Activity')
-
-        def call(self, showresource=True):
-            eids = ','.join(str(row[0]) for row in self.cw_rset)
-            rql = ('Any R,D,DUR,WO,DESCR,S,A, SN,RT,WT ORDERBY D DESC '
-                   'WHERE '
-                   '   A is Activity, A done_by R, R title RT, '
-                   '   A diem D, A duration DUR, '
-                   '   A done_for WO, WO title WT, '
-                   '   A description DESCR, A in_state S, S name SN, '
-                   '   A eid IN (%s)' % eids)
-            rset = self._cw.execute(rql)
-            self.wview('resource.table', rset, 'null')
-
-    class ResourcesTable(RsetTableView):
-        __regid__ = 'resource.table'
-        # notice you may wish a stricter selector to check rql's shape
-        __select__ = is_instance('Resource')
-        # my table headers
-        headers  = ['Resource', 'diem', 'duration', 'workpackage', 'description', 'state']
-        # I want a table where attributes are editable (reledit inside)
-        finalvid = 'editable-final'
-
-        cellvids = {3: 'editable-final'}
-        # display facets and actions with a menu
-        layout_args = {'display_filter': 'top',
-                       'add_view_actions': None}
-
-To obtain an editable table, you may specify the 'editable-table' view identifier
-using some of `cellvids`, `finalvid` or `nonfinalvid`.
-
-The previous example results in:
-
-.. image:: ../../images/views-table-shadow.png
-
-In order to activate table filter mechanism, the `display_filter` option is given
-as a layout argument. A small arrow will be displayed at the table's top right
-corner. Clicking on `show filter form` action, will display the filter form as
-below:
-
-.. image:: ../../images/views-table-filter-shadow.png
-
-By the same way, you can display additional actions for the selected entities
-by setting `add_view_actions` layout option to `True`. This will add actions
-returned by the view's :meth:`~cubicweb.web.views.tableview.TableMixIn.table_actions`.
-
-You can notice that all columns of the result set are not displayed. This is
-because of given `headers`, implying to display only columns from 0 to
-len(headers).
-
-Also Notice that the `ResourcesTable` view relies on a particular rql shape
-(which is not ensured by the way, the only checked thing is that the result set
-contains instance of the `Resource` type). That usually implies that you can't
-use this view for user specific queries (e.g. generated by facets or typed
-manually).
-
-
-So another option would be to write this view using
-:class:`~cubicweb.web.views.tableview.EntityTableView`, as below.
-
-.. sourcecode:: python
-
-    class ResourcesTable(EntityTableView):
-        __regid__ = 'resource.table'
-        __select__ = is_instance('Resource')
-        # table columns definition
-        columns  = ['resource', 'diem', 'duration', 'workpackage', 'description', 'in_state']
-        # I want a table where attributes are editable (reledit inside)
-        finalvid = 'editable-final'
-        # display facets and actions with a menu
-        layout_args = {'display_filter': 'top',
-                       'add_view_actions': None}
-
-        def workpackage_cell(entity):
-            activity = entity.reverse_done_in[0]
-            activity.view('reledit', rtype='done_for', role='subject', w=w)
-        def workpackage_sortvalue(entity):
-            activity = entity.reverse_done_in[0]
-            return activity.done_for[0].sortvalue()
-
-        column_renderers = {
-            'resource': MainEntityColRenderer(),
-            'workpackage': EntityTableColRenderer(
-               header='Workpackage',
-               renderfunc=worpackage_cell,
-               sortfunc=worpackage_sortvalue,),
-            'in_state': EntityTableColRenderer(
-               renderfunc=lambda w,x: w(x.cw_adapt_to('IWorkflowable').printable_state),
-               sortfunc=lambda x: x.cw_adapt_to('IWorkflowable').printable_state),
-         }
-
-Notice the following point:
-
-* `cell_<column>(w, entity)` will be searched for rendering the content of a
-  cell. If not found, `column` is expected to be an attribute of `entity`.
-
-* `cell_sortvalue_<column>(entity)` should return a typed value to use for
-  javascript sorting or None for not sortable columns (the default).
-
-* The :func:`etable_entity_sortvalue` decorator will set a 'sortvalue' function
-  for the column containing the main entity (the one given as argument to all
-  methods), which will call `entity.sortvalue()`.
-
-* You can set a column header using the :func:`etable_header_title` decorator.
-  This header will be translated. If it's not an already existing msgid, think
-  to mark it using `_()` (the example supposes headers are schema defined msgid).
-
-
-Pro/cons of each approach
-`````````````````````````
-:class:`EntityTableView` and :class:`RsetableView` provides basically the same
-set of features, though they don't share the same properties. Let's try to sum
-up pro and cons of each class.
-
-* `EntityTableView` view is:
-
-  - more verbose, but usually easier to understand
-
-  - easily extended (easy to add/remove columns for instance)
-
-  - doesn't rely on a particular rset shape. Simply give it a title and will be
-    listed in the 'possible views' box if any.
-
-* `RsetTableView` view is:
-
-  - hard to beat to display barely a result set, or for cases where some of
-    `headers`, `displaycols` or `cellvids` could be defined to enhance the table
-    while you don't care about e.g. pagination or facets.
-
-  - hardly extensible, as you usually have to change places where the view is
-    called to modify the RQL (hence the view's result set shape).
--- a/doc/book/en/devweb/views/urlpublish.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,137 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-URL publishing
---------------
-
-(:mod:`cubicweb.web.views.urlpublishing`)
-
-.. automodule:: cubicweb.web.views.urlpublishing
-
-.. autoclass:: cubicweb.web.views.urlpublishing.URLPublisherComponent
-   :members:
-
-
-You can write your own *URLPathEvaluator* class to handle custom paths.
-For instance, if you want */my-card-id* to redirect to the corresponding
-card's primary view, you would write:
-
-.. sourcecode:: python
-
-    class CardWikiidEvaluator(URLPathEvaluator):
-        priority = 3 # make it be evaluated *before* RestPathEvaluator
-
-        def evaluate_path(self, req, segments):
-            if len(segments) != 1:
-                raise PathDontMatch()
-            rset = req.execute('Any C WHERE C wikiid %(w)s',
-                               {'w': segments[0]})
-            if len(rset) == 0:
-                # Raise NotFound if no card is found
-                raise PathDontMatch()
-            return None, rset
-
-On the other hand, you can also deactivate some of the standard
-evaluators in your final application. The only thing you have to
-do is to unregister them, for instance in a *registration_callback*
-in your cube:
-
-.. sourcecode:: python
-
-    def registration_callback(vreg):
-        vreg.unregister(RestPathEvaluator)
-
-You can even replace the :class:`cubicweb.web.views.urlpublishing.URLPublisherComponent`
-class if you want to customize the whole toolchain process or if you want
-to plug into an early enough extension point to control your request
-parameters:
-
-.. sourcecode:: python
-
-    class SanitizerPublisherComponent(URLPublisherComponent):
-        """override default publisher component to explicitly ignore
-        unauthorized request parameters in anonymous mode.
-        """
-        unauthorized_form_params = ('rql', 'vid', '__login', '__password')
-
-        def process(self, req, path):
-            if req.session.anonymous_session:
-                self._remove_unauthorized_params(req)
-            return super(SanitizerPublisherComponent, self).process(req, path)
-
-        def _remove_unauthorized_params(self, req):
-            for param in req.form.keys():
-                if param in self.unauthorized_form_params:
-                     req.form.pop(param)
-
-
-    def registration_callback(vreg):
-        vreg.register_and_replace(SanitizerPublisherComponent, URLPublisherComponent)
-
-
-.. autoclass:: cubicweb.web.views.urlpublishing.RawPathEvaluator
-.. autoclass:: cubicweb.web.views.urlpublishing.EidPathEvaluator
-.. autoclass:: cubicweb.web.views.urlpublishing.URLRewriteEvaluator
-.. autoclass:: cubicweb.web.views.urlpublishing.RestPathEvaluator
-.. autoclass:: cubicweb.web.views.urlpublishing.ActionPathEvaluator
-
-URL rewriting
--------------
-
-(:mod:`cubicweb.web.views.urlrewrite`)
-
-.. autoclass:: cubicweb.web.views.urlrewrite.URLRewriter
-   :members:
-
-.. autoclass:: cubicweb.web.views.urlrewrite.SimpleReqRewriter
-   :members:
-
-.. autoclass:: cubicweb.web.views.urlrewrite.SchemaBasedRewriter
-   :members:
-
-
-``SimpleReqRewriter`` is enough for a certain number of simple cases. If it is not sufficient, ``SchemaBasedRewriter`` allows to do more elaborate things.
-
-Here is an example of ``SimpleReqRewriter`` usage with plain string:
-
-.. sourcecode:: python
-
-   from cubicweb.web.views.urlrewrite import SimpleReqRewriter
-   class TrackerSimpleReqRewriter(SimpleReqRewriter):
-       rules = [
-        ('/versions', dict(vid='versionsinfo')),
-        ]
-
-When the url is `<base_url>/versions`, the view with the __regid__ `versionsinfo` is displayed.
-
-Here is an example of ``SimpleReqRewriter`` usage with regular expressions:
-
-.. sourcecode:: python
-
-    from cubicweb.web.views.urlrewrite import (
-        SimpleReqRewriter, rgx)
-
-    class BlogReqRewriter(SimpleReqRewriter):
-        rules = [
-            (rgx('/blogentry/([a-z_]+)\.rss'),
-             dict(rql=('Any X ORDERBY CD DESC LIMIT 20 WHERE X is BlogEntry,'
-                       'X creation_date CD, X created_by U, '
-                       'U login "%(user)s"'
-                       % {'user': r'\1'}), vid='rss'))
-            ]
-
-When a url matches the regular expression, the view with the __regid__
-`rss` which match the result set is displayed.
-
-Here is an example of ``SchemaBasedRewriter`` usage:
-
-.. sourcecode:: python
-
-    from cubicweb.web.views.urlrewrite import (
-        SchemaBasedRewriter, rgx, build_rset)
-
-    class TrackerURLRewriter(SchemaBasedRewriter):
-        rules = [
-            (rgx('/project/([^/]+)/([^/]+)/tests'),
-             build_rset(rql='Version X WHERE X version_of P, P name %(project)s, X num %(num)s',
-                        rgxgroups=[('project', 1), ('num', 2)], vid='versiontests')),
-            ]
--- a/doc/book/en/devweb/views/views.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,120 +0,0 @@
-
-.. _Views:
-
-Principles
-----------
-
-We'll start with a description of the interface providing a basic
-understanding of the available classes and methods, then detail the
-view selection principle.
-
-A `View` is an object responsible for the rendering of data from the
-model into an end-user consummable form. They typically churn out an
-XHTML stream, but there are views concerned with email other non-html
-outputs.
-
-.. _views_base_class:
-
-Discovering possible views
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-It is possible to configure the web user interface to have a left box
-showing all the views than can be applied to the current result set.
-
-To enable this, click on your login at the top right corner. Chose
-"user preferences", then "boxes", then "possible views box" and check
-"visible = yes" before validating your changes.
-
-The views listed there we either not selected because of a lower
-score, or they were deliberately excluded by the main template logic.
-
-
-Basic class for views
-~~~~~~~~~~~~~~~~~~~~~
-
-Class :class:`~cubicweb.view.View`
-``````````````````````````````````
-
-.. autoclass:: cubicweb.view.View
-
-The basic interface for views is as follows (remember that the result
-set has a tabular structure with rows and columns, hence cells):
-
-* `render(**context)`, render the view by calling `call` or
-  `cell_call` depending on the context
-
-* `call(**kwargs)`, call the view for a complete result set or null
-  (the default implementation calls `cell_call()` on each cell of the
-  result set)
-
-* `cell_call(row, col, **kwargs)`, call the view for a given cell of a
-  result set (`row` and `col` being integers used to access the cell)
-
-* `url()`, returns the URL enabling us to get the view with the current
-  result set
-
-* `wview(__vid, rset, __fallback_vid=None, **kwargs)`, call the view of
-  identifier `__vid` on the given result set. It is possible to give a
-  fallback view identifier that will be used if the requested view is
-  not applicable to the result set.
-
-* `html_headers()`, returns a list of HTML headers to be set by the
-  main template
-
-* `page_title()`, returns the title to use in the HTML header `title`
-
-Other basic view classes
-````````````````````````
-Here are some of the subclasses of :class:`~cubicweb.view.View` defined in :mod:`cubicweb.view`
-that are more concrete as they relate to data rendering within the application:
-
-.. autoclass:: cubicweb.view.EntityView
-.. autoclass:: cubicweb.view.StartupView
-.. autoclass:: cubicweb.view.EntityStartupView
-.. autoclass:: cubicweb.view.AnyRsetView
-
-Examples of views class
-```````````````````````
-
-- Using `templatable`, `content_type` and HTTP cache configuration
-
-.. sourcecode:: python
-
-    class RSSView(XMLView):
-        __regid__ = 'rss'
-        title = _('rss')
-        templatable = False
-        content_type = 'text/xml'
-        http_cache_manager = MaxAgeHTTPCacheManager
-        cache_max_age = 60*60*2 # stay in http cache for 2 hours by default
-
-
-- Using a custom selector
-
-.. sourcecode:: python
-
-    class SearchForAssociationView(EntityView):
-        """view called by the edition view when the user asks
-        to search for something to link to the edited eid
-        """
-        __regid__ = 'search-associate'
-        title = _('search for association')
-        __select__ = one_line_rset() & match_search_state('linksearch') & is_instance('Any')
-
-
-XML views, binaries views...
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-For views generating other formats than HTML (an image generated dynamically
-for example), and which can not simply be included in the HTML page generated
-by the main template (see above), you have to:
-
-* set the attribute `templatable` of the class to `False`
-* set, through the attribute `content_type` of the class, the MIME
-  type generated by the view to `application/octet-stream` or any
-  relevant and more specialised mime type
-
-For views dedicated to binary content creation (like dynamically generated
-images), we have to set the attribute `binary` of the class to `True` (which
-implies that `templatable == False`, so that the attribute `w` of the view could be
-replaced by a binary flow instead of unicode).
--- a/doc/book/en/devweb/views/wdoc.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-Online documentation system
-===========================
-
-.. automodule:: cubicweb.web.views.wdoc
-
-Help views
-----------
-.. autoclass:: cubicweb.web.views.wdoc.InlineHelpView
-
-Actions
--------
-.. autoclass:: cubicweb.web.views.wdoc.HelpAction
-.. autoclass:: cubicweb.web.views.wdoc.AboutAction
--- a/doc/book/en/devweb/views/xmlrss.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-.. _XmlAndRss:
-
-XML and RSS views
------------------
-
-(:mod:`cubicweb.web.views.xmlrss`)
-
-Overview
-+++++++++
-
-*rss*
-    Creates a RSS/XML view and call the view `rssitem` for each entity of
-    the result set.
-
-*rssitem*
-    Create a RSS/XML view for each entity based on the results of the dublin core
-    methods of the entity (`dc_*`)
-
-RSS Channel Example
-++++++++++++++++++++
-
-Assuming you have several blog entries, click on the title of the
-search box in the left column. A larger search box should appear. Enter:
-
-.. sourcecode:: sql
-
-   Any X ORDERBY D WHERE X is BlogEntry, X creation_date D
-
-and you get a list of blog entries.
-
-Click on your login at the top right corner. Chose "user preferences",
-then "boxes", then "possible views box" and check "visible = yes"
-before validating your changes.
-
-Enter the same query in the search box and you will see the same list,
-plus a box titled "possible views" in the left column. Click on
-"entityview", then "RSS".
-
-You just applied the "RSS" view to the RQL selection you requested.
-
-That's it, you have a RSS channel for your blog.
-
-Try again with:
-
-.. sourcecode:: sql
-
-    Any X ORDERBY D WHERE X is BlogEntry, X creation_date D,
-    X entry_of B, B title "MyLife"
-
-Another RSS channel, but a bit more focused.
-
-A last one for the road:
-
-.. sourcecode:: sql
-
-    Any C ORDERBY D WHERE C is Comment, C creation_date D LIMIT 15
-
-displayed with the RSS view, that's a channel for the last fifteen
-comments posted.
-
-[WRITE ME]
-
-* show that the RSS view can be used to display an ordered selection
-  of blog entries, thus providing a RSS channel
-
-* show that a different selection (by category) means a different channel
Binary file doc/book/en/images/03-transitions-view_en.png has changed
Binary file doc/book/en/images/archi_globale.png has changed
Binary file doc/book/en/images/archi_globale_en.png has changed
Binary file doc/book/en/images/breadcrumbs_header.png has changed
Binary file doc/book/en/images/facet_date_range.png has changed
Binary file doc/book/en/images/facet_has_image.png has changed
Binary file doc/book/en/images/facet_overview.png has changed
Binary file doc/book/en/images/facet_range.png has changed
Binary file doc/book/en/images/lax-book_00-login_en.png has changed
Binary file doc/book/en/images/lax-book_01-start_en.png has changed
Binary file doc/book/en/images/lax-book_02-cookie-values_en.png has changed
Binary file doc/book/en/images/lax-book_02-create-blog_en.png has changed
Binary file doc/book/en/images/lax-book_03-list-one-blog_en.png has changed
Binary file doc/book/en/images/lax-book_03-site-config-panel_en.png has changed
Binary file doc/book/en/images/lax-book_03-state-submitted_en.png has changed
Binary file doc/book/en/images/lax-book_03-transitions-view_en.png has changed
Binary file doc/book/en/images/lax-book_04-detail-one-blog_en.png has changed
Binary file doc/book/en/images/lax-book_05-list-two-blog_en.png has changed
Binary file doc/book/en/images/lax-book_06-add-relation-entryof_en.png has changed
Binary file doc/book/en/images/lax-book_06-main-template-logo_en.png has changed
Binary file doc/book/en/images/lax-book_07-detail-one-blogentry_en.png has changed
Binary file doc/book/en/images/lax-book_08-schema_en.png has changed
Binary file doc/book/en/images/lax-book_09-new-view-blogentry_en.png has changed
Binary file doc/book/en/images/lax-book_10-blog-with-two-entries_en.png has changed
Binary file doc/book/en/images/main_template.png has changed
--- a/doc/book/en/images/main_template.svg	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,207 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="1036.6421"
-   height="845.07812"
-   id="svg2"
-   sodipodi:version="0.32"
-   inkscape:version="0.46"
-   sodipodi:docname="main_template.svg"
-   inkscape:output_extension="org.inkscape.output.svg.inkscape"
-   version="1.0"
-   inkscape:export-filename="/home/auc/cw/doc/book/en/images/main_template.png"
-   inkscape:export-xdpi="60.659016"
-   inkscape:export-ydpi="60.659016">
-  <defs
-     id="defs4">
-    <inkscape:perspective
-       sodipodi:type="inkscape:persp3d"
-       inkscape:vp_x="0 : 526.18109 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_z="744.09448 : 526.18109 : 1"
-       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
-       id="perspective10" />
-  </defs>
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="0.80355603"
-     inkscape:cx="510.91495"
-     inkscape:cy="422.53906"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     inkscape:window-width="925"
-     inkscape:window-height="1168"
-     inkscape:window-x="0"
-     inkscape:window-y="0"
-     inkscape:snap-bbox="true" />
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Calque 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(162.2968,90.697922)">
-    <rect
-       style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.775;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="rect2439"
-       width="854.37006"
-       height="698.2019"
-       x="20.307629"
-       y="-20.575344" />
-    <rect
-       style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.775;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="rect3301"
-       width="816.3457"
-       height="508.15628"
-       x="31.751091"
-       y="96.33345" />
-    <g
-       id="g3220"
-       transform="matrix(1.0035394,0,0,1,0.5745006,0)">
-      <rect
-         y="-89.447922"
-         x="-161.0468"
-         height="55.714287"
-         width="1031.1713"
-         id="rect3240"
-         style="fill:#dfdfdf;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.50000024;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
-      <text
-         id="text3264"
-         y="-51.771908"
-         x="757.85767"
-         style="font-size:23.38711166px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans"
-         xml:space="preserve"><tspan
-           id="tspan3266"
-           y="-51.771908"
-           x="757.85767"
-           sodipodi:role="line">header</tspan></text>
-    </g>
-    <rect
-       style="fill:#dfdfdf;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.775;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="rect3270"
-       width="167.87744"
-       height="707.71222"
-       x="-160.02441"
-       y="-24.671618" />
-    <g
-       id="g2434"
-       transform="matrix(0.975467,0,0,1,0.6942419,-3.6587365)">
-      <rect
-         y="35.365849"
-         x="29.548275"
-         height="55.714287"
-         width="842.59979"
-         id="rect3279"
-         style="fill:#dfdfdf;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
-      <text
-         id="text3281"
-         y="72.885193"
-         x="681.65283"
-         style="font-size:23.38711166px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans"
-         xml:space="preserve"><tspan
-           id="tspan3283"
-           y="72.885193"
-           x="681.65283"
-           sodipodi:role="line">contentheader</tspan></text>
-    </g>
-    <g
-       id="g3170"
-       transform="matrix(1.0023324,0,0,1,-2.0421673,-10.976211)">
-      <rect
-         y="698.6355"
-         x="-158.28485"
-         height="55.714287"
-         width="1032.5997"
-         id="rect3285"
-         style="fill:#dfdfdf;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
-      <text
-         id="text3287"
-         y="736.52045"
-         x="770.28204"
-         style="font-size:23.38711166px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans"
-         xml:space="preserve"><tspan
-           id="tspan3289"
-           y="736.52045"
-           x="770.28204"
-           sodipodi:role="line">footer</tspan></text>
-    </g>
-    <g
-       id="g3211" />
-    <g
-       id="g3215"
-       transform="matrix(0.9712065,0,0,1,0.7659296,-17.074106)">
-      <rect
-         style="fill:#dfdfdf;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-         id="rect3291"
-         width="844.62012"
-         height="55.714287"
-         x="27.850754"
-         y="629.88562" />
-      <text
-         id="text3293"
-         y="666.60339"
-         x="692.85773"
-         style="font-size:23.38711166px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans"
-         xml:space="preserve"><tspan
-           id="tspan3295"
-           y="666.60339"
-           x="692.85773"
-           sodipodi:role="line">contentfooter</tspan></text>
-    </g>
-    <text
-       xml:space="preserve"
-       style="font-size:23.38711166px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
-       x="-143.67273"
-       y="20.58094"
-       id="text3297"
-       sodipodi:linespacing="125%"><tspan
-         sodipodi:role="line"
-         id="tspan2432"
-         x="-143.67273"
-         y="20.58094">left column</tspan></text>
-    <text
-       transform="scale(0.9876573,1.0124969)"
-       id="text3175"
-       y="12.071429"
-       x="721.0575"
-       style="font-size:23.09845161px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans"
-       xml:space="preserve"><tspan
-         id="tspan3177"
-         y="12.071429"
-         x="721.0575"
-         sodipodi:role="line">contentcol</tspan></text>
-    <text
-       transform="scale(0.9876573,1.0124969)"
-       id="text3179"
-       y="126.27104"
-       x="701.45959"
-       style="font-size:23.09845161px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans"
-       xml:space="preserve"><tspan
-         id="tspan3181"
-         y="126.27104"
-         x="701.45959"
-         sodipodi:role="line">contentmain</tspan></text>
-  </g>
-</svg>
Binary file doc/book/en/images/main_template_layout.png has changed
Binary file doc/book/en/images/primaryview_template.png has changed
--- a/doc/book/en/images/primaryview_template.svg	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,285 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="1036.6421"
-   height="845.07812"
-   id="svg2"
-   sodipodi:version="0.32"
-   inkscape:version="0.46"
-   sodipodi:docname="primaryview_template.svg"
-   inkscape:output_extension="org.inkscape.output.svg.inkscape"
-   version="1.0"
-   inkscape:export-filename="/home/steph/local/fcubicweb/cubicweb/doc/book/en/images/primaryview_template.png"
-   inkscape:export-xdpi="43.451603"
-   inkscape:export-ydpi="43.451603">
-  <defs
-     id="defs4">
-    <inkscape:perspective
-       sodipodi:type="inkscape:persp3d"
-       inkscape:vp_x="0 : 526.18109 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_z="744.09448 : 526.18109 : 1"
-       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
-       id="perspective10" />
-  </defs>
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="0.9357135"
-     inkscape:cx="518.32104"
-     inkscape:cy="337.0428"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     inkscape:window-width="1307"
-     inkscape:window-height="1168"
-     inkscape:window-x="0"
-     inkscape:window-y="0" />
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Calque 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(162.2968,90.697922)">
-    <g
-       id="g3869"
-       transform="matrix(1,0,0,1.0373644,0,-72.039777)"
-       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
-       inkscape:export-xdpi="60.912449"
-       inkscape:export-ydpi="60.912449">
-      <rect
-         y="-15.840891"
-         x="-159.08963"
-         height="770.11017"
-         width="1033.0049"
-         id="rect3301"
-         style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.90144825;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
-      <text
-         id="text3865"
-         y="19.784882"
-         x="-150.07172"
-         style="font-size:28.67479324px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans"
-         xml:space="preserve"><tspan
-           id="tspan3867"
-           y="19.784882"
-           x="-150.07172"
-           sodipodi:role="line">contentmain</tspan></text>
-    </g>
-    <rect
-       style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.45654476;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="rect2383"
-       width="772.32111"
-       height="43.888428"
-       x="-131.1837"
-       y="86.559296"
-       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
-       inkscape:export-xdpi="60.912449"
-       inkscape:export-ydpi="60.912449" />
-    <text
-       xml:space="preserve"
-       style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.50000000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%"
-       x="-122.69418"
-       y="115.50363"
-       id="text2385"
-       sodipodi:linespacing="125%"
-       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
-       inkscape:export-xdpi="60.912449"
-       inkscape:export-ydpi="60.912449"><tspan
-         sodipodi:role="line"
-         x="-122.69418"
-         y="115.50363"
-         id="tspan3163">navcontenttop</tspan></text>
-    <rect
-       style="fill:#ffd5d5;fill-rule:evenodd;stroke:#000000;stroke-width:3.06523442;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="rect3167"
-       width="770.26868"
-       height="203.16078"
-       x="-125.88269"
-       y="172.90417"
-       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
-       inkscape:export-xdpi="60.912449"
-       inkscape:export-ydpi="60.912449" />
-    <text
-       xml:space="preserve"
-       style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
-       x="348.26724"
-       y="205.34305"
-       id="text3169"
-       sodipodi:linespacing="125%"
-       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
-       inkscape:export-xdpi="60.912449"
-       inkscape:export-ydpi="60.912449"><tspan
-         sodipodi:role="line"
-         x="348.26724"
-         y="205.34305"
-         id="tspan3171">render_entity_attributes()</tspan></text>
-    <rect
-       style="fill:#ffd5d5;fill-rule:evenodd;stroke:#000000;stroke-width:3.06523442;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="rect3173"
-       width="769.93549"
-       height="237.84663"
-       x="-125.03326"
-       y="391.32156"
-       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
-       inkscape:export-xdpi="60.912449"
-       inkscape:export-ydpi="60.912449" />
-    <text
-       xml:space="preserve"
-       style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
-       x="360.99954"
-       y="428.38055"
-       id="text3175"
-       sodipodi:linespacing="125%"
-       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
-       inkscape:export-xdpi="60.912449"
-       inkscape:export-ydpi="60.912449"><tspan
-         sodipodi:role="line"
-         x="360.99954"
-         y="428.38055"
-         id="tspan3177">render_entity_relations()</tspan></text>
-    <rect
-       style="fill:#ffd5d5;fill-rule:evenodd;stroke:#000000;stroke-width:2.15903592;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="rect3185"
-       width="178.93939"
-       height="612.36584"
-       x="667.10443"
-       y="84.64225"
-       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
-       inkscape:export-xdpi="60.912449"
-       inkscape:export-ydpi="60.912449" />
-    <text
-       xml:space="preserve"
-       style="font-size:22px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.50000000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%"
-       x="105.32364"
-       y="-810.65997"
-       id="text3187"
-       transform="matrix(0,1,-1,0,0,0)"
-       sodipodi:linespacing="125%"
-       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
-       inkscape:export-xdpi="60.912449"
-       inkscape:export-ydpi="60.912449"><tspan
-         sodipodi:role="line"
-         id="tspan2408">render_side_boxes()</tspan></text>
-    <rect
-       style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:3.0652349;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="rect3191"
-       width="771.97766"
-       height="55.647793"
-       x="-127.80586"
-       y="642.0293"
-       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
-       inkscape:export-xdpi="60.912449"
-       inkscape:export-ydpi="60.912449" />
-    <text
-       xml:space="preserve"
-       style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
-       x="-121.22153"
-       y="674.1748"
-       id="text3181"
-       sodipodi:linespacing="125%"
-       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
-       inkscape:export-xdpi="60.912449"
-       inkscape:export-ydpi="60.912449"><tspan
-         sodipodi:role="line"
-         x="-121.22153"
-         y="674.1748"
-         id="tspan3183">navcontentbottom</tspan></text>
-    <rect
-       style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.68198514;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="rect3881"
-       width="986.90503"
-       height="45.800392"
-       x="-128.34428"
-       y="-31.574066"
-       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
-       inkscape:export-xdpi="60.912449"
-       inkscape:export-ydpi="60.912449" />
-    <text
-       xml:space="preserve"
-       style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
-       x="355.60541"
-       y="-2.7424495"
-       id="text3883"
-       sodipodi:linespacing="125%"
-       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
-       inkscape:export-xdpi="60.912449"
-       inkscape:export-ydpi="60.912449"><tspan
-         sodipodi:role="line"
-         x="355.60541"
-         y="-2.7424495"
-         id="tspan3885">render_entity_toolbox(), render_entity_title()</tspan></text>
-    <rect
-       style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.68198514;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="rect3890"
-       width="986.90503"
-       height="45.800392"
-       x="-128.87863"
-       y="19.723684"
-       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
-       inkscape:export-xdpi="60.912449"
-       inkscape:export-ydpi="60.912449" />
-    <text
-       xml:space="preserve"
-       style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
-       x="565.71027"
-       y="50.135612"
-       id="text3892"
-       sodipodi:linespacing="125%"
-       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
-       inkscape:export-xdpi="60.912449"
-       inkscape:export-ydpi="60.912449"><tspan
-         sodipodi:role="line"
-         x="565.71027"
-         y="50.135612"
-         id="tspan3894">render_entity_summary()</tspan></text>
-    <text
-       xml:space="preserve"
-       style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
-       x="87.154541"
-       y="114.2578"
-       id="text3899"
-       sodipodi:linespacing="125%"
-       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
-       inkscape:export-xdpi="60.912449"
-       inkscape:export-ydpi="60.912449"><tspan
-         sodipodi:role="line"
-         id="tspan3903"
-         x="87.154541"
-         y="114.2578">content_navigation_components('navcontenttop')</tspan></text>
-    <text
-       xml:space="preserve"
-       style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
-       x="88.46772"
-       y="675.71582"
-       id="text2410"
-       sodipodi:linespacing="125%"
-       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
-       inkscape:export-xdpi="60.912449"
-       inkscape:export-ydpi="60.912449"><tspan
-         sodipodi:role="line"
-         id="tspan2412"
-         x="88.46772"
-         y="675.71582">content_navigation_components('navcontenttop')</tspan></text>
-  </g>
-</svg>
Binary file doc/book/en/images/request_session.png has changed
--- a/doc/book/en/images/request_session.svg	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,206 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="85.960938"
-   height="12.382812"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.48.3.1 r9886"
-   sodipodi:docname="request_session.svg">
-  <defs
-     id="defs4">
-    <marker
-       inkscape:stockid="Arrow1Lend"
-       orient="auto"
-       refY="0.0"
-       refX="0.0"
-       id="Arrow1Lend"
-       style="overflow:visible;">
-      <path
-         id="path3822"
-         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
-         style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
-         transform="scale(0.8) rotate(180) translate(12.5,0)" />
-    </marker>
-  </defs>
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="0.98994949"
-     inkscape:cx="25.928992"
-     inkscape:cy="-185.87004"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     fit-margin-top="0"
-     fit-margin-left="0"
-     fit-margin-right="0"
-     fit-margin-bottom="0"
-     inkscape:window-width="958"
-     inkscape:window-height="1160"
-     inkscape:window-x="0"
-     inkscape:window-y="38"
-     inkscape:window-maximized="0"
-     inkscape:snap-global="true" />
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(-263.52249,-495.73373)">
-    <rect
-       style="fill:#ffffff;stroke:#000000;stroke-width:0.92460138;stroke-opacity:1"
-       id="rect3773"
-       width="214.15233"
-       height="184.80336"
-       x="57.578697"
-       y="366.01306" />
-    <rect
-       id="rect2985"
-       width="216.86372"
-       height="183.54575"
-       x="348.50262"
-       y="367.78079"
-       style="fill:#ffffff;stroke:#000000;stroke-width:0.55298227;stroke-opacity:1" />
-    <text
-       xml:space="preserve"
-       style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
-       x="376.7869"
-       y="399.80365"
-       id="text3755"
-       sodipodi:linespacing="125%"><tspan
-         sodipodi:role="line"
-         id="tspan3757"
-         x="376.7869"
-         y="399.80365">Repository</tspan></text>
-    <rect
-       style="fill:#ffffff;stroke:#000000;stroke-opacity:1"
-       id="rect3759"
-       width="144.45181"
-       height="104.04572"
-       x="237.38585"
-       y="423.03714" />
-    <text
-       xml:space="preserve"
-       style="font-size:24px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
-       x="262.63968"
-       y="470.51431"
-       id="text3761"
-       sodipodi:linespacing="125%"><tspan
-         sodipodi:role="line"
-         id="tspan3763"
-         x="262.63968"
-         y="470.51431">REPOAPI</tspan></text>
-    <text
-       xml:space="preserve"
-       style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
-       x="262.63968"
-       y="507.88998"
-       id="text3765"
-       sodipodi:linespacing="125%"><tspan
-         sodipodi:role="line"
-         id="tspan3767"
-         x="262.63968"
-         y="507.88998">connection</tspan></text>
-    <text
-       xml:space="preserve"
-       style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
-       x="419.21332"
-       y="509.91025"
-       id="text3769"
-       sodipodi:linespacing="125%"><tspan
-         sodipodi:role="line"
-         id="tspan3771"
-         x="419.21332"
-         y="509.91025">session</tspan></text>
-    <text
-       xml:space="preserve"
-       style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
-       x="102.02541"
-       y="397.78333"
-       id="text3775"
-       sodipodi:linespacing="125%"><tspan
-         sodipodi:role="line"
-         id="tspan3777"
-         x="102.02541"
-         y="397.78333">Client</tspan></text>
-    <text
-       xml:space="preserve"
-       style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
-       x="116.16754"
-       y="507.88995"
-       id="text3779"
-       sodipodi:linespacing="125%"><tspan
-         sodipodi:role="line"
-         id="tspan3781"
-         x="116.16754"
-         y="507.88995">request</tspan></text>
-    <text
-       xml:space="preserve"
-       style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
-       x="361.50729"
-       y="585.89832"
-       id="text3802"
-       sodipodi:linespacing="125%"><tspan
-         sodipodi:role="line"
-         id="tspan3804"
-         x="361.50729"
-         y="585.89832">database </tspan><tspan
-         sodipodi:role="line"
-         x="361.50729"
-         y="605.89832"
-         id="tspan3806">connection</tspan></text>
-    <rect
-       style="fill:#ffffff;stroke:#000000;stroke-width:1.48014534;stroke-opacity:1"
-       id="rect3808"
-       width="192.09367"
-       height="58.095726"
-       x="365.79443"
-       y="621.50018" />
-    <text
-       xml:space="preserve"
-       style="font-size:36px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
-       x="369.5885"
-       y="662.66992"
-       id="text3810"
-       sodipodi:linespacing="125%"><tspan
-         sodipodi:role="line"
-         id="tspan3812"
-         x="369.5885"
-         y="662.66992">Database</tspan></text>
-    <path
-       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:none"
-       d="M 197.57252,125.76645 195.76971,55.592808"
-       id="path4260"
-       inkscape:connector-type="polyline"
-       inkscape:connector-curvature="3"
-       inkscape:connection-start="#rect3808"
-       inkscape:connection-start-point="d4"
-       inkscape:connection-end="#rect2985"
-       inkscape:connection-end-point="d4"
-       transform="translate(263.52249,495.73373)" />
-  </g>
-</svg>
Binary file doc/book/en/images/server-class-diagram.png has changed
Binary file doc/book/en/images/tutos-base_blog-form_en.png has changed
Binary file doc/book/en/images/tutos-base_blog-primary-after-post-creation_en.png has changed
Binary file doc/book/en/images/tutos-base_blog-primary_en.png has changed
Binary file doc/book/en/images/tutos-base_blogs-list_en.png has changed
Binary file doc/book/en/images/tutos-base_form-generic-relations_en.png has changed
Binary file doc/book/en/images/tutos-base_index_en.png has changed
Binary file doc/book/en/images/tutos-base_login-form_en.png has changed
Binary file doc/book/en/images/tutos-base_myblog-blogentry-taggable-commentable-primary_en.png has changed
Binary file doc/book/en/images/tutos-base_myblog-community-custom-primary_en.png has changed
Binary file doc/book/en/images/tutos-base_myblog-community-default-primary_en.png has changed
Binary file doc/book/en/images/tutos-base_myblog-community-taggable-primary_en.png has changed
Binary file doc/book/en/images/tutos-base_myblog-custom-footer_en.png has changed
Binary file doc/book/en/images/tutos-base_myblog-schema_en.png has changed
Binary file doc/book/en/images/tutos-base_myblog-siteinfo_en.png has changed
Binary file doc/book/en/images/tutos-base_schema_en.png has changed
Binary file doc/book/en/images/tutos-base_siteconfig_en.png has changed
Binary file doc/book/en/images/tutos-base_user-menu_en.png has changed
Binary file doc/book/en/images/tutos-photowebsite_background-image.png has changed
Binary file doc/book/en/images/tutos-photowebsite_boxes.png has changed
Binary file doc/book/en/images/tutos-photowebsite_breadcrumbs.png has changed
Binary file doc/book/en/images/tutos-photowebsite_facets.png has changed
Binary file doc/book/en/images/tutos-photowebsite_grey-box.png has changed
Binary file doc/book/en/images/tutos-photowebsite_index-after.png has changed
Binary file doc/book/en/images/tutos-photowebsite_index-before.png has changed
Binary file doc/book/en/images/tutos-photowebsite_login-box.png has changed
Binary file doc/book/en/images/tutos-photowebsite_prevnext.png has changed
Binary file doc/book/en/images/tutos-photowebsite_ui1.png has changed
Binary file doc/book/en/images/tutos-photowebsite_ui2.png has changed
Binary file doc/book/en/images/tutos-photowebsite_ui3.png has changed
Binary file doc/book/en/images/undo_history-view_w600.png has changed
Binary file doc/book/en/images/undo_mesage_w600.png has changed
Binary file doc/book/en/images/undo_startup-link_w600.png has changed
Binary file doc/book/en/images/views-table-filter-shadow.png has changed
Binary file doc/book/en/images/views-table-filter.png has changed
Binary file doc/book/en/images/views-table-shadow.png has changed
Binary file doc/book/en/images/views-table.png has changed
--- a/doc/book/en/index.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _contents:
-
-=====================================================
-|cubicweb| - The Semantic Web is a construction game!
-=====================================================
-
-|cubicweb| is a semantic web application framework, licensed under the LGPL,
-that empowers developers to efficiently build web applications by reusing
-components (called `cubes`) and following the well known object-oriented design
-principles.
-
-Its main features are:
-
-* an engine driven by the explicit :ref:`data model
-  <TutosBaseCustomizingTheApplicationDataModel>` of the application,
-
-* a query language named :ref:`RQL <RQL>` similar to W3C's SPARQL,
-
-* a :ref:`selection+view <TutosBaseCustomizingTheApplicationCustomViews>`
-  mechanism for semi-automatic XHTML/XML/JSON/text generation,
-
-* a library of reusable :ref:`components <Cube>` (data model and views) that
-  fulfill common needs,
-
-* the power and flexibility of the Python_ programming language,
-
-* the reliability of SQL databases, LDAP directories, Subversion and Mercurial
-  for storage backends.
-
-Built since 2000 from an R&D effort still continued, supporting 100,000s of
-daily visits at some production sites, |cubicweb| is a proven end to end solution
-for semantic web application development that promotes quality, reusability and
-efficiency.
-
-The unbeliever will read the :ref:`Tutorials`.
-
-The hacker will join development at the forge_.
-
-The impatient developer will move right away to :ref:`SetUpEnv` then to :ref:`ConfigEnv`.
-
-The chatter lover will join the `jabber forum`_, the `mailing-list`_ and the blog_.
-
-.. _Logilab: http://www.logilab.fr/
-.. _forge: http://www.cubicweb.org/project/
-.. _Python: http://www.python.org/
-.. _`jabber forum`: http://www.logilab.org/blogentry/6718
-.. _`mailing-list`: http://lists.cubicweb.org/mailman/listinfo/cubicweb
-.. _blog: http://www.cubicweb.org/blog/1238
-
-.. toctree::
-   :maxdepth: 2
-
-   intro/index
-   tutorials/index
-
-.. toctree::
-   :maxdepth: 3
-
-   devrepo/index
-   devweb/index
-
-.. toctree::
-   :maxdepth: 2
-
-   admin/index
-   additionnal_services/index
-   annexes/index
-
-See also:
-
-* the :ref:`genindex`,
-* the :ref:`modindex`,
--- a/doc/book/en/intro/concepts.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,306 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _Concepts:
-
-The Core Concepts of |cubicweb|
-===============================
-
-This section defines some terms and core concepts of the |cubicweb| framework. To
-avoid confusion while reading this book, take time to go through the following
-definitions and use this section as a reference during your reading.
-
-
-.. _Cube:
-
-Cubes
------
-
-A cube is a software component made of three parts: its data model
-(:mod:`schema`), its logic (:mod:`entities`) and its user interface
-(:mod:`views`).
-
-A cube can use other cubes as building blocks and assemble them to provide a
-whole with richer functionnalities than its parts. The cubes `cubicweb-blog`_ and
-`cubicweb-comment`_ could be used to make a cube named *myblog* with commentable
-blog entries.
-
-The `CubicWeb.org Forge`_ offers a large number of cubes developed by the community
-and available under a free software license.
-
-.. note::
-
- The command :command:`cubicweb-ctl list` displays the list of available cubes.
-
-.. _`CubicWeb.org Forge`: http://www.cubicweb.org/project/
-.. _`cubicweb-blog`: http://www.cubicweb.org/project/cubicweb-blog
-.. _`cubicweb-comment`: http://www.cubicweb.org/project/cubicweb-comment
-
-
-.. _Instance:
-
-Instances
----------
-
-An instance is a runnable application installed on a computer and based on a
-cube.
-
-The instance directory contains the configuration files. Several instances can be
-created and based on the same cube. For exemple, several software forges can be
-set up on one computer system based on the `cubicweb-forge`_ cube.
-
-.. _`cubicweb-forge`: http://www.cubicweb.org/project/cubicweb-forge
-
-Instances can be of three different types: all-in-one, web engine or data
-repository. For applications that support high traffic, several web (front-end)
-and data (back-end) instances can be set-up to share the load.
-
-.. image:: ../images/archi_globale_en.png
-
-The command :command:`cubicweb-ctl list` also displays the list of instances
-installed on your system.
-
-.. note::
-
-  The term application is used to refer to "something that should do something as
-  a whole", eg more like a project and so can refer to an instance or to a cube,
-  depending on the context. This book will try to use *application*, *cube* and
-  *instance* as appropriate.
-
-
-.. _RepositoryIntro:
-
-Data Repository
----------------
-
-The data repository [1]_ encapsulates and groups an access to one or
-more data sources (including SQL databases, LDAP repositories, other
-|cubicweb| instance repositories, filesystems, Google AppEngine's
-DataStore, etc).
-
-All interactions with the repository are done using the `Relation Query Language`
-(:ref:`RQL`). The repository federates the data sources and hides them from the
-querier, which does not realize when a query spans several data sources
-and requires running sub-queries and merges to complete.
-
-Application logic can be mapped to data events happenning within the
-repository, like creation of entities, deletion of relations,
-etc. This is used for example to send email notifications when the
-state of an object changes. See :ref:`HookIntro` below.
-
-.. [1] not to be confused with a Mercurial repository or a Debian repository.
-.. _`Python Remote Objects`: http://pythonhosted.org/Pyro4/
-
-.. _WebEngineIntro:
-
-Web Engine
-----------
-
-The web engine replies to http requests and runs the user interface.
-
-By default the web engine provides a `CRUD`_ user interface based on
-the data model of the instance. Entities can be created, displayed,
-updated and deleted. As the default user interface is not very fancy,
-it is usually necessary to develop your own.
-
-It is common to run the web engine and the repository in the same
-process (see instances of type all-in-one above), but this is not a
-requirement. A repository can be set up to be accessed remotely using
-Pyro (`Python Remote Objects`_) and act as a standalone server, which
-can be directly accessed or also through a standalone web engine.
-
-.. _`CRUD`: http://en.wikipedia.org/wiki/Create,_read,_update_and_delete
-
-.. _SchemaIntro:
-
-Schema (Data Model)
--------------------
-
-The data model of a cube is described as an entity-relationship schema using a
-comprehensive language made of Python classes imported from the yams_ library.
-
-.. _yams: http://www.logilab.org/project/yams/
-
-An `entity type` defines a sequence of attributes. Attributes may be
-of the following types: `String`, `Int`, `Float`, `Boolean`, `Date`,
-`Time`, `Datetime`, `Interval`, `Password`, `Bytes`, `RichString`.
-
-A `relation type` is used to define an oriented binary relation
-between entity types.  The left-hand part of a relation is named the
-`subject` and the right-hand part is named the `object`.
-
-A `relation definition` is a triple (*subject entity type*, *relation type*, *object
-entity type*) associated with a set of properties such as cardinality,
-constraints, etc.
-
-Permissions can be set on entity types or relation definition to control who
-will be able to create, read, update or delete entities and relations. Permissions
-are granted to groups (to which users may belong) or using rql expressions (if the
-rql expression returns some results, the permission is granted).
-
-Some meta-data necessary to the system are added to the data model. That includes
-entities like users and groups, the entities used to store the data model
-itself and attributes like unique identifier, creation date, creator, etc.
-
-When you create a new |cubicweb| instance, the schema is stored in the database.
-When the cubes the instance is based on evolve, they may change their data model
-and provide migration scripts that will be executed when the administrator will
-run the upgrade process for the instance.
-
-
-.. _VRegistryIntro:
-
-Registries and application objects
-----------------------------------
-
-Application objects
-~~~~~~~~~~~~~~~~~~~
-
-Besides a few core functionalities, almost every feature of the framework is
-achieved by dynamic objects (`application objects` or `appobjects`) stored in a
-two-levels registry. Each object is affected to a registry with
-an identifier in this registry. You may have more than one object sharing an
-identifier in the same registry:
-
-  object's `__registry__` : object's `__regid__` : [list of app objects]
-
-In other words, the `registry` contains several (sub-)registries which hold a
-list of appobjects associated to an identifier.
-
-The base class of appobjects is :class:`cubicweb.appobject.AppObject`.
-
-Selectors
-~~~~~~~~~
-
-At runtime, appobjects can be selected in a registry according to some
-contextual information. Selection is done by comparing the *score*
-returned by each appobject's *selector*.
-
-The better the object fits the context, the higher the score. Scores
-are the glue that ties appobjects to the data model. Using them
-appropriately is an essential part of the construction of well behaved
-cubes.
-
-|cubicweb| provides a set of basic selectors that may be parametrized.  Also,
-selectors can be combined with the `~` unary operator (negation) and the binary
-operators `&` and `|` (respectivly 'and' and 'or') to build more complex
-selectors. Of course complex selectors may be combined too. Last but not least, you
-can write your own selectors.
-
-The `registry`
-~~~~~~~~~~~~~~~
-
-At startup, the `registry` inspects a number of directories looking
-for compatible class definitions. After a recording process, the
-objects are assigned to registries and become available through the
-selection process.
-
-In a cube, application object classes are looked in the following modules or
-packages:
-
-- `entities`
-- `views`
-- `hooks`
-- `sobjects`
-
-There are three common ways to look up some application object from a
-registry:
-
-* get the most appropriate object by specifying an identifier and
-  context objects. The object with the greatest score is
-  selected. There should always be a single appobject with a greater
-  score than others for a particular context.
-
-* get all objects applying to a context by specifying a registry. A
-  list of objects will be returned containing the object with the
-  highest score (> 0) for each identifier in that registry.
-
-* get the object within a particular registry/identifier. No selection
-  process is involved: the registry will expect to find a single
-  object in that cell.
-
-
-.. _RQLIntro:
-
-The RQL query language
-----------------------
-
-No need for a complicated ORM when you have a powerful data
-manipulation language.
-
-All the persistent data in a |cubicweb| instance is retrieved and
-modified using RQL (see :ref:`rql_intro`).
-
-This query language is inspired by SQL but is on a higher level in order to
-emphasize browsing relations.
-
-
-Result set
-~~~~~~~~~~
-
-Every request made (using RQL) to the data repository returns an object we call a
-Result Set. It enables easy use of the retrieved data, providing a translation
-layer between the backend's native datatypes and |cubicweb| schema's EntityTypes.
-
-Result sets provide access to the raw data, yielding either basic Python data
-types, or schema-defined high-level entities, in a straightforward way.
-
-
-.. _ViewIntro:
-
-Views
------
-
-**CubicWeb is data driven**
-
-The view system is loosely coupled to data through the selection system explained
-above. Views are application objects with a dedicated interface to 'render'
-something, eg producing some html, text, xml, pdf, or whatsover that can be
-displayed to a user.
-
-Views actually are partitioned into different kind of objects such as
-`templates`, `boxes`, `components` and proper `views`, which are more
-high-level abstraction useful to build the user interface in an object
-oriented way.
-
-
-.. _HookIntro:
-
-Hooks and operations
---------------------
-
-**CubicWeb provides an extensible data repository**
-
-The data model defined using Yams types allows to express the data
-model in a comfortable way. However several aspects of the data model
-can not be expressed there. For instance:
-
-* managing computed attributes
-
-* enforcing complicated business rules
-
-* real-world side-effects linked to data events (email notification
-  being a prime example)
-
-The hook system is much like the triggers of an SQL database engine,
-except that:
-
-* it is not limited to one specific SQL backend (every one of them
-  having an idiomatic way to encode triggers), nor to SQL backends at
-  all (think about LDAP or a Subversion repository)
-
-* it is well-coupled to the rest of the framework
-
-Hooks are also application objects (in the `hooks` registry) and
-selected on events such as after/before add/update/delete on
-entities/relations, server startup or shutdown, etc.
-
-`Operations` may be instantiated by hooks to do further processing at different
-steps of the transaction's commit / rollback, which usually can not be done
-safely at the hook execution time.
-
-Hooks and operation are an essential building block of any moderately complicated
-cubicweb application.
-
-.. note::
-   RQL queries executed in hooks and operations are *unsafe* by default, i.e. the
-   read and write security is deactivated unless explicitly asked.
--- a/doc/book/en/intro/history.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-A little history...
-===================
-
-*CubicWeb* is a semantic web application framework that Logilab_ started
-developing in 2001 as an offspring of its Narval_ research project. *CubicWeb*
-is written in Python and includes a data server and a web engine.
-
-Its data server publishes data federated from different sources like
-SQL databases, LDAP directories, `VCS`_ repositories or even from other
-CubicWeb data servers.
-
-.. _`VCS`: http://en.wikipedia.org/wiki/Revision_control
-
-Its web engine was designed to let the final user control what content to select
-and how to display it. It allows one to browse the federated data sources and
-display the results with the rendering that best fits the context. This
-flexibility of the user interface gives back to the user some capabilities
-usually only accessible to application developers.
-
-*CubicWeb* has been developed by Logilab_ and used in-house for many years
-before it was first installed for its clients in 2006 as version 2.
-
-In 2008, *CubicWeb* version 3 became downloadable for free under the
-terms of the LGPL license. Its community is now steadily growing
-without hampering the fast-paced stream of changes thanks to the time
-and energy originally put in the design of the framework.
-
-
-.. _Narval: http://www.logilab.org/project/narval-moved
-.. _Logilab: http://www.logilab.fr/
--- a/doc/book/en/intro/index.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _Part1:
-
---------------------------
-Introduction to *CubicWeb*
---------------------------
-
-This first part of the book offers different reading path to
-discover the *CubicWeb* framework, provides a tutorial to get a quick
-overview of its features and lists its key concepts.
-
-
-.. toctree::
-   :maxdepth: 2
-   :numbered:
-
-   history
-   concepts.rst
--- a/doc/book/en/makefile	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-SRC=.
-
-# You can set these sphinx variables from the command line.
-SPHINXOPTS    =
-SPHINXBUILD   = sphinx-build
-PAPER         =
-#BUILDDIR      = build
-BUILDDIR      = ../..
-CWDIR         = ../../..
-JSDIR         = ${CWDIR}/web/data
-JSTORST       = ${CWDIR}/doc/tools/pyjsrest.py
-BUILDJS       = devweb/js_api
-
-# Internal variables for sphinx
-PAPEROPT_a4     = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS   = -d ${BUILDDIR}/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-
-
-
-.PHONY: help clean html web pickle htmlhelp latex changes linkcheck
-
-help:
-	@echo "Please use \`make <target>' where <target> is one of"
-	@echo "  all       to make standalone HTML files, developer manual and API doc"
-	@echo "  html      to make standalone HTML files"
-	@echo "---  "
-	@echo "  pickle    to make pickle files (usable by e.g. sphinx-web)"
-	@echo "  htmlhelp  to make HTML files and a HTML help project"
-	@echo "  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
-	@echo "  changes   to make an overview over all changed/added/deprecated items"
-	@echo "  linkcheck to check all external links for integrity"
-
-clean:
-	rm -f *.html
-	-rm -rf ${BUILDDIR}/html ${BUILDDIR}/doctrees
-	-rm -rf ${BUILDJS}
-
-all: html
-
-# run sphinx ###
-html: js
-	mkdir -p ${BUILDDIR}/html ${BUILDDIR}/doctrees
-	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) ${BUILDDIR}/html
-	@echo
-	@echo "Build finished. The HTML pages are in ${BUILDDIR}/html."
-
-js:
-	mkdir -p ${BUILDJS}
-	$(JSTORST) -p ${JSDIR} -o ${BUILDJS}
-
-pickle:
-	mkdir -p ${BUILDDIR}/pickle ${BUILDDIR}/doctrees
-	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) ${BUILDDIR}/pickle
-	@echo
-	@echo "Build finished; now you can process the pickle files or run"
-	@echo "  sphinx-web ${BUILDDIR}/pickle"
-	@echo "to start the sphinx-web server."
-
-web: pickle
-
-htmlhelp:
-	mkdir -p ${BUILDDIR}/htmlhelp ${BUILDDIR}/doctrees
-	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) ${BUILDDIR}/htmlhelp
-	@echo
-	@echo "Build finished; now you can run HTML Help Workshop with the" \
-	      ".hhp project file in ${BUILDDIR}/htmlhelp."
-
-latex:
-	mkdir -p ${BUILDDIR}/latex ${BUILDDIR}/doctrees
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) ${BUILDDIR}/latex
-	@echo
-	@echo "Build finished; the LaTeX files are in ${BUILDDIR}/latex."
-	@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
-	      "run these through (pdf)latex."
-
-changes:
-	mkdir -p ${BUILDDIR}/changes ${BUILDDIR}/doctrees
-	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) ${BUILDDIR}/changes
-	@echo
-	@echo "The overview file is in ${BUILDDIR}/changes."
-
-linkcheck:
-	mkdir -p ${BUILDDIR}/linkcheck ${BUILDDIR}/doctrees
-	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) ${BUILDDIR}/linkcheck
-	@echo
-	@echo "Link check complete; look for any errors in the above output " \
-	      "or in ${BUILDDIR}/linkcheck/output.txt."
--- a/doc/book/en/tutorials/advanced/index.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-
-.. _TutosPhotoWebSite:
-
-Building a photo gallery with |cubicweb|
-========================================
-
-Desired features
-----------------
-
-* basically a photo gallery
-
-* photo stored on the file system and displayed dynamically through a web interface
-
-* navigation through folder (album), tags, geographical zone, people on the
-  picture... using facets
-
-* advanced security (not everyone can see everything). More on this later.
-
-
-.. toctree::
-   :maxdepth: 2
-
-   part01_create-cube
-   part02_security
-   part03_bfss
-   part04_ui-base
-   part05_ui-advanced
-
-
--- a/doc/book/en/tutorials/advanced/part01_create-cube.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,155 +0,0 @@
-.. _TutosPhotoWebSiteCubeCreation:
-
-Cube creation and schema definition
------------------------------------
-
-.. _adv_tuto_create_new_cube:
-
-Step 1: creating a new cube for my web site
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-One note about my development environment: I wanted to use the packaged
-version of CubicWeb and cubes while keeping my cube in my user
-directory, let's say `~src/cubes`.  I achieve this by setting the
-following environment variables::
-
-  CW_CUBES_PATH=~/src/cubes
-  CW_MODE=user
-
-I can now create the cube which will hold custom code for this web
-site using::
-
-  cubicweb-ctl newcube --directory=~/src/cubes sytweb
-
-
-.. _adv_tuto_assemble_cubes:
-
-Step 2: pick building blocks into existing cubes
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Almost everything I want to handle in my web-site is somehow already modelized in
-existing cubes that I'll extend for my need. So I'll pick the following cubes:
-
-* `folder`, containing the `Folder` entity type, which will be used as
-  both 'album' and a way to map file system folders. Entities are
-  added to a given folder using the `filed_under` relation.
-
-* `file`, containing `File` entity type, gallery view, and a file system import
-  utility.
-
-* `zone`, containing the `Zone` entity type for hierarchical geographical
-  zones. Entities (including sub-zones) are added to a given zone using the
-  `situated_in` relation.
-
-* `person`, containing the `Person` entity type plus some basic views.
-
-* `comment`, providing a full commenting system allowing one to comment entity types
-  supporting the `comments` relation by adding a `Comment` entity.
-
-* `tag`, providing a full tagging system as an easy and powerful way to classify
-  entities supporting the `tags` relation by linking the to `Tag` entities. This
-  will allows navigation into a large number of picture.
-
-Ok, now I'll tell my cube requires all this by editing :file:`cubes/sytweb/__pkginfo__.py`:
-
-  .. sourcecode:: python
-
-    __depends__ = {'cubicweb': '>= 3.10.0',
-                   'cubicweb-file': '>= 1.9.0',
-		   'cubicweb-folder': '>= 1.1.0',
-		   'cubicweb-person': '>= 1.2.0',
-		   'cubicweb-comment': '>= 1.2.0',
-		   'cubicweb-tag': '>= 1.2.0',
-		   'cubicweb-zone': None}
-
-Notice that you can express minimal version of the cube that should be used,
-`None` meaning whatever version available. All packages starting with 'cubicweb-'
-will be recognized as being cube, not bare python packages. You can still specify
-this explicitly using instead the `__depends_cubes__` dictionary which should
-contains cube's name without the prefix. So the example below would be written
-as:
-
-  .. sourcecode:: python
-
-    __depends__ = {'cubicweb': '>= 3.10.0'}
-    __depends_cubes__ = {'file': '>= 1.9.0',
-		         'folder': '>= 1.1.0',
-		   	 'person': '>= 1.2.0',
-		   	 'comment': '>= 1.2.0',
-		   	 'tag': '>= 1.2.0',
-		   	 'zone': None}
-
-If your cube is packaged for debian, it's a good idea to update the
-`debian/control` file at the same time, so you won't forget it.
-
-
-Step 3: glue everything together in my cube's schema
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. sourcecode:: python
-
-    from yams.buildobjs import RelationDefinition
-
-    class comments(RelationDefinition):
-	subject = 'Comment'
-	object = 'File'
-	cardinality = '1*'
-	composite = 'object'
-
-    class tags(RelationDefinition):
-	subject = 'Tag'
-	object = 'File'
-
-    class filed_under(RelationDefinition):
-	subject = 'File'
-	object = 'Folder'
-
-    class situated_in(RelationDefinition):
-	subject = 'File'
-	object = 'Zone'
-
-    class displayed_on(RelationDefinition):
-	subject = 'Person'
-	object = 'File'
-
-
-This schema:
-
-* allows to comment and tag on `File` entity type by adding the `comments` and
-  `tags` relations. This should be all we've to do for this feature since the
-  related cubes provide 'pluggable section' which are automatically displayed on
-  the primary view of entity types supporting the relation.
-
-* adds a `situated_in` relation definition so that image entities can be
-  geolocalized.
-
-* add a new relation `displayed_on` relation telling who can be seen on a
-  picture.
-
-This schema will probably have to evolve as time goes (for security handling at
-least), but since the possibility to let a schema evolve is one of CubicWeb's
-features (and goals), we won't worry about it for now and see that later when needed.
-
-
-Step 4: creating the instance
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Now that I have a schema, I want to create an instance. To
-do so using this new 'sytweb' cube, I run::
-
-  cubicweb-ctl create sytweb sytweb_instance
-
-Hint: if you get an error while the database is initialized, you can
-avoid having to answer the questions again by running::
-
-   cubicweb-ctl db-create sytweb_instance
-
-This will use your already configured instance and start directly from the create
-database step, thus skipping questions asked by the 'create' command.
-
-Once the instance and database are fully initialized, run ::
-
-  cubicweb-ctl start sytweb_instance
-
-to start the instance, check you can connect on it, etc...
-
--- a/doc/book/en/tutorials/advanced/part02_security.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,440 +0,0 @@
-.. _TutosPhotoWebSiteSecurity:
-
-Security, testing and migration
--------------------------------
-
-This part will cover various topics:
-
-* configuring security
-* migrating existing instance
-* writing some unit tests
-
-Here is the ``read`` security model I want:
-
-* folders, files, images and comments should have one of the following visibility:
-
-  - ``public``, everyone can see it
-  - ``authenticated``, only authenticated users can see it
-  - ``restricted``, only a subset of authenticated users can see it
-
-* managers (e.g. me) can see everything
-* only authenticated users can see people
-* everyone can see classifier entities, such as tag and zone
-
-Also, unless explicitly specified, the visibility of an image should be the same as
-its parent folder, as well as visibility of a comment should be the same as the
-commented entity. If there is no parent entity, the default visibility is
-``authenticated``.
-
-Regarding write security, that's much easier:
-* anonymous can't write anything
-* authenticated users can only add comment
-* managers will add the remaining stuff
-
-Now, let's implement that!
-
-Proper security in CubicWeb is done at the schema level, so you don't have to
-bother with it in views: users will only see what they can see automatically.
-
-.. _adv_tuto_security:
-
-Step 1: configuring security into the schema
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In schema, you can grant access according to groups, or to some RQL expressions:
-users get access if the expression returns some results. To implement the read
-security defined earlier, groups are not enough, we'll need some RQL expression. Here
-is the idea:
-
-* add a `visibility` attribute on Folder, File and Comment, which may be one of
-  the value explained above
-
-* add a `may_be_read_by` relation from Folder, File and Comment to users,
-  which will define who can see the entity
-
-* security propagation will be done in hook.
-
-So the first thing to do is to modify my cube's schema.py to define those
-relations:
-
-.. sourcecode:: python
-
-    from yams.constraints import StaticVocabularyConstraint
-
-    class visibility(RelationDefinition):
-	subject = ('Folder', 'File', 'Comment')
-	object = 'String'
-	constraints = [StaticVocabularyConstraint(('public', 'authenticated',
-						   'restricted', 'parent'))]
-	default = 'parent'
-	cardinality = '11' # required
-
-    class may_be_read_by(RelationDefinition):
-        __permissions__ = {
-	    'read':   ('managers', 'users'),
-	    'add':    ('managers',),
-	    'delete': ('managers',),
-	    }
-
-	subject = ('Folder', 'File', 'Comment',)
-	object = 'CWUser'
-
-We can note the following points:
-
-* we've added a new `visibility` attribute to folder, file, image and comment
-  using a `RelationDefinition`
-
-* `cardinality = '11'` means this attribute is required. This is usually hidden
-  under the `required` argument given to the `String` constructor, but we can
-  rely on this here (same thing for StaticVocabularyConstraint, which is usually
-  hidden by the `vocabulary` argument)
-
-* the `parent` possible value will be used for visibility propagation
-
-* think to secure the `may_be_read_by` permissions, else any user can add/delete it
-  by default, which somewhat breaks our security model...
-
-Now, we should be able to define security rules in the schema, based on these new
-attribute and relation. Here is the code to add to *schema.py*:
-
-.. sourcecode:: python
-
-    from cubicweb.schema import ERQLExpression
-
-    VISIBILITY_PERMISSIONS = {
-	'read':   ('managers',
-		   ERQLExpression('X visibility "public"'),
-		   ERQLExpression('X may_be_read_by U')),
-	'add':    ('managers',),
-	'update': ('managers', 'owners',),
-	'delete': ('managers', 'owners'),
-	}
-    AUTH_ONLY_PERMISSIONS = {
-	    'read':   ('managers', 'users'),
-	    'add':    ('managers',),
-	    'update': ('managers', 'owners',),
-	    'delete': ('managers', 'owners'),
-	    }
-    CLASSIFIERS_PERMISSIONS = {
-	    'read':   ('managers', 'users', 'guests'),
-	    'add':    ('managers',),
-	    'update': ('managers', 'owners',),
-	    'delete': ('managers', 'owners'),
-	    }
-
-    from cubes.folder.schema import Folder
-    from cubes.file.schema import File
-    from cubes.comment.schema import Comment
-    from cubes.person.schema import Person
-    from cubes.zone.schema import Zone
-    from cubes.tag.schema import Tag
-
-    Folder.__permissions__ = VISIBILITY_PERMISSIONS
-    File.__permissions__ = VISIBILITY_PERMISSIONS
-    Comment.__permissions__ = VISIBILITY_PERMISSIONS.copy()
-    Comment.__permissions__['add'] = ('managers', 'users',)
-    Person.__permissions__ = AUTH_ONLY_PERMISSIONS
-    Zone.__permissions__ = CLASSIFIERS_PERMISSIONS
-    Tag.__permissions__ = CLASSIFIERS_PERMISSIONS
-
-What's important in there:
-
-* `VISIBILITY_PERMISSIONS` provides read access to managers group, if
-  `visibility` attribute's value is 'public', or if user (designed by the 'U'
-  variable in the expression) is linked to the entity (the 'X' variable) through
-  the `may_be_read_by` permission
-
-* we modify permissions of the entity types we use by importing them and
-  modifying their `__permissions__` attribute
-
-* notice the `.copy()`: we only want to modify 'add' permission for `Comment`,
-  not for all entity types using `VISIBILITY_PERMISSIONS`!
-
-* the remaining part of the security model is done using regular groups:
-
-  - `users` is the group to which all authenticated users will belong
-  - `guests` is the group of anonymous users
-
-
-.. _adv_tuto_security_propagation:
-
-Step 2: security propagation in hooks
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-To fullfill the requirements, we have to implement::
-
-  Also, unless explicity specified, visibility of an image should be the same as
-  its parent folder, as well as visibility of a comment should be the same as the
-  commented entity.
-
-This kind of `active` rule will be done using CubicWeb's hook
-system. Hooks are triggered on database events such as addition of a new
-entity or relation.
-
-The tricky part of the requirement is in *unless explicitly specified*, notably
-because when the entity is added, we don't know yet its 'parent'
-entity (e.g. Folder of an File, File commented by a Comment). To handle such things,
-CubicWeb provides `Operation`, which allow to schedule things to do at commit time.
-
-In our case we will:
-
-* on entity creation, schedule an operation that will set default visibility
-
-* when a "parent" relation is added, propagate parent's visibility unless the
-  child already has a visibility set
-
-Here is the code in cube's *hooks.py*:
-
-.. sourcecode:: python
-
-    from cubicweb.predicates import is_instance
-    from cubicweb.server import hook
-
-    class SetVisibilityOp(hook.DataOperationMixIn, hook.Operation):
-
-	def precommit_event(self):
-	    for eid in self.get_data():
-		entity = self.session.entity_from_eid(eid)
-		if entity.visibility == 'parent':
-		    entity.cw_set(visibility=u'authenticated')
-
-    class SetVisibilityHook(hook.Hook):
-	__regid__ = 'sytweb.setvisibility'
-	__select__ = hook.Hook.__select__ & is_instance('Folder', 'File', 'Comment')
-	events = ('after_add_entity',)
-
-	def __call__(self):
-	    SetVisibilityOp.get_instance(self._cw).add_data(self.entity.eid)
-
-    class SetParentVisibilityHook(hook.Hook):
-	__regid__ = 'sytweb.setparentvisibility'
-	__select__ = hook.Hook.__select__ & hook.match_rtype('filed_under', 'comments')
-	events = ('after_add_relation',)
-
-	def __call__(self):
-	    parent = self._cw.entity_from_eid(self.eidto)
-	    child = self._cw.entity_from_eid(self.eidfrom)
-	    if child.visibility == 'parent':
-		child.cw_set(visibility=parent.visibility)
-
-Notice:
-
-* hooks are application objects, hence have selectors that should match entity or
-  relation types to which the hook applies. To match a relation type, we use the
-  hook specific `match_rtype` selector.
-
-* usage of `DataOperationMixIn`: instead of adding an operation for each added entity,
-  DataOperationMixIn allows to create a single one and to store entity's eids to be
-  processed in the transaction data. This is a good pratice to avoid heavy
-  operations manipulation cost when creating a lot of entities in the same
-  transaction.
-
-* the `precommit_event` method of the operation will be called at transaction's
-  commit time.
-
-* in a hook, `self._cw` is the repository session, not a web request as usually
-  in views
-
-* according to hook's event, you have access to different attributes on the hook
-  instance. Here:
-
-  - `self.entity` is the newly added entity on 'after_add_entity' events
-
-  - `self.eidfrom` / `self.eidto` are the eid of the subject / object entity on
-    'after_add_relation' events (you may also get the relation type using
-    `self.rtype`)
-
-The `parent` visibility value is used to tell "propagate using parent security"
-because we want that attribute to be required, so we can't use None value else
-we'll get an error before we get any chance to propagate...
-
-Now, we also want to propagate the `may_be_read_by` relation. Fortunately,
-CubicWeb provides some base hook classes for such things, so we only have to add
-the following code to *hooks.py*:
-
-.. sourcecode:: python
-
-    # relations where the "parent" entity is the subject
-    S_RELS = set()
-    # relations where the "parent" entity is the object
-    O_RELS = set(('filed_under', 'comments',))
-
-    class AddEntitySecurityPropagationHook(hook.PropagateRelationHook):
-	"""propagate permissions when new entity are added"""
-	__regid__ = 'sytweb.addentity_security_propagation'
-	__select__ = (hook.PropagateRelationHook.__select__
-		      & hook.match_rtype_sets(S_RELS, O_RELS))
-	main_rtype = 'may_be_read_by'
-	subject_relations = S_RELS
-	object_relations = O_RELS
-
-    class AddPermissionSecurityPropagationHook(hook.PropagateRelationAddHook):
-	"""propagate permissions when new entity are added"""
-	__regid__ = 'sytweb.addperm_security_propagation'
-	__select__ = (hook.PropagateRelationAddHook.__select__
-		      & hook.match_rtype('may_be_read_by',))
-	subject_relations = S_RELS
-	object_relations = O_RELS
-
-    class DelPermissionSecurityPropagationHook(hook.PropagateRelationDelHook):
-	__regid__ = 'sytweb.delperm_security_propagation'
-	__select__ = (hook.PropagateRelationDelHook.__select__
-		      & hook.match_rtype('may_be_read_by',))
-	subject_relations = S_RELS
-	object_relations = O_RELS
-
-* the `AddEntitySecurityPropagationHook` will propagate the relation
-  when `filed_under` or `comments` relations are added
-
-  - the `S_RELS` and `O_RELS` set as well as the `match_rtype_sets` selector are
-    used here so that if my cube is used by another one, it'll be able to
-    configure security propagation by simply adding relation to one of the two
-    sets.
-
-* the two others will propagate permissions changes on parent entities to
-  children entities
-
-
-.. _adv_tuto_tesing_security:
-
-Step 3: testing our security
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Security is tricky. Writing some tests for it is a very good idea. You should
-even write them first, as Test Driven Development recommends!
-
-Here is a small test case that will check the basis of our security
-model, in *test/unittest_sytweb.py*:
-
-.. sourcecode:: python
-
-    from cubicweb.devtools.testlib import CubicWebTC
-    from cubicweb import Binary
-
-    class SecurityTC(CubicWebTC):
-
-        def test_visibility_propagation(self):
-            with self.admin_access.repo_cnx() as cnx:
-                # create a user for later security checks
-                toto = self.create_user(cnx, 'toto')
-                cnx.commit()
-                # init some data using the default manager connection
-                folder = cnx.create_entity('Folder',
-                                           name=u'restricted',
-                                           visibility=u'restricted')
-                photo1 = cnx.create_entity('File',
-                                           data_name=u'photo1.jpg',
-                                           data=Binary('xxx'),
-                                           filed_under=folder)
-                cnx.commit()
-                # visibility propagation
-                self.assertEquals(photo1.visibility, 'restricted')
-                # unless explicitly specified
-                photo2 = cnx.create_entity('File',
-                                           data_name=u'photo2.jpg',
-                                           data=Binary('xxx'),
-                                           visibility=u'public',
-                                           filed_under=folder)
-                cnx.commit()
-                self.assertEquals(photo2.visibility, 'public')
-            with self.new_access('toto').repo_cnx() as cnx:
-                # test security
-                self.assertEqual(1, len(cnx.execute('File X'))) # only the public one
-                self.assertEqual(0, len(cnx.execute('Folder X'))) # restricted...
-            with self.admin_access.repo_cnx() as cnx:
-                # may_be_read_by propagation
-                folder = cnx.entity_from_eid(folder.eid)
-                folder.cw_set(may_be_read_by=toto)
-                cnx.commit()
-            with self.new_access('toto').repo_cnx() as cnx:
-                photo1 = cnx.entity_from_eid(photo1.eid)
-                self.failUnless(photo1.may_be_read_by)
-                # test security with permissions
-                self.assertEquals(2, len(cnx.execute('File X'))) # now toto has access to photo2
-                self.assertEquals(1, len(cnx.execute('Folder X'))) # and to restricted folder
-
-    if __name__ == '__main__':
-        from logilab.common.testlib import unittest_main
-        unittest_main()
-
-It's not complete, but shows most things you'll want to do in tests: adding some
-content, creating users and connecting as them in the test, etc...
-
-To run it type:
-
-.. sourcecode:: bash
-
-    $ pytest unittest_sytweb.py
-    ========================  unittest_sytweb.py  ========================
-    -> creating tables [....................]
-    -> inserting default user and default groups.
-    -> storing the schema in the database [....................]
-    -> database for instance data initialized.
-    .
-    ----------------------------------------------------------------------
-    Ran 1 test in 22.547s
-
-    OK
-
-
-The first execution is taking time, since it creates a sqlite database for the
-test instance. The second one will be much quicker:
-
-.. sourcecode:: bash
-
-    $ pytest unittest_sytweb.py
-    ========================  unittest_sytweb.py  ========================
-    .
-    ----------------------------------------------------------------------
-    Ran 1 test in 2.662s
-
-    OK
-
-If you do some changes in your schema, you'll have to force regeneration of that
-database. You do that by removing the tmpdb files before running the test: ::
-
-    $ rm data/database/tmpdb*
-
-
-.. Note::
-  pytest is a very convenient utility used to control test execution. It is available from the `logilab-common`_ package.
-
-.. _`logilab-common`: http://www.logilab.org/project/logilab-common
-
-.. _adv_tuto_migration_script:
-
-Step 4: writing the migration script and migrating the instance
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Prior to those changes, I created an instance, fed it with some data, so I
-don't want to create a new one, but to migrate the existing one. Let's see how to
-do that.
-
-Migration commands should be put in the cube's *migration* directory, in a
-file named file:`<X.Y.Z>_Any.py` ('Any' being there mostly for historical reasons).
-
-Here I'll create a *migration/0.2.0_Any.py* file containing the following
-instructions:
-
-.. sourcecode:: python
-
-  add_relation_type('may_be_read_by')
-  add_relation_type('visibility')
-  sync_schema_props_perms()
-
-Then I update the version number in the cube's *__pkginfo__.py* to 0.2.0. And
-that's it! Those instructions will:
-
-* update the instance's schema by adding our two new relations and update the
-  underlying database tables accordingly (the first two instructions)
-
-* update schema's permissions definition (the last instruction)
-
-
-To migrate my instance I simply type::
-
-   cubicweb-ctl upgrade sytweb_instance
-
-You'll then be asked some questions to do the migration step by step. You should say
-YES when it asks if a backup of your database should be done, so you can get back
-to initial state if anything goes wrong...
--- a/doc/book/en/tutorials/advanced/part03_bfss.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-Storing images on the file-system
----------------------------------
-
-Step 1: configuring the BytesFileSystem storage
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-To avoid cluttering my database, and to ease file manipulation, I don't want them
-to be stored in the database. I want to be able create File entities for some
-files on the server file system, where those file will be accessed to get
-entities data. To do so I've to set a custom :class:`BytesFileSystemStorage`
-storage for the File 'data' attribute, which hold the actual file's content.
-
-Since the function to register a custom storage needs to have a repository
-instance as first argument, we've to call it in a server startup hook. So I added
-in `cubes/sytweb/hooks.py` :
-
-.. sourcecode:: python
-
-    from os import makedirs
-    from os.path import join, exists
-
-    from cubicweb.server import hook
-    from cubicweb.server.sources import storages
-
-    class ServerStartupHook(hook.Hook):
-        __regid__ = 'sytweb.serverstartup'
-        events = ('server_startup', 'server_maintenance')
-
-        def __call__(self):
-            bfssdir = join(self.repo.config.appdatahome, 'bfss')
-            if not exists(bfssdir):
-                makedirs(bfssdir)
-                print 'created', bfssdir
-            storage = storages.BytesFileSystemStorage(bfssdir)
-            storages.set_attribute_storage(self.repo, 'File', 'data', storage)
-
-.. Note::
-
-  * how we built the hook's registry identifier (`__regid__`): you can introduce
-    'namespaces' by using there python module like naming identifiers. This is
-    especially important for hooks where you usually want a new custom hook, not
-    overriding / specializing an existant one, but the concept may be applied to
-    any application objects
-
-  * we catch two events here: "server_startup" and "server_maintenance". The first
-    is called on regular repository startup (eg, as a server), the other for
-    maintenance task such as shell or upgrade. In both cases, we need to have
-    the storage set, else we'll be in trouble...
-
-  * the path given to the storage is the place where file added through the ui
-    (or in the database before migration) will be located
-
-  * beware that by doing this, you can't anymore write queries that will try to
-    restrict on File `data` attribute. Hopefuly we don't do that usually
-    on file's content or more generally on attributes for the Bytes type
-
-Now, if you've already added some photos through the web ui, you'll have to
-migrate existing data so file's content will be stored on the file-system instead
-of the database. There is a migration command to do so, let's run it in the
-cubicweb shell (in real life, you would have to put it in a migration script as we
-have seen last time):
-
-::
-
-   $ cubicweb-ctl shell sytweb_instance
-   entering the migration python shell
-   just type migration commands or arbitrary python code and type ENTER to execute it
-   type "exit" or Ctrl-D to quit the shell and resume operation
-   >>> storage_changed('File', 'data')
-   [........................]
-
-
-That's it. Now, files added through the web ui will have their content stored on
-the file-system, and you'll also be able to import files from the file-system as
-explained in the next part.
-
-Step 2: importing some data into the instance
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Hey, we start to have some nice features, let us give a try to this new web
-site. For instance if I have a 'photos/201005WePyrenees' containing pictures for
-a particular event, I can import it to my web site by typing ::
-
-  $ cubicweb-ctl fsimport -F sytweb_instance photos/201005WePyrenees/
-  ** importing directory /home/syt/photos/201005WePyrenees
-  importing IMG_8314.JPG
-  importing IMG_8274.JPG
-  importing IMG_8286.JPG
-  importing IMG_8308.JPG
-  importing IMG_8304.JPG
-
-.. Note::
-  The -F option means that folders should be mapped, hence my photos will be
-  linked to a Folder entity corresponding to the file-system folder.
-
-Let's take a look at the web ui:
-
-.. image:: ../../images/tutos-photowebsite_ui1.png
-
-Nothing different, I can't see the new folder... But remember our security model!
-By default, files are only accessible to authenticated users, and I'm looking at
-the site as anonymous, e.g. not authenticated. If I login, I can now see:
-
-.. image:: ../../images/tutos-photowebsite_ui2.png
-
-Yeah, it's there! You will notice that I can see some entities as well as
-folders and images the anonymous user can't. It just works **everywhere in the
-ui** since it's handled at the repository level, thanks to our security model.
-
-Now if I click on the recently inserted folder, I can see
-
-.. image:: ../../images/tutos-photowebsite_ui3.png
-
-Great! There is even my pictures in the folder. I can know give to this folder a
-nicer name (provided I don't intend to import from it anymore, else already
-imported photos will be reimported), change permissions, title for some pictures,
-etc... Having a good content is much more difficult than having a good web site
-;)
-
-
-Conclusion
-~~~~~~~~~~
-
-We started to see here an advanced feature of our repository: the ability
-to store some parts of our data-model into a custom storage, outside the
-database. There is currently only the :class:`BytesFileSystemStorage` available,
-but you can expect to see more coming in a near future (or write your own!).
-
-Also, we can know start to feed our web-site with some nice pictures!
-The site isn't perfect (far from it actually) but it's usable, and we can
-now start using it and improve it on the way. The Incremental Cubic Way :)
--- a/doc/book/en/tutorials/advanced/part04_ui-base.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,361 +0,0 @@
-Let's make it more user friendly
-================================
-
-
-Step 1: let's improve site's usability for our visitors
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The first thing I've noticed is that people to whom I send links to photos with
-some login/password authentication get lost, because they don't grasp they have
-to login by clicking on the 'authenticate' link. That's much probably because
-they only get a 404 when trying to access an unauthorized folder, and the site
-doesn't make clear that 1. you're not authenticated, 2. you could get more
-content by authenticating yourself.
-
-So, to improve this situation, I decided that I should:
-
-* make a login box appears for anonymous, so they see at a first glance a place
-  to put the login / password information I provided
-
-* customize the 404 page, proposing to login to anonymous.
-
-Here is the code, samples from my cube's `views.py` file:
-
-.. sourcecode:: python
-
-    from cubicweb.predicates import is_instance
-    from cubicweb.web import component
-    from cubicweb.web.views import error
-    from cubicweb.predicates import anonymous_user
-
-    class FourOhFour(error.FourOhFour):
-	__select__ = error.FourOhFour.__select__ & anonymous_user()
-
-	def call(self):
-	    self.w(u"<h1>%s</h1>" % self._cw._('this resource does not exist'))
-	    self.w(u"<p>%s</p>" % self._cw._('have you tried to login?'))
-
-
-    class LoginBox(component.CtxComponent):
-	"""display a box containing links to all startup views"""
-	__regid__ = 'sytweb.loginbox'
-	__select__ = component.CtxComponent.__select__ & anonymous_user()
-
-	title = _('Authenticate yourself')
-	order = 70
-
-	def render_body(self, w):
-	    cw = self._cw
-	    form = cw.vreg['forms'].select('logform', cw)
-	    form.render(w=w, table_class='', display_progress_div=False)
-
-The first class provides a new specific implementation of the default page you
-get on 404 error, to display an adapted message to anonymous user.
-
-.. Note::
-
-  Thanks to the selection mecanism, it will be selected for anoymous user,
-  since the additional `anonymous_user()` selector gives it a higher score than
-  the default, and not for authenticated since this selector will return 0 in
-  such case (hence the object won't be selectable)
-
-The second class defines a simple box, that will be displayed by default with
-boxes in the left column, thanks to default :class:`component.CtxComponent`
-selector. The HTML is written to match default CubicWeb boxes style. The code
-fetch the actual login form and render it.
-
-
-.. figure:: ../../images/tutos-photowebsite_login-box.png
-   :alt: login box / 404 screenshot
-
-   The login box and the custom 404 page for an anonymous visitor (translated in french)
-
-
-Step 2: providing a custom index page
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Another thing we can easily do to improve the site is... A nicer index page
-(e.g. the first page you get when accessing the web site)! The default one is
-quite intimidating (that should change in a near future). I will provide a much
-simpler index page that simply list available folders (e.g. photo albums in that
-site).
-
-.. sourcecode:: python
-
-    from cubicweb.web.views import startup
-
-    class IndexView(startup.IndexView):
-	def call(self, **kwargs):
-	    self.w(u'<div>\n')
-	    if self._cw.cnx.anonymous_connection:
-		self.w(u'<h4>%s</h4>\n' % self._cw._('Public Albums'))
-	    else:
-		self.w(u'<h4>%s</h4>\n' % self._cw._('Albums for %s') % self._cw.user.login)
-	    self._cw.vreg['views'].select('tree', self._cw).render(w=self.w)
-	    self.w(u'</div>\n')
-
-    def registration_callback(vreg):
-	vreg.register_all(globals().values(), __name__, (IndexView,))
-	vreg.register_and_replace(IndexView, startup.IndexView)
-
-As you can see, we override the default index view found in
-`cubicweb.web.views.startup`, geting back nothing but its identifier and selector
-since we override the top level view's `call` method.
-
-.. Note::
-
-  in that case, we want our index view to **replace** the existing one. To do so
-  we've to implements the `registration_callback` function, in which we tell to
-  register everything in the module *but* our IndexView, then we register it
-  instead of the former index view.
-
-Also, we added a title that tries to make it more evident that the visitor is
-authenticated, or not. Hopefuly people will get it now!
-
-
-.. figure:: ../../images/tutos-photowebsite_index-before.png
-   :alt: default index page screenshot
-
-   The default index page
-
-.. figure:: ../../images/tutos-photowebsite_index-after.png
-   :alt: new index page screenshot
-
-   Our simpler, less intimidating, index page (still translated in french)
-
-
-Step 3: more navigation improvments
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-There are still a few problems I want to solve...
-
-* Images in a folder are displayed in a somewhat random order. I would like to
-  have them ordered by file's name (which will usually, inside a given folder,
-  also result ordering photo by their date and time)
-
-* When clicking a photo from an album view, you've to get back to the gallery
-  view to go to the next photo. This is pretty annoying...
-
-* Also, when viewing an image, there is no clue about the folder to which this
-  image belongs to.
-
-I will first try to explain the ordering problem. By default, when accessing
-related entities by using the ORM's API, you should get them ordered according to
-the target's class `cw_fetch_order`. If we take a look at the file cube'schema,
-we can see:
-
-.. sourcecode:: python
-
-    class File(AnyEntity):
-	"""customized class for File entities"""
-	__regid__ = 'File'
-	fetch_attrs, cw_fetch_order = fetch_config(['data_name', 'title'])
-
-
-By default, `fetch_config` will return a `cw_fetch_order` method that will order
-on the first attribute in the list. So, we could expect to get files ordered by
-their name. But we don't.  What's up doc ?
-
-The problem is that files are related to folder using the `filed_under` relation.
-And that relation is ambiguous, eg it can lead to `File` entities, but also to
-`Folder` entities. In such case, since both entity types doesn't share the
-attribute on which we want to sort, we'll get linked entities sorted on a common
-attribute (usually `modification_date`).
-
-To fix this, we've to help the ORM. We'll do this in the method from the `ITree`
-folder's adapter, used in the folder's primary view to display the folder's
-content. Here's the code, that I've put in our cube's `entities.py` file, since
-it's more logical stuff than view stuff:
-
-.. sourcecode:: python
-
-    from cubes.folder import entities as folder
-
-    class FolderITreeAdapter(folder.FolderITreeAdapter):
-
-	def different_type_children(self, entities=True):
-	    rql = self.entity.cw_related_rql(self.tree_relation,
-					     self.parent_role, ('File',))
-	    rset = self._cw.execute(rql, {'x': self.entity.eid})
-	    if entities:
-		return list(rset.entities())
-	    return rset
-
-    def registration_callback(vreg):
-	vreg.register_and_replace(FolderITreeAdapter, folder.FolderITreeAdapter)
-
-As you can see, we simple inherit from the adapter defined in the `folder` cube,
-then we override the `different_type_children` method to give a clue to the ORM's
-`cw_related_rql` method, that is responsible to generate the rql to get entities
-related to the folder by the `filed_under` relation (the value of the
-`tree_relation` attribute).  The clue is that we only want to consider the `File`
-target entity type. By doing this, we remove the ambiguity and get back a RQL
-query that correctly order files by their `data_name` attribute.
-
-
-.. Note::
-
-    * As seen earlier, we want to **replace** the folder's `ITree` adapter by our
-      implementation, hence the custom `registration_callback` method.
-
-
-Ouf. That one was tricky...
-
-Now the easier parts. Let's start by adding some links on the file's primary view
-to see the previous / next image in the same folder. CubicWeb's provide a
-component that do exactly that. To make it appears, one have to be adaptable to
-the `IPrevNext` interface. Here is the related code sample, extracted from our
-cube's `views.py` file:
-
-.. sourcecode:: python
-
-    from cubicweb.predicates import is_instance
-    from cubicweb.web.views import navigation
-
-
-    class FileIPrevNextAdapter(navigation.IPrevNextAdapter):
-	__select__ = is_instance('File')
-
-	def previous_entity(self):
-	    rset = self._cw.execute('File F ORDERBY FDN DESC LIMIT 1 WHERE '
-				    'X filed_under FOLDER, F filed_under FOLDER, '
-				    'F data_name FDN, X data_name > FDN, X eid %(x)s',
-				    {'x': self.entity.eid})
-	    if rset:
-		return rset.get_entity(0, 0)
-
-	def next_entity(self):
-	    rset = self._cw.execute('File F ORDERBY FDN ASC LIMIT 1 WHERE '
-				    'X filed_under FOLDER, F filed_under FOLDER, '
-				    'F data_name FDN, X data_name < FDN, X eid %(x)s',
-				    {'x': self.entity.eid})
-	    if rset:
-		return rset.get_entity(0, 0)
-
-
-The `IPrevNext` interface implemented by the adapter simply consist in the
-`previous_entity` / `next_entity` methods, that should respectivly return the
-previous / next entity or `None`. We make an RQL query to get files in the same
-folder, ordered similarly (eg by their `data_name` attribute). We set
-ascendant/descendant ordering and a strict comparison with current file's name
-(the "X" variable representing the current file).
-
-Notice that this query supposes we wont have two files of the same name in the
-same folder, else things may go wrong. Fixing this is out of the scope of this
-blog. And as I would like to have at some point a smarter, context sensitive
-previous/next entity, I'll probably never fix this query (though if I had to, I
-would probably choosing to add a constraint in the schema so that we can't add
-two files of the same name in a folder).
-
-One more thing: by default, the component will be displayed below the content
-zone (the one with the white background). You can change this in the site's
-properties through the ui, but you can also change the default value in the code
-by modifying the `context` attribute of the component:
-
-.. sourcecode:: python
-
-    navigation.NextPrevNavigationComponent.context = 'navcontentbottom'
-
-.. Note::
-
-   `context` may be one of 'navtop', 'navbottom', 'navcontenttop' or
-   'navcontentbottom'; the first two being outside the main content zone, the two
-   others inside it.
-
-.. figure:: ../../images/tutos-photowebsite_prevnext.png
-   :alt: screenshot of the previous/next entity component
-
-   The previous/next entity component, at the bottom of the main content zone.
-
-Now, the only remaining stuff in my todo list is to see the file's folder. I'll use
-the standard breadcrumb component to do so. Similarly as what we've seen before, this
-component is controled by the :class:`IBreadCrumbs` interface, so we'll have to provide a custom
-adapter for `File` entity, telling the a file's parent entity is its folder:
-
-.. sourcecode:: python
-
-    from cubicweb.web.views import ibreadcrumbs
-
-    class FileIBreadCrumbsAdapter(ibreadcrumbs.IBreadCrumbsAdapter):
-	__select__ = is_instance('File')
-
-	def parent_entity(self):
-	    if self.entity.filed_under:
-		return self.entity.filed_under[0]
-
-In that case, we simply use attribute notation provided by the ORM to get the
-folder in which the current file (e.g. `self.entity`) is located.
-
-.. Note::
-
-   The :class:`IBreadCrumbs` interface is a `breadcrumbs` method, but the default
-   :class:`IBreadCrumbsAdapter` provides a default implementation for it that will look
-   at the value returned by its `parent_entity` method. It also provides a
-   default implementation for this method for entities adapting to the `ITree`
-   interface, but as our `File` doesn't, we've to provide a custom adapter.
-
-.. figure:: ../../images/tutos-photowebsite_breadcrumbs.png
-   :alt: screenshot of the breadcrumb component
-
-   The breadcrumb component when on a file entity, now displaying parent folder.
-
-
-Step 4: preparing the release and migrating the instance
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Now that greatly enhanced our cube, it's time to release it to upgrade production site.
-I'll probably detail that process later, but I currently simply transfer the new code
-to the server running the web site.
-
-However, I've still today some step to respect to get things done properly...
-
-First, as I've added some translatable string, I've to run: ::
-
-  $ cubicweb-ctl i18ncube sytweb
-
-To update the cube's gettext catalogs (the '.po' files under the cube's `i18n`
-directory). Once the above command is executed, I'll then update translations.
-
-To see if everything is ok on my test instance, I do: ::
-
-  $ cubicweb-ctl i18ninstance sytweb
-  $ cubicweb-ctl start -D sytweb
-
-The first command compile i18n catalogs (e.g. generates '.mo' files) for my test
-instance. The second command start it in debug mode, so I can open my browser and
-navigate through the web site to see if everything is ok...
-
-.. Note::
-
-   In the 'cubicweb-ctl i18ncube' command, `sytweb` refers to the **cube**, while
-   in the two other, it refers to the **instance** (if you can't see the
-   difference, reread CubicWeb's concept chapter !).
-
-
-Once I've checked it's ok, I simply have to bump the version number in the
-`__pkginfo__` module to trigger a migration once I'll have updated the code on
-the production site. I can check then check the migration is also going fine, by
-first restoring a dump from the production site, then upgrading my test instance.
-
-To generate a dump from the production site: ::
-
-  $ cubicweb-ctl db-dump sytweb
-  pg_dump -Fc --username=syt --no-owner --file /home/syt/etc/cubicweb.d/sytweb/backup/tmpYIN0YI/system sytweb
-  -> backup file /home/syt/etc/cubicweb.d/sytweb/backup/sytweb-2010-07-13_10-22-40.tar.gz
-
-I can now get back the dump file ('sytweb-2010-07-13_10-22-40.tar.gz') to my test
-machine (using `scp` for instance) to restore it and start migration: ::
-
-  $ cubicweb-ctl db-restore sytweb sytweb-2010-07-13_10-22-40.tar.gz
-  $ cubicweb-ctl upgrade sytweb
-
-You'll have to answer some questions, as we've seen in `an earlier post`_.
-
-Now that everything is tested, I can transfer the new code to the production
-server, `apt-get upgrade` cubicweb and its dependencies, and eventually
-upgrade the production instance.
-
-
-.. _`several improvments`: http://www.cubicweb.org/blogentry/1179899
-.. _`3.8`: http://www.cubicweb.org/blogentry/917107
-.. _`first blog of this series`: http://www.cubicweb.org/blogentry/824642
-.. _`an earlier post`: http://www.cubicweb.org/867464
--- a/doc/book/en/tutorials/advanced/part05_ui-advanced.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,374 +0,0 @@
-Building my photos web site with |cubicweb| part V: let's make it even more user friendly
-=========================================================================================
-
-.. _uiprops:
-
-Step 1: tired of the default look?
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-OK... Now our site has its most desired features. But... I would like to make it look
-somewhat like *my* website. It is not www.cubicweb.org after all. Let's tackle this
-first!
-
-The first thing we can to is to change the logo. There are various way to achieve
-this. The easiest way is to put a :file:`logo.png` file into the cube's :file:`data`
-directory. As data files are looked at according to cubes order (CubicWeb
-resources coming last), that file will be selected instead of CubicWeb's one.
-
-.. Note::
-   As the location for static resources are cached, you'll have to restart
-   your instance for this to be taken into account.
-
-Though there are some cases where you don't want to use a :file:`logo.png` file.
-For instance if it's a JPEG file. You can still change the logo by defining in
-the cube's :file:`uiprops.py` file:
-
-.. sourcecode:: python
-
-   LOGO = data('logo.jpg')
-
-The uiprops machinery is used to define some static file resources,
-such as the logo, default Javascript / CSS files, as well as CSS
-properties (we'll see that later).
-
-.. Note::
-   This file is imported specifically by |cubicweb|, with a predefined name space,
-   containing for instance the `data` function, telling the file is somewhere
-   in a cube or CubicWeb's data directory.
-
-   One side effect of this is that it can't be imported as a regular python
-   module.
-
-The nice thing is that in debug mode, change to a :file:`uiprops.py` file are detected
-and then automatically reloaded.
-
-Now, as it's a photos web-site, I would like to have a photo of mine as background...
-After some trials I won't detail here, I've found a working recipe explained `here`_.
-All I've to do is to override some stuff of the default CubicWeb user interface to
-apply it as explained.
-
-The first thing to to get the ``<img/>`` tag as first element after the
-``<body>`` tag.  If you know a way to avoid this by simply specifying the image
-in the CSS, tell me!  The easiest way to do so is to override the
-:class:`HTMLPageHeader` view, since that's the one that is directly called once
-the ``<body>`` has been written. How did I find this?  By looking in the
-:mod:`cubiweb.web.views.basetemplates` module, since I know that global page
-layouts sits there. I could also have grep the "body" tag in
-:mod:`cubicweb.web.views`... Finding this was the hardest part. Now all I need is
-to customize it to write that ``img`` tag, as below:
-
-.. sourcecode:: python
-
-    class HTMLPageHeader(basetemplates.HTMLPageHeader):
-	# override this since it's the easier way to have our bg image
-	# as the first element following <body>
-	def call(self, **kwargs):
-            self.w(u'<img id="bg-image" src="%sbackground.jpg" alt="background image"/>'
-                   % self._cw.datadir_url)
-	    super(HTMLPageHeader, self).call(**kwargs)
-
-
-    def registration_callback(vreg):
-	vreg.register_all(globals().values(), __name__, (HTMLPageHeader))
-	vreg.register_and_replace(HTMLPageHeader, basetemplates.HTMLPageHeader)
-
-
-As you may have guessed, my background image is in a :file:`background.jpg` file
-in the cube's :file:`data` directory, but there are still some things to explain
-to newcomers here:
-
-* The :meth:`call` method is there the main access point of the view. It's called by
-  the view's :meth:`render` method. It is not the only access point for a view, but
-  this will be detailed later.
-
-* Calling `self.w` writes something to the output stream. Except for binary views
-  (which do not generate text), it *must* be passed an Unicode string.
-
-* The proper way to get a file in :file:`data` directory is to use the `datadir_url`
-  attribute of the incoming request (e.g. `self._cw`).
-
-I won't explain again the :func:`registration_callback` stuff, you should understand it
-now!  If not, go back to previous posts in the series :)
-
-Fine. Now all I've to do is to add a bit of CSS to get it to behave nicely (which
-is not the case at all for now). I'll put all this in a :file:`cubes.sytweb.css`
-file, stored as usual in our :file:`data` directory:
-
-.. sourcecode:: css
-
-
-    /* fixed full screen background image
-     * as explained on http://webdesign.about.com/od/css3/f/blfaqbgsize.htm
-     *
-     * syt update: set z-index=0 on the img instead of z-index=1 on div#page & co to
-     * avoid pb with the user actions menu
-     */
-    img#bg-image {
-	position: fixed;
-	top: 0;
-	left: 0;
-	width: 100%;
-	height: 100%;
-	z-index: 0;
-    }
-
-    div#page, table#header, div#footer {
-	background: transparent;
-	position: relative;
-    }
-
-    /* add some space around the logo
-     */
-    img#logo {
-	padding: 5px 15px 0px 15px;
-    }
-
-    /* more dark font for metadata to have a chance to see them with the background
-     *  image
-     */
-    div.metadata {
-	color: black;
-    }
-
-You can see here stuff explained in the cited page, with only a slight modification
-explained in the comments, plus some additional rules to make things somewhat cleaner:
-
-* a bit of padding around the logo
-
-* darker metadata which appears by default below the content (the white frame in the page)
-
-To get this CSS file used everywhere in the site, I have to modify the :file:`uiprops.py` file
-introduced above:
-
-.. sourcecode:: python
-
-   STYLESHEETS = sheet['STYLESHEETS'] + [data('cubes.sytweb.css')]
-
-.. Note::
-   `sheet` is another predefined variable containing values defined by
-   already process `:file:`uiprops.py`` file, notably the CubicWeb's one.
-
-Here we simply want our CSS in addition to CubicWeb's base CSS files, so we
-redefine the `STYLESHEETS` variable to existing CSS (accessed through the `sheet`
-variable) with our one added. I could also have done:
-
-.. sourcecode:: python
-
-   sheet['STYLESHEETS'].append(data('cubes.sytweb.css'))
-
-But this is less interesting since we don't see the overriding mechanism...
-
-At this point, the site should start looking good, the background image being
-resized to fit the screen.
-
-.. image:: ../../images/tutos-photowebsite_background-image.png
-
-The final touch: let's customize CubicWeb's CSS to get less orange... By simply adding
-
-.. sourcecode:: python
-
-  contextualBoxTitleBg = incontextBoxTitleBg = '#AAAAAA'
-
-and reloading the page we've just seen, we know have a nice greyed box instead of
-the orange one:
-
-.. image:: ../../images/tutos-photowebsite_grey-box.png
-
-This is because CubicWeb's CSS include some variables which are
-expanded by values defined in uiprops file. In our case we controlled the
-properties of the CSS `background` property of boxes with CSS class
-`contextualBoxTitleBg` and `incontextBoxTitleBg`.
-
-
-Step 2: configuring boxes
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Boxes present to the user some ways to use the application. Let's first do a few
-user interface tweaks in our :file:`views.py` file:
-
-.. sourcecode:: python
-
-  from cubicweb.predicates import none_rset
-  from cubicweb.web.views import bookmark
-  from cubes.zone import views as zone
-  from cubes.tag import views as tag
-
-  # change bookmarks box selector so it's only displayed on startup views
-  bookmark.BookmarksBox.__select__ = bookmark.BookmarksBox.__select__ & none_rset()
-  # move zone box to the left instead of in the context frame and tweak its order
-  zone.ZoneBox.context = 'left'
-  zone.ZoneBox.order = 100
-  # move tags box to the left instead of in the context frame and tweak its order
-  tag.TagsBox.context = 'left'
-  tag.TagsBox.order = 102
-  # hide similarity box, not interested
-  tag.SimilarityBox.visible = False
-
-The idea is to move all boxes in the left column, so we get more space for the
-photos.  Now, serious things: I want a box similar to the tags box but to handle
-the `Person displayed_on File` relation. We can do this simply by adding a
-:class:`AjaxEditRelationCtxComponent` subclass to our views, as below:
-
-.. sourcecode:: python
-
-    from logilab.common.decorators import monkeypatch
-    from cubicweb import ValidationError
-    from cubicweb.web.views import uicfg, component
-    from cubicweb.web.views import basecontrollers
-
-    # hide displayed_on relation using uicfg since it will be displayed by the box below
-    uicfg.primaryview_section.tag_object_of(('*', 'displayed_on', '*'), 'hidden')
-
-    class PersonBox(component.AjaxEditRelationCtxComponent):
-	__regid__ = 'sytweb.displayed-on-box'
-	# box position
-	order = 101
-	context = 'left'
-	# define relation to be handled
-	rtype = 'displayed_on'
-	role = 'object'
-	target_etype = 'Person'
-	# messages
-	added_msg = _('person has been added')
-	removed_msg = _('person has been removed')
-	# bind to js_* methods of the json controller
-	fname_vocabulary = 'unrelated_persons'
-	fname_validate = 'link_to_person'
-	fname_remove = 'unlink_person'
-
-
-    @monkeypatch(basecontrollers.JSonController)
-    @basecontrollers.jsonize
-    def js_unrelated_persons(self, eid):
-	"""return tag unrelated to an entity"""
-	rql = "Any F + ' ' + S WHERE P surname S, P firstname F, X eid %(x)s, NOT P displayed_on X"
-	return [name for (name,) in self._cw.execute(rql, {'x' : eid})]
-
-
-    @monkeypatch(basecontrollers.JSonController)
-    def js_link_to_person(self, eid, people):
-	req = self._cw
-	for name in people:
-	    name = name.strip().title()
-	    if not name:
-		continue
-	    try:
-		firstname, surname = name.split(None, 1)
-	    except:
-		raise ValidationError(eid, {('displayed_on', 'object'): 'provide <first name> <surname>'})
-	    rset = req.execute('Person P WHERE '
-			       'P firstname %(firstname)s, P surname %(surname)s',
-			       locals())
-	    if rset:
-		person = rset.get_entity(0, 0)
-	    else:
-		person = req.create_entity('Person', firstname=firstname,
-						surname=surname)
-	    req.execute('SET P displayed_on X WHERE '
-			'P eid %(p)s, X eid %(x)s, NOT P displayed_on X',
-			{'p': person.eid, 'x' : eid})
-
-    @monkeypatch(basecontrollers.JSonController)
-    def js_unlink_person(self, eid, personeid):
-	self._cw.execute('DELETE P displayed_on X WHERE P eid %(p)s, X eid %(x)s',
-			 {'p': personeid, 'x': eid})
-
-
-You basically subclass to configure with some class attributes. The `fname_*`
-attributes give the name of methods that should be defined on the json control to
-make the AJAX part of the widget work: one to get the vocabulary, one to add a
-relation and another to delete a relation. These methods must start by a `js_`
-prefix and are added to the controller using the `@monkeypatch` decorator. In my
-case, the most complicated method is the one which adds a relation, since it
-tries to see if the person already exists, and else automatically create it,
-assuming the user entered "firstname surname".
-
-Let's see how it looks like on a file primary view:
-
-.. image:: ../../images/tutos-photowebsite_boxes.png
-
-Great, it's now as easy for me to link my pictures to people than to tag them.
-Also, visitors get a consistent display of these two pieces of information.
-
-.. Note::
-  The ui component system has been refactored in `CubicWeb 3.10`_, which also
-  introduced the :class:`AjaxEditRelationCtxComponent` class.
-
-
-Step 3: configuring facets
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The last feature we'll add today is facet configuration. If you access to the
-'/file' url, you'll see a set of 'facets' appearing in the left column. Facets
-provide an intuitive way to build a query incrementally, by proposing to the user
-various way to restrict the result set. For instance CubicWeb proposes a facet to
-restrict based on who created an entity; the tag cube proposes a facet to
-restrict based on tags; the zoe cube a facet to restrict based on geographical
-location, and so on. In that gist, I want to propose a facet to restrict based on
-the people displayed on the picture. To do so, there are various classes in the
-:mod:`cubicweb.web.facet` module which simply have to be configured using class
-attributes as we've done for the box. In our case, we'll define a subclass of
-:class:`RelationFacet`.
-
-.. Note::
-
-   Since that's ui stuff, we'll continue to add code below to our
-   :file:`views.py` file. Though we begin to have a lot of various code their, so
-   it's may be a good time to split our views module into submodules of a `view`
-   package. In our case of a simple application (glue) cube, we could start using
-   for instance the layout below: ::
-
-     views/__init__.py   # uicfg configuration, facets
-     views/layout.py     # header/footer/background stuff
-     views/components.py # boxes, adapters
-     views/pages.py      # index view, 404 view
-
-.. sourcecode:: python
-
-    from cubicweb.web import facet
-
-    class DisplayedOnFacet(facet.RelationFacet):
-	__regid__ = 'displayed_on-facet'
-	# relation to be displayed
-	rtype = 'displayed_on'
-	role = 'object'
-	# view to use to display persons
-	label_vid = 'combobox'
-
-Let's say we also want to filter according to the `visibility` attribute. This is
-even simpler as we just have to derive from the :class:`AttributeFacet` class:
-
-.. sourcecode:: python
-
-    class VisibilityFacet(facet.AttributeFacet):
-	__regid__ = 'visibility-facet'
-	rtype = 'visibility'
-
-Now if I search for some pictures on my site, I get the following facets available:
-
-.. image:: ../../images/tutos-photowebsite_facets.png
-
-.. Note::
-
-  By default a facet must be applyable to every entity in the result set and
-  provide at leat two elements of vocabulary to be displayed (for instance you
-  won't see the `created_by` facet if the same user has created all
-  entities). This may explain why you don't see yours...
-
-
-Conclusion
-~~~~~~~~~~
-
-We started to see the power behind the infrastructure provided by the
-framework, both on the pure ui (CSS, Javascript) side and on the Python side
-(high level generic classes for components, including boxes and facets). We now
-have, with a few lines of code, a full-featured web site with a personalized look.
-
-Of course we'll probably want more as time goes, but we can now
-concentrate on making good pictures, publishing albums and sharing them with
-friends...
-
-
-
-.. _`CubicWeb 3.10`: http://www.cubicweb.org/blogentry/1330518
-.. _`here`: http://webdesign.about.com/od/css3/f/blfaqbgsize.htm
--- a/doc/book/en/tutorials/base/blog-in-five-minutes.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _TutosBaseBlogFiveMinutes:
-
-Get a blog running in five minutes!
------------------------------------
-
-For Debian or Ubuntu users, first install the following packages
-(:ref:`DebianInstallation`)::
-
-    cubicweb, cubicweb-dev, cubicweb-blog
-
-Windows or Mac OS X users must install |cubicweb| from source (see
-:ref:`SourceInstallation` and :ref:`WindowsInstallation`).
-
-Then create and initialize your instance::
-
-    cubicweb-ctl create blog myblog
-
-You'll be asked a few questions, and you can keep the default answer for most of
-them. The one question you'll have to think about is the database you'll want to
-use for that instance. For a quick test, if you don't have `postgresql` installed
-and configured (see :ref:`PostgresqlConfiguration`), it's highly recommended to
-choose `sqlite` when asked for which database driver to use, since it has a much
-simple setup (no database server needed).
-
-One the process is completed (including database initialisation), you can start
-your instance by using: ::
-
-    cubicweb-ctl start -D myblog
-
-The `-D` option activates the debugging mode. Removing it will launch the instance
-as a daemon in the background, and ``cubicweb-ctl stop myblog`` will stop
-it in that case. 
-
-
-About file system permissions
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Unless you installed from sources, the above commands assume that you have root
-access to the :file:`/etc/` directory. In order to initialize your instance as a
-regular user, within your home directory, you can use the :envvar:`CW_MODE`
-environment variable: ::
-
-  export CW_MODE=user
-
-then create a :file:`~/etc/cubicweb.d` directory that will hold your instances.
-
-More information about how to configure your own environment is
-available in :ref:`ResourceMode`.
-
-
-Instance parameters
-~~~~~~~~~~~~~~~~~~~
-
-If you would like to change database parameters such as the database host or the
-user name used to connect to the database, edit the `sources` file located in the
-:file:`/etc/cubicweb.d/myblog` directory.
-
-Then relaunch the database creation::
-
-     cubicweb-ctl db-create myblog
-
-Other parameters, like web server or emails parameters, can be modified in the
-:file:`/etc/cubicweb.d/myblog/all-in-one.conf` file.
-
-You'll have to restart the instance after modification in one of those files.
-
-This is it. Your blog is functional and running. Visit http://localhost:8080 and enjoy it!
-
--- a/doc/book/en/tutorials/base/conclusion.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-What's next?
-------------
-
-In this tutorial, we have seen that you can, right after the installation of
-|cubicweb|, build a web application in a few minutes by defining a data model as
-assembling cubes. You get a working application that you can then customize there
-and there while keeping something that works. This is important in agile
-development practices, you can right from the start of the project show things
-to customer and so take the right decision early in the process.
-
-The next steps will be to discover hooks, security, data sources, digging deeper
-into view writing and interface customisation... Yet a lot of fun stuff to
-discover! You will find more `tutorials and howtos`_ in the blog published on the
-CubicWeb.org website.
-
-.. _`tutorials and howtos`: http://www.cubicweb.org/view?rql=Any+X+ORDERBY+D+DESC+WHERE+X+is+BlogEntry%2C+T+tags+X%2C+T+name+IN+%28%22tutorial%22%2C+%22howto%22%29%2C+X+creation_date+D
--- a/doc/book/en/tutorials/base/customizing-the-application.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,539 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _TutosBaseCustomizingTheApplication:
-
-Customizing your application
-----------------------------
-
-So far so good. The point is that usually, you won't get enough by assembling
-cubes out-of-the-box. You will want to customize them, have a personal look and
-feel, add your own data model and so on. Or maybe start from scratch?
-
-So let's get a bit deeper and start coding our own cube. In our case, we want
-to customize the blog we created to add more features to it.
-
-
-Create your own cube
-~~~~~~~~~~~~~~~~~~~~
-
-First, notice that if you've installed |cubicweb| using Debian packages, you will
-need the additional ``cubicweb-dev`` package to get the commands necessary to
-|cubicweb| development. All `cubicweb-ctl` commands are described in details in
-:ref:`cubicweb-ctl`.
-
-Once your |cubicweb| development environment is set up, you can create a new
-cube::
-
-  cubicweb-ctl newcube myblog
-
-This will create in the cubes directory (:file:`/path/to/grshell/cubes` for source
-installation, :file:`/usr/share/cubicweb/cubes` for Debian packages installation)
-a directory named :file:`blog` reflecting the structure described in
-:ref:`cubelayout`.
-
-For packages installation, you can still create new cubes in your home directory
-using the following configuration. Let's say you want to develop your new cubes
-in `~src/cubes`, then set the following environment variables: ::
-
-  CW_CUBES_PATH=~/src/cubes
-
-and then create your new cube using: ::
-
-  cubicweb-ctl newcube --directory=~/src/cubes myblog
-
-.. Note::
-
-   We previously used `myblog` as the name of our *instance*. We're now creating
-   a *cube* with the same name. Both are different things. We'll now try to
-   specify when we talk about one or another, but keep in mind this difference.
-
-
-Cube metadata
-~~~~~~~~~~~~~
-
-A simple set of metadata about your cube are stored in the :file:`__pkginfo__.py`
-file. In our case, we want to extend the blog cube, so we have to tell that our
-cube depends on this cube, by modifying the ``__depends__`` dictionary in that
-file:
-
-.. sourcecode:: python
-
-   __depends__ =  {'cubicweb': '>= 3.10.7',
-                   'cubicweb-blog': None}
-
-where the ``None`` means we do not depends on a particular version of the cube.
-
-.. _TutosBaseCustomizingTheApplicationDataModel:
-
-Extending the data model
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-The data model or schema is the core of your |cubicweb| application.  It defines
-the type of content your application will handle. It is defined in the file
-:file:`schema.py` of the cube.
-
-
-Defining our model
-******************
-
-For the sake of example, let's say we want a new entity type named `Community`
-with a name, a description. A `Community` will hold several blogs.
-
-.. sourcecode:: python
-
-  from yams.buildobjs import EntityType, RelationDefinition, String, RichString
-
-  class Community(EntityType):
-      name = String(maxsize=50, required=True)
-      description = RichString()
-
-  class community_blog(RelationDefinition):
-      subject = 'Community'
-      object = 'Blog'
-      cardinality = '*?'
-      composite = 'subject'
-
-The first step is the import from the :mod:`yams` package necessary classes to build
-the schema.
-
-This file defines the following:
-
-* a `Community` has a title and a description as attributes
-
-  - the name is a string that is required and can't be longer than 50 characters
-
-  - the description is a string that is not constrained and may contains rich
-    content such as HTML or Restructured text.
-
-* a `Community` may be linked to a `Blog` using the `community_blog` relation
-
-  - ``*`` means a community may be linked to 0 to N blog, ``?`` means a blog may
-    be linked to 0 to 1 community. For completeness, remember that you can also
-    use ``+`` for 1 to N, and ``1`` for single, mandatory relation (e.g. one to one);
-
-  - this is a composite relation where `Community` (e.g. the subject of the
-    relation) is the composite. That means that if you delete a community, its
-    blog will be deleted as well.
-
-Of course, there are a lot of other data types and things such as constraints,
-permissions, etc, that may be defined in the schema, but those won't be covered
-in this tutorial.
-
-Notice that our schema refers to the `Blog` entity type which is not defined
-here.  But we know this type is available since we depend on the `blog` cube
-which is defining it.
-
-
-Applying changes to the model into our instance
-***********************************************
-
-Now the problem is that we created an instance using the `blog` cube, not our
-`myblog` cube, so if we don't do anything there is no way that we'll see anything
-changing in the instance.
-
-One easy way, as we've no really valuable data in the instance would be to trash and recreated it::
-
-  cubicweb-ctl stop myblog # or Ctrl-C in the terminal running the server in debug mode
-  cubicweb-ctl delete myblog
-  cubicweb-ctl create myblog
-  cubicweb-ctl start -D myblog
-
-Another way is to add our cube to the instance using the cubicweb-ctl shell
-facility. It's a python shell connected to the instance with some special
-commands available to manipulate it (the same as you'll have in migration
-scripts, which are not covered in this tutorial). In that case, we're interested
-in the `add_cube` command: ::
-
-  $ cubicweb-ctl stop myblog # or Ctrl-C in the terminal running the server in debug mode
-  $ cubicweb-ctl shell myblog
-  entering the migration python shell
-  just type migration commands or arbitrary python code and type ENTER to execute it
-  type "exit" or Ctrl-D to quit the shell and resume operation
-  >>> add_cube('myblog')
-  >>>
-  $ cubicweb-ctl start -D myblog
-
-The `add_cube` command is enough since it automatically updates our
-application to the cube's schema. There are plenty of other migration
-commands of a more finer grain. They are described in :ref:`migration`
-
-As explained, leave the shell by typing Ctrl-D. If you restart the instance and
-take another look at the schema, you'll see that changes to the data model have
-actually been applied (meaning database schema updates and all necessary stuff
-has been done).
-
-.. image:: ../../images/tutos-base_myblog-schema_en.png
-   :alt: the instance schema after adding our cube
-
-If you follow the 'info' link in the user pop-up menu, you'll also see that the
-instance is using blog and myblog cubes.
-
-.. image:: ../../images/tutos-base_myblog-siteinfo_en.png
-   :alt: the instance schema after adding our cube
-
-You can now add some communities, link them to blog, etc... You'll see that the
-framework provides default views for this entity type (we have not yet defined any
-view for it!), and also that the blog primary view will show the community it's
-linked to if any. All this thanks to the model driven interface provided by the
-framework.
-
-You'll then be able to redefine each of them according to your needs
-and preferences. We'll now see how to do such thing.
-
-.. _TutosBaseCustomizingTheApplicationCustomViews:
-
-Defining your views
-~~~~~~~~~~~~~~~~~~~
-
-|cubicweb| provides a lot of standard views in directory
-:file:`cubicweb/web/views/`. We already talked about 'primary' and 'list' views,
-which are views which apply to one ore more entities.
-
-A view is defined by a python class which includes:
-
-  - an identifier: all objects used to build the user interface in |cubicweb| are
-    recorded in a registry and this identifier will be used as a key in that
-    registry. There may be multiple views for the same identifier.
-
-  - a *selector*, which is a kind of filter telling how well a view suit to a
-    particular context. When looking for a particular view (e.g. given an
-    identifier), |cubicweb| computes for each available view with that identifier
-    a score which is returned by the selector. Then the view with the highest
-    score is used. The standard library of predicates is in
-    :mod:`cubicweb.predicates`.
-
-A view has a set of methods inherited from the :class:`cubicweb.view.View` class,
-though you usually don't derive directly from this class but from one of its more
-specific child class.
-
-Last but not least, |cubicweb| provides a set of default views accepting any kind
-of entities.
-
-Want a proof? Create a community as you've already done for other entity types
-through the index page, you'll then see something like that:
-
-.. image:: ../../images/tutos-base_myblog-community-default-primary_en.png
-   :alt: the default primary view for our community entity type
-
-
-If you notice the weird messages that appear in the page: those are messages
-generated for the new data model, which have no translation yet. To fix that,
-we'll have to use dedicated `cubicweb-ctl` commands:
-
-.. sourcecode: bash
-
-  cubicweb-ctl i18ncube myblog # build/update cube's message catalogs
-  # then add translation into .po file into the cube's i18n directory
-  cubicweb-ctl i18ninstance myblog # recompile instance's message catalogs
-  cubicweb-ctl restart -D myblog # instance has to be restarted to consider new catalogs
-
-You'll then be able to redefine each of them according to your needs and
-preferences. So let's see how to do such thing.
-
-Changing the layout of the application
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The layout is the general organization of the pages in the site. Views that generate
-the layout are sometimes referred to as 'templates'. They are implemented in the
-framework in the module :mod:`cubicweb.web.views.basetemplates`. By overriding
-classes in this module, you can customize whatever part you wish of the default
-layout.
-
-But notice that |cubicweb| provides many other ways to customize the
-interface, thanks to actions and components (which you can individually
-(de)activate, control their location, customize their look...) as well as
-"simple" CSS customization. You should first try to achieve your goal using such
-fine grained parametrization rather then overriding a whole template, which usually
-embeds customisation access points that you may loose in the process.
-
-But for the sake of example, let's say we want to change the generic page
-footer...  We can simply add to the module ``views`` of our cube,
-e.g. :file:`cubes/myblog/views.py`, the code below:
-
-.. sourcecode:: python
-
-  from cubicweb.web.views import basetemplates
-
-  class MyHTMLPageFooter(basetemplates.HTMLPageFooter):
-
-      def footer_content(self):
-	  self.w(u'This website has been created with <a href="http://cubicweb.org">CubicWeb</a>.')
-
-  def registration_callback(vreg):
-      vreg.register_all(globals().values(), __name__, (MyHTMLPageFooter,))
-      vreg.register_and_replace(MyHTMLPageFooter, basetemplates.HTMLPageFooter)
-
-
-* Our class inherits from the default page footer to ease getting things right,
-  but this is not mandatory.
-
-* When we want to write something to the output stream, we simply call `self.w`,
-  with *must be passed an unicode string*.
-
-* The latest function is the most exotic stuff. The point is that without it, you
-  would get an error at display time because the framework wouldn't be able to
-  choose which footer to use between :class:`HTMLPageFooter` and
-  :class:`MyHTMLPageFooter`, since both have the same selector, hence the same
-  score...  In this case, we want our footer to replace the default one, so we have
-  to define a :func:`registration_callback` function to control object
-  registration: the first instruction tells to register everything in the module
-  but the :class:`MyHTMLPageFooter` class, then the second to register it instead
-  of :class:`HTMLPageFooter`. Without this function, everything in the module is
-  registered blindly.
-
-.. Note::
-
-  When a view is modified while running in debug mode, it is not required to
-  restart the instance server. Save the Python file and reload the page in your
-  web browser to view the changes.
-
-We will now have this simple footer on every page of the site.
-
-
-Primary view customization
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The 'primary' view (i.e. any view with the identifier set to 'primary') is the one used to
-display all the information about a single entity. The standard primary view is one
-of the most sophisticated views of all. It has several customisation points, but
-its power comes with `uicfg`, allowing you to control it without having to
-subclass it.
-
-However this is a bit off-topic for this first tutorial. Let's say we simply want a
-custom primary view for my `Community` entity type, using directly the view
-interface without trying to benefit from the default implementation (you should
-do that though if you're rewriting reusable cubes; everything is described in more
-details in :ref:`primary_view`).
-
-
-So... Some code! That we'll put again in the module ``views`` of our cube.
-
-.. sourcecode:: python
-
-  from cubicweb.predicates import is_instance
-  from cubicweb.web.views import primary
-
-  class CommunityPrimaryView(primary.PrimaryView):
-      __select__ = is_instance('Community')
-
-      def cell_call(self, row, col):
-          entity = self.cw_rset.get_entity(row, col)
-          self.w(u'<h1>Welcome to the "%s" community</h1>' % entity.printable_value('name'))
-          if entity.description:
-              self.w(u'<p>%s</p>' % entity.printable_value('description'))
-
-What's going on here?
-
-* Our class inherits from the default primary view, here mainly to get the correct
-  view identifier, since we don't use any of its features.
-
-* We set on it a selector telling that it only applies when trying to display
-  some entity of the `Community` type. This is enough to get an higher score than
-  the default view for entities of this type.
-
-* View applying to entities usually have to define `cell_call` as entry point,
-  and are given `row` and `col` arguments tell to which entity in the result set
-  the view is applied. We can then get this entity from the result set
-  (`self.cw_rset`) by using the `get_entity` method.
-
-* To ease thing, we access our entity's attribute for display using its
-  printable_value method, which will handle formatting and escaping when
-  necessary. As you can see, you can also access attributes by their name on the
-  entity to get the raw value.
-
-
-You can now reload the page of the community we just created and see the changes.
-
-.. image:: ../../images/tutos-base_myblog-community-custom-primary_en.png
-   :alt: the custom primary view for our community entity type
-
-We've seen here a lot of thing you'll have to deal with to write views in
-|cubicweb|. The good news is that this is almost everything that is used to
-build higher level layers.
-
-.. Note::
-
-  As things get complicated and the volume of code in your cube increases, you can
-  of course still split your views module into a python package with subpackages.
-
-You can find more details about views and selectors in :ref:`Views`.
-
-
-Write entities to add logic in your data
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-|cubicweb| provides an ORM to easily programmaticaly manipulate
-entities (just like the one we have fetched earlier by calling
-`get_entity` on a result set). By default, entity
-types are instances of the :class:`AnyEntity` class, which holds a set of
-predefined methods as well as property automatically generated for
-attributes/relations of the type it represents.
-
-You can redefine each entity to provide additional methods or whatever you want
-to help you write your application. Customizing an entity requires that your
-entity:
-
-- inherits from :class:`cubicweb.entities.AnyEntity` or any subclass
-
-- defines a :attr:`__regid__` linked to the corresponding data type of your schema
-
-You may then want to add your own methods, override default implementation of some
-method, etc...
-
-.. sourcecode:: python
-
-    from cubicweb.entities import AnyEntity, fetch_config
-
-
-    class Community(AnyEntity):
-        """customized class for Community entities"""
-        __regid__ = 'Community'
-
-        fetch_attrs, cw_fetch_order = fetch_config(['name'])
-
-        def dc_title(self):
-            return self.name
-
-        def display_cw_logo(self):
-            return 'CubicWeb' in self.description
-
-In this example:
-
-* we used convenience :func:`fetch_config` function to tell which attributes
-  should be prefetched by the ORM when looking for some related entities of this
-  type, and how they should be ordered
-
-* we overrode the standard `dc_title` method, used in various place in the interface
-  to display the entity (though in this case the default implementation would
-  have had the same result)
-
-* we implemented here a method :meth:`display_cw_logo` which tests if the blog
-  entry title contains 'CW'.  It can then be used when you're writing code
-  involving 'Community' entities in your views, hooks, etc. For instance, you can
-  modify your previous views as follows:
-
-.. sourcecode:: python
-
-
-  class CommunityPrimaryView(primary.PrimaryView):
-      __select__ = is_instance('Community')
-
-      def cell_call(self, row, col):
-          entity = self.cw_rset.get_entity(row, col)
-          self.w(u'<h1>Welcome to the "%s" community</h1>' % entity.printable_value('name'))
-          if entity.display_cw_logo():
-              self.w(u'<img src="http://www.cubicweb.org/doc/en/_static/cubicweb.png"/>')
-          if entity.description:
-              self.w(u'<p>%s</p>' % entity.printable_value('description'))
-
-Then each community whose description contains 'CW' is shown with the |cubicweb|
-logo in front of it.
-
-.. Note::
-
-  As for view, you don't have to restart your instance when modifying some entity
-  classes while your server is running in debug mode, the code will be
-  automatically reloaded.
-
-
-Extending the application by using more cubes!
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-One of the goal of the |cubicweb| framework was to have truly reusable
-components. To do so, they must both behave nicely when plugged into the
-application and be easily customisable, from the data model to the user
-interface. And I think the result is pretty successful, thanks to system such as
-the selection mechanism and the choice to write views as python code which allows
-to build our page using true object oriented programming techniques, that no
-template language provides.
-
-
-A library of standard cubes is available from `CubicWeb Forge`_, to address a
-lot of common concerns such has manipulating people, files, things to do, etc. In
-our community blog case, we could be interested for instance in functionalities
-provided by the `comment` and `tag` cubes. The former provides threaded
-discussion functionalities, the latter a simple tag mechanism to classify content.
-Let's say we want to try those. We will first modify our cube's :file:`__pkginfo__.py`
-file:
-
-.. sourcecode:: python
-
-   __depends__ =  {'cubicweb': '>= 3.10.7',
-                   'cubicweb-blog': None,
-                   'cubicweb-comment': None,
-                   'cubicweb-tag': None}
-
-Now, we'll simply tell on which entity types we want to activate the 'comment'
-and 'tag' facilities by adding respectively the 'comments' and 'tags' relations on
-them in our schema (:file:`schema.py`).
-
-.. sourcecode:: python
-
-  class comments(RelationDefinition):
-      subject = 'Comment'
-      object = 'BlogEntry'
-      cardinality = '1*'
-      composite = 'object'
-
-  class tags(RelationDefinition):
-      subject = 'Tag'
-      object = ('Community', 'BlogEntry')
-
-
-So in the case above we activated comments on `BlogEntry` entities and tags on
-both `Community` and `BlogEntry`. Various views from both `comment` and `tag`
-cubes will then be automatically displayed when one of those relations is
-supported.
-
-Let's synchronize the data model as we've done earlier: ::
-
-
-  $ cubicweb-ctl stop myblog
-  $ cubicweb-ctl shell myblog
-  entering the migration python shell
-  just type migration commands or arbitrary python code and type ENTER to execute it
-  type "exit" or Ctrl-D to quit the shell and resume operation
-  >>> add_cubes(('comment', 'tag'))
-  >>>
-
-Then restart the instance. Let's look at a blog entry:
-
-.. image:: ../../images/tutos-base_myblog-blogentry-taggable-commentable-primary_en.png
-   :alt: the primary view for a blog entry with comments and tags activated
-
-As you can see, we now have a box displaying tags and a section proposing to add
-a comment and displaying existing one below the post. All this without changing
-anything in our views, thanks to the design of generic views provided by the
-framework. Though if we take a look at a community, we won't see the tags box!
-That's because by default this box try to locate itself in the left column within
-the white frame, and this column is handled by the primary view we
-hijacked. Let's change our view to make it more extensible, by keeping both our
-custom rendering but also extension points provided by the default
-implementation.
-
-
-.. sourcecode:: python
-
-  class CommunityPrimaryView(primary.PrimaryView):
-      __select__ = is_instance('Community')
-
-      def render_entity_title(self, entity):
-	  self.w(u'<h1>Welcome to the "%s" community</h1>' % entity.printable_value('name'))
-
-      def render_entity_attributes(self, entity):
-	  if entity.display_cw_logo():
-	      self.w(u'<img src="http://www.cubicweb.org/doc/en/_static/cubicweb.png"/>')
-	  if entity.description:
-	      self.w(u'<p>%s</p>' % entity.printable_value('description'))
-
-It appears now properly:
-
-.. image:: ../../images/tutos-base_myblog-community-taggable-primary_en.png
-   :alt: the custom primary view for a community entry with tags activated
-
-You can control part of the interface independently from each others, piece by
-piece. Really.
-
-
-
-.. _`CubicWeb Forge`: http://www.cubicweb.org/project
--- a/doc/book/en/tutorials/base/discovering-the-ui.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,161 +0,0 @@
-
-.. _TutosBaseDiscoveringTheUI:
-
-Discovering the web interface
------------------------------
-
-You can now access your web instance to create blogs and post messages
-by visiting the URL http://localhost:8080/.
-
-By default, anonymous access is disabled, so a login form will appear. If you
-asked to allow anonymous access when initializing the instance, click on the
-'login' link in the top right hand corner. To login, you need then use the admin
-account you specified at the time you initialized the database with
-``cubicweb-ctl create``.
-
-.. image:: ../../images/tutos-base_login-form_en.png
-   :alt: the login form
-
-
-Once authenticated, you can start playing with your instance. The default index
-page looks like the following:
-
-.. image:: ../../images/tutos-base_index_en.png
-   :alt: the index page
-
-
-Minimal configuration
-~~~~~~~~~~~~~~~~~~~~~
-
-Before creating entities, let's change that 'unset title' thing that appears
-here and there. This comes from a |cubicweb| system properties. To set it,
-click on the 'site configuration link' in the pop-up menu behind your login name
-in the upper left-hand corner
-
-.. image:: ../../images/tutos-base_user-menu_en.png
-   :alt: the user pop-up menu
-
-The site title is in the 'Ui' section. Simply set it to the desired value and
-click the 'validate' button.
-
-.. image:: ../../images/tutos-base_siteconfig_en.png
-   :alt: the site configuration form
-
-You should see a 'changes applied' message. You can now go back to the
-index page by clicking on the |cubicweb| logo in the upper left-hand corner.
-
-You will much likely still see 'unset title' at this point. This is because by
-default the index page is cached. Force a refresh of the page (by typing Ctrl-R
-in Firefox for instance) and you should now see the title you entered.
-
-
-Adding entities
-~~~~~~~~~~~~~~~
-
-The ``blog`` cube defines several entity types, among them ``Blog`` which is a
-container for ``BlogEntry`` (i.e. posts) on a particular topic. We can get a
-graphical view of the schema by clicking on the 'site schema' link in the user
-pop-up menu we've already seen:
-
-.. image:: ../../images/tutos-base_schema_en.png
-   :alt: graphical view of the schema (aka data-model)
-
-Nice isn't it? Notice that this, as most other stuff we'll see in this tutorial,
-is generated by the framework according to the model of the application. In our
-case, the model defined by the ``blog`` cube.
-
-Now let us create a few of these entities.
-
-
-Add a blog
-**********
-
-Clicking on the `[+]` at the left of the 'Blog' link on the index page will lead
-you to an HTML form to create a blog.
-
-.. image:: ../../images/tutos-base_blog-form_en.png
-   :alt: the blog creation form
-
-For instance, call this new blog 'Tech-blog' and type in 'everything about
-technology' as the description , then validate the form by clicking on
-'Validate'. You will be redirected to the `primary` view of the newly created blog.
-
-.. image:: ../../images/tutos-base_blog-primary_en.png
-   :alt: the blog primary view
-
-
-Add a blog post
-***************
-
-There are several ways to add a blog entry. The simplest is to click on the 'add
-blog entry' link in the actions box on viewing the blog you have just created.
-You will then see a form to create a post, with a 'blog entry of' field preset
-to the blog we're coming from. Enter a title, some content, click the 'validate'
-button and you're done. You will be redirected to the blog primary view, though you
-now see that it contains the blog post you've just created.
-
-.. image:: ../../images/tutos-base_blog-primary-after-post-creation_en.png
-   :alt: the blog primary view after creation of a post
-
-Notice there are some new boxes that appears in the left column.
-
-You can achieve the same thing by following the same path as we did for the blog
-creation, e.g. by clicking on the `[+]` at the left of the 'Blog entry' link on
-the index page. The diffidence being that since there is no context information,
-the 'blog entry of' selector won't be preset to the blog.
-
-
-If you click on the 'modify' link of the action box, you are back to
-the form to edit the entity you just created, except that the form now
-has another section with a combo-box entitled 'add relation'. It
-provisos a generic way to edit relations which don't appears in the
-above form. Choose the relation you want to add and a second combo box
-appears where you can pick existing entities.  If there are too many
-of them, you will be offered to navigate to the target entity, that is
-go away from the form and go back to it later, once you've selected
-the entity you want to link with.
-
-.. image:: ../../images/tutos-base_form-generic-relations_en.png
-   :alt: the generic relations combo box
-
-This combo box can't appear until the entity is actually created. That's why you
-haven't seen it at creation time. You could also have hit 'Apply' instead of
-'validate' and it would have showed up.
-
-
-About ui auto-adaptation
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-One of the things that make |cubicweb| different of other frameworks is
-its automatic user interface that adapts itself according to the data being
-displayed. Let's see an example.
-
-If you go back to the home page an click on the 'Blog' link, you will be redirected
-to the primary view of the blog, the same we've seen earlier. Now, add another
-blog, go back to the index page, and click again on this link. You will see
-a very different view (namely the 'list' view).
-
-.. image:: ../../images/tutos-base_blogs-list_en.png
-   :alt: the list view when there are more than one blog to display
-
-This is because in the first case, the framework chose to use the 'primary'
-view since there was only one entity in the data to be displayed. Now that there
-are two entities, the 'list' view is more appropriate and hence is being used.
-
-There are various other places where |cubicweb| adapts to display data in the best
-way, the main being provided by the view *selection* mechanism that will be detailed
-later.
-
-
-Digging deeper
-~~~~~~~~~~~~~~
-
-By following principles explained below, you should now be able to
-create new users for your application, to configure with a finer
-grain, etc... You will notice that the index page lists a lot of types
-you don't know about. Most are built-in types provided by the framework
-to make the whole system work. You may ignore them in a first time and
-discover them as time goes.
-
-One thing that is worth playing with is the search box. It may be used in various
-way, from simple full text search to advanced queries using the :ref:`RQL` .
--- a/doc/book/en/tutorials/base/index.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _TutosBase:
-
-Building a simple blog with |cubicweb|
-======================================
-
-|cubicweb| is a semantic web application framework that favors reuse and
-object-oriented design.
-
-
-This tutorial is designed to help in your very first steps to start with
-|cubicweb|. We will tour through basic concepts such as:
-
-* getting an application running by using existing components
-* discovering the default user interface
-* basic extending and customizing the look and feel of that application
-
-More advanced concepts are covered in :ref:`TutosPhotoWebSite`.
-
-
-.. _TutosBaseVocab:
-
-Some vocabulary
----------------
-
-|cubicweb| comes with a few words of vocabulary that you should know to
-understand what we're talking about. To follow this tutorial, you should at least
-know that:
-
-* a `cube` is a component that usually includes a model defining some data types
-  and a set of views to display them. A cube can be built by assembling other
-  cubes;
-
-* an `instance` is a specific installation of one or more cubes and includes
-  configuration files, a web server and a database.
-
-Reading :ref:`Concepts` for more vocabulary will be required at some point.
-
-Now, let's start the hot stuff!
-
-.. toctree::
-   :maxdepth: 2
-
-   blog-in-five-minutes
-   discovering-the-ui
-   customizing-the-application
-   conclusion
--- a/doc/book/en/tutorials/index.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-.. _Tutorials:
-
----------
-Tutorials
----------
-
-We present two tutorials of different levels. The blog building
-tutorial introduces one smoothly to the basic concepts.
-
-Then there is a photo gallery construction tutorial which highlights
-more advanced concepts such as unit tests, security settings,
-migration scripts.
-
-.. toctree::
-   :maxdepth: 1
-   :numbered:
-
-   base/index
-   advanced/index
-   tools/windmill.rst
-   textreports/index
--- a/doc/book/en/tutorials/textreports/index.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-Writing text reports with RestructuredText
-==========================================
-
-|cubicweb| offers several text formats for the RichString type used in schemas,
-including restructuredtext.
-
-Three additional restructuredtext roles are defined by |cubicweb|:
-
-.. autofunction:: cubicweb.ext.rest.eid_reference_role
-.. autofunction:: cubicweb.ext.rest.rql_role
-.. autofunction:: cubicweb.ext.rest.bookmark_role
--- a/doc/book/en/tutorials/tools/windmill.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,229 +0,0 @@
-==========================
-Use Windmill with CubicWeb
-==========================
-
-Windmill_ implements cross browser testing, in-browser recording and playback,
-and functionality for fast accurate debugging and test environment integration.
-
-.. _Windmill: http://www.getwindmill.com/
-
-`Online features list <http://www.getwindmill.com/features>`_ is available.
-
-
-Installation
-============
-
-Windmill
---------
-
-You have to install Windmill manually for now. If you're using Debian, there is
-no binary package (`yet <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=579109>`_).
-
-The simplest solution is to use a *setuptools/pip* command (for a clean
-environment, take a look to the `virtualenv
-<http://pypi.python.org/pypi/virtualenv>`_ project as well)::
-
-    $ pip install windmill
-    $ curl -O http://github.com/windmill/windmill/tarball/master
-
-However, the Windmill project doesn't release frequently. Our recommandation is
-to used the last snapshot of the Git repository::
-
-    $ git clone git://github.com/windmill/windmill.git HEAD
-    $ cd windmill
-    $ python setup.py develop
-
-Install instructions are `available <http://wiki.github.com/windmill/windmill/installing>`_.
-
-Be sure to have the windmill module in your PYTHONPATH afterwards::
-
-    $ python -c "import windmill"
-
-X dummy
--------
-
-In order to reduce unecessary system load from your test machines, It's
-recommended to use X dummy server for testing the Unix web clients, you need a
-dummy video X driver (as xserver-xorg-video-dummy package in Debian) coupled
-with a light X server as `Xvfb <http://en.wikipedia.org/wiki/Xvfb>`_.
-
-    The dummy driver is a special driver available with the XFree86 DDX. To use
-    the dummy driver, simply substitue it for your normal card driver in the
-    Device section of your xorg.conf configuration file. For example, if you
-    normally uses an ati driver, then you will have a Device section with
-    Driver "ati" to let the X server know that you want it to load and use the
-    ati driver; however, for these conformance tests, you would change that
-    line to Driver "dummy" and remove any other ati specific options from the
-    Device section.
-
-    *From: http://www.x.org/wiki/XorgTesting*
-
-Then, you can run the X server with the following command ::
-
-    $ /usr/bin/X11/Xvfb :1 -ac -screen 0 1280x1024x8 -fbdir /tmp
-
-
-Windmill usage
-==============
-
-Record your use case
---------------------
-
-- start your instance manually
-- start Windmill_ with url site as last argument (read Usage_ or use *'-h'*
-  option to find required command line arguments)
-- use the record button
-- click on save to obtain python code of your use case
-- copy the content to a new file in a *windmill* directory
-
-.. _Usage: http://wiki.github.com/windmill/windmill/running-tests
-
-If you are using firefox as client, consider the "firebug" option.
-
-If you have a running instance, you can refine the test by the *loadtest* windmill option::
-
-    $ windmill -m firebug loadtest=<test_file.py> <instance url>
-
-Or use the internal windmill shell to explore available commands::
-
-    $ windmill -m firebug shell <instance url>
-
-And enter python commands:
-
-.. sourcecode:: python
-
-    >>> load_test(<your test file>)
-    >>> run_test(<your test file>)
-
-
-
-Integrate Windmill tests into CubicWeb
-======================================
-
-Set environment
----------------
-
-You have to create a new unit test file and a `windmill` directory and copy all
-your windmill use case into it.
-
-.. sourcecode:: python
-
-    # test_windmill.py
-
-    # Run all scenarii found in windmill directory
-    from cubicweb.devtools.cwwindmill import (CubicWebWindmillUseCase,
-                                              unittest_main)
-
-    if __name__ == '__main__':
-        unittest_main()
-
-Run your tests
---------------
-
-You can easily run your windmill test suite through `pytest` or :mod:`unittest`.
-You have to copy a *test_windmill.py* file from :mod:`web.test`.
-
-To run your test series::
-
-    $ pytest test/test_windmill.py
-
-By default, CubicWeb will use **firefox** as the default browser and will try
-to run test instance server on localhost. In the general case, You've no need
-to change anything.
-
-Check :class:`cubicweb.devtools.cwwindmill.CubicWebWindmillUseCase` for
-Windmill configuration. You can edit windmill settings with following class attributes:
-
-* browser
-  identification string (firefox|ie|safari|chrome) (firefox by default)
-* test_dir
-  testing file path or directory (windmill directory under your unit case
-  file by default)
-* edit_test
-  load and edit test for debugging (False by default)
-
-Examples:
-
-.. sourcecode:: python
-
-    browser = 'firefox'
-    test_dir = osp.join(__file__, 'windmill')
-    edit_test = False
-
-If you want to change cubicweb test server parameters, you can check class
-variables from :class:`CubicWebServerConfig` or inherit it with overriding the
-:attr:`configcls` attribute in :class:`CubicWebServerTC` ::
-
-.. sourcecode:: python
-
-    class OtherCubicWebServerConfig(CubicWebServerConfig):
-        port = 9999
-
-    class NewCubicWebServerTC(CubicWebServerTC):
-        configcls = OtherCubicWebServerConfig
-
-For instance, CubicWeb framework windmill tests can be manually run by::
-
-    $ pytest web/test/test_windmill.py
-
-Edit your tests
----------------
-
-You can toggle the `edit_test` variable to enable test edition.
-
-But if you are using `pytest` as test runner, use the `-i` option directly.
-The test series will be loaded and you can run assertions step-by-step::
-
-    $ pytest -i test/test_windmill.py
-
-In this case, the `firebug` extension will be loaded automatically for you.
-
-Afterwards, don't forget to save your edited test into the right file (no autosave feature).
-
-Best practises
---------------
-
-Don't run another instance on the same port. You risk to silence some
-regressions (test runner will automatically fail in further versions).
-
-Start your use case by using an assert on the expected primary url page.
-Otherwise all your tests could fail without clear explanation of the used
-navigation.
-
-In the same location of the *test_windmill.py*, create a *windmill/* with your
-windmill recorded use cases.
-
-
-Caveats
-=======
-
-File Upload
------------
-
-Windmill can't do file uploads. This is a limitation of browser Javascript
-support / sandboxing, not of Windmill per se.  It would be nice if there were
-some command that would prime the Windmill HTTP proxy to add a particular file
-to the next HTTP request that comes through, so that uploads could at least be
-faked.
-
-.. http://groups.google.com/group/windmill-dev/browse_thread/thread/cf9dc969722bd6bb/01aa18fdd652f7ff?lnk=gst&q=input+type+file#01aa18fdd652f7ff
-
-.. http://davisagli.com/blog/in-browser-integration-testing-with-windmill
-
-.. http://groups.google.com/group/windmill-dev/browse_thread/thread/b7bebcc38ed30dc7
-
-
-Preferences
-===========
-
-A *.windmill/prefs.py* could be used to redefine default configuration values.
-
-.. define CubicWeb preferences in the parent test case instead with a dedicated firefox profile
-
-For managing browser extensions, read `advanced topic chapter
-<http://wiki.github.com/windmill/windmill/advanced-topics>`_.
-
-More configuration examples could be seen in *windmill/conf/global_settings.py*
-as template.
-
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/intro/concepts.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,297 @@
+.. -*- coding: utf-8 -*-
+
+.. _Concepts:
+
+The Core Concepts of |cubicweb|
+===============================
+
+This section defines some terms and core concepts of the |cubicweb| framework. To
+avoid confusion while reading this book, take time to go through the following
+definitions and use this section as a reference during your reading.
+
+
+.. _Cube:
+
+Cubes
+-----
+
+A cube is a software component made of three parts:
+
+- its data model (:mod:`schema`),
+- its logic (:mod:`entities`) and
+- its user interface (:mod:`views`).
+
+A cube can use other cubes as building blocks and assemble them to provide a
+whole with richer functionnalities than its parts. The cubes `cubicweb-blog`_ and
+`cubicweb-comment`_ could be used to make a cube named *myblog* with commentable
+blog entries.
+
+The `CubicWeb.org Forge`_ offers a large number of cubes developed by the community
+and available under a free software license.
+
+.. note::
+
+   The command :command:`cubicweb-ctl list` displays the list of available cubes.
+
+.. _`CubicWeb.org Forge`: http://www.cubicweb.org/project/
+.. _`cubicweb-blog`: http://www.cubicweb.org/project/cubicweb-blog
+.. _`cubicweb-comment`: http://www.cubicweb.org/project/cubicweb-comment
+
+
+.. _Instance:
+
+Instances
+---------
+
+An instance is a runnable application installed on a computer and
+based on one or more cubes.
+
+The instance directory contains the configuration files. Several
+instances can be created and based on the same cube. For example,
+several software forges can be set up on one computer system based on
+the `cubicweb-forge`_ cube.
+
+.. _`cubicweb-forge`: http://www.cubicweb.org/project/cubicweb-forge
+
+The command :command:`cubicweb-ctl list` also displays the list of instances
+installed on your system.
+
+.. note::
+
+  The term application is used to refer to "something that should do something as
+  a whole", eg more like a project and so can refer to an instance or to a cube,
+  depending on the context. This book will try to use *application*, *cube* and
+  *instance* as appropriate.
+
+
+.. _RepositoryIntro:
+
+Data Repository
+---------------
+
+The data repository [1]_ encapsulates and groups an access to one or
+more data sources (including SQL databases, LDAP repositories, other
+|cubicweb| instance repositories, filesystems, Google AppEngine's
+DataStore, etc).
+
+All interactions with the repository are done using the `Relation Query Language`
+(:ref:`RQL`). The repository federates the data sources and hides them from the
+querier, which does not realize when a query spans several data sources
+and requires running sub-queries and merges to complete.
+
+Application logic can be mapped to data events happenning within the
+repository, like creation of entities, deletion of relations,
+etc. This is used for example to send email notifications when the
+state of an object changes. See :ref:`HookIntro` below.
+
+.. [1] not to be confused with a Mercurial repository or a Debian repository.
+.. _`Python Remote Objects`: http://pythonhosted.org/Pyro4/
+
+.. _WebEngineIntro:
+
+Web Engine
+----------
+
+The web engine replies to http requests and runs the user interface.
+
+By default the web engine provides a `CRUD`_ user interface based on
+the data model of the instance. Entities can be created, displayed,
+updated and deleted. As the default user interface is not very fancy,
+it is usually necessary to develop your own.
+
+.. _`CRUD`: http://en.wikipedia.org/wiki/Create,_read,_update_and_delete
+
+.. _SchemaIntro:
+
+Schema (Data Model)
+-------------------
+
+The data model of a cube is described as an entity-relationship schema using a
+comprehensive language made of Python classes imported from the yams_ library.
+
+.. _yams: http://www.logilab.org/project/yams/
+
+An `entity type` defines a sequence of attributes. Attributes may be
+of the following types: `String`, `Int`, `Float`, `Boolean`, `Date`,
+`Time`, `Datetime`, `Interval`, `Password`, `Bytes`, `RichString`.
+
+A `relation type` is used to define an oriented binary relation
+between entity types.  The left-hand part of a relation is named the
+`subject` and the right-hand part is named the `object`.
+
+A `relation definition` is a triple (*subject entity type*, *relation type*, *object
+entity type*) associated with a set of properties such as cardinality,
+constraints, etc.
+
+Permissions can be set on entity types or relation definition to control who
+will be able to create, read, update or delete entities and relations. Permissions
+are granted to groups (to which users may belong) or using rql expressions (if the
+rql expression returns some results, the permission is granted).
+
+Some meta-data necessary to the system are added to the data model. That includes
+entities like users and groups, the entities used to store the data model
+itself and attributes like unique identifier, creation date, creator, etc.
+
+When you create a new |cubicweb| instance, the schema is stored in the database.
+When the cubes the instance is based on evolve, they may change their data model
+and provide migration scripts that will be executed when the administrator will
+run the upgrade process for the instance.
+
+
+.. _VRegistryIntro:
+
+Registries and application objects
+----------------------------------
+
+Application objects
+~~~~~~~~~~~~~~~~~~~
+
+Besides a few core functionalities, almost every feature of the framework is
+achieved by dynamic objects (`application objects` or `appobjects`) stored in a
+two-levels registry. Each object is affected to a registry with
+an identifier in this registry. You may have more than one object sharing an
+identifier in the same registry:
+
+  object's `__registry__` : object's `__regid__` : [list of app objects]
+
+In other words, the `registry` contains several (sub-)registries which hold a
+list of appobjects associated to an identifier.
+
+The base class of appobjects is :class:`cubicweb.appobject.AppObject`.
+
+Selectors
+~~~~~~~~~
+
+At runtime, appobjects can be selected in a registry according to some
+contextual information. Selection is done by comparing the *score*
+returned by each appobject's *selector*.
+
+The better the object fits the context, the higher the score. Scores
+are the glue that ties appobjects to the data model. Using them
+appropriately is an essential part of the construction of well behaved
+cubes.
+
+|cubicweb| provides a set of basic selectors that may be parametrized.  Also,
+selectors can be combined with the `~` unary operator (negation) and the binary
+operators `&` and `|` (respectivly 'and' and 'or') to build more complex
+selectors. Of course complex selectors may be combined too. Last but not least, you
+can write your own selectors.
+
+The `registry`
+~~~~~~~~~~~~~~~
+
+At startup, the `registry` inspects a number of directories looking
+for compatible class definitions. After a recording process, the
+objects are assigned to registries and become available through the
+selection process.
+
+In a cube, application object classes are looked in the following modules or
+packages:
+
+- `entities`
+- `views`
+- `hooks`
+- `sobjects`
+
+There are three common ways to look up some application object from a
+registry:
+
+* get the most appropriate object by specifying an identifier and
+  context objects. The object with the greatest score is
+  selected. There should always be a single appobject with a greater
+  score than others for a particular context.
+
+* get all objects applying to a context by specifying a registry. A
+  list of objects will be returned containing the object with the
+  highest score (> 0) for each identifier in that registry.
+
+* get the object within a particular registry/identifier. No selection
+  process is involved: the registry will expect to find a single
+  object in that cell.
+
+
+.. _RQLIntro:
+
+The RQL query language
+----------------------
+
+No need for a complicated ORM when you have a powerful data
+manipulation language.
+
+All the persistent data in a |cubicweb| instance is retrieved and
+modified using RQL (see :ref:`rql_intro`).
+
+This query language is inspired by SQL but is on a higher level in order to
+emphasize browsing relations.
+
+
+Result set
+~~~~~~~~~~
+
+Every request made (using RQL) to the data repository returns an object we call a
+Result Set. It enables easy use of the retrieved data, providing a translation
+layer between the backend's native datatypes and |cubicweb| schema's EntityTypes.
+
+Result sets provide access to the raw data, yielding either basic Python data
+types, or schema-defined high-level entities, in a straightforward way.
+
+
+.. _ViewIntro:
+
+Views
+-----
+
+**CubicWeb is data driven**
+
+The view system is loosely coupled to data through the selection system explained
+above. Views are application objects with a dedicated interface to 'render'
+something, eg producing some html, text, xml, pdf, or whatsover that can be
+displayed to a user.
+
+Views actually are partitioned into different kind of objects such as
+`templates`, `boxes`, `components` and proper `views`, which are more
+high-level abstraction useful to build the user interface in an object
+oriented way.
+
+
+.. _HookIntro:
+
+Hooks and operations
+--------------------
+
+**CubicWeb provides an extensible data repository**
+
+The data model defined using Yams types allows to express the data
+model in a comfortable way. However several aspects of the data model
+can not be expressed there. For instance:
+
+* managing computed attributes
+
+* enforcing complicated business rules
+
+* real-world side-effects linked to data events (email notification
+  being a prime example)
+
+The hook system is much like the triggers of an SQL database engine,
+except that:
+
+* it is not limited to one specific SQL backend (every one of them
+  having an idiomatic way to encode triggers), nor to SQL backends at
+  all (think about LDAP or a Subversion repository)
+
+* it is well-coupled to the rest of the framework
+
+Hooks are also application objects (in the `hooks` registry) and
+selected on events such as after/before add/update/delete on
+entities/relations, server startup or shutdown, etc.
+
+`Operations` may be instantiated by hooks to do further processing at different
+steps of the transaction's commit / rollback, which usually can not be done
+safely at the hook execution time.
+
+Hooks and operation are an essential building block of any moderately complicated
+cubicweb application.
+
+.. note::
+   RQL queries executed in hooks and operations are *unsafe* by default, i.e. the
+   read and write security is deactivated unless explicitly asked.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/intro/history.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,32 @@
+.. -*- coding: utf-8 -*-
+
+A little history...
+===================
+
+*CubicWeb* is a semantic web application framework that Logilab_ started
+developing in 2001 as an offspring of its Narval_ research project. *CubicWeb*
+is written in Python and includes a data server and a web engine.
+
+Its data server publishes data federated from different sources like
+SQL databases, LDAP directories, `VCS`_ repositories or even from other
+CubicWeb data servers.
+
+.. _`VCS`: http://en.wikipedia.org/wiki/Revision_control
+
+Its web engine was designed to let the final user control what content to select
+and how to display it. It allows one to browse the federated data sources and
+display the results with the rendering that best fits the context. This
+flexibility of the user interface gives back to the user some capabilities
+usually only accessible to application developers.
+
+*CubicWeb* has been developed by Logilab_ and used in-house for many years
+before it was first installed for its clients in 2006 as version 2.
+
+In 2008, *CubicWeb* version 3 became downloadable for free under the
+terms of the LGPL license. Its community is now steadily growing
+without hampering the fast-paced stream of changes thanks to the time
+and energy originally put in the design of the framework.
+
+
+.. _Narval: http://www.logilab.org/project/narval-moved
+.. _Logilab: http://www.logilab.fr/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/intro/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,19 @@
+.. -*- coding: utf-8 -*-
+
+.. _Part1:
+
+--------------------------
+Introduction to *CubicWeb*
+--------------------------
+
+This first part of the book offers different reading path to
+discover the *CubicWeb* framework, provides a tutorial to get a quick
+overview of its features and lists its key concepts.
+
+
+.. toctree::
+   :maxdepth: 2
+   :numbered:
+
+   history
+   concepts.rst
--- a/doc/book/mode_plan.py	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of CubicWeb.
-#
-# CubicWeb is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""
->>> from mode_plan import *
->>> ls()
-<list of directory content>
->>> ren('A01','A03')
-rename A010-joe.en.txt to A030-joe.en.txt
-accept [y/N]?
-"""
-
-def ren(a,b):
-    names = glob.glob('%s*'%a)
-    for name in names :
-        print 'rename %s to %s' % (name, name.replace(a,b))
-    if raw_input('accept [y/N]?').lower() =='y':
-        for name in names:
-            os.system('hg mv %s %s' % (name, name.replace(a,b)))
-
-
-def ls(): print '\n'.join(sorted(os.listdir('.')))
-
-def move():
-    filenames = []
-    for name in sorted(os.listdir('.')):
-        num = name[:2]
-        if num.isdigit():
-            filenames.append( (int(num), name) )
-
-
-    #print filenames
-
-    for num, name in filenames:
-        if num >= start:
-            print 'hg mv %s %2i%s' %(name,num+1,name[2:])
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/changes/3.14.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,165 @@
+3.14 (09/11/2011)
+=================
+
+First notice CW 3.14 depends on yams 0.34 (which is incompatible with prior
+cubicweb releases regarding instance re-creation).
+
+
+API changes
+-----------
+
+* `Entity.fetch_rql` `restriction` argument has been deprecated and should be
+  replaced with a call to the new `Entity.fetch_rqlst` method, get the returned
+  value (a rql `Select` node) and use the RQL syntax tree API to include the
+  above-mentionned restrictions.
+
+  Backward compat is kept with proper warning.
+
+* `Entity.fetch_order` and `Entity.fetch_unrelated_order` class methods have been
+  replaced by `Entity.cw_fetch_order` and `Entity.cw_fetch_unrelated_order` with
+  a different prototype:
+
+  - instead of taking (attr, var) as two string argument, they now take (select,
+    attr, var) where select is the rql syntax tree beinx constructed and var the
+    variable *node*.
+
+  - instead of returning some string to be inserted in the ORDERBY clause, it has
+    to modify the syntax tree
+
+  Backward compat is kept with proper warning, BESIDE cases below:
+
+  - custom order method return **something else the a variable name with or
+    without the sorting order** (e.g. cases where you sort on the value of a
+    registered procedure as it was done in the tracker for instance). In such
+    case, an error is logged telling that this sorting is ignored until API
+    upgrade.
+
+  - client code use direct access to one of those methods on an entity (no code
+    known to do that).
+
+* `Entity._rest_attr_info` class method has been renamed to
+  `Entity.cw_rest_attr_info`
+
+  No backward compat yet since this is a protected method an no code is known to
+  use it outside cubicweb itself.
+
+* `AnyEntity.linked_to` has been removed as part of a refactoring of this
+  functionality (link a entity to another one at creation step). It was replaced
+  by a `EntityFieldsForm.linked_to` property.
+
+  In the same refactoring, `cubicweb.web.formfield.relvoc_linkedto`,
+  `cubicweb.web.formfield.relvoc_init` and
+  `cubicweb.web.formfield.relvoc_unrelated` were removed and replaced by
+  RelationField methods with the same names, that take a form as a parameter.
+
+  **No backward compatibility yet**. It's still time to cry for it.
+  Cubes known to be affected: tracker, vcsfile, vcreview.
+
+* `CWPermission` entity type and its associated require_permission relation type
+  (abstract) and require_group relation definitions have been moved to a new
+  `localperms` cube. With this have gone some functions from the
+  `cubicweb.schemas` package as well as some views. This makes cubicweb itself
+  smaller while you get all the local permissions stuff into a single,
+  documented, place.
+
+  Backward compat is kept for existing instances, **though you should have
+  installed the localperms cubes**. A proper error should be displayed when
+  trying to migrate to 3.14 an instance the use `CWPermission` without the new
+  cube installed. For new instances / test, you should add a dependancy on the
+  new cube in cubes using this feature, along with a dependancy on cubicweb >=
+  3.14.
+
+* jQuery has been updated to 1.6.4 and jquery-tablesorter to 2.0.5. No backward
+  compat issue known.
+
+* Table views refactoring : new `RsetTableView` and `EntityTableView`, as well as
+  rewritten an enhanced version of `PyValTableView` on the same bases, with logic
+  moved to some column renderers and a layout. Those should be well documented
+  and deprecates former `TableView`, `EntityAttributesTableView` and `CellView`,
+  which are however kept for backward compat, with some warnings that may not be
+  very clear unfortunatly (you may see your own table view subclass name here,
+  which doesn't make the problem that clear). Notice that `_cw.view('table',
+  rset, *kwargs)` will be routed to the new `RsetTableView` or to the old
+  `TableView` depending on given extra arguments. See #1986413.
+
+* `display_name` don't call .lower() anymore. This may leads to changes in your
+  user interface. Different msgid for upper/lower cases version of entity type
+  names, as this is the only proper way to handle this with some languages.
+
+* `IEditControlAdapter` has been deprecated in favor of `EditController`
+  overloading, which was made easier by adding dedicated selectors called
+  `match_edited_type` and `match_form_id`.
+
+* Pre 3.6 API backward compat has been dropped, though *data* migration
+  compatibility has been kept. You may have to fix errors due to old API usage
+  for your instance before to be able to run migration, but then you should be
+  able to upgrade even a pre 3.6 database.
+
+* Deprecated `cubicweb.web.views.iprogress` in favor of new `iprogress` cube.
+
+* Deprecated `cubicweb.web.views.flot` in favor of new `jqplot` cube.
+
+
+Unintrusive API changes
+-----------------------
+
+* Refactored properties forms (eg user preferences and site wide properties) as
+  well as pagination components to ease overridding.
+
+* New `cubicweb.web.uihelper` module with high-level helpers for uicfg.
+
+* New `anonymized_request` decorator to temporary run stuff as an anonymous
+  user, whatever the currently logged in user.
+
+* New 'verbatimattr' attribute view.
+
+* New facet and form widget for Integer used to store binary mask.
+
+* New `js_href` function to generated proper javascript href.
+
+* `match_kwargs` and `match_form_params` selectors both accept a new
+  `once_is_enough` argument.
+
+* `printable_value` is now a method of request, and may be given dict of
+   formatters to use.
+
+* `[Rset]TableView` allows to set None in 'headers', meaning the label should be
+  fetched from the result set as done by default.
+
+* Field vocabulary computation on entity creation now takes `__linkto`
+  information into accounet.
+
+* Started a `cubicweb.pylintext` pylint plugin to help pylint analyzing cubes.
+
+
+RQL
+---
+
+* Support for HAVING in 'SET' and 'DELETE' queries.
+
+* new `AT_TZ` function to get back a timestamp at a given time-zone.
+
+* new `WEEKDAY` date extraction function
+
+
+User interface changes
+----------------------
+
+* Datafeed source now present an history of the latest import's log, including
+  global status and debug/info/warning/error messages issued during
+  imports. Import logs older than a configurable amount of time are automatically
+  deleted.
+
+* Breadcrumbs component is properly kept when creating an entity with '__linkto'.
+
+* users and groups management now really lead to that (i.e. includes *groups*
+  management).
+
+* New 'jsonp' controller with 'jsonexport' and 'ejsonexport' views.
+
+
+Configuration
+-------------
+
+* Added option 'resources-concat' to make javascript/css files concatenation
+  optional.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/changes/3.15.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,98 @@
+3.15 (12/04/2012)
+=================
+
+New functionnalities
+--------------------
+
+* Add Zmq server, based on the cutting edge ZMQ (http://www.zeromq.org/) socket
+  library.  This allows to access distant instance, in a similar way as Pyro.
+
+* Publish/subscribe mechanism using ZMQ for communication among cubicweb
+  instances.  The new zmq-address-sub and zmq-address-pub configuration variables
+  define where this communication occurs.  As of this release this mechanism is
+  used for entity cache invalidation.
+
+* Improved WSGI support. While there is still some caveats, most of the code
+  which was twisted only is now generic and allows related functionalities to work
+  with a WSGI front-end.
+
+* Full undo/transaction support : undo of modification has eventually been
+  implemented, and the configuration simplified (basically you activate it or not
+  on an instance basis).
+
+* Controlling HTTP status code used is not much more easier :
+
+  - `WebRequest` now has a `status_out` attribut to control the response status ;
+
+  - most web-side exceptions take an optional ``status`` argument.
+
+
+API changes
+-----------
+
+* The base registry implementation has been moved to a new
+  `logilab.common.registry` module (see #1916014). This includes code from :
+
+  * `cubicweb.vreg` (the whole things that was in there)
+  * `cw.appobject` (base selectors and all).
+
+  In the process, some renaming was done:
+
+  * the top level registry is now `RegistryStore` (was `VRegistry`), but that
+    should not impact cubicweb client code ;
+
+  * former selectors functions are now known as "predicate", though you still use
+    predicates to build an object'selector ;
+
+  * for consistency, the `objectify_selector` decoraror has hence be renamed to
+    `objectify_predicate` ;
+
+  * on the CubicWeb side, the `selectors` module has been renamed to
+    `predicates`.
+
+  Debugging refactoring dropped the more need for the `lltrace` decorator.  There
+  should be full backward compat with proper deprecation warnings.  Notice the
+  `yes` predicate and `objectify_predicate` decorator, as well as the
+  `traced_selection` function should now be imported from the
+  `logilab.common.registry` module.
+
+* All login forms are now submitted to <app_root>/login. Redirection to requested
+  page is now handled by the login controller (it was previously handle by the
+  session manager).
+
+* `Publisher.publish` has been renamed to `Publisher.handle_request`. This
+  method now contains generic version of logic previously handled by
+  Twisted. `Controller.publish` is **not** affected.
+
+
+Unintrusive API changes
+-----------------------
+
+* New 'ldapfeed' source type, designed to replace 'ldapuser' source with
+  data-feed (i.e. copy based) source ideas.
+
+* New 'zmqrql' source type, similar to 'pyrorql' but using ømq instead of Pyro.
+
+* A new registry called `services` has appeared, where you can register
+  server-side `cubicweb.server.Service` child classes. Their `call` method can be
+  invoked from a web-side AppObject instance using new `self._cw.call_service`
+  method or a server-side one using `self.session.call_service`. This is a new
+  way to call server-side methods, much cleaner than monkey patching the
+  Repository class, which becomes a deprecated way to perform similar tasks.
+
+* a new `ajax-func` registry now hosts all remote functions (i.e. functions
+  callable through the `asyncRemoteExec` JS api). A convenience `ajaxfunc`
+  decorator will let you expose your python function easily without all the
+  appobject standard boilerplate. Backward compatibility is preserved.
+
+* the 'json' controller is now deprecated in favor of the 'ajax' one.
+
+* `WebRequest.build_url` can now take a __secure__ argument. When True cubicweb
+  try to generate an https url.
+
+
+User interface changes
+----------------------
+
+A new 'undohistory' view expose the undoable transactions and give access to undo
+some of them.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/changes/3.16.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,97 @@
+3.16 (25/01/2013)
+=================
+
+New functionalities
+-------------------
+
+* Add a new dataimport store (`SQLGenObjectStore`). This store enables a fast
+  import of data (entity creation, link creation) in CubicWeb, by directly
+  flushing information in SQL.  This may only be used with PostgreSQL, as it
+  requires the 'COPY FROM' command.
+
+
+API changes
+-----------
+
+* Orm: `set_attributes` and `set_relations` are unified (and
+  deprecated) in favor of `cw_set` that works in all cases.
+
+* db-api/configuration: all the external repository connection information is
+  now in an URL (see `#2521848 <http://www.cubicweb.org/2521848>`_),
+  allowing to drop specific options of pyro nameserver host, group, etc and fix
+  broken `ZMQ <http://www.zeromq.org/>`_ source. Configuration related changes:
+
+  * Dropped 'pyro-ns-host', 'pyro-instance-id', 'pyro-ns-group' from the client side
+    configuration, in favor of 'repository-uri'. **NO MIGRATION IS DONE**,
+    supposing there is no web-only configuration in the wild.
+
+  * Stop discovering the connection method through `repo_method` class attribute
+    of the configuration, varying according to the configuration class. This is
+    a first step on the way to a simpler configuration handling.
+
+  DB-API related changes:
+
+  * Stop indicating the connection method using `ConnectionProperties`.
+
+  * Drop `_cnxtype` attribute from `Connection` and `cnxtype` from
+    `Session`. The former is replaced by a `is_repo_in_memory` property
+    and the later is totaly useless.
+
+  * Turn `repo_connect` into `_repo_connect` to mark it as a private function.
+
+  * Deprecate `in_memory_cnx` which becomes useless, use `_repo_connect` instead
+    if necessary.
+
+* the "tcp://" uri scheme used for `ZMQ <http://www.zeromq.org/>`_
+  communications (in a way reminiscent of Pyro) is now named
+  "zmqpickle-tcp://", so as to make room for future zmq-based lightweight
+  communications (without python objects pickling).
+
+* Request.base_url gets a `secure=True` optional parameter that yields
+  an https url if possible, allowing hook-generated content to send
+  secure urls (e.g. when sending mail notifications)
+
+* Dataimport ucsvreader gets a new boolean `ignore_errors`
+  parameter.
+
+
+Unintrusive API changes
+-----------------------
+
+* Drop of `cubicweb.web.uicfg.AutoformSectionRelationTags.bw_tag_map`,
+  deprecated since 3.6.
+
+
+User interface changes
+----------------------
+
+* The RQL search bar has now some auto-completion support. It means
+  relation types or entity types can be suggested while typing. It is
+  an awesome improvement over the current behaviour !
+
+* The `action box` associated with `table` views (from `tableview.py`)
+  has been transformed into a nice-looking series of small tabs; it
+  means that the possible actions are immediately visible and need not
+  be discovered by clicking on an almost invisible icon on the upper
+  right.
+
+* The `uicfg` module has moved to web/views/ and ui configuration
+  objects are now selectable. This will reduce the amount of
+  subclassing and whole methods replacement usually needed to
+  customize the ui behaviour in many cases.
+
+* Remove changelog view, as neither cubicweb nor known
+  cubes/applications were properly feeding related files.
+
+
+Other changes
+-------------
+
+* 'pyrorql' sources will be automatically updated to use an URL to locate the source
+  rather than configuration option. 'zmqrql' sources were broken before this change,
+  so no upgrade is needed...
+
+* Debugging filters for Hooks and Operations have been added.
+
+* Some cubicweb-ctl commands used to show the output of `msgcat` and
+  `msgfmt`; they don't anymore.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/changes/3.17.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,61 @@
+3.17 (02/05/2013)
+=================
+
+New functionalities
+-------------------
+
+* add a command to compare db schema and file system schema
+  (see `#464991 <http://www.cubicweb.org/464991>`_)
+
+* Add CubicWebRequestBase.content with the content of the HTTP request (see #2742453)
+  (see `#2742453 <http://www.cubicweb.org/2742453>`_)
+
+* Add directive bookmark to ReST rendering
+  (see `#2545595 <http://www.cubicweb.org/ticket/2545595>`_)
+
+* Allow user defined final type
+  (see `#124342 <https://www.logilab.org/ticket/124342>`_)
+
+
+API changes
+-----------
+
+* drop typed_eid() in favour of int() (see `#2742462 <http://www.cubicweb.org/2742462>`_)
+
+* The SIOC views and adapters have been removed from CubicWeb and moved to the
+  `sioc` cube.
+
+* The web page embedding views and adapters have been removed from CubicWeb and
+  moved to the `embed` cube.
+
+* The email sending views and controllers have been removed from CubicWeb and
+  moved to the `massmailing` cube.
+
+* ``RenderAndSendNotificationView`` is deprecated in favor of
+  ``ActualNotificationOp`` the new operation use the more efficient *data*
+  idiom.
+
+* Looping task can now have a interval <= ``0``. Negative interval disable the
+  looping task entirely.
+
+* We now serve html instead of xhtml.
+  (see `#2065651 <http://www.cubicweb.org/ticket/2065651>`_)
+
+
+Deprecation
+-----------
+
+* ``ldapuser`` have been deprecated. It'll be fully dropped in the next
+  version. If you are still using ldapuser switch to ``ldapfeed`` **NOW**!
+
+* ``hijack_user`` have been deprecated. It will be dropped soon.
+
+
+Deprecated Code Drops
+---------------------
+
+* The progress views and adapters have been removed from CubicWeb. These
+  classes were deprecated since 3.14.0. They are still available in the
+  `iprogress` cube.
+
+* API deprecated since 3.7 have been dropped.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/changes/3.18.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,101 @@
+3.18 (10/01/2014)
+=================
+
+The migration script does not handle sqlite nor mysql instances.
+
+
+New functionalities
+-------------------
+
+* add a security debugging tool
+  (see `#2920304 <http://www.cubicweb.org/2920304>`_)
+
+* introduce an `add` permission on attributes, to be interpreted at
+  entity creation time only and allow the implementation of complex
+  `update` rules that don't block entity creation (before that the
+  `update` attribute permission was interpreted at entity creation and
+  update time)
+
+* the primary view display controller (uicfg) now has a
+  `set_fields_order` method similar to the one available for forms
+
+* new method `ResultSet.one(col=0)` to retrive a single entity and enforce the
+  result has only one row (see `#3352314 <https://www.cubicweb.org/ticket/3352314>`_)
+
+* new method `RequestSessionBase.find` to look for entities
+  (see `#3361290 <https://www.cubicweb.org/ticket/3361290>`_)
+
+* the embedded jQuery copy has been updated to version 1.10.2, and jQuery UI to
+  version 1.10.3.
+
+* initial support for wsgi for the debug mode, available through the new
+  ``wsgi`` cubicweb-ctl command, which can use either python's builtin
+  wsgi server or the werkzeug module if present.
+
+* a ``rql-table`` directive is now available in ReST fields
+
+* cubicweb-ctl upgrade can now generate the static data resource directory
+  directly, without a manual call to gen-static-datadir.
+
+API changes
+-----------
+
+* not really an API change, but the entity permission checks are now
+  systematically deferred to an operation, instead of a) trying in a
+  hook and b) if it failed, retrying later in an operation
+
+* The default value storage for attributes is no longer String, but
+  Bytes.  This opens the road to storing arbitrary python objects, e.g.
+  numpy arrays, and fixes a bug where default values whose truth value
+  was False were not properly migrated.
+
+* `symmetric` relations are no more handled by an rql rewrite but are
+  now handled with hooks (from the `activeintegrity` category); this
+  may have some consequences for applications that do low-level database
+  manipulations or at times disable (some) hooks.
+
+* `unique together` constraints (multi-columns unicity constraints)
+  get a `name` attribute that maps the CubicWeb contraint entities to
+  corresponding backend index.
+
+* BreadCrumbEntityVComponent's open_breadcrumbs method now includes
+  the first breadcrumbs separator
+
+* entities can be compared for equality and hashed
+
+* the ``on_fire_transition`` predicate accepts a sequence of possible
+  transition names
+
+* the GROUP_CONCAT rql aggregate function no longer repeats duplicate
+  values, on the sqlite and postgresql backends
+
+Deprecation
+-----------
+
+* ``pyrorql`` sources have been deprecated. Multisource will be fully dropped
+  in the next version. If you are still using pyrorql, switch to ``datafeed``
+  **NOW**!
+
+* the old multi-source system
+
+* `find_one_entity` and `find_entities` in favor of `find`
+  (see `#3361290 <https://www.cubicweb.org/ticket/3361290>`_)
+
+* the `TmpFileViewMixin` and `TmpPngView` classes (see
+  `#3400448 <https://www.cubicweb.org/ticket/3400448>`_)
+
+Deprecated Code Drops
+---------------------
+
+* ``ldapuser`` have been dropped; use ``ldapfeed`` now
+  (see `#2936496 <http://www.cubicweb.org/2936496>`_)
+
+* action ``GotRhythm`` was removed, make sure you do not
+  import it in your cubes (even to unregister it)
+  (see `#3093362 <http://www.cubicweb.org/3093362>`_)
+
+* all 3.8 backward compat is gone
+
+* all 3.9 backward compat (including the javascript side) is gone
+
+* the ``twisted`` (web-only) instance type has been removed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/changes/3.19.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,180 @@
+3.19 (28/04/2015)
+=================
+
+New functionalities
+-------------------
+
+* implement Cross Origin Resource Sharing (CORS)
+  (see `#2491768 <http://www.cubicweb.org/2491768>`_)
+
+* system_source.create_eid can get a range of IDs, to reduce overhead of batch
+  entity creation
+
+Behaviour Changes
+-----------------
+
+* The anonymous property of Session and Connection are now computed from the
+  related user login. If it matches the ``anonymous-user`` in the config the
+  connection is anonymous. Beware that the ``anonymous-user`` config is web
+  specific. Therefore, no session may be anonymous in a repository only setup.
+
+
+New Repository Access API
+-------------------------
+
+Connection replaces Session
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A new explicit Connection object replaces Session as the main repository entry
+point. Connection holds all the necessary methods to be used server-side
+(``execute``, ``commit``, ``rollback``, ``call_service``, ``entity_from_eid``,
+etc...). One obtains a new Connection object using ``session.new_cnx()``.
+Connection objects need to have an explicit begin and end. Use them as a context
+manager to never miss an end::
+
+    with session.new_cnx() as cnx:
+        cnx.execute('INSERT Elephant E, E name "Babar"')
+        cnx.commit()
+        cnx.execute('INSERT Elephant E, E name "Celeste"')
+        cnx.commit()
+    # Once you get out of the "with" clause, the connection is closed.
+
+Using the same Connection object in multiple threads will give you access to the
+same Transaction. However, Connection objects are not thread safe (hence at your
+own risks).
+
+``repository.internal_session`` is deprecated in favor of
+``repository.internal_cnx``. Note that internal connections are now `safe` by default,
+i.e. the integrity hooks are enabled.
+
+Backward compatibility is preserved on Session.
+
+
+dbapi vs repoapi
+~~~~~~~~~~~~~~~~
+
+A new API has been introduced to replace the dbapi. It is called `repoapi`.
+
+There are three relevant functions for now:
+
+* ``repoapi.get_repository`` returns a Repository object either from an
+  URI when used as ``repoapi.get_repository(uri)`` or from a config
+  when used as ``repoapi.get_repository(config=config)``.
+
+* ``repoapi.connect(repo, login, **credentials)`` returns a ClientConnection
+  associated with the user identified by the credentials. The
+  ClientConnection is associated with its own Session that is closed
+  when the ClientConnection is closed. A ClientConnection is a
+  Connection-like object to be used client side.
+
+* ``repoapi.anonymous_cnx(repo)`` returns a ClientConnection associated
+  with the anonymous user if described in the config.
+
+
+repoapi.ClientConnection replace dbapi.Connection and company
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+On the client/web side, the Request is now using a ``repoapi.ClientConnection``
+instead of a ``dbapi.connection``. The ``ClientConnection`` has multiple backward
+compatible methods to make it look like a ``dbapi.Cursor`` and ``dbapi.Connection``.
+
+Session used on the Web side are now the same than the one used Server side.
+Some backward compatibility methods have been installed on the server side Session
+to ease the transition.
+
+The authentication stack has been altered to use the ``repoapi`` instead of
+the ``dbapi``. Cubes adding new element to this stack are likely to break.
+
+Session data can be accessed using the cnx.data dictionary, while
+transaction data is available through cnx.transaction_data.  These
+replace the [gs]et_shared_data methods with optional txid kwarg.
+
+New API in tests
+~~~~~~~~~~~~~~~~
+
+All current methods and attributes used to access the repo on ``CubicWebTC`` are
+deprecated. You may now use a ``RepoAccess`` object. A ``RepoAccess`` object is
+linked to a new ``Session`` for a specified user. It is able to create
+``Connection``, ``ClientConnection`` and web side requests linked to this
+session::
+
+    access = self.new_access('babar') # create a new RepoAccess for user babar
+    with access.repo_cnx() as cnx:
+        # some work with server side cnx
+        cnx.execute(...)
+        cnx.commit()
+        cnx.execute(...)
+        cnx.commit()
+
+    with access.client_cnx() as cnx:
+        # some work with client side cnx
+        cnx.execute(...)
+        cnx.commit()
+
+    with access.web_request(elephant='babar') as req:
+        # some work with client side cnx
+        elephant_name = req.form['elephant']
+        req.execute(...)
+        req.cnx.commit()
+
+By default ``testcase.admin_access`` contains a ``RepoAccess`` object for the
+default admin session.
+
+
+API changes
+-----------
+
+* ``RepositorySessionManager.postlogin`` is now called with two arguments,
+  request and session. And this now happens before the session is linked to the
+  request.
+
+* ``SessionManager`` and ``AuthenticationManager`` now take a repo object at
+  initialization time instead of a vreg.
+
+* The ``async`` argument of ``_cw.call_service`` has been dropped. All calls are
+  now  synchronous. The zmq notification bus looks like a good replacement for
+  most async use cases.
+
+* ``repo.stats()`` is now deprecated. The same information is available through
+  a service (``_cw.call_service('repo_stats')``).
+
+* ``repo.gc_stats()`` is now deprecated. The same information is available through
+  a service (``_cw.call_service('repo_gc_stats')``).
+
+* ``repo.register_user()`` is now deprecated.  The functionality is now
+  available through a service (``_cw.call_service('register_user')``).
+
+* ``request.set_session`` no longer takes an optional ``user`` argument.
+
+* CubicwebTC does not have repo and cnx as class attributes anymore. They are
+  standard instance attributes. ``set_cnx`` and ``_init_repo`` class methods
+  become instance methods.
+
+* ``set_cnxset`` and ``free_cnxset`` are deprecated. cnxset are now
+  automatically managed.
+
+* The implementation of cascading deletion when deleting `composite`
+  entities has changed. There comes a semantic change: merely deleting
+  a composite relation does not entail any more the deletion of the
+  component side of the relation.
+
+* ``_cw.user_callback`` and ``_cw.user_rql_callback`` are deprecated.  Users
+  are encouraged to write an actual controller (e.g. using ``ajaxfunc``)
+  instead of storing a closure in the session data.
+
+* A new ``entity.cw_linkable_rql`` method provides the rql to fetch all entities
+  that are already or may be related to the current entity using the given
+  relation.
+
+
+Deprecated Code Drops
+---------------------
+
+* session.hijack_user mechanism has been dropped.
+
+* EtypeRestrictionComponent has been removed, its functionality has been
+  replaced by facets a while ago.
+
+* the old multi-source support has been removed.  Only copy-based sources
+  remain, such as datafeed or ldapfeed.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/changes/3.20.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,100 @@
+3.20 (06/01/2015)
+=================
+
+New features
+------------
+
+* virtual relations: a new ComputedRelation class can be used in
+  schema.py; its `rule` attribute is an RQL snippet that defines the new
+  relation.
+
+* computed attributes: an attribute can now be defined with a `formula`
+  argument (also an RQL snippet); it will be read-only, and updated
+  automatically.
+
+  Both of these features are described in `CWEP-002`_, and the updated
+  "Data model" chapter of the CubicWeb book.
+
+* cubicweb-ctl plugins can use the ``cubicweb.utils.admincnx`` function
+  to get a Connection object from an instance name.
+
+* new 'tornado' wsgi backend
+
+* session cookies have the HttpOnly flag, so they're no longer exposed to
+  javascript
+
+* rich text fields can be formatted as markdown
+
+* the edit controller detects concurrent editions, and raises a ValidationError
+  if an entity was modified between form generation and submission
+
+* cubicweb can use a postgresql "schema" (namespace) for its tables
+
+* "cubicweb-ctl configure" can be used to set values of the admin user
+  credentials in the sources configuration file
+
+* in debug mode, setting the _cwtracehtml parameter on a request allows tracing
+  where each bit of output is produced
+
+.. _CWEP-002: http://hg.logilab.org/review/cwep/file/tip/CWEP-002.rst
+
+
+API Changes
+-----------
+
+* ``ucsvreader()`` and ``ucsvreader_pb()`` from the ``dataimport`` module have
+  2 new keyword arguments ``delimiter`` and ``quotechar`` to replace the
+  ``separator`` and ``quote`` arguments respectively. This makes the API match
+  that of Python's ``csv.reader()``.  The old arguments are still supported
+  though deprecated.
+
+* the migration environment's ``remove_cube`` function is now called ``drop_cube``.
+
+* cubicweb.old.css is now cubicweb.css.  The previous "new"
+  cubicweb.css, along with its cubicweb.reset.css companion, have been
+  removed.
+
+* the jquery-treeview plugin was updated to its latest version
+
+
+Deprecated Code Drops
+----------------------
+
+* most of 3.10 and 3.11 backward compat is gone; this includes:
+
+  - CtxComponent.box_action() and CtxComponent.build_link()
+  
+  - cubicweb.devtools.htmlparser.XMLDemotingValidator
+  
+  - various methods and properties on Entities, replaced by cw_edited
+    and cw_attr_cache
+  
+  - 'commit_event' method on hooks, replaced by 'postcommit_event'
+  
+  - server.hook.set_operation(), replaced by
+    Operation.get_instance(...).add_data()
+  
+  - View.div_id(), View.div_class() and View.create_url()
+  
+  - `*VComponent` classes
+  
+  - in forms, Field.value() and Field.help() must take the form and
+    the field itself as arguments
+  
+  - form.render() must get `w` as a named argument, and
+    renderer.render() must take `w` as first argument
+  
+  - in breadcrumbs, the optional `recurs` argument must be a set, not
+    False
+  
+  - cubicweb.web.views.idownloadable.{download_box,IDownloadableLineView}
+  
+  - primary views no longer have `render_entity_summary` and `summary`
+    methods
+  
+  - WFHistoryVComponent's `cell_call` method is replaced by
+    `render_body`
+  
+  - cubicweb.dataimport.ObjectStore.add(), replaced by create_entity
+  
+  - ManageView.{folders,display_folders}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/changes/3.21.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,77 @@
+3.21
+====
+
+New features
+------------
+
+* the datadir-url configuration option lets one choose where static data files
+  are served (instead of the default ${base-url}/data/)
+
+* some integrity checking that was previously implemented in Python was
+  moved to the SQL backend.  This includes some constraints, and
+  referential integrity.  Some consequences are that:
+
+  - disabling integrity hooks no longer disables those checks
+  - upgrades that modify constraints will fail when running on sqlite
+    (but upgrades aren't supported on sqlite anyway)
+
+  Note: as of 3.21.0, the upgrade script only works on PostgreSQL.  The
+  migration for SQLServer will be added in a future bugfix release.
+
+* for easier instance monitoring, cubicweb can regularly dump some statistics
+  (basically those exposed by the 'info' and 'gc' views) in json format to a file
+
+User-visible changes
+--------------------
+
+* the use of fckeditor for text form fields is disabled by default
+
+* the 'https-deny-anonymous' configuration setting no longer exists
+
+Code movement
+-------------
+
+The cubicweb.web.views.timeline module (providing the timeline-json, timeline
+and static-timeline views) has moved to a standalone cube_
+
+.. _cube: https://www.cubicweb.org/project/cubicweb-timeline
+
+API changes
+-----------
+
+* req.set_cookie's "expires" argument, if not None, is expected to be a
+  date or a datetime in UTC.  It was previously interpreted as localtime
+  with the UTC offset the server started in, which was inconsistent (we
+  are not aware of any users of that API).
+
+* the way to run tests on a postgresql backend has changed slightly, use
+  cubicweb.devtools.{start,stop}pgcluster in setUpModule and tearDownModule
+
+* the Connection and ClientConnection objects introduced in CubicWeb 3.19 have
+  been unified.  To connect to a repository, use::
+
+    session = repo.new_session(login, password=...)
+    with session.new_cnx() as cnx:
+        cnx.execute(...)
+
+  In tests, the 'repo_cnx' and 'client_cnx' methods of RepoAccess are now
+  aliases to 'cnx'.
+
+Deprecated code drops
+---------------------
+
+* the user_callback api has been removed; people should use plain
+  ajax functions instead
+
+* the `Pyro` and `Zmq-pickle` remote repository access methods have
+  been entirely removed (emerging alternatives such as rqlcontroller
+  and cwclientlib should be used instead).  Note that as a side effect,
+  "repository-only" instances (i.e. without a http component) are no
+  longer possible.  If you have any such instances, you will need to
+  rename the configuration file from repository.conf to all-in-one.conf
+  and run ``cubicweb-ctl upgrade`` to update it.  Likewise, remote cubicweb-ctl
+  shell is no longer available.
+
+* the old (deprecated since 3.19) `DBAPI` api is completely removed
+
+* cubicweb.toolsutils.config_connect() has been removed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/changes/changelog.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,12 @@
+===================
+ Changelog history
+===================
+
+.. include:: 3.21.rst
+.. include:: 3.20.rst
+.. include:: 3.19.rst
+.. include:: 3.18.rst
+.. include:: 3.17.rst
+.. include:: 3.16.rst
+.. include:: 3.15.rst
+.. include:: 3.14.rst
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/changes/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,14 @@
+Release notes
+-------------
+
+.. toctree::
+    :maxdepth: 1
+
+    3.21
+    3.20
+    3.19
+    3.18
+    3.17
+    3.16
+    3.15
+    3.14
--- a/doc/coding_standards_css.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-CSS Coding Standards
---------------------
-
-(Draft, to be continued)
-
-:Naming: camelCase
-
-Indentation rules
-~~~~~~~~~~~~~~~~~
-- 2 espaces avant les propriétés
-
-- pas d'espace avant les ":", un espace après
-
-- 1 seul espace entre les différentes valeurs pour une même propriété
-
-
-Documentation
-~~~~~~~~~~~~~
-Please keep rules semantically linked grouped together, with a comment about
-what they are for.
-
-Recommendation
-~~~~~~~~~~~~~~
-- Try to use existing classes rather than introduce new ones
-
-- Keep things as simple as possible while in the framework
-
-- Think about later customization by application
-
-- Avoid introducing a new CSS file for a few lines of CSS, at least while the
-  framework doesn't include packing functionalities
-
-
--- a/doc/coding_standards_js.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-Javascript Coding Standards
----------------------------
-
-(Draft, to be continued)
-
-:Naming: camelCase, except for CONSTANTS
-
-Indentation rules
-~~~~~~~~~~~~~~~~~
-- espace avant accolade ouvrante
-
-- retour à la ligne après accolade ouvrante (éventuellement pas
-  de retour à la ligne s'il y a tout sur la même ligne, mais ce n'est
-  pas le cas ici.
-
-- no tabs
-
-
-Documentation
-~~~~~~~~~~~~~
-XXX explain comment format for documentation generation
-
-
-Coding
-~~~~~~
-- Don't forget 'var' before variable definition, and semi-colon (';') after **each** statement.
-- Check the firebug console for deprecation warnings
-
-
-API usage
-~~~~~~~~~
-- unless intended, use jQuery('container') rather than jqNode('container')
-
-
-See also
-~~~~~~~~
-http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/conf.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,225 @@
+# -*- coding: utf-8 -*-
+# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+"""
+
+"""
+#
+# Cubicweb documentation build configuration file, created by
+# sphinx-quickstart on Fri Oct 31 09:10:36 2008.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# The contents of this file are pickled, so don't put values in the namespace
+# that aren't pickleable (module imports are okay, they're removed automatically).
+#
+# All configuration values have a default value; values that are commented out
+# serve to show the default value.
+
+from os import path as osp
+
+path = __file__
+path = osp.dirname(path)  # ./doc
+path = osp.dirname(path)  # ./
+path = osp.join(path, '__pkginfo__.py')  # ./__pkginfo__.py
+cw = {}
+execfile(path, {}, cw)
+
+# If your extensions are in another directory, add it here. If the directory
+# is relative to the documentation root, use os.path.abspath to make it
+# absolute, like shown here.
+#sys.path.append(os.path.abspath('some/directory'))
+
+# General configuration
+# ---------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = [
+  'sphinx.ext.autodoc', 
+  'sphinx.ext.viewcode',
+  'logilab.common.sphinx_ext',
+  ]
+
+autoclass_content = 'both'
+
+# Add any paths that contain templates here, relative to this directory.
+#templates_path = []
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General substitutions.
+project = 'CubicWeb'
+copyright = '2001-2015, Logilab'
+
+# The default replacements for |version| and |release|, also used in various
+# other places throughout the built documents.
+#
+# The short X.Y version.
+version = '.'.join(str(n) for n in cw['numversion'][:2])
+# The full version, including alpha/beta/rc tags.
+release = cw['version']
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+unused_docs = []
+
+# A list of glob-style patterns that should be excluded when looking
+# for source files. [1] They are matched against the source file names
+# relative to the source directory, using slashes as directory
+# separators on all platforms.
+exclude_patterns = ['book/_maybe_to_integrate']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+
+# Options for HTML output
+# -----------------------
+
+# The style sheet to use for HTML and HTML Help pages. A file of that name
+# must exist either in Sphinx' static/ path, or in one of the custom paths
+# given in html_static_path.
+#html_style = 'sphinx-default.css'
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+html_title = '%s %s' % (project, release)
+
+html_theme_path = ['_themes']
+html_theme = 'cubicweb'
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (within the static path) to place at the top of
+# the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+html_use_modindex = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, the reST sources are included in the HTML build as _sources/<name>.
+#html_copy_source = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+html_file_suffix = '.html'
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Cubicwebdoc'
+
+
+# Options for LaTeX output
+# ------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, document class [howto/manual]).
+latex_documents = [
+  ('index', 'Cubicweb.tex', 'Cubicweb Documentation',
+   'Logilab', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True
+
+#aafig_format = dict(latex='pdf', html='svg', text=None)
+
+rst_epilog = """
+.. |cubicweb| replace:: *CubicWeb*
+.. |yams| replace:: *Yams*
+.. |rql| replace:: *RQL*
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/dev/coding_standards_css.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,33 @@
+CSS Coding Standards
+--------------------
+
+(Draft, to be continued)
+
+:Naming: camelCase
+
+Indentation rules
+~~~~~~~~~~~~~~~~~
+- 2 espaces avant les propriétés
+
+- pas d'espace avant les ":", un espace après
+
+- 1 seul espace entre les différentes valeurs pour une même propriété
+
+
+Documentation
+~~~~~~~~~~~~~
+Please keep rules semantically linked grouped together, with a comment about
+what they are for.
+
+Recommendation
+~~~~~~~~~~~~~~
+- Try to use existing classes rather than introduce new ones
+
+- Keep things as simple as possible while in the framework
+
+- Think about later customization by application
+
+- Avoid introducing a new CSS file for a few lines of CSS, at least while the
+  framework doesn't include packing functionalities
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/dev/coding_standards_js.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,37 @@
+Javascript Coding Standards
+---------------------------
+
+(Draft, to be continued)
+
+:Naming: camelCase, except for CONSTANTS
+
+Indentation rules
+~~~~~~~~~~~~~~~~~
+- espace avant accolade ouvrante
+
+- retour à la ligne après accolade ouvrante (éventuellement pas
+  de retour à la ligne s'il y a tout sur la même ligne, mais ce n'est
+  pas le cas ici.
+
+- no tabs
+
+
+Documentation
+~~~~~~~~~~~~~
+XXX explain comment format for documentation generation
+
+
+Coding
+~~~~~~
+- Don't forget 'var' before variable definition, and semi-colon (';') after **each** statement.
+- Check the firebug console for deprecation warnings
+
+
+API usage
+~~~~~~~~~
+- unless intended, use jQuery('container') rather than jqNode('container')
+
+
+See also
+~~~~~~~~
+http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/dev/documenting.txt	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,85 @@
+====
+Book
+====
+
+----
+Part
+----
+
+Chapter
+=======
+
+.. _Level1AnchorForLaterReference:
+
+Level 1 section
+---------------
+
+Level 2 section
+~~~~~~~~~~~~~~~
+
+Level 3 section
+```````````````
+
+
+
+*CubicWeb*
+
+
+inline directives:
+  :file:`directory/file`
+  :envvar:`AN_ENV_VARIABLE`
+  :command:`command --option arguments`
+
+  :ref:, :mod:
+
+
+.. sourcecode:: python
+
+   class SomePythonCode:
+     ...
+
+.. XXX a comment, wont be rendered
+
+
+a [foot note]_
+
+.. [foot note] the foot note content
+
+
+Boxes
+=====
+
+- warning box: 
+    .. warning::
+
+       Warning content
+- note box:
+    .. note::
+
+       Note content
+
+
+
+Cross references
+================
+
+To arbitrary section
+--------------------
+
+:ref:`identifier` ou :ref:`label <identifier>`
+
+Label required of referencing node which as no title, else the node's title will be used.
+
+
+To API objects
+--------------
+See the autodoc sphinx extension documentation. Quick overview:
+
+* ref to a class: :class:`cubicweb.devtools.testlib.AutomaticWebTest`
+
+* if you can to see only the class name in the generated documentation, add a ~:
+  :class:`~cubicweb.devtools.testlib.AutomaticWebTest`
+
+* you can also use :mod: (module), :exc: (exception), :func: (function), :meth: (method)...
+
+* syntax explained above to specify label explicitly may also be used
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/dev/features_list.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,223 @@
+=================
+CubicWeb features
+=================
+
+This page  tries to resume features found in the bare cubicweb framework,
+how mature and documented they are.
+
+:code maturity (CM):
+
+  - 0: experimental, not ready at all for production, may be killed
+
+  - 1: draft / unsatisfying, api may change in a near future, much probably in long
+       term
+
+  - 2: good enough, api sounds good but will probably evolve a bit with more
+    hindsight
+
+  - 3: mature, backward incompatible changes unexpected (may still evolve though,
+    of course)
+
+
+:documentation level (DL):
+
+  - 0: no documentation
+
+  - 1: poor documentation
+
+  - 2: some valuable documentation but some parts keep uncovered
+
+  - 3: good / complete documentation
+
+
+Instance configuration and maintainance
+=======================================
+
++====================================================================+====+====+
+|  FEATURE                                                           | CM | DL |
++====================================================================+====+====+
+| setup - installation                                               | 2  | 3  |
+| setup - environment variables                                      | 3  | 2  |
+| setup - running modes                                              | 2  | 2  |
+| setup - administration tasks                                       | 2  | 2  |
+| setup - configuration file                                         | 2  | 1  |
++--------------------------------------------------------------------+----+----+
+| configuration - user / groups handling                             | 3  | 1  |
+| configuration - site configuration                                 | 3  | 1  |
+| configuration - distributed configuration                          | 2  | 1  |
++--------------------------------------------------------------------+----+----+
+| multi-sources - capabilities                                       | NA | 0  |
+| multi-sources - configuration                                      | 2  | 0  |
+| multi-sources - ldap integration                                   | 2  | 1  |
++--------------------------------------------------------------------+----+----+
+| usage - custom ReST markup                                         | 2  | 0  |
+| usage - personal preferences                                       | 2  | 1  |
++--------------------------------------------------------------------+----+----+
+
+
+Core development
+================
+
++====================================================================+====+====+
+|  FEATURE                                                           | CM | DL |
++====================================================================+====+====+
+| base - concepts                                                    | NA | 3  |
+| base - security model                                              | NA | 2  |
+| base - database initialization                                     | 2  | 1  |
++--------------------------------------------------------------------+----+----+
+| rql - base                                                         | 2  | 2  |
+| rql - write                                                        | 2  | 2  |
+| rql - function                                                     | 2  | 0  |
+| rql - outer joins                                                  | 2  | 1  |
+| rql - aggregates                                                   | 2  | 1  |
+| rql - subqueries                                                   | 2  | 0  |
++--------------------------------------------------------------------+----+----+
+| schema - base                                                      | 2  | 3  |
+| schema - constraints                                               | 3  | 2  |
+| schema - security                                                  | 2  | 2  |
+| schema - inheritance                                               | 1  | 1  |
+| schema - customization                                             | 1  | 1  |
+| schema - introspection                                             | 2  | 1  |
++--------------------------------------------------------------------+----+----+
+| vregistry - appobject                                              | 2  | 2  |
+| vregistry - registration                                           | 2  | 2  |
+| vregistry - selection                                              | 3  | 2  |
+| vregistry - core selectors                                         | 3  | 3  |
+| vregistry - custom selectors                                       | 2  | 1  |
+| vregistry - debugging selection                                    | 2  | 1  |
++--------------------------------------------------------------------+----+----+
+| entities - interfaces                                              | 2  | ?  |
+| entities - customization (`dc_`, ...)                              | 2  | ?  |
+| entities - app logic                                               | 2  | 2  |
+| entities - orm configuration                                       | 2  | 1  |
+| entities - pluggable mixins                                        | 1  | 0  |
+| entities - workflow                                                | 3  | 2  |
++--------------------------------------------------------------------+----+----+
+| dbapi - connection                                                 | 3  | 1  |
+| dbapi - data management                                            | 1  | 1  |
+| dbapi - result set                                                 | 3  | 1  |
+| dbapi - transaction, undo                                          | 2  | 0  |
++--------------------------------------------------------------------+----+----+
+| cube - layout                                                      | 2  | 3  |
+| cube - new cube                                                    | 2  | 2  |
++--------------------------------------------------------------------+----+----+
+| migration - context                                                | 2  | 1  |
+| migration - commands                                               | 2  | 2  |
++--------------------------------------------------------------------+----+----+
+| testlib - CubicWebTC                                               | 2  | 1  |
+| testlib - automatic tests                                          | 2  | 2  |
++--------------------------------------------------------------------+----+----+
+| i18n - mark string                                                 | 3  | 2  |
+| i18n - customize strings from other cubes / cubicweb               | 3  | 1  |
+| i18n - update catalog                                              | 3  | 2  |
++--------------------------------------------------------------------+----+----+
+| more - reloading tips                                              | NA | 0  |
+| more - site_cubicweb                                               | 2  | ?  |
+| more - adding options in configuration file                        | 3  | 0  |
+| more - adding options in site configuration / preferences          | 3  | ?  |
+| more - optimizing / profiling                                      | 2  | 1  |
+| more - c-c plugins                                                 | 3  | 0  |
+| more - crypto services                                             | 0  | 0  |
+| more - massive import                                              | 2  | 0  |
+| more - mime type based conversion                                  | 2  | 0  |
+| more - CWCache                                                     | 1  | 0  |
++--------------------------------------------------------------------+----+----+
+
+
+Web UI development
+==================
+
++====================================================================+====+====+
+|  FEATURE                                                           | CM | DL |
++====================================================================+====+====+
+| base - web request                                                 | 2  | 2  |
+| base - exceptions                                                  | 2  | 0  |
+| base - session, authentication                                     | 1  | 0  |
+| base - http caching                                                | 2  | 1  |
+| base - external resources                                          | 2  | 2  |
+| base - static files                                                | 2  | ?  |
+| base - data sharing                                                | 2  | 2  |
+| base - graphical chart customization                               | 1  | 1  |
++--------------------------------------------------------------------+----+----+
+| publishing - cycle                                                 | 2  | 2  |
+| publishing - error handling                                        | 2  | 1  |
+| publishing - transactions                                          | NA | ?  |
++--------------------------------------------------------------------+----+----+
+| controller - base                                                  | 2  | 2  |
+| controller - view                                                  | 2  | 1  |
+| controller - edit                                                  | 2  | 1  |
+| controller - json                                                  | 2  | 1  |
++--------------------------------------------------------------------+----+----+
+| views - base                                                       | 2  | 2  |
+| views - templates                                                  | 2  | 2  |
+| views - boxes                                                      | 2  | 1  |
+| views - components                                                 | 2  | 1  |
+| views - primary                                                    | 2  | 1  |
+| views - tabs                                                       | 2  | 1  |
+| views - xml                                                        | 2  | 0  |
+| views - text                                                       | 2  | 1  |
+| views - table                                                      | 2  | 1  |
+| views - plot                                                       | 2  | 0  |
+| views - navigation                                                 | 2  | 0  |
+| views - calendar, timeline                                         | 2  | 0  |
+| views - index                                                      | 2  | 2  |
+| views - breadcrumbs                                                | 2  | 1  |
+| views - actions                                                    | 2  | 1  |
+| views - debugging                                                  | 2  | 1  |
++--------------------------------------------------------------------+----+----+
+| form - base                                                        | 2  | 1  |
+| form - fields                                                      | 2  | 1  |
+| form - widgets                                                     | 2  | 1  |
+| form - captcha                                                     | 2  | 0  |
+| form - renderers                                                   | 2  | 0  |
+| form - validation error handling                                   | 2  | 0  |
+| form - autoform                                                    | 2  | 2  |
+| form - reledit                                                     | 2  | 0  |
++--------------------------------------------------------------------+----+----+
+| facets - base                                                      | 2  | ?  |
+| facets - configuration                                             | 2  | 1  |
+| facets - custom facets                                             | 2  | 0  |
++--------------------------------------------------------------------+----+----+
+| css - base                                                         | 1  | 1  |
+| css - customization                                                | 1  | 1  |
++--------------------------------------------------------------------+----+----+
+| js - base                                                          | 1  | 1  |
+| js - jquery                                                        | 1  | 1  |
+| js - base functions                                                | 1  | 0  |
+| js - ajax                                                          | 1  | 0  |
+| js - widgets                                                       | 1  | 1  |
++--------------------------------------------------------------------+----+----+
+| other - page template                                              | 0  | 0  |
+| other - inline doc (wdoc)                                          | 2  | 0  |
+| other - magic search                                               | 2  | 0  |
+| other - url mapping                                                | 1  | 1  |
+| other - apache style url rewrite                                   | 1  | 1  |
+| other - sparql                                                     | 1  | 0  |
+| other - bookmarks                                                  | 2  | 1  |
++--------------------------------------------------------------------+----+----+
+
+
+Repository development
+======================
+
++====================================================================+====+====+
+|  FEATURE                                                           | CM | DL |
++====================================================================+====+====+
+| base - session                                                     | 2  | 2  |
+| base - more security control                                       | 2  | 0  |
+| base - debugging                                                   | 2  | 0  |
++--------------------------------------------------------------------+----+----+
+| hooks - development                                                | 2  | 2  |
+| hooks - abstract hooks                                             | 2  | 0  |
+| hooks - core hooks                                                 | 2  | 0  |
+| hooks - control                                                    | 2  | 0  |
+| hooks - operation                                                  | 2  | 2  |
++--------------------------------------------------------------------+----+----+
+| notification - sending email                                       | 2  | ?  |
+| notification - base views                                          | 1  | ?  |
+| notification - supervisions                                        | 1  | 0  |
++--------------------------------------------------------------------+----+----+
+| source - storages                                                  | 2  | 0  |
+| source - authentication plugins                                    | 2  | 0  |
+| source - custom sources                                            | 2  | 0  |
++--------------------------------------------------------------------+----+----+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/dev/refactoring-the-css-with-uiprops.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,42 @@
+=========================================
+Refactoring the CSSs with UI properties
+=========================================
+
+Overview
+=========
+
+Managing styles progressively became difficult in CubicWeb. The
+introduction of uiprops is an attempt to fix this problem.
+
+The goal is to make it possible to use variables in our CSSs.
+
+These variables are defined or computed in the uiprops.py python file
+and inserted in the CSS using the Python string interpolation syntax.
+
+A quick example, put in ``uiprops.py``::
+
+  defaultBgColor = '#eee'
+
+and in your css::
+
+  body { background-color: %(defaultBgColor)s; }
+
+
+The good practices are:
+
+- define a variable in uiprops to avoid repetitions in the CSS
+  (colors, borders, fonts, etc.)
+
+- define a variable in uiprops when you need to compute values
+  (compute a color palette, etc.)
+
+The algorithm implemented in CubicWeb is the following:
+
+- read uiprops file while walk up the chain of cube dependencies: if
+  cube myblog depends on cube comment, the variables defined in myblog
+  will have precedence over the ones in comment
+
+- replace the %(varname)s in all the CSSs of all the cubes
+
+Keep in mind that the browser will then interpret the CSSs and apply
+the standard cascading mechanism.
--- a/doc/features_list.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,224 +0,0 @@
-=================
-CubicWeb features
-=================
-
-This page  tries to resume features found in the bare cubicweb framework,
-how mature and documented they are.
-
-:code maturity (CM):
-
-  - 0: experimental, not ready at all for production, may be killed
-
-  - 1: draft / unsatisfying, api may change in a near future, much probably in long
-       term
-
-  - 2: good enough, api sounds good but will probably evolve a bit with more
-    hindsight
-
-  - 3: mature, backward incompatible changes unexpected (may still evolve though,
-    of course)
-
-
-:documentation level (DL):
-
-  - 0: no documentation
-
-  - 1: poor documentation
-
-  - 2: some valuable documentation but some parts keep uncovered
-
-  - 3: good / complete documentation
-
-
-Instance configuration and maintainance
-=======================================
-
-+====================================================================+====+====+
-|  FEATURE                                                           | CM | DL |
-+====================================================================+====+====+
-| setup - installation                                               | 2  | 3  |
-| setup - environment variables                                      | 3  | 2  |
-| setup - running modes                                              | 2  | 2  |
-| setup - administration tasks                                       | 2  | 2  |
-| setup - configuration file                                         | 2  | 1  |
-+--------------------------------------------------------------------+----+----+
-| configuration - user / groups handling                             | 3  | 1  |
-| configuration - site configuration                                 | 3  | 1  |
-| configuration - distributed configuration                          | 2  | 1  |
-| configuration - pyro                                               | 2  | 2  |
-+--------------------------------------------------------------------+----+----+
-| multi-sources - capabilities                                       | NA | 0  |
-| multi-sources - configuration                                      | 2  | 0  |
-| multi-sources - ldap integration                                   | 2  | 1  |
-+--------------------------------------------------------------------+----+----+
-| usage - custom ReST markup                                         | 2  | 0  |
-| usage - personal preferences                                       | 2  | 1  |
-+--------------------------------------------------------------------+----+----+
-
-
-Core development
-================
-
-+====================================================================+====+====+
-|  FEATURE                                                           | CM | DL |
-+====================================================================+====+====+
-| base - concepts                                                    | NA | 3  |
-| base - security model                                              | NA | 2  |
-| base - database initialization                                     | 2  | 1  |
-+--------------------------------------------------------------------+----+----+
-| rql - base                                                         | 2  | 2  |
-| rql - write                                                        | 2  | 2  |
-| rql - function                                                     | 2  | 0  |
-| rql - outer joins                                                  | 2  | 1  |
-| rql - aggregates                                                   | 2  | 1  |
-| rql - subqueries                                                   | 2  | 0  |
-+--------------------------------------------------------------------+----+----+
-| schema - base                                                      | 2  | 3  |
-| schema - constraints                                               | 3  | 2  |
-| schema - security                                                  | 2  | 2  |
-| schema - inheritance                                               | 1  | 1  |
-| schema - customization                                             | 1  | 1  |
-| schema - introspection                                             | 2  | 1  |
-+--------------------------------------------------------------------+----+----+
-| vregistry - appobject                                              | 2  | 2  |
-| vregistry - registration                                           | 2  | 2  |
-| vregistry - selection                                              | 3  | 2  |
-| vregistry - core selectors                                         | 3  | 3  |
-| vregistry - custom selectors                                       | 2  | 1  |
-| vregistry - debugging selection                                    | 2  | 1  |
-+--------------------------------------------------------------------+----+----+
-| entities - interfaces                                              | 2  | ?  |
-| entities - customization (dc_,...)                                 | 2  | ?  |
-| entities - app logic                                               | 2  | 2  |
-| entities - orm configuration                                       | 2  | 1  |
-| entities - pluggable mixins                                        | 1  | 0  |
-| entities - workflow                                                | 3  | 2  |
-+--------------------------------------------------------------------+----+----+
-| dbapi - connection                                                 | 3  | 1  |
-| dbapi - data management                                            | 1  | 1  |
-| dbapi - result set                                                 | 3  | 1  |
-| dbapi - transaction, undo                                          | 2  | 0  |
-+--------------------------------------------------------------------+----+----+
-| cube - layout                                                      | 2  | 3  |
-| cube - new cube                                                    | 2  | 2  |
-+--------------------------------------------------------------------+----+----+
-| migration - context                                                | 2  | 1  |
-| migration - commands                                               | 2  | 2  |
-+--------------------------------------------------------------------+----+----+
-| testlib - CubicWebTC                                               | 2  | 1  |
-| testlib - automatic tests                                          | 2  | 2  |
-+--------------------------------------------------------------------+----+----+
-| i18n - mark string                                                 | 3  | 2  |
-| i18n - customize strings from other cubes / cubicweb               | 3  | 1  |
-| i18n - update catalog                                              | 3  | 2  |
-+--------------------------------------------------------------------+----+----+
-| more - reloading tips                                              | NA | 0  |
-| more - site_cubicweb                                               | 2  | ?  |
-| more - adding options in configuration file                        | 3  | 0  |
-| more - adding options in site configuration / preferences          | 3  | ?  |
-| more - optimizing / profiling                                      | 2  | 1  |
-| more - c-c plugins                                                 | 3  | 0  |
-| more - crypto services                                             | 0  | 0  |
-| more - massive import                                              | 2  | 0  |
-| more - mime type based conversion                                  | 2  | 0  |
-| more - CWCache                                                     | 1  | 0  |
-+--------------------------------------------------------------------+----+----+
-
-
-Web UI development
-==================
-
-+====================================================================+====+====+
-|  FEATURE                                                           | CM | DL |
-+====================================================================+====+====+
-| base - web request                                                 | 2  | 2  |
-| base - exceptions                                                  | 2  | 0  |
-| base - session, authentication                                     | 1  | 0  |
-| base - http caching                                                | 2  | 1  |
-| base - external resources                                          | 2  | 2  |
-| base - static files                                                | 2  | ?  |
-| base - data sharing                                                | 2  | 2  |
-| base - graphical chart customization                               | 1  | 1  |
-+--------------------------------------------------------------------+----+----+
-| publishing - cycle                                                 | 2  | 2  |
-| publishing - error handling                                        | 2  | 1  |
-| publishing - transactions                                          | NA | ?  |
-+--------------------------------------------------------------------+----+----+
-| controller - base                                                  | 2  | 2  |
-| controller - view                                                  | 2  | 1  |
-| controller - edit                                                  | 2  | 1  |
-| controller - json                                                  | 2  | 1  |
-+--------------------------------------------------------------------+----+----+
-| views - base                                                       | 2  | 2  |
-| views - templates                                                  | 2  | 2  |
-| views - boxes                                                      | 2  | 1  |
-| views - components                                                 | 2  | 1  |
-| views - primary                                                    | 2  | 1  |
-| views - tabs                                                       | 2  | 1  |
-| views - xml                                                        | 2  | 0  |
-| views - text                                                       | 2  | 1  |
-| views - table                                                      | 2  | 1  |
-| views - plot                                                       | 2  | 0  |
-| views - navigation                                                 | 2  | 0  |
-| views - calendar, timeline                                         | 2  | 0  |
-| views - index                                                      | 2  | 2  |
-| views - breadcrumbs                                                | 2  | 1  |
-| views - actions                                                    | 2  | 1  |
-| views - debugging                                                  | 2  | 1  |
-+--------------------------------------------------------------------+----+----+
-| form - base                                                        | 2  | 1  |
-| form - fields                                                      | 2  | 1  |
-| form - widgets                                                     | 2  | 1  |
-| form - captcha                                                     | 2  | 0  |
-| form - renderers                                                   | 2  | 0  |
-| form - validation error handling                                   | 2  | 0  |
-| form - autoform                                                    | 2  | 2  |
-| form - reledit                                                     | 2  | 0  |
-+--------------------------------------------------------------------+----+----+
-| facets - base                                                      | 2  | ?  |
-| facets - configuration                                             | 2  | 1  |
-| facets - custom facets                                             | 2  | 0  |
-+--------------------------------------------------------------------+----+----+
-| css - base                                                         | 1  | 1  |
-| css - customization                                                | 1  | 1  |
-+--------------------------------------------------------------------+----+----+
-| js - base                                                          | 1  | 1  |
-| js - jquery                                                        | 1  | 1  |
-| js - base functions                                                | 1  | 0  |
-| js - ajax                                                          | 1  | 0  |
-| js - widgets                                                       | 1  | 1  |
-+--------------------------------------------------------------------+----+----+
-| other - page template                                              | 0  | 0  |
-| other - inline doc (wdoc)                                          | 2  | 0  |
-| other - magic search                                               | 2  | 0  |
-| other - url mapping                                                | 1  | 1  |
-| other - apache style url rewrite                                   | 1  | 1  |
-| other - sparql                                                     | 1  | 0  |
-| other - bookmarks                                                  | 2  | 1  |
-+--------------------------------------------------------------------+----+----+
-
-
-Repository development
-======================
-
-+====================================================================+====+====+
-|  FEATURE                                                           | CM | DL |
-+====================================================================+====+====+
-| base - session                                                     | 2  | 2  |
-| base - more security control                                       | 2  | 0  |
-| base - debugging                                                   | 2  | 0  |
-+--------------------------------------------------------------------+----+----+
-| hooks - development                                                | 2  | 2  |
-| hooks - abstract hooks                                             | 2  | 0  |
-| hooks - core hooks                                                 | 2  | 0  |
-| hooks - control                                                    | 2  | 0  |
-| hooks - operation                                                  | 2  | 2  |
-+--------------------------------------------------------------------+----+----+
-| notification - sending email                                       | 2  | ?  |
-| notification - base views                                          | 1  | ?  |
-| notification - supervisions                                        | 1  | 0  |
-+--------------------------------------------------------------------+----+----+
-| source - storages                                                  | 2  | 0  |
-| source - authentication plugins                                    | 2  | 0  |
-| source - custom sources                                            | 2  | 0  |
-+--------------------------------------------------------------------+----+----+
Binary file doc/images/03-transitions-view_en.png has changed
Binary file doc/images/breadcrumbs_header.png has changed
Binary file doc/images/facet_date_range.png has changed
Binary file doc/images/facet_has_image.png has changed
Binary file doc/images/facet_overview.png has changed
Binary file doc/images/facet_range.png has changed
Binary file doc/images/lax-book_00-login_en.png has changed
Binary file doc/images/lax-book_01-start_en.png has changed
Binary file doc/images/lax-book_02-cookie-values_en.png has changed
Binary file doc/images/lax-book_02-create-blog_en.png has changed
Binary file doc/images/lax-book_03-list-one-blog_en.png has changed
Binary file doc/images/lax-book_03-site-config-panel_en.png has changed
Binary file doc/images/lax-book_03-state-submitted_en.png has changed
Binary file doc/images/lax-book_03-transitions-view_en.png has changed
Binary file doc/images/lax-book_04-detail-one-blog_en.png has changed
Binary file doc/images/lax-book_05-list-two-blog_en.png has changed
Binary file doc/images/lax-book_06-add-relation-entryof_en.png has changed
Binary file doc/images/lax-book_06-main-template-logo_en.png has changed
Binary file doc/images/lax-book_07-detail-one-blogentry_en.png has changed
Binary file doc/images/lax-book_08-schema_en.png has changed
Binary file doc/images/lax-book_09-new-view-blogentry_en.png has changed
Binary file doc/images/lax-book_10-blog-with-two-entries_en.png has changed
Binary file doc/images/main_template.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/images/main_template.svg	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="1036.6421"
+   height="845.07812"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   sodipodi:docname="main_template.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0"
+   inkscape:export-filename="/home/auc/cw/doc/book/en/images/main_template.png"
+   inkscape:export-xdpi="60.659016"
+   inkscape:export-ydpi="60.659016">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.80355603"
+     inkscape:cx="510.91495"
+     inkscape:cy="422.53906"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="925"
+     inkscape:window-height="1168"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:snap-bbox="true" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Calque 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(162.2968,90.697922)">
+    <rect
+       style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.775;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect2439"
+       width="854.37006"
+       height="698.2019"
+       x="20.307629"
+       y="-20.575344" />
+    <rect
+       style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.775;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect3301"
+       width="816.3457"
+       height="508.15628"
+       x="31.751091"
+       y="96.33345" />
+    <g
+       id="g3220"
+       transform="matrix(1.0035394,0,0,1,0.5745006,0)">
+      <rect
+         y="-89.447922"
+         x="-161.0468"
+         height="55.714287"
+         width="1031.1713"
+         id="rect3240"
+         style="fill:#dfdfdf;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.50000024;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <text
+         id="text3264"
+         y="-51.771908"
+         x="757.85767"
+         style="font-size:23.38711166px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           id="tspan3266"
+           y="-51.771908"
+           x="757.85767"
+           sodipodi:role="line">header</tspan></text>
+    </g>
+    <rect
+       style="fill:#dfdfdf;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.775;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect3270"
+       width="167.87744"
+       height="707.71222"
+       x="-160.02441"
+       y="-24.671618" />
+    <g
+       id="g2434"
+       transform="matrix(0.975467,0,0,1,0.6942419,-3.6587365)">
+      <rect
+         y="35.365849"
+         x="29.548275"
+         height="55.714287"
+         width="842.59979"
+         id="rect3279"
+         style="fill:#dfdfdf;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <text
+         id="text3281"
+         y="72.885193"
+         x="681.65283"
+         style="font-size:23.38711166px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           id="tspan3283"
+           y="72.885193"
+           x="681.65283"
+           sodipodi:role="line">contentheader</tspan></text>
+    </g>
+    <g
+       id="g3170"
+       transform="matrix(1.0023324,0,0,1,-2.0421673,-10.976211)">
+      <rect
+         y="698.6355"
+         x="-158.28485"
+         height="55.714287"
+         width="1032.5997"
+         id="rect3285"
+         style="fill:#dfdfdf;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <text
+         id="text3287"
+         y="736.52045"
+         x="770.28204"
+         style="font-size:23.38711166px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           id="tspan3289"
+           y="736.52045"
+           x="770.28204"
+           sodipodi:role="line">footer</tspan></text>
+    </g>
+    <g
+       id="g3211" />
+    <g
+       id="g3215"
+       transform="matrix(0.9712065,0,0,1,0.7659296,-17.074106)">
+      <rect
+         style="fill:#dfdfdf;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         id="rect3291"
+         width="844.62012"
+         height="55.714287"
+         x="27.850754"
+         y="629.88562" />
+      <text
+         id="text3293"
+         y="666.60339"
+         x="692.85773"
+         style="font-size:23.38711166px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           id="tspan3295"
+           y="666.60339"
+           x="692.85773"
+           sodipodi:role="line">contentfooter</tspan></text>
+    </g>
+    <text
+       xml:space="preserve"
+       style="font-size:23.38711166px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
+       x="-143.67273"
+       y="20.58094"
+       id="text3297"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan2432"
+         x="-143.67273"
+         y="20.58094">left column</tspan></text>
+    <text
+       transform="scale(0.9876573,1.0124969)"
+       id="text3175"
+       y="12.071429"
+       x="721.0575"
+       style="font-size:23.09845161px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans"
+       xml:space="preserve"><tspan
+         id="tspan3177"
+         y="12.071429"
+         x="721.0575"
+         sodipodi:role="line">contentcol</tspan></text>
+    <text
+       transform="scale(0.9876573,1.0124969)"
+       id="text3179"
+       y="126.27104"
+       x="701.45959"
+       style="font-size:23.09845161px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans"
+       xml:space="preserve"><tspan
+         id="tspan3181"
+         y="126.27104"
+         x="701.45959"
+         sodipodi:role="line">contentmain</tspan></text>
+  </g>
+</svg>
Binary file doc/images/main_template_layout.png has changed
Binary file doc/images/primaryview_template.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/images/primaryview_template.svg	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,285 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="1036.6421"
+   height="845.07812"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   sodipodi:docname="primaryview_template.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0"
+   inkscape:export-filename="/home/steph/local/fcubicweb/cubicweb/doc/book/en/images/primaryview_template.png"
+   inkscape:export-xdpi="43.451603"
+   inkscape:export-ydpi="43.451603">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.9357135"
+     inkscape:cx="518.32104"
+     inkscape:cy="337.0428"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1307"
+     inkscape:window-height="1168"
+     inkscape:window-x="0"
+     inkscape:window-y="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Calque 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(162.2968,90.697922)">
+    <g
+       id="g3869"
+       transform="matrix(1,0,0,1.0373644,0,-72.039777)"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449">
+      <rect
+         y="-15.840891"
+         x="-159.08963"
+         height="770.11017"
+         width="1033.0049"
+         id="rect3301"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.90144825;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <text
+         id="text3865"
+         y="19.784882"
+         x="-150.07172"
+         style="font-size:28.67479324px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           id="tspan3867"
+           y="19.784882"
+           x="-150.07172"
+           sodipodi:role="line">contentmain</tspan></text>
+    </g>
+    <rect
+       style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.45654476;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect2383"
+       width="772.32111"
+       height="43.888428"
+       x="-131.1837"
+       y="86.559296"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449" />
+    <text
+       xml:space="preserve"
+       style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.50000000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%"
+       x="-122.69418"
+       y="115.50363"
+       id="text2385"
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449"><tspan
+         sodipodi:role="line"
+         x="-122.69418"
+         y="115.50363"
+         id="tspan3163">navcontenttop</tspan></text>
+    <rect
+       style="fill:#ffd5d5;fill-rule:evenodd;stroke:#000000;stroke-width:3.06523442;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect3167"
+       width="770.26868"
+       height="203.16078"
+       x="-125.88269"
+       y="172.90417"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449" />
+    <text
+       xml:space="preserve"
+       style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
+       x="348.26724"
+       y="205.34305"
+       id="text3169"
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449"><tspan
+         sodipodi:role="line"
+         x="348.26724"
+         y="205.34305"
+         id="tspan3171">render_entity_attributes()</tspan></text>
+    <rect
+       style="fill:#ffd5d5;fill-rule:evenodd;stroke:#000000;stroke-width:3.06523442;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect3173"
+       width="769.93549"
+       height="237.84663"
+       x="-125.03326"
+       y="391.32156"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449" />
+    <text
+       xml:space="preserve"
+       style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
+       x="360.99954"
+       y="428.38055"
+       id="text3175"
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449"><tspan
+         sodipodi:role="line"
+         x="360.99954"
+         y="428.38055"
+         id="tspan3177">render_entity_relations()</tspan></text>
+    <rect
+       style="fill:#ffd5d5;fill-rule:evenodd;stroke:#000000;stroke-width:2.15903592;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect3185"
+       width="178.93939"
+       height="612.36584"
+       x="667.10443"
+       y="84.64225"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449" />
+    <text
+       xml:space="preserve"
+       style="font-size:22px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.50000000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%"
+       x="105.32364"
+       y="-810.65997"
+       id="text3187"
+       transform="matrix(0,1,-1,0,0,0)"
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449"><tspan
+         sodipodi:role="line"
+         id="tspan2408">render_side_boxes()</tspan></text>
+    <rect
+       style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:3.0652349;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect3191"
+       width="771.97766"
+       height="55.647793"
+       x="-127.80586"
+       y="642.0293"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449" />
+    <text
+       xml:space="preserve"
+       style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
+       x="-121.22153"
+       y="674.1748"
+       id="text3181"
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449"><tspan
+         sodipodi:role="line"
+         x="-121.22153"
+         y="674.1748"
+         id="tspan3183">navcontentbottom</tspan></text>
+    <rect
+       style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.68198514;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect3881"
+       width="986.90503"
+       height="45.800392"
+       x="-128.34428"
+       y="-31.574066"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449" />
+    <text
+       xml:space="preserve"
+       style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
+       x="355.60541"
+       y="-2.7424495"
+       id="text3883"
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449"><tspan
+         sodipodi:role="line"
+         x="355.60541"
+         y="-2.7424495"
+         id="tspan3885">render_entity_toolbox(), render_entity_title()</tspan></text>
+    <rect
+       style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.68198514;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect3890"
+       width="986.90503"
+       height="45.800392"
+       x="-128.87863"
+       y="19.723684"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449" />
+    <text
+       xml:space="preserve"
+       style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
+       x="565.71027"
+       y="50.135612"
+       id="text3892"
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449"><tspan
+         sodipodi:role="line"
+         x="565.71027"
+         y="50.135612"
+         id="tspan3894">render_entity_summary()</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
+       x="87.154541"
+       y="114.2578"
+       id="text3899"
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449"><tspan
+         sodipodi:role="line"
+         id="tspan3903"
+         x="87.154541"
+         y="114.2578">content_navigation_components('navcontenttop')</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
+       x="88.46772"
+       y="675.71582"
+       id="text2410"
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449"><tspan
+         sodipodi:role="line"
+         id="tspan2412"
+         x="88.46772"
+         y="675.71582">content_navigation_components('navcontenttop')</tspan></text>
+  </g>
+</svg>
Binary file doc/images/request_session.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/images/request_session.svg	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,206 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="85.960938"
+   height="12.382812"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.3.1 r9886"
+   sodipodi:docname="request_session.svg">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Lend"
+       style="overflow:visible;">
+      <path
+         id="path3822"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
+         transform="scale(0.8) rotate(180) translate(12.5,0)" />
+    </marker>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.98994949"
+     inkscape:cx="25.928992"
+     inkscape:cy="-185.87004"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="958"
+     inkscape:window-height="1160"
+     inkscape:window-x="0"
+     inkscape:window-y="38"
+     inkscape:window-maximized="0"
+     inkscape:snap-global="true" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-263.52249,-495.73373)">
+    <rect
+       style="fill:#ffffff;stroke:#000000;stroke-width:0.92460138;stroke-opacity:1"
+       id="rect3773"
+       width="214.15233"
+       height="184.80336"
+       x="57.578697"
+       y="366.01306" />
+    <rect
+       id="rect2985"
+       width="216.86372"
+       height="183.54575"
+       x="348.50262"
+       y="367.78079"
+       style="fill:#ffffff;stroke:#000000;stroke-width:0.55298227;stroke-opacity:1" />
+    <text
+       xml:space="preserve"
+       style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+       x="376.7869"
+       y="399.80365"
+       id="text3755"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3757"
+         x="376.7869"
+         y="399.80365">Repository</tspan></text>
+    <rect
+       style="fill:#ffffff;stroke:#000000;stroke-opacity:1"
+       id="rect3759"
+       width="144.45181"
+       height="104.04572"
+       x="237.38585"
+       y="423.03714" />
+    <text
+       xml:space="preserve"
+       style="font-size:24px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+       x="262.63968"
+       y="470.51431"
+       id="text3761"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3763"
+         x="262.63968"
+         y="470.51431">REPOAPI</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+       x="262.63968"
+       y="507.88998"
+       id="text3765"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3767"
+         x="262.63968"
+         y="507.88998">connection</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+       x="419.21332"
+       y="509.91025"
+       id="text3769"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3771"
+         x="419.21332"
+         y="509.91025">session</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+       x="102.02541"
+       y="397.78333"
+       id="text3775"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3777"
+         x="102.02541"
+         y="397.78333">Client</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+       x="116.16754"
+       y="507.88995"
+       id="text3779"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3781"
+         x="116.16754"
+         y="507.88995">request</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+       x="361.50729"
+       y="585.89832"
+       id="text3802"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3804"
+         x="361.50729"
+         y="585.89832">database </tspan><tspan
+         sodipodi:role="line"
+         x="361.50729"
+         y="605.89832"
+         id="tspan3806">connection</tspan></text>
+    <rect
+       style="fill:#ffffff;stroke:#000000;stroke-width:1.48014534;stroke-opacity:1"
+       id="rect3808"
+       width="192.09367"
+       height="58.095726"
+       x="365.79443"
+       y="621.50018" />
+    <text
+       xml:space="preserve"
+       style="font-size:36px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+       x="369.5885"
+       y="662.66992"
+       id="text3810"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3812"
+         x="369.5885"
+         y="662.66992">Database</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:none"
+       d="M 197.57252,125.76645 195.76971,55.592808"
+       id="path4260"
+       inkscape:connector-type="polyline"
+       inkscape:connector-curvature="3"
+       inkscape:connection-start="#rect3808"
+       inkscape:connection-start-point="d4"
+       inkscape:connection-end="#rect2985"
+       inkscape:connection-end-point="d4"
+       transform="translate(263.52249,495.73373)" />
+  </g>
+</svg>
Binary file doc/images/server-class-diagram.png has changed
Binary file doc/images/tutos-base_blog-form_en.png has changed
Binary file doc/images/tutos-base_blog-primary-after-post-creation_en.png has changed
Binary file doc/images/tutos-base_blog-primary_en.png has changed
Binary file doc/images/tutos-base_blogs-list_en.png has changed
Binary file doc/images/tutos-base_form-generic-relations_en.png has changed
Binary file doc/images/tutos-base_index_en.png has changed
Binary file doc/images/tutos-base_login-form_en.png has changed
Binary file doc/images/tutos-base_myblog-blogentry-taggable-commentable-primary_en.png has changed
Binary file doc/images/tutos-base_myblog-community-custom-primary_en.png has changed
Binary file doc/images/tutos-base_myblog-community-default-primary_en.png has changed
Binary file doc/images/tutos-base_myblog-community-taggable-primary_en.png has changed
Binary file doc/images/tutos-base_myblog-custom-footer_en.png has changed
Binary file doc/images/tutos-base_myblog-schema_en.png has changed
Binary file doc/images/tutos-base_myblog-siteinfo_en.png has changed
Binary file doc/images/tutos-base_schema_en.png has changed
Binary file doc/images/tutos-base_siteconfig_en.png has changed
Binary file doc/images/tutos-base_user-menu_en.png has changed
Binary file doc/images/tutos-photowebsite_background-image.png has changed
Binary file doc/images/tutos-photowebsite_boxes.png has changed
Binary file doc/images/tutos-photowebsite_breadcrumbs.png has changed
Binary file doc/images/tutos-photowebsite_facets.png has changed
Binary file doc/images/tutos-photowebsite_grey-box.png has changed
Binary file doc/images/tutos-photowebsite_index-after.png has changed
Binary file doc/images/tutos-photowebsite_index-before.png has changed
Binary file doc/images/tutos-photowebsite_login-box.png has changed
Binary file doc/images/tutos-photowebsite_prevnext.png has changed
Binary file doc/images/tutos-photowebsite_ui1.png has changed
Binary file doc/images/tutos-photowebsite_ui2.png has changed
Binary file doc/images/tutos-photowebsite_ui3.png has changed
Binary file doc/images/undo_history-view_w600.png has changed
Binary file doc/images/undo_mesage_w600.png has changed
Binary file doc/images/undo_startup-link_w600.png has changed
Binary file doc/images/views-table-filter-shadow.png has changed
Binary file doc/images/views-table-filter.png has changed
Binary file doc/images/views-table-shadow.png has changed
Binary file doc/images/views-table.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,125 @@
+=====================================================
+|cubicweb| - The Semantic Web is a construction game!
+=====================================================
+
+|cubicweb| is a semantic web application framework, licensed under the LGPL,
+that empowers developers to efficiently build web applications by reusing
+components (called `cubes`) and following the well known object-oriented design
+principles.
+
+Main Features
+~~~~~~~~~~~~~
+
+* an engine driven by the explicit :ref:`data model
+  <TutosBaseCustomizingTheApplicationDataModel>` of the application,
+
+* a query language named :ref:`RQL <RQL>` similar to W3C's SPARQL,
+
+* a :ref:`selection+view <TutosBaseCustomizingTheApplicationCustomViews>`
+  mechanism for semi-automatic XHTML/XML/JSON/text generation,
+
+* a library of reusable :ref:`components <Cube>` (data model and views) that
+  fulfill common needs,
+
+* the power and flexibility of the Python_ programming language,
+
+* the reliability of SQL databases, LDAP directories, Subversion and Mercurial
+  for storage backends.
+
+Built since 2000 from an R&D effort still continued, supporting 100,000s of
+daily visits at some production sites, |cubicweb| is a proven end to end solution
+for semantic web application development that promotes quality, reusability and
+efficiency.
+
+QuickStart
+~~~~~~~~~~
+
+The impatient developer will move right away to :ref:`SetUpEnv` then to :ref:`ConfigEnv`.
+
+Social
+~~~~~~
+
+*   Chat on the `jabber forum`_
+*   Discuss on the `mailing-list`_
+*   Discover on the `blog`_
+*   Contribute on the forge_
+
+
+.. _Logilab: http://www.logilab.fr/
+.. _forge: http://www.cubicweb.org/project/
+.. _Python: http://www.python.org/
+.. _`jabber forum`: http://www.logilab.org/blogentry/6718
+.. _`mailing-list`: http://lists.cubicweb.org/mailman/listinfo/cubicweb
+.. _blog: http://www.cubicweb.org/blog/1238
+
+
+Narrative Documentation
+~~~~~~~~~~~~~~~~~~~~~~~
+
+A.k.a. "The Book"
+
+.. toctree::
+   :maxdepth: 2
+
+   book/intro/index
+
+.. toctree::
+   :maxdepth: 2
+
+   tutorials/index
+   
+.. toctree::
+   :maxdepth: 3
+
+   book/devrepo/index
+   book/devweb/index
+
+.. toctree::
+   :maxdepth: 2
+
+   book/admin/index
+   book/additionnal_services/index
+   book/annexes/index
+
+
+
+Changes
+~~~~~~~
+
+.. toctree::
+   :maxdepth: 2
+
+   changes/changelog
+
+
+Reference documentation
+~~~~~~~~~~~~~~~~~~~~~~~
+
+API
+'''
+
+.. toctree::
+    :maxdepth: 1
+    :glob:
+
+    api/*
+
+.. toctree::
+    :maxdepth: 1
+
+    js_api/index
+
+Developpers
+~~~~~~~~~~~
+
+.. toctree::
+    :maxdepth: 1
+    :glob:
+
+    dev/*
+
+Indexes
+~~~~~~~
+
+* the :ref:`genindex`,
+* the :ref:`modindex`,
--- a/doc/refactoring-the-css-with-uiprops.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-=========================================
-Refactoring the CSSs with UI properties
-=========================================
-
-Overview
-=========
-
-Managing styles progressively became difficult in CubicWeb. The
-introduction of uiprops is an attempt to fix this problem.
-
-The goal is to make it possible to use variables in our CSSs.
-
-These variables are defined or computed in the uiprops.py python file
-and inserted in the CSS using the Python string interpolation syntax.
-
-A quick example, put in ``uiprops.py``::
-
-  defaultBgColor = '#eee'
-
-and in your css::
-
-  body { background-color: %(defaultBgColor)s; }
-
-
-The good practices are:
-
-- define a variable in uiprops to avoid repetitions in the CSS
-  (colors, borders, fonts, etc.)
-
-- define a variable in uiprops when you need to compute values
-  (compute a color palette, etc.)
-
-The algorithm implemented in CubicWeb is the following:
-
-- read uiprops file while walk up the chain of cube dependencies: if
-  cube myblog depends on cube comment, the variables defined in myblog
-  will have precedence over the ones in comment
-
-- replace the %(varname)s in all the CSSs of all the cubes
-
-Keep in mind that the browser will then interpret the CSSs and apply
-the standard cascading mechanism.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/tools/mode_plan.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,50 @@
+# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+"""
+>>> from mode_plan import *
+>>> ls()
+<list of directory content>
+>>> ren('A01','A03')
+rename A010-joe.en.txt to A030-joe.en.txt
+accept [y/N]?
+"""
+
+def ren(a,b):
+    names = glob.glob('%s*'%a)
+    for name in names :
+        print 'rename %s to %s' % (name, name.replace(a,b))
+    if raw_input('accept [y/N]?').lower() =='y':
+        for name in names:
+            os.system('hg mv %s %s' % (name, name.replace(a,b)))
+
+
+def ls(): print '\n'.join(sorted(os.listdir('.')))
+
+def move():
+    filenames = []
+    for name in sorted(os.listdir('.')):
+        num = name[:2]
+        if num.isdigit():
+            filenames.append( (int(num), name) )
+
+
+    #print filenames
+
+    for num, name in filenames:
+        if num >= start:
+            print 'hg mv %s %2i%s' %(name,num+1,name[2:])
--- a/doc/tools/pyjsrest.py	Mon May 09 17:24:03 2016 +0200
+++ b/doc/tools/pyjsrest.py	Tue Jun 21 07:42:30 2016 +0200
@@ -92,6 +92,9 @@
                 f_rst.write(rst_content)
     stream = open(osp.join(rst_dir, 'index.rst'), 'w')
     stream.write('''
+Javascript API
+==============
+
 .. toctree::
     :maxdepth: 1
 
@@ -134,7 +137,6 @@
     'cubicweb.preferences',
     'cubicweb.edition',
     'cubicweb.reledit',
-    'cubicweb.timeline-ext',
 ]
 
 FILES_TO_IGNORE = set([
@@ -152,7 +154,6 @@
     'cubicweb.fckcwconfig-full.js',
     'cubicweb.goa.js',
     'cubicweb.compat.js',
-    'cubicweb.timeline-bundle.js',
     ])
 
 if __name__ == '__main__':
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/tutorials/advanced/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,29 @@
+
+.. _TutosPhotoWebSite:
+
+Building a photo gallery with |cubicweb|
+========================================
+
+Desired features
+----------------
+
+* basically a photo gallery
+
+* photo stored on the file system and displayed dynamically through a web interface
+
+* navigation through folder (album), tags, geographical zone, people on the
+  picture... using facets
+
+* advanced security (not everyone can see everything). More on this later.
+
+
+.. toctree::
+   :maxdepth: 2
+
+   part01_create-cube
+   part02_security
+   part03_bfss
+   part04_ui-base
+   part05_ui-advanced
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/tutorials/advanced/part01_create-cube.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,155 @@
+.. _TutosPhotoWebSiteCubeCreation:
+
+Cube creation and schema definition
+-----------------------------------
+
+.. _adv_tuto_create_new_cube:
+
+Step 1: creating a new cube for my web site
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+One note about my development environment: I wanted to use the packaged
+version of CubicWeb and cubes while keeping my cube in my user
+directory, let's say `~src/cubes`.  I achieve this by setting the
+following environment variables::
+
+  CW_CUBES_PATH=~/src/cubes
+  CW_MODE=user
+
+I can now create the cube which will hold custom code for this web
+site using::
+
+  cubicweb-ctl newcube --directory=~/src/cubes sytweb
+
+
+.. _adv_tuto_assemble_cubes:
+
+Step 2: pick building blocks into existing cubes
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Almost everything I want to handle in my web-site is somehow already modelized in
+existing cubes that I'll extend for my need. So I'll pick the following cubes:
+
+* `folder`, containing the `Folder` entity type, which will be used as
+  both 'album' and a way to map file system folders. Entities are
+  added to a given folder using the `filed_under` relation.
+
+* `file`, containing `File` entity type, gallery view, and a file system import
+  utility.
+
+* `zone`, containing the `Zone` entity type for hierarchical geographical
+  zones. Entities (including sub-zones) are added to a given zone using the
+  `situated_in` relation.
+
+* `person`, containing the `Person` entity type plus some basic views.
+
+* `comment`, providing a full commenting system allowing one to comment entity types
+  supporting the `comments` relation by adding a `Comment` entity.
+
+* `tag`, providing a full tagging system as an easy and powerful way to classify
+  entities supporting the `tags` relation by linking the to `Tag` entities. This
+  will allows navigation into a large number of picture.
+
+Ok, now I'll tell my cube requires all this by editing :file:`cubes/sytweb/__pkginfo__.py`:
+
+  .. sourcecode:: python
+
+    __depends__ = {'cubicweb': '>= 3.10.0',
+                   'cubicweb-file': '>= 1.9.0',
+		   'cubicweb-folder': '>= 1.1.0',
+		   'cubicweb-person': '>= 1.2.0',
+		   'cubicweb-comment': '>= 1.2.0',
+		   'cubicweb-tag': '>= 1.2.0',
+		   'cubicweb-zone': None}
+
+Notice that you can express minimal version of the cube that should be used,
+`None` meaning whatever version available. All packages starting with 'cubicweb-'
+will be recognized as being cube, not bare python packages. You can still specify
+this explicitly using instead the `__depends_cubes__` dictionary which should
+contains cube's name without the prefix. So the example below would be written
+as:
+
+  .. sourcecode:: python
+
+    __depends__ = {'cubicweb': '>= 3.10.0'}
+    __depends_cubes__ = {'file': '>= 1.9.0',
+		         'folder': '>= 1.1.0',
+		   	 'person': '>= 1.2.0',
+		   	 'comment': '>= 1.2.0',
+		   	 'tag': '>= 1.2.0',
+		   	 'zone': None}
+
+If your cube is packaged for debian, it's a good idea to update the
+`debian/control` file at the same time, so you won't forget it.
+
+
+Step 3: glue everything together in my cube's schema
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. sourcecode:: python
+
+    from yams.buildobjs import RelationDefinition
+
+    class comments(RelationDefinition):
+	subject = 'Comment'
+	object = 'File'
+	cardinality = '1*'
+	composite = 'object'
+
+    class tags(RelationDefinition):
+	subject = 'Tag'
+	object = 'File'
+
+    class filed_under(RelationDefinition):
+	subject = 'File'
+	object = 'Folder'
+
+    class situated_in(RelationDefinition):
+	subject = 'File'
+	object = 'Zone'
+
+    class displayed_on(RelationDefinition):
+	subject = 'Person'
+	object = 'File'
+
+
+This schema:
+
+* allows to comment and tag on `File` entity type by adding the `comments` and
+  `tags` relations. This should be all we've to do for this feature since the
+  related cubes provide 'pluggable section' which are automatically displayed on
+  the primary view of entity types supporting the relation.
+
+* adds a `situated_in` relation definition so that image entities can be
+  geolocalized.
+
+* add a new relation `displayed_on` relation telling who can be seen on a
+  picture.
+
+This schema will probably have to evolve as time goes (for security handling at
+least), but since the possibility to let a schema evolve is one of CubicWeb's
+features (and goals), we won't worry about it for now and see that later when needed.
+
+
+Step 4: creating the instance
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Now that I have a schema, I want to create an instance. To
+do so using this new 'sytweb' cube, I run::
+
+  cubicweb-ctl create sytweb sytweb_instance
+
+Hint: if you get an error while the database is initialized, you can
+avoid having to answer the questions again by running::
+
+   cubicweb-ctl db-create sytweb_instance
+
+This will use your already configured instance and start directly from the create
+database step, thus skipping questions asked by the 'create' command.
+
+Once the instance and database are fully initialized, run ::
+
+  cubicweb-ctl start sytweb_instance
+
+to start the instance, check you can connect on it, etc...
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/tutorials/advanced/part02_security.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,440 @@
+.. _TutosPhotoWebSiteSecurity:
+
+Security, testing and migration
+-------------------------------
+
+This part will cover various topics:
+
+* configuring security
+* migrating existing instance
+* writing some unit tests
+
+Here is the ``read`` security model I want:
+
+* folders, files, images and comments should have one of the following visibility:
+
+  - ``public``, everyone can see it
+  - ``authenticated``, only authenticated users can see it
+  - ``restricted``, only a subset of authenticated users can see it
+
+* managers (e.g. me) can see everything
+* only authenticated users can see people
+* everyone can see classifier entities, such as tag and zone
+
+Also, unless explicitly specified, the visibility of an image should be the same as
+its parent folder, as well as visibility of a comment should be the same as the
+commented entity. If there is no parent entity, the default visibility is
+``authenticated``.
+
+Regarding write security, that's much easier:
+* anonymous can't write anything
+* authenticated users can only add comment
+* managers will add the remaining stuff
+
+Now, let's implement that!
+
+Proper security in CubicWeb is done at the schema level, so you don't have to
+bother with it in views: users will only see what they can see automatically.
+
+.. _adv_tuto_security:
+
+Step 1: configuring security into the schema
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In schema, you can grant access according to groups, or to some RQL expressions:
+users get access if the expression returns some results. To implement the read
+security defined earlier, groups are not enough, we'll need some RQL expression. Here
+is the idea:
+
+* add a `visibility` attribute on Folder, File and Comment, which may be one of
+  the value explained above
+
+* add a `may_be_read_by` relation from Folder, File and Comment to users,
+  which will define who can see the entity
+
+* security propagation will be done in hook.
+
+So the first thing to do is to modify my cube's schema.py to define those
+relations:
+
+.. sourcecode:: python
+
+    from yams.constraints import StaticVocabularyConstraint
+
+    class visibility(RelationDefinition):
+	subject = ('Folder', 'File', 'Comment')
+	object = 'String'
+	constraints = [StaticVocabularyConstraint(('public', 'authenticated',
+						   'restricted', 'parent'))]
+	default = 'parent'
+	cardinality = '11' # required
+
+    class may_be_read_by(RelationDefinition):
+        __permissions__ = {
+	    'read':   ('managers', 'users'),
+	    'add':    ('managers',),
+	    'delete': ('managers',),
+	    }
+
+	subject = ('Folder', 'File', 'Comment',)
+	object = 'CWUser'
+
+We can note the following points:
+
+* we've added a new `visibility` attribute to folder, file, image and comment
+  using a `RelationDefinition`
+
+* `cardinality = '11'` means this attribute is required. This is usually hidden
+  under the `required` argument given to the `String` constructor, but we can
+  rely on this here (same thing for StaticVocabularyConstraint, which is usually
+  hidden by the `vocabulary` argument)
+
+* the `parent` possible value will be used for visibility propagation
+
+* think to secure the `may_be_read_by` permissions, else any user can add/delete it
+  by default, which somewhat breaks our security model...
+
+Now, we should be able to define security rules in the schema, based on these new
+attribute and relation. Here is the code to add to *schema.py*:
+
+.. sourcecode:: python
+
+    from cubicweb.schema import ERQLExpression
+
+    VISIBILITY_PERMISSIONS = {
+	'read':   ('managers',
+		   ERQLExpression('X visibility "public"'),
+		   ERQLExpression('X may_be_read_by U')),
+	'add':    ('managers',),
+	'update': ('managers', 'owners',),
+	'delete': ('managers', 'owners'),
+	}
+    AUTH_ONLY_PERMISSIONS = {
+	    'read':   ('managers', 'users'),
+	    'add':    ('managers',),
+	    'update': ('managers', 'owners',),
+	    'delete': ('managers', 'owners'),
+	    }
+    CLASSIFIERS_PERMISSIONS = {
+	    'read':   ('managers', 'users', 'guests'),
+	    'add':    ('managers',),
+	    'update': ('managers', 'owners',),
+	    'delete': ('managers', 'owners'),
+	    }
+
+    from cubes.folder.schema import Folder
+    from cubes.file.schema import File
+    from cubes.comment.schema import Comment
+    from cubes.person.schema import Person
+    from cubes.zone.schema import Zone
+    from cubes.tag.schema import Tag
+
+    Folder.__permissions__ = VISIBILITY_PERMISSIONS
+    File.__permissions__ = VISIBILITY_PERMISSIONS
+    Comment.__permissions__ = VISIBILITY_PERMISSIONS.copy()
+    Comment.__permissions__['add'] = ('managers', 'users',)
+    Person.__permissions__ = AUTH_ONLY_PERMISSIONS
+    Zone.__permissions__ = CLASSIFIERS_PERMISSIONS
+    Tag.__permissions__ = CLASSIFIERS_PERMISSIONS
+
+What's important in there:
+
+* `VISIBILITY_PERMISSIONS` provides read access to managers group, if
+  `visibility` attribute's value is 'public', or if user (designed by the 'U'
+  variable in the expression) is linked to the entity (the 'X' variable) through
+  the `may_be_read_by` permission
+
+* we modify permissions of the entity types we use by importing them and
+  modifying their `__permissions__` attribute
+
+* notice the `.copy()`: we only want to modify 'add' permission for `Comment`,
+  not for all entity types using `VISIBILITY_PERMISSIONS`!
+
+* the remaining part of the security model is done using regular groups:
+
+  - `users` is the group to which all authenticated users will belong
+  - `guests` is the group of anonymous users
+
+
+.. _adv_tuto_security_propagation:
+
+Step 2: security propagation in hooks
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To fullfill the requirements, we have to implement::
+
+  Also, unless explicity specified, visibility of an image should be the same as
+  its parent folder, as well as visibility of a comment should be the same as the
+  commented entity.
+
+This kind of `active` rule will be done using CubicWeb's hook
+system. Hooks are triggered on database events such as addition of a new
+entity or relation.
+
+The tricky part of the requirement is in *unless explicitly specified*, notably
+because when the entity is added, we don't know yet its 'parent'
+entity (e.g. Folder of an File, File commented by a Comment). To handle such things,
+CubicWeb provides `Operation`, which allow to schedule things to do at commit time.
+
+In our case we will:
+
+* on entity creation, schedule an operation that will set default visibility
+
+* when a "parent" relation is added, propagate parent's visibility unless the
+  child already has a visibility set
+
+Here is the code in cube's *hooks.py*:
+
+.. sourcecode:: python
+
+    from cubicweb.predicates import is_instance
+    from cubicweb.server import hook
+
+    class SetVisibilityOp(hook.DataOperationMixIn, hook.Operation):
+
+	def precommit_event(self):
+	    for eid in self.get_data():
+		entity = self.session.entity_from_eid(eid)
+		if entity.visibility == 'parent':
+		    entity.cw_set(visibility=u'authenticated')
+
+    class SetVisibilityHook(hook.Hook):
+	__regid__ = 'sytweb.setvisibility'
+	__select__ = hook.Hook.__select__ & is_instance('Folder', 'File', 'Comment')
+	events = ('after_add_entity',)
+
+	def __call__(self):
+	    SetVisibilityOp.get_instance(self._cw).add_data(self.entity.eid)
+
+    class SetParentVisibilityHook(hook.Hook):
+	__regid__ = 'sytweb.setparentvisibility'
+	__select__ = hook.Hook.__select__ & hook.match_rtype('filed_under', 'comments')
+	events = ('after_add_relation',)
+
+	def __call__(self):
+	    parent = self._cw.entity_from_eid(self.eidto)
+	    child = self._cw.entity_from_eid(self.eidfrom)
+	    if child.visibility == 'parent':
+		child.cw_set(visibility=parent.visibility)
+
+Notice:
+
+* hooks are application objects, hence have selectors that should match entity or
+  relation types to which the hook applies. To match a relation type, we use the
+  hook specific `match_rtype` selector.
+
+* usage of `DataOperationMixIn`: instead of adding an operation for each added entity,
+  DataOperationMixIn allows to create a single one and to store entity's eids to be
+  processed in the transaction data. This is a good pratice to avoid heavy
+  operations manipulation cost when creating a lot of entities in the same
+  transaction.
+
+* the `precommit_event` method of the operation will be called at transaction's
+  commit time.
+
+* in a hook, `self._cw` is the repository session, not a web request as usually
+  in views
+
+* according to hook's event, you have access to different attributes on the hook
+  instance. Here:
+
+  - `self.entity` is the newly added entity on 'after_add_entity' events
+
+  - `self.eidfrom` / `self.eidto` are the eid of the subject / object entity on
+    'after_add_relation' events (you may also get the relation type using
+    `self.rtype`)
+
+The `parent` visibility value is used to tell "propagate using parent security"
+because we want that attribute to be required, so we can't use None value else
+we'll get an error before we get any chance to propagate...
+
+Now, we also want to propagate the `may_be_read_by` relation. Fortunately,
+CubicWeb provides some base hook classes for such things, so we only have to add
+the following code to *hooks.py*:
+
+.. sourcecode:: python
+
+    # relations where the "parent" entity is the subject
+    S_RELS = set()
+    # relations where the "parent" entity is the object
+    O_RELS = set(('filed_under', 'comments',))
+
+    class AddEntitySecurityPropagationHook(hook.PropagateRelationHook):
+	"""propagate permissions when new entity are added"""
+	__regid__ = 'sytweb.addentity_security_propagation'
+	__select__ = (hook.PropagateRelationHook.__select__
+		      & hook.match_rtype_sets(S_RELS, O_RELS))
+	main_rtype = 'may_be_read_by'
+	subject_relations = S_RELS
+	object_relations = O_RELS
+
+    class AddPermissionSecurityPropagationHook(hook.PropagateRelationAddHook):
+	"""propagate permissions when new entity are added"""
+	__regid__ = 'sytweb.addperm_security_propagation'
+	__select__ = (hook.PropagateRelationAddHook.__select__
+		      & hook.match_rtype('may_be_read_by',))
+	subject_relations = S_RELS
+	object_relations = O_RELS
+
+    class DelPermissionSecurityPropagationHook(hook.PropagateRelationDelHook):
+	__regid__ = 'sytweb.delperm_security_propagation'
+	__select__ = (hook.PropagateRelationDelHook.__select__
+		      & hook.match_rtype('may_be_read_by',))
+	subject_relations = S_RELS
+	object_relations = O_RELS
+
+* the `AddEntitySecurityPropagationHook` will propagate the relation
+  when `filed_under` or `comments` relations are added
+
+  - the `S_RELS` and `O_RELS` set as well as the `match_rtype_sets` selector are
+    used here so that if my cube is used by another one, it'll be able to
+    configure security propagation by simply adding relation to one of the two
+    sets.
+
+* the two others will propagate permissions changes on parent entities to
+  children entities
+
+
+.. _adv_tuto_tesing_security:
+
+Step 3: testing our security
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Security is tricky. Writing some tests for it is a very good idea. You should
+even write them first, as Test Driven Development recommends!
+
+Here is a small test case that will check the basis of our security
+model, in *test/unittest_sytweb.py*:
+
+.. sourcecode:: python
+
+    from cubicweb.devtools.testlib import CubicWebTC
+    from cubicweb import Binary
+
+    class SecurityTC(CubicWebTC):
+
+        def test_visibility_propagation(self):
+            with self.admin_access.repo_cnx() as cnx:
+                # create a user for later security checks
+                toto = self.create_user(cnx, 'toto')
+                cnx.commit()
+                # init some data using the default manager connection
+                folder = cnx.create_entity('Folder',
+                                           name=u'restricted',
+                                           visibility=u'restricted')
+                photo1 = cnx.create_entity('File',
+                                           data_name=u'photo1.jpg',
+                                           data=Binary('xxx'),
+                                           filed_under=folder)
+                cnx.commit()
+                # visibility propagation
+                self.assertEquals(photo1.visibility, 'restricted')
+                # unless explicitly specified
+                photo2 = cnx.create_entity('File',
+                                           data_name=u'photo2.jpg',
+                                           data=Binary('xxx'),
+                                           visibility=u'public',
+                                           filed_under=folder)
+                cnx.commit()
+                self.assertEquals(photo2.visibility, 'public')
+            with self.new_access('toto').repo_cnx() as cnx:
+                # test security
+                self.assertEqual(1, len(cnx.execute('File X'))) # only the public one
+                self.assertEqual(0, len(cnx.execute('Folder X'))) # restricted...
+            with self.admin_access.repo_cnx() as cnx:
+                # may_be_read_by propagation
+                folder = cnx.entity_from_eid(folder.eid)
+                folder.cw_set(may_be_read_by=toto)
+                cnx.commit()
+            with self.new_access('toto').repo_cnx() as cnx:
+                photo1 = cnx.entity_from_eid(photo1.eid)
+                self.failUnless(photo1.may_be_read_by)
+                # test security with permissions
+                self.assertEquals(2, len(cnx.execute('File X'))) # now toto has access to photo2
+                self.assertEquals(1, len(cnx.execute('Folder X'))) # and to restricted folder
+
+    if __name__ == '__main__':
+        from logilab.common.testlib import unittest_main
+        unittest_main()
+
+It's not complete, but shows most things you'll want to do in tests: adding some
+content, creating users and connecting as them in the test, etc...
+
+To run it type:
+
+.. sourcecode:: bash
+
+    $ pytest unittest_sytweb.py
+    ========================  unittest_sytweb.py  ========================
+    -> creating tables [....................]
+    -> inserting default user and default groups.
+    -> storing the schema in the database [....................]
+    -> database for instance data initialized.
+    .
+    ----------------------------------------------------------------------
+    Ran 1 test in 22.547s
+
+    OK
+
+
+The first execution is taking time, since it creates a sqlite database for the
+test instance. The second one will be much quicker:
+
+.. sourcecode:: bash
+
+    $ pytest unittest_sytweb.py
+    ========================  unittest_sytweb.py  ========================
+    .
+    ----------------------------------------------------------------------
+    Ran 1 test in 2.662s
+
+    OK
+
+If you do some changes in your schema, you'll have to force regeneration of that
+database. You do that by removing the tmpdb files before running the test: ::
+
+    $ rm data/database/tmpdb*
+
+
+.. Note::
+  pytest is a very convenient utility used to control test execution. It is available from the `logilab-common`_ package.
+
+.. _`logilab-common`: http://www.logilab.org/project/logilab-common
+
+.. _adv_tuto_migration_script:
+
+Step 4: writing the migration script and migrating the instance
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Prior to those changes, I created an instance, fed it with some data, so I
+don't want to create a new one, but to migrate the existing one. Let's see how to
+do that.
+
+Migration commands should be put in the cube's *migration* directory, in a
+file named file:`<X.Y.Z>_Any.py` ('Any' being there mostly for historical reasons).
+
+Here I'll create a *migration/0.2.0_Any.py* file containing the following
+instructions:
+
+.. sourcecode:: python
+
+  add_relation_type('may_be_read_by')
+  add_relation_type('visibility')
+  sync_schema_props_perms()
+
+Then I update the version number in the cube's *__pkginfo__.py* to 0.2.0. And
+that's it! Those instructions will:
+
+* update the instance's schema by adding our two new relations and update the
+  underlying database tables accordingly (the first two instructions)
+
+* update schema's permissions definition (the last instruction)
+
+
+To migrate my instance I simply type::
+
+   cubicweb-ctl upgrade sytweb_instance
+
+You'll then be asked some questions to do the migration step by step. You should say
+YES when it asks if a backup of your database should be done, so you can get back
+to initial state if anything goes wrong...
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/tutorials/advanced/part03_bfss.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,131 @@
+Storing images on the file-system
+---------------------------------
+
+Step 1: configuring the BytesFileSystem storage
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To avoid cluttering my database, and to ease file manipulation, I don't want them
+to be stored in the database. I want to be able create File entities for some
+files on the server file system, where those file will be accessed to get
+entities data. To do so I've to set a custom :class:`BytesFileSystemStorage`
+storage for the File 'data' attribute, which hold the actual file's content.
+
+Since the function to register a custom storage needs to have a repository
+instance as first argument, we've to call it in a server startup hook. So I added
+in `cubes/sytweb/hooks.py` :
+
+.. sourcecode:: python
+
+    from os import makedirs
+    from os.path import join, exists
+
+    from cubicweb.server import hook
+    from cubicweb.server.sources import storages
+
+    class ServerStartupHook(hook.Hook):
+        __regid__ = 'sytweb.serverstartup'
+        events = ('server_startup', 'server_maintenance')
+
+        def __call__(self):
+            bfssdir = join(self.repo.config.appdatahome, 'bfss')
+            if not exists(bfssdir):
+                makedirs(bfssdir)
+                print 'created', bfssdir
+            storage = storages.BytesFileSystemStorage(bfssdir)
+            storages.set_attribute_storage(self.repo, 'File', 'data', storage)
+
+.. Note::
+
+  * how we built the hook's registry identifier (`__regid__`): you can introduce
+    'namespaces' by using there python module like naming identifiers. This is
+    especially important for hooks where you usually want a new custom hook, not
+    overriding / specializing an existant one, but the concept may be applied to
+    any application objects
+
+  * we catch two events here: "server_startup" and "server_maintenance". The first
+    is called on regular repository startup (eg, as a server), the other for
+    maintenance task such as shell or upgrade. In both cases, we need to have
+    the storage set, else we'll be in trouble...
+
+  * the path given to the storage is the place where file added through the ui
+    (or in the database before migration) will be located
+
+  * beware that by doing this, you can't anymore write queries that will try to
+    restrict on File `data` attribute. Hopefuly we don't do that usually
+    on file's content or more generally on attributes for the Bytes type
+
+Now, if you've already added some photos through the web ui, you'll have to
+migrate existing data so file's content will be stored on the file-system instead
+of the database. There is a migration command to do so, let's run it in the
+cubicweb shell (in real life, you would have to put it in a migration script as we
+have seen last time):
+
+::
+
+   $ cubicweb-ctl shell sytweb_instance
+   entering the migration python shell
+   just type migration commands or arbitrary python code and type ENTER to execute it
+   type "exit" or Ctrl-D to quit the shell and resume operation
+   >>> storage_changed('File', 'data')
+   [........................]
+
+
+That's it. Now, files added through the web ui will have their content stored on
+the file-system, and you'll also be able to import files from the file-system as
+explained in the next part.
+
+Step 2: importing some data into the instance
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Hey, we start to have some nice features, let us give a try to this new web
+site. For instance if I have a 'photos/201005WePyrenees' containing pictures for
+a particular event, I can import it to my web site by typing ::
+
+  $ cubicweb-ctl fsimport -F sytweb_instance photos/201005WePyrenees/
+  ** importing directory /home/syt/photos/201005WePyrenees
+  importing IMG_8314.JPG
+  importing IMG_8274.JPG
+  importing IMG_8286.JPG
+  importing IMG_8308.JPG
+  importing IMG_8304.JPG
+
+.. Note::
+  The -F option means that folders should be mapped, hence my photos will be
+  linked to a Folder entity corresponding to the file-system folder.
+
+Let's take a look at the web ui:
+
+.. image:: ../../images/tutos-photowebsite_ui1.png
+
+Nothing different, I can't see the new folder... But remember our security model!
+By default, files are only accessible to authenticated users, and I'm looking at
+the site as anonymous, e.g. not authenticated. If I login, I can now see:
+
+.. image:: ../../images/tutos-photowebsite_ui2.png
+
+Yeah, it's there! You will notice that I can see some entities as well as
+folders and images the anonymous user can't. It just works **everywhere in the
+ui** since it's handled at the repository level, thanks to our security model.
+
+Now if I click on the recently inserted folder, I can see
+
+.. image:: ../../images/tutos-photowebsite_ui3.png
+
+Great! There is even my pictures in the folder. I can know give to this folder a
+nicer name (provided I don't intend to import from it anymore, else already
+imported photos will be reimported), change permissions, title for some pictures,
+etc... Having a good content is much more difficult than having a good web site
+;)
+
+
+Conclusion
+~~~~~~~~~~
+
+We started to see here an advanced feature of our repository: the ability
+to store some parts of our data-model into a custom storage, outside the
+database. There is currently only the :class:`BytesFileSystemStorage` available,
+but you can expect to see more coming in a near future (or write your own!).
+
+Also, we can know start to feed our web-site with some nice pictures!
+The site isn't perfect (far from it actually) but it's usable, and we can
+now start using it and improve it on the way. The Incremental Cubic Way :)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/tutorials/advanced/part04_ui-base.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,361 @@
+Let's make it more user friendly
+================================
+
+
+Step 1: let's improve site's usability for our visitors
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The first thing I've noticed is that people to whom I send links to photos with
+some login/password authentication get lost, because they don't grasp they have
+to login by clicking on the 'authenticate' link. That's much probably because
+they only get a 404 when trying to access an unauthorized folder, and the site
+doesn't make clear that 1. you're not authenticated, 2. you could get more
+content by authenticating yourself.
+
+So, to improve this situation, I decided that I should:
+
+* make a login box appears for anonymous, so they see at a first glance a place
+  to put the login / password information I provided
+
+* customize the 404 page, proposing to login to anonymous.
+
+Here is the code, samples from my cube's `views.py` file:
+
+.. sourcecode:: python
+
+    from cubicweb.predicates import is_instance
+    from cubicweb.web import component
+    from cubicweb.web.views import error
+    from cubicweb.predicates import anonymous_user
+
+    class FourOhFour(error.FourOhFour):
+	__select__ = error.FourOhFour.__select__ & anonymous_user()
+
+	def call(self):
+	    self.w(u"<h1>%s</h1>" % self._cw._('this resource does not exist'))
+	    self.w(u"<p>%s</p>" % self._cw._('have you tried to login?'))
+
+
+    class LoginBox(component.CtxComponent):
+	"""display a box containing links to all startup views"""
+	__regid__ = 'sytweb.loginbox'
+	__select__ = component.CtxComponent.__select__ & anonymous_user()
+
+	title = _('Authenticate yourself')
+	order = 70
+
+	def render_body(self, w):
+	    cw = self._cw
+	    form = cw.vreg['forms'].select('logform', cw)
+	    form.render(w=w, table_class='', display_progress_div=False)
+
+The first class provides a new specific implementation of the default page you
+get on 404 error, to display an adapted message to anonymous user.
+
+.. Note::
+
+  Thanks to the selection mecanism, it will be selected for anoymous user,
+  since the additional `anonymous_user()` selector gives it a higher score than
+  the default, and not for authenticated since this selector will return 0 in
+  such case (hence the object won't be selectable)
+
+The second class defines a simple box, that will be displayed by default with
+boxes in the left column, thanks to default :class:`component.CtxComponent`
+selector. The HTML is written to match default CubicWeb boxes style. The code
+fetch the actual login form and render it.
+
+
+.. figure:: ../../images/tutos-photowebsite_login-box.png
+   :alt: login box / 404 screenshot
+
+   The login box and the custom 404 page for an anonymous visitor (translated in french)
+
+
+Step 2: providing a custom index page
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Another thing we can easily do to improve the site is... A nicer index page
+(e.g. the first page you get when accessing the web site)! The default one is
+quite intimidating (that should change in a near future). I will provide a much
+simpler index page that simply list available folders (e.g. photo albums in that
+site).
+
+.. sourcecode:: python
+
+    from cubicweb.web.views import startup
+
+    class IndexView(startup.IndexView):
+	def call(self, **kwargs):
+	    self.w(u'<div>\n')
+	    if self._cw.cnx.anonymous_connection:
+		self.w(u'<h4>%s</h4>\n' % self._cw._('Public Albums'))
+	    else:
+		self.w(u'<h4>%s</h4>\n' % self._cw._('Albums for %s') % self._cw.user.login)
+	    self._cw.vreg['views'].select('tree', self._cw).render(w=self.w)
+	    self.w(u'</div>\n')
+
+    def registration_callback(vreg):
+	vreg.register_all(globals().values(), __name__, (IndexView,))
+	vreg.register_and_replace(IndexView, startup.IndexView)
+
+As you can see, we override the default index view found in
+`cubicweb.web.views.startup`, geting back nothing but its identifier and selector
+since we override the top level view's `call` method.
+
+.. Note::
+
+  in that case, we want our index view to **replace** the existing one. To do so
+  we've to implements the `registration_callback` function, in which we tell to
+  register everything in the module *but* our IndexView, then we register it
+  instead of the former index view.
+
+Also, we added a title that tries to make it more evident that the visitor is
+authenticated, or not. Hopefuly people will get it now!
+
+
+.. figure:: ../../images/tutos-photowebsite_index-before.png
+   :alt: default index page screenshot
+
+   The default index page
+
+.. figure:: ../../images/tutos-photowebsite_index-after.png
+   :alt: new index page screenshot
+
+   Our simpler, less intimidating, index page (still translated in french)
+
+
+Step 3: more navigation improvments
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are still a few problems I want to solve...
+
+* Images in a folder are displayed in a somewhat random order. I would like to
+  have them ordered by file's name (which will usually, inside a given folder,
+  also result ordering photo by their date and time)
+
+* When clicking a photo from an album view, you've to get back to the gallery
+  view to go to the next photo. This is pretty annoying...
+
+* Also, when viewing an image, there is no clue about the folder to which this
+  image belongs to.
+
+I will first try to explain the ordering problem. By default, when accessing
+related entities by using the ORM's API, you should get them ordered according to
+the target's class `cw_fetch_order`. If we take a look at the file cube'schema,
+we can see:
+
+.. sourcecode:: python
+
+    class File(AnyEntity):
+	"""customized class for File entities"""
+	__regid__ = 'File'
+	fetch_attrs, cw_fetch_order = fetch_config(['data_name', 'title'])
+
+
+By default, `fetch_config` will return a `cw_fetch_order` method that will order
+on the first attribute in the list. So, we could expect to get files ordered by
+their name. But we don't.  What's up doc ?
+
+The problem is that files are related to folder using the `filed_under` relation.
+And that relation is ambiguous, eg it can lead to `File` entities, but also to
+`Folder` entities. In such case, since both entity types doesn't share the
+attribute on which we want to sort, we'll get linked entities sorted on a common
+attribute (usually `modification_date`).
+
+To fix this, we've to help the ORM. We'll do this in the method from the `ITree`
+folder's adapter, used in the folder's primary view to display the folder's
+content. Here's the code, that I've put in our cube's `entities.py` file, since
+it's more logical stuff than view stuff:
+
+.. sourcecode:: python
+
+    from cubes.folder import entities as folder
+
+    class FolderITreeAdapter(folder.FolderITreeAdapter):
+
+	def different_type_children(self, entities=True):
+	    rql = self.entity.cw_related_rql(self.tree_relation,
+					     self.parent_role, ('File',))
+	    rset = self._cw.execute(rql, {'x': self.entity.eid})
+	    if entities:
+		return list(rset.entities())
+	    return rset
+
+    def registration_callback(vreg):
+	vreg.register_and_replace(FolderITreeAdapter, folder.FolderITreeAdapter)
+
+As you can see, we simple inherit from the adapter defined in the `folder` cube,
+then we override the `different_type_children` method to give a clue to the ORM's
+`cw_related_rql` method, that is responsible to generate the rql to get entities
+related to the folder by the `filed_under` relation (the value of the
+`tree_relation` attribute).  The clue is that we only want to consider the `File`
+target entity type. By doing this, we remove the ambiguity and get back a RQL
+query that correctly order files by their `data_name` attribute.
+
+
+.. Note::
+
+    * As seen earlier, we want to **replace** the folder's `ITree` adapter by our
+      implementation, hence the custom `registration_callback` method.
+
+
+Ouf. That one was tricky...
+
+Now the easier parts. Let's start by adding some links on the file's primary view
+to see the previous / next image in the same folder. CubicWeb's provide a
+component that do exactly that. To make it appears, one have to be adaptable to
+the `IPrevNext` interface. Here is the related code sample, extracted from our
+cube's `views.py` file:
+
+.. sourcecode:: python
+
+    from cubicweb.predicates import is_instance
+    from cubicweb.web.views import navigation
+
+
+    class FileIPrevNextAdapter(navigation.IPrevNextAdapter):
+	__select__ = is_instance('File')
+
+	def previous_entity(self):
+	    rset = self._cw.execute('File F ORDERBY FDN DESC LIMIT 1 WHERE '
+				    'X filed_under FOLDER, F filed_under FOLDER, '
+				    'F data_name FDN, X data_name > FDN, X eid %(x)s',
+				    {'x': self.entity.eid})
+	    if rset:
+		return rset.get_entity(0, 0)
+
+	def next_entity(self):
+	    rset = self._cw.execute('File F ORDERBY FDN ASC LIMIT 1 WHERE '
+				    'X filed_under FOLDER, F filed_under FOLDER, '
+				    'F data_name FDN, X data_name < FDN, X eid %(x)s',
+				    {'x': self.entity.eid})
+	    if rset:
+		return rset.get_entity(0, 0)
+
+
+The `IPrevNext` interface implemented by the adapter simply consist in the
+`previous_entity` / `next_entity` methods, that should respectivly return the
+previous / next entity or `None`. We make an RQL query to get files in the same
+folder, ordered similarly (eg by their `data_name` attribute). We set
+ascendant/descendant ordering and a strict comparison with current file's name
+(the "X" variable representing the current file).
+
+Notice that this query supposes we wont have two files of the same name in the
+same folder, else things may go wrong. Fixing this is out of the scope of this
+blog. And as I would like to have at some point a smarter, context sensitive
+previous/next entity, I'll probably never fix this query (though if I had to, I
+would probably choosing to add a constraint in the schema so that we can't add
+two files of the same name in a folder).
+
+One more thing: by default, the component will be displayed below the content
+zone (the one with the white background). You can change this in the site's
+properties through the ui, but you can also change the default value in the code
+by modifying the `context` attribute of the component:
+
+.. sourcecode:: python
+
+    navigation.NextPrevNavigationComponent.context = 'navcontentbottom'
+
+.. Note::
+
+   `context` may be one of 'navtop', 'navbottom', 'navcontenttop' or
+   'navcontentbottom'; the first two being outside the main content zone, the two
+   others inside it.
+
+.. figure:: ../../images/tutos-photowebsite_prevnext.png
+   :alt: screenshot of the previous/next entity component
+
+   The previous/next entity component, at the bottom of the main content zone.
+
+Now, the only remaining stuff in my todo list is to see the file's folder. I'll use
+the standard breadcrumb component to do so. Similarly as what we've seen before, this
+component is controled by the :class:`IBreadCrumbs` interface, so we'll have to provide a custom
+adapter for `File` entity, telling the a file's parent entity is its folder:
+
+.. sourcecode:: python
+
+    from cubicweb.web.views import ibreadcrumbs
+
+    class FileIBreadCrumbsAdapter(ibreadcrumbs.IBreadCrumbsAdapter):
+	__select__ = is_instance('File')
+
+	def parent_entity(self):
+	    if self.entity.filed_under:
+		return self.entity.filed_under[0]
+
+In that case, we simply use attribute notation provided by the ORM to get the
+folder in which the current file (e.g. `self.entity`) is located.
+
+.. Note::
+
+   The :class:`IBreadCrumbs` interface is a `breadcrumbs` method, but the default
+   :class:`IBreadCrumbsAdapter` provides a default implementation for it that will look
+   at the value returned by its `parent_entity` method. It also provides a
+   default implementation for this method for entities adapting to the `ITree`
+   interface, but as our `File` doesn't, we've to provide a custom adapter.
+
+.. figure:: ../../images/tutos-photowebsite_breadcrumbs.png
+   :alt: screenshot of the breadcrumb component
+
+   The breadcrumb component when on a file entity, now displaying parent folder.
+
+
+Step 4: preparing the release and migrating the instance
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Now that greatly enhanced our cube, it's time to release it to upgrade production site.
+I'll probably detail that process later, but I currently simply transfer the new code
+to the server running the web site.
+
+However, I've still today some step to respect to get things done properly...
+
+First, as I've added some translatable string, I've to run: ::
+
+  $ cubicweb-ctl i18ncube sytweb
+
+To update the cube's gettext catalogs (the '.po' files under the cube's `i18n`
+directory). Once the above command is executed, I'll then update translations.
+
+To see if everything is ok on my test instance, I do: ::
+
+  $ cubicweb-ctl i18ninstance sytweb
+  $ cubicweb-ctl start -D sytweb
+
+The first command compile i18n catalogs (e.g. generates '.mo' files) for my test
+instance. The second command start it in debug mode, so I can open my browser and
+navigate through the web site to see if everything is ok...
+
+.. Note::
+
+   In the 'cubicweb-ctl i18ncube' command, `sytweb` refers to the **cube**, while
+   in the two other, it refers to the **instance** (if you can't see the
+   difference, reread CubicWeb's concept chapter !).
+
+
+Once I've checked it's ok, I simply have to bump the version number in the
+`__pkginfo__` module to trigger a migration once I'll have updated the code on
+the production site. I can check then check the migration is also going fine, by
+first restoring a dump from the production site, then upgrading my test instance.
+
+To generate a dump from the production site: ::
+
+  $ cubicweb-ctl db-dump sytweb
+  pg_dump -Fc --username=syt --no-owner --file /home/syt/etc/cubicweb.d/sytweb/backup/tmpYIN0YI/system sytweb
+  -> backup file /home/syt/etc/cubicweb.d/sytweb/backup/sytweb-2010-07-13_10-22-40.tar.gz
+
+I can now get back the dump file ('sytweb-2010-07-13_10-22-40.tar.gz') to my test
+machine (using `scp` for instance) to restore it and start migration: ::
+
+  $ cubicweb-ctl db-restore sytweb sytweb-2010-07-13_10-22-40.tar.gz
+  $ cubicweb-ctl upgrade sytweb
+
+You'll have to answer some questions, as we've seen in `an earlier post`_.
+
+Now that everything is tested, I can transfer the new code to the production
+server, `apt-get upgrade` cubicweb and its dependencies, and eventually
+upgrade the production instance.
+
+
+.. _`several improvments`: http://www.cubicweb.org/blogentry/1179899
+.. _`3.8`: http://www.cubicweb.org/blogentry/917107
+.. _`first blog of this series`: http://www.cubicweb.org/blogentry/824642
+.. _`an earlier post`: http://www.cubicweb.org/867464
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/tutorials/advanced/part05_ui-advanced.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,374 @@
+Building my photos web site with |cubicweb| part V: let's make it even more user friendly
+=========================================================================================
+
+.. _uiprops:
+
+Step 1: tired of the default look?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OK... Now our site has its most desired features. But... I would like to make it look
+somewhat like *my* website. It is not www.cubicweb.org after all. Let's tackle this
+first!
+
+The first thing we can to is to change the logo. There are various way to achieve
+this. The easiest way is to put a :file:`logo.png` file into the cube's :file:`data`
+directory. As data files are looked at according to cubes order (CubicWeb
+resources coming last), that file will be selected instead of CubicWeb's one.
+
+.. Note::
+   As the location for static resources are cached, you'll have to restart
+   your instance for this to be taken into account.
+
+Though there are some cases where you don't want to use a :file:`logo.png` file.
+For instance if it's a JPEG file. You can still change the logo by defining in
+the cube's :file:`uiprops.py` file:
+
+.. sourcecode:: python
+
+   LOGO = data('logo.jpg')
+
+The uiprops machinery is used to define some static file resources,
+such as the logo, default Javascript / CSS files, as well as CSS
+properties (we'll see that later).
+
+.. Note::
+   This file is imported specifically by |cubicweb|, with a predefined name space,
+   containing for instance the `data` function, telling the file is somewhere
+   in a cube or CubicWeb's data directory.
+
+   One side effect of this is that it can't be imported as a regular python
+   module.
+
+The nice thing is that in debug mode, change to a :file:`uiprops.py` file are detected
+and then automatically reloaded.
+
+Now, as it's a photos web-site, I would like to have a photo of mine as background...
+After some trials I won't detail here, I've found a working recipe explained `here`_.
+All I've to do is to override some stuff of the default CubicWeb user interface to
+apply it as explained.
+
+The first thing to to get the ``<img/>`` tag as first element after the
+``<body>`` tag.  If you know a way to avoid this by simply specifying the image
+in the CSS, tell me!  The easiest way to do so is to override the
+:class:`HTMLPageHeader` view, since that's the one that is directly called once
+the ``<body>`` has been written. How did I find this?  By looking in the
+:mod:`cubiweb.web.views.basetemplates` module, since I know that global page
+layouts sits there. I could also have grep the "body" tag in
+:mod:`cubicweb.web.views`... Finding this was the hardest part. Now all I need is
+to customize it to write that ``img`` tag, as below:
+
+.. sourcecode:: python
+
+    class HTMLPageHeader(basetemplates.HTMLPageHeader):
+	# override this since it's the easier way to have our bg image
+	# as the first element following <body>
+	def call(self, **kwargs):
+            self.w(u'<img id="bg-image" src="%sbackground.jpg" alt="background image"/>'
+                   % self._cw.datadir_url)
+	    super(HTMLPageHeader, self).call(**kwargs)
+
+
+    def registration_callback(vreg):
+	vreg.register_all(globals().values(), __name__, (HTMLPageHeader))
+	vreg.register_and_replace(HTMLPageHeader, basetemplates.HTMLPageHeader)
+
+
+As you may have guessed, my background image is in a :file:`background.jpg` file
+in the cube's :file:`data` directory, but there are still some things to explain
+to newcomers here:
+
+* The :meth:`call` method is there the main access point of the view. It's called by
+  the view's :meth:`render` method. It is not the only access point for a view, but
+  this will be detailed later.
+
+* Calling `self.w` writes something to the output stream. Except for binary views
+  (which do not generate text), it *must* be passed an Unicode string.
+
+* The proper way to get a file in :file:`data` directory is to use the `datadir_url`
+  attribute of the incoming request (e.g. `self._cw`).
+
+I won't explain again the :func:`registration_callback` stuff, you should understand it
+now!  If not, go back to previous posts in the series :)
+
+Fine. Now all I've to do is to add a bit of CSS to get it to behave nicely (which
+is not the case at all for now). I'll put all this in a :file:`cubes.sytweb.css`
+file, stored as usual in our :file:`data` directory:
+
+.. sourcecode:: css
+
+
+    /* fixed full screen background image
+     * as explained on http://webdesign.about.com/od/css3/f/blfaqbgsize.htm
+     *
+     * syt update: set z-index=0 on the img instead of z-index=1 on div#page & co to
+     * avoid pb with the user actions menu
+     */
+    img#bg-image {
+	position: fixed;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	z-index: 0;
+    }
+
+    div#page, table#header, div#footer {
+	background: transparent;
+	position: relative;
+    }
+
+    /* add some space around the logo
+     */
+    img#logo {
+	padding: 5px 15px 0px 15px;
+    }
+
+    /* more dark font for metadata to have a chance to see them with the background
+     *  image
+     */
+    div.metadata {
+	color: black;
+    }
+
+You can see here stuff explained in the cited page, with only a slight modification
+explained in the comments, plus some additional rules to make things somewhat cleaner:
+
+* a bit of padding around the logo
+
+* darker metadata which appears by default below the content (the white frame in the page)
+
+To get this CSS file used everywhere in the site, I have to modify the :file:`uiprops.py` file
+introduced above:
+
+.. sourcecode:: python
+
+   STYLESHEETS = sheet['STYLESHEETS'] + [data('cubes.sytweb.css')]
+
+.. Note::
+   `sheet` is another predefined variable containing values defined by
+   already process `:file:`uiprops.py`` file, notably the CubicWeb's one.
+
+Here we simply want our CSS in addition to CubicWeb's base CSS files, so we
+redefine the `STYLESHEETS` variable to existing CSS (accessed through the `sheet`
+variable) with our one added. I could also have done:
+
+.. sourcecode:: python
+
+   sheet['STYLESHEETS'].append(data('cubes.sytweb.css'))
+
+But this is less interesting since we don't see the overriding mechanism...
+
+At this point, the site should start looking good, the background image being
+resized to fit the screen.
+
+.. image:: ../../images/tutos-photowebsite_background-image.png
+
+The final touch: let's customize CubicWeb's CSS to get less orange... By simply adding
+
+.. sourcecode:: python
+
+  contextualBoxTitleBg = incontextBoxTitleBg = '#AAAAAA'
+
+and reloading the page we've just seen, we know have a nice greyed box instead of
+the orange one:
+
+.. image:: ../../images/tutos-photowebsite_grey-box.png
+
+This is because CubicWeb's CSS include some variables which are
+expanded by values defined in uiprops file. In our case we controlled the
+properties of the CSS `background` property of boxes with CSS class
+`contextualBoxTitleBg` and `incontextBoxTitleBg`.
+
+
+Step 2: configuring boxes
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Boxes present to the user some ways to use the application. Let's first do a few
+user interface tweaks in our :file:`views.py` file:
+
+.. sourcecode:: python
+
+  from cubicweb.predicates import none_rset
+  from cubicweb.web.views import bookmark
+  from cubes.zone import views as zone
+  from cubes.tag import views as tag
+
+  # change bookmarks box selector so it's only displayed on startup views
+  bookmark.BookmarksBox.__select__ = bookmark.BookmarksBox.__select__ & none_rset()
+  # move zone box to the left instead of in the context frame and tweak its order
+  zone.ZoneBox.context = 'left'
+  zone.ZoneBox.order = 100
+  # move tags box to the left instead of in the context frame and tweak its order
+  tag.TagsBox.context = 'left'
+  tag.TagsBox.order = 102
+  # hide similarity box, not interested
+  tag.SimilarityBox.visible = False
+
+The idea is to move all boxes in the left column, so we get more space for the
+photos.  Now, serious things: I want a box similar to the tags box but to handle
+the `Person displayed_on File` relation. We can do this simply by adding a
+:class:`AjaxEditRelationCtxComponent` subclass to our views, as below:
+
+.. sourcecode:: python
+
+    from logilab.common.decorators import monkeypatch
+    from cubicweb import ValidationError
+    from cubicweb.web.views import uicfg, component
+    from cubicweb.web.views import basecontrollers
+
+    # hide displayed_on relation using uicfg since it will be displayed by the box below
+    uicfg.primaryview_section.tag_object_of(('*', 'displayed_on', '*'), 'hidden')
+
+    class PersonBox(component.AjaxEditRelationCtxComponent):
+	__regid__ = 'sytweb.displayed-on-box'
+	# box position
+	order = 101
+	context = 'left'
+	# define relation to be handled
+	rtype = 'displayed_on'
+	role = 'object'
+	target_etype = 'Person'
+	# messages
+	added_msg = _('person has been added')
+	removed_msg = _('person has been removed')
+	# bind to js_* methods of the json controller
+	fname_vocabulary = 'unrelated_persons'
+	fname_validate = 'link_to_person'
+	fname_remove = 'unlink_person'
+
+
+    @monkeypatch(basecontrollers.JSonController)
+    @basecontrollers.jsonize
+    def js_unrelated_persons(self, eid):
+	"""return tag unrelated to an entity"""
+	rql = "Any F + ' ' + S WHERE P surname S, P firstname F, X eid %(x)s, NOT P displayed_on X"
+	return [name for (name,) in self._cw.execute(rql, {'x' : eid})]
+
+
+    @monkeypatch(basecontrollers.JSonController)
+    def js_link_to_person(self, eid, people):
+	req = self._cw
+	for name in people:
+	    name = name.strip().title()
+	    if not name:
+		continue
+	    try:
+		firstname, surname = name.split(None, 1)
+	    except:
+		raise ValidationError(eid, {('displayed_on', 'object'): 'provide <first name> <surname>'})
+	    rset = req.execute('Person P WHERE '
+			       'P firstname %(firstname)s, P surname %(surname)s',
+			       locals())
+	    if rset:
+		person = rset.get_entity(0, 0)
+	    else:
+		person = req.create_entity('Person', firstname=firstname,
+						surname=surname)
+	    req.execute('SET P displayed_on X WHERE '
+			'P eid %(p)s, X eid %(x)s, NOT P displayed_on X',
+			{'p': person.eid, 'x' : eid})
+
+    @monkeypatch(basecontrollers.JSonController)
+    def js_unlink_person(self, eid, personeid):
+	self._cw.execute('DELETE P displayed_on X WHERE P eid %(p)s, X eid %(x)s',
+			 {'p': personeid, 'x': eid})
+
+
+You basically subclass to configure with some class attributes. The `fname_*`
+attributes give the name of methods that should be defined on the json control to
+make the AJAX part of the widget work: one to get the vocabulary, one to add a
+relation and another to delete a relation. These methods must start by a `js_`
+prefix and are added to the controller using the `@monkeypatch` decorator. In my
+case, the most complicated method is the one which adds a relation, since it
+tries to see if the person already exists, and else automatically create it,
+assuming the user entered "firstname surname".
+
+Let's see how it looks like on a file primary view:
+
+.. image:: ../../images/tutos-photowebsite_boxes.png
+
+Great, it's now as easy for me to link my pictures to people than to tag them.
+Also, visitors get a consistent display of these two pieces of information.
+
+.. Note::
+  The ui component system has been refactored in `CubicWeb 3.10`_, which also
+  introduced the :class:`AjaxEditRelationCtxComponent` class.
+
+
+Step 3: configuring facets
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The last feature we'll add today is facet configuration. If you access to the
+'/file' url, you'll see a set of 'facets' appearing in the left column. Facets
+provide an intuitive way to build a query incrementally, by proposing to the user
+various way to restrict the result set. For instance CubicWeb proposes a facet to
+restrict based on who created an entity; the tag cube proposes a facet to
+restrict based on tags; the zoe cube a facet to restrict based on geographical
+location, and so on. In that gist, I want to propose a facet to restrict based on
+the people displayed on the picture. To do so, there are various classes in the
+:mod:`cubicweb.web.facet` module which simply have to be configured using class
+attributes as we've done for the box. In our case, we'll define a subclass of
+:class:`RelationFacet`.
+
+.. Note::
+
+   Since that's ui stuff, we'll continue to add code below to our
+   :file:`views.py` file. Though we begin to have a lot of various code their, so
+   it's may be a good time to split our views module into submodules of a `view`
+   package. In our case of a simple application (glue) cube, we could start using
+   for instance the layout below: ::
+
+     views/__init__.py   # uicfg configuration, facets
+     views/layout.py     # header/footer/background stuff
+     views/components.py # boxes, adapters
+     views/pages.py      # index view, 404 view
+
+.. sourcecode:: python
+
+    from cubicweb.web import facet
+
+    class DisplayedOnFacet(facet.RelationFacet):
+	__regid__ = 'displayed_on-facet'
+	# relation to be displayed
+	rtype = 'displayed_on'
+	role = 'object'
+	# view to use to display persons
+	label_vid = 'combobox'
+
+Let's say we also want to filter according to the `visibility` attribute. This is
+even simpler as we just have to derive from the :class:`AttributeFacet` class:
+
+.. sourcecode:: python
+
+    class VisibilityFacet(facet.AttributeFacet):
+	__regid__ = 'visibility-facet'
+	rtype = 'visibility'
+
+Now if I search for some pictures on my site, I get the following facets available:
+
+.. image:: ../../images/tutos-photowebsite_facets.png
+
+.. Note::
+
+  By default a facet must be applyable to every entity in the result set and
+  provide at leat two elements of vocabulary to be displayed (for instance you
+  won't see the `created_by` facet if the same user has created all
+  entities). This may explain why you don't see yours...
+
+
+Conclusion
+~~~~~~~~~~
+
+We started to see the power behind the infrastructure provided by the
+framework, both on the pure ui (CSS, Javascript) side and on the Python side
+(high level generic classes for components, including boxes and facets). We now
+have, with a few lines of code, a full-featured web site with a personalized look.
+
+Of course we'll probably want more as time goes, but we can now
+concentrate on making good pictures, publishing albums and sharing them with
+friends...
+
+
+
+.. _`CubicWeb 3.10`: http://www.cubicweb.org/blogentry/1330518
+.. _`here`: http://webdesign.about.com/od/css3/f/blfaqbgsize.htm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/tutorials/base/blog-in-five-minutes.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,70 @@
+.. -*- coding: utf-8 -*-
+
+.. _TutosBaseBlogFiveMinutes:
+
+Get a blog running in five minutes!
+-----------------------------------
+
+For Debian or Ubuntu users, first install the following packages
+(:ref:`DebianInstallation`)::
+
+    cubicweb, cubicweb-dev, cubicweb-blog
+
+Windows or Mac OS X users must install |cubicweb| from source (see
+:ref:`SourceInstallation` and :ref:`WindowsInstallation`).
+
+Then create and initialize your instance::
+
+    cubicweb-ctl create blog myblog
+
+You'll be asked a few questions, and you can keep the default answer for most of
+them. The one question you'll have to think about is the database you'll want to
+use for that instance. For a quick test, if you don't have `postgresql` installed
+and configured (see :ref:`PostgresqlConfiguration`), it's highly recommended to
+choose `sqlite` when asked for which database driver to use, since it has a much
+simple setup (no database server needed).
+
+One the process is completed (including database initialisation), you can start
+your instance by using: ::
+
+    cubicweb-ctl start -D myblog
+
+The `-D` option activates the debugging mode. Removing it will launch the instance
+as a daemon in the background, and ``cubicweb-ctl stop myblog`` will stop
+it in that case. 
+
+
+About file system permissions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Unless you installed from sources, the above commands assume that you have root
+access to the :file:`/etc/` directory. In order to initialize your instance as a
+regular user, within your home directory, you can use the :envvar:`CW_MODE`
+environment variable: ::
+
+  export CW_MODE=user
+
+then create a :file:`~/etc/cubicweb.d` directory that will hold your instances.
+
+More information about how to configure your own environment is
+available in :ref:`ResourceMode`.
+
+
+Instance parameters
+~~~~~~~~~~~~~~~~~~~
+
+If you would like to change database parameters such as the database host or the
+user name used to connect to the database, edit the `sources` file located in the
+:file:`/etc/cubicweb.d/myblog` directory.
+
+Then relaunch the database creation::
+
+     cubicweb-ctl db-create myblog
+
+Other parameters, like web server or emails parameters, can be modified in the
+:file:`/etc/cubicweb.d/myblog/all-in-one.conf` file.
+
+You'll have to restart the instance after modification in one of those files.
+
+This is it. Your blog is functional and running. Visit http://localhost:8080 and enjoy it!
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/tutorials/base/conclusion.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,18 @@
+.. -*- coding: utf-8 -*-
+
+What's next?
+------------
+
+In this tutorial, we have seen that you can, right after the installation of
+|cubicweb|, build a web application in a few minutes by defining a data model as
+assembling cubes. You get a working application that you can then customize there
+and there while keeping something that works. This is important in agile
+development practices, you can right from the start of the project show things
+to customer and so take the right decision early in the process.
+
+The next steps will be to discover hooks, security, data sources, digging deeper
+into view writing and interface customisation... Yet a lot of fun stuff to
+discover! You will find more `tutorials and howtos`_ in the blog published on the
+CubicWeb.org website.
+
+.. _`tutorials and howtos`: http://www.cubicweb.org/view?rql=Any+X+ORDERBY+D+DESC+WHERE+X+is+BlogEntry%2C+T+tags+X%2C+T+name+IN+%28%22tutorial%22%2C+%22howto%22%29%2C+X+creation_date+D
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/tutorials/base/customizing-the-application.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,539 @@
+.. -*- coding: utf-8 -*-
+
+.. _TutosBaseCustomizingTheApplication:
+
+Customizing your application
+----------------------------
+
+So far so good. The point is that usually, you won't get enough by assembling
+cubes out-of-the-box. You will want to customize them, have a personal look and
+feel, add your own data model and so on. Or maybe start from scratch?
+
+So let's get a bit deeper and start coding our own cube. In our case, we want
+to customize the blog we created to add more features to it.
+
+
+Create your own cube
+~~~~~~~~~~~~~~~~~~~~
+
+First, notice that if you've installed |cubicweb| using Debian packages, you will
+need the additional ``cubicweb-dev`` package to get the commands necessary to
+|cubicweb| development. All `cubicweb-ctl` commands are described in details in
+:ref:`cubicweb-ctl`.
+
+Once your |cubicweb| development environment is set up, you can create a new
+cube::
+
+  cubicweb-ctl newcube myblog
+
+This will create in the cubes directory (:file:`/path/to/grshell/cubes` for source
+installation, :file:`/usr/share/cubicweb/cubes` for Debian packages installation)
+a directory named :file:`blog` reflecting the structure described in
+:ref:`cubelayout`.
+
+For packages installation, you can still create new cubes in your home directory
+using the following configuration. Let's say you want to develop your new cubes
+in `~src/cubes`, then set the following environment variables: ::
+
+  CW_CUBES_PATH=~/src/cubes
+
+and then create your new cube using: ::
+
+  cubicweb-ctl newcube --directory=~/src/cubes myblog
+
+.. Note::
+
+   We previously used `myblog` as the name of our *instance*. We're now creating
+   a *cube* with the same name. Both are different things. We'll now try to
+   specify when we talk about one or another, but keep in mind this difference.
+
+
+Cube metadata
+~~~~~~~~~~~~~
+
+A simple set of metadata about your cube are stored in the :file:`__pkginfo__.py`
+file. In our case, we want to extend the blog cube, so we have to tell that our
+cube depends on this cube, by modifying the ``__depends__`` dictionary in that
+file:
+
+.. sourcecode:: python
+
+   __depends__ =  {'cubicweb': '>= 3.10.7',
+                   'cubicweb-blog': None}
+
+where the ``None`` means we do not depends on a particular version of the cube.
+
+.. _TutosBaseCustomizingTheApplicationDataModel:
+
+Extending the data model
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+The data model or schema is the core of your |cubicweb| application.  It defines
+the type of content your application will handle. It is defined in the file
+:file:`schema.py` of the cube.
+
+
+Defining our model
+******************
+
+For the sake of example, let's say we want a new entity type named `Community`
+with a name, a description. A `Community` will hold several blogs.
+
+.. sourcecode:: python
+
+  from yams.buildobjs import EntityType, RelationDefinition, String, RichString
+
+  class Community(EntityType):
+      name = String(maxsize=50, required=True)
+      description = RichString()
+
+  class community_blog(RelationDefinition):
+      subject = 'Community'
+      object = 'Blog'
+      cardinality = '*?'
+      composite = 'subject'
+
+The first step is the import from the :mod:`yams` package necessary classes to build
+the schema.
+
+This file defines the following:
+
+* a `Community` has a title and a description as attributes
+
+  - the name is a string that is required and can't be longer than 50 characters
+
+  - the description is a string that is not constrained and may contains rich
+    content such as HTML or Restructured text.
+
+* a `Community` may be linked to a `Blog` using the `community_blog` relation
+
+  - ``*`` means a community may be linked to 0 to N blog, ``?`` means a blog may
+    be linked to 0 to 1 community. For completeness, remember that you can also
+    use ``+`` for 1 to N, and ``1`` for single, mandatory relation (e.g. one to one);
+
+  - this is a composite relation where `Community` (e.g. the subject of the
+    relation) is the composite. That means that if you delete a community, its
+    blog will be deleted as well.
+
+Of course, there are a lot of other data types and things such as constraints,
+permissions, etc, that may be defined in the schema, but those won't be covered
+in this tutorial.
+
+Notice that our schema refers to the `Blog` entity type which is not defined
+here.  But we know this type is available since we depend on the `blog` cube
+which is defining it.
+
+
+Applying changes to the model into our instance
+***********************************************
+
+Now the problem is that we created an instance using the `blog` cube, not our
+`myblog` cube, so if we don't do anything there is no way that we'll see anything
+changing in the instance.
+
+One easy way, as we've no really valuable data in the instance would be to trash and recreated it::
+
+  cubicweb-ctl stop myblog # or Ctrl-C in the terminal running the server in debug mode
+  cubicweb-ctl delete myblog
+  cubicweb-ctl create myblog
+  cubicweb-ctl start -D myblog
+
+Another way is to add our cube to the instance using the cubicweb-ctl shell
+facility. It's a python shell connected to the instance with some special
+commands available to manipulate it (the same as you'll have in migration
+scripts, which are not covered in this tutorial). In that case, we're interested
+in the `add_cube` command: ::
+
+  $ cubicweb-ctl stop myblog # or Ctrl-C in the terminal running the server in debug mode
+  $ cubicweb-ctl shell myblog
+  entering the migration python shell
+  just type migration commands or arbitrary python code and type ENTER to execute it
+  type "exit" or Ctrl-D to quit the shell and resume operation
+  >>> add_cube('myblog')
+  >>>
+  $ cubicweb-ctl start -D myblog
+
+The `add_cube` command is enough since it automatically updates our
+application to the cube's schema. There are plenty of other migration
+commands of a more finer grain. They are described in :ref:`migration`
+
+As explained, leave the shell by typing Ctrl-D. If you restart the instance and
+take another look at the schema, you'll see that changes to the data model have
+actually been applied (meaning database schema updates and all necessary stuff
+has been done).
+
+.. image:: ../../images/tutos-base_myblog-schema_en.png
+   :alt: the instance schema after adding our cube
+
+If you follow the 'info' link in the user pop-up menu, you'll also see that the
+instance is using blog and myblog cubes.
+
+.. image:: ../../images/tutos-base_myblog-siteinfo_en.png
+   :alt: the instance schema after adding our cube
+
+You can now add some communities, link them to blog, etc... You'll see that the
+framework provides default views for this entity type (we have not yet defined any
+view for it!), and also that the blog primary view will show the community it's
+linked to if any. All this thanks to the model driven interface provided by the
+framework.
+
+You'll then be able to redefine each of them according to your needs
+and preferences. We'll now see how to do such thing.
+
+.. _TutosBaseCustomizingTheApplicationCustomViews:
+
+Defining your views
+~~~~~~~~~~~~~~~~~~~
+
+|cubicweb| provides a lot of standard views in directory
+:file:`cubicweb/web/views/`. We already talked about 'primary' and 'list' views,
+which are views which apply to one ore more entities.
+
+A view is defined by a python class which includes:
+
+  - an identifier: all objects used to build the user interface in |cubicweb| are
+    recorded in a registry and this identifier will be used as a key in that
+    registry. There may be multiple views for the same identifier.
+
+  - a *selector*, which is a kind of filter telling how well a view suit to a
+    particular context. When looking for a particular view (e.g. given an
+    identifier), |cubicweb| computes for each available view with that identifier
+    a score which is returned by the selector. Then the view with the highest
+    score is used. The standard library of predicates is in
+    :mod:`cubicweb.predicates`.
+
+A view has a set of methods inherited from the :class:`cubicweb.view.View` class,
+though you usually don't derive directly from this class but from one of its more
+specific child class.
+
+Last but not least, |cubicweb| provides a set of default views accepting any kind
+of entities.
+
+Want a proof? Create a community as you've already done for other entity types
+through the index page, you'll then see something like that:
+
+.. image:: ../../images/tutos-base_myblog-community-default-primary_en.png
+   :alt: the default primary view for our community entity type
+
+
+If you notice the weird messages that appear in the page: those are messages
+generated for the new data model, which have no translation yet. To fix that,
+we'll have to use dedicated `cubicweb-ctl` commands:
+
+.. sourcecode: bash
+
+  cubicweb-ctl i18ncube myblog # build/update cube's message catalogs
+  # then add translation into .po file into the cube's i18n directory
+  cubicweb-ctl i18ninstance myblog # recompile instance's message catalogs
+  cubicweb-ctl restart -D myblog # instance has to be restarted to consider new catalogs
+
+You'll then be able to redefine each of them according to your needs and
+preferences. So let's see how to do such thing.
+
+Changing the layout of the application
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The layout is the general organization of the pages in the site. Views that generate
+the layout are sometimes referred to as 'templates'. They are implemented in the
+framework in the module :mod:`cubicweb.web.views.basetemplates`. By overriding
+classes in this module, you can customize whatever part you wish of the default
+layout.
+
+But notice that |cubicweb| provides many other ways to customize the
+interface, thanks to actions and components (which you can individually
+(de)activate, control their location, customize their look...) as well as
+"simple" CSS customization. You should first try to achieve your goal using such
+fine grained parametrization rather then overriding a whole template, which usually
+embeds customisation access points that you may loose in the process.
+
+But for the sake of example, let's say we want to change the generic page
+footer...  We can simply add to the module ``views`` of our cube,
+e.g. :file:`cubes/myblog/views.py`, the code below:
+
+.. sourcecode:: python
+
+  from cubicweb.web.views import basetemplates
+
+  class MyHTMLPageFooter(basetemplates.HTMLPageFooter):
+
+      def footer_content(self):
+	  self.w(u'This website has been created with <a href="http://cubicweb.org">CubicWeb</a>.')
+
+  def registration_callback(vreg):
+      vreg.register_all(globals().values(), __name__, (MyHTMLPageFooter,))
+      vreg.register_and_replace(MyHTMLPageFooter, basetemplates.HTMLPageFooter)
+
+
+* Our class inherits from the default page footer to ease getting things right,
+  but this is not mandatory.
+
+* When we want to write something to the output stream, we simply call `self.w`,
+  which *must be passed a unicode string*.
+
+* The latest function is the most exotic stuff. The point is that without it, you
+  would get an error at display time because the framework wouldn't be able to
+  choose which footer to use between :class:`HTMLPageFooter` and
+  :class:`MyHTMLPageFooter`, since both have the same selector, hence the same
+  score...  In this case, we want our footer to replace the default one, so we have
+  to define a :func:`registration_callback` function to control object
+  registration: the first instruction tells to register everything in the module
+  but the :class:`MyHTMLPageFooter` class, then the second to register it instead
+  of :class:`HTMLPageFooter`. Without this function, everything in the module is
+  registered blindly.
+
+.. Note::
+
+  When a view is modified while running in debug mode, it is not required to
+  restart the instance server. Save the Python file and reload the page in your
+  web browser to view the changes.
+
+We will now have this simple footer on every page of the site.
+
+
+Primary view customization
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The 'primary' view (i.e. any view with the identifier set to 'primary') is the one used to
+display all the information about a single entity. The standard primary view is one
+of the most sophisticated views of all. It has several customisation points, but
+its power comes with `uicfg`, allowing you to control it without having to
+subclass it.
+
+However this is a bit off-topic for this first tutorial. Let's say we simply want a
+custom primary view for my `Community` entity type, using directly the view
+interface without trying to benefit from the default implementation (you should
+do that though if you're rewriting reusable cubes; everything is described in more
+details in :ref:`primary_view`).
+
+
+So... Some code! That we'll put again in the module ``views`` of our cube.
+
+.. sourcecode:: python
+
+  from cubicweb.predicates import is_instance
+  from cubicweb.web.views import primary
+
+  class CommunityPrimaryView(primary.PrimaryView):
+      __select__ = is_instance('Community')
+
+      def cell_call(self, row, col):
+          entity = self.cw_rset.get_entity(row, col)
+          self.w(u'<h1>Welcome to the "%s" community</h1>' % entity.printable_value('name'))
+          if entity.description:
+              self.w(u'<p>%s</p>' % entity.printable_value('description'))
+
+What's going on here?
+
+* Our class inherits from the default primary view, here mainly to get the correct
+  view identifier, since we don't use any of its features.
+
+* We set on it a selector telling that it only applies when trying to display
+  some entity of the `Community` type. This is enough to get an higher score than
+  the default view for entities of this type.
+
+* View applying to entities usually have to define `cell_call` as entry point,
+  and are given `row` and `col` arguments tell to which entity in the result set
+  the view is applied. We can then get this entity from the result set
+  (`self.cw_rset`) by using the `get_entity` method.
+
+* To ease thing, we access our entity's attribute for display using its
+  printable_value method, which will handle formatting and escaping when
+  necessary. As you can see, you can also access attributes by their name on the
+  entity to get the raw value.
+
+
+You can now reload the page of the community we just created and see the changes.
+
+.. image:: ../../images/tutos-base_myblog-community-custom-primary_en.png
+   :alt: the custom primary view for our community entity type
+
+We've seen here a lot of thing you'll have to deal with to write views in
+|cubicweb|. The good news is that this is almost everything that is used to
+build higher level layers.
+
+.. Note::
+
+  As things get complicated and the volume of code in your cube increases, you can
+  of course still split your views module into a python package with subpackages.
+
+You can find more details about views and selectors in :ref:`Views`.
+
+
+Write entities to add logic in your data
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+|cubicweb| provides an ORM to easily programmaticaly manipulate
+entities (just like the one we have fetched earlier by calling
+`get_entity` on a result set). By default, entity
+types are instances of the :class:`AnyEntity` class, which holds a set of
+predefined methods as well as property automatically generated for
+attributes/relations of the type it represents.
+
+You can redefine each entity to provide additional methods or whatever you want
+to help you write your application. Customizing an entity requires that your
+entity:
+
+- inherits from :class:`cubicweb.entities.AnyEntity` or any subclass
+
+- defines a :attr:`__regid__` linked to the corresponding data type of your schema
+
+You may then want to add your own methods, override default implementation of some
+method, etc...
+
+.. sourcecode:: python
+
+    from cubicweb.entities import AnyEntity, fetch_config
+
+
+    class Community(AnyEntity):
+        """customized class for Community entities"""
+        __regid__ = 'Community'
+
+        fetch_attrs, cw_fetch_order = fetch_config(['name'])
+
+        def dc_title(self):
+            return self.name
+
+        def display_cw_logo(self):
+            return 'CubicWeb' in self.description
+
+In this example:
+
+* we used convenience :func:`fetch_config` function to tell which attributes
+  should be prefetched by the ORM when looking for some related entities of this
+  type, and how they should be ordered
+
+* we overrode the standard `dc_title` method, used in various place in the interface
+  to display the entity (though in this case the default implementation would
+  have had the same result)
+
+* we implemented here a method :meth:`display_cw_logo` which tests if the blog
+  entry title contains 'CW'.  It can then be used when you're writing code
+  involving 'Community' entities in your views, hooks, etc. For instance, you can
+  modify your previous views as follows:
+
+.. sourcecode:: python
+
+
+  class CommunityPrimaryView(primary.PrimaryView):
+      __select__ = is_instance('Community')
+
+      def cell_call(self, row, col):
+          entity = self.cw_rset.get_entity(row, col)
+          self.w(u'<h1>Welcome to the "%s" community</h1>' % entity.printable_value('name'))
+          if entity.display_cw_logo():
+              self.w(u'<img src="http://www.cubicweb.org/doc/en/_static/cubicweb.png"/>')
+          if entity.description:
+              self.w(u'<p>%s</p>' % entity.printable_value('description'))
+
+Then each community whose description contains 'CW' is shown with the |cubicweb|
+logo in front of it.
+
+.. Note::
+
+  As for view, you don't have to restart your instance when modifying some entity
+  classes while your server is running in debug mode, the code will be
+  automatically reloaded.
+
+
+Extending the application by using more cubes!
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+One of the goal of the |cubicweb| framework was to have truly reusable
+components. To do so, they must both behave nicely when plugged into the
+application and be easily customisable, from the data model to the user
+interface. And I think the result is pretty successful, thanks to system such as
+the selection mechanism and the choice to write views as python code which allows
+to build our page using true object oriented programming techniques, that no
+template language provides.
+
+
+A library of standard cubes is available from `CubicWeb Forge`_, to address a
+lot of common concerns such has manipulating people, files, things to do, etc. In
+our community blog case, we could be interested for instance in functionalities
+provided by the `comment` and `tag` cubes. The former provides threaded
+discussion functionalities, the latter a simple tag mechanism to classify content.
+Let's say we want to try those. We will first modify our cube's :file:`__pkginfo__.py`
+file:
+
+.. sourcecode:: python
+
+   __depends__ =  {'cubicweb': '>= 3.10.7',
+                   'cubicweb-blog': None,
+                   'cubicweb-comment': None,
+                   'cubicweb-tag': None}
+
+Now, we'll simply tell on which entity types we want to activate the 'comment'
+and 'tag' facilities by adding respectively the 'comments' and 'tags' relations on
+them in our schema (:file:`schema.py`).
+
+.. sourcecode:: python
+
+  class comments(RelationDefinition):
+      subject = 'Comment'
+      object = 'BlogEntry'
+      cardinality = '1*'
+      composite = 'object'
+
+  class tags(RelationDefinition):
+      subject = 'Tag'
+      object = ('Community', 'BlogEntry')
+
+
+So in the case above we activated comments on `BlogEntry` entities and tags on
+both `Community` and `BlogEntry`. Various views from both `comment` and `tag`
+cubes will then be automatically displayed when one of those relations is
+supported.
+
+Let's synchronize the data model as we've done earlier: ::
+
+
+  $ cubicweb-ctl stop myblog
+  $ cubicweb-ctl shell myblog
+  entering the migration python shell
+  just type migration commands or arbitrary python code and type ENTER to execute it
+  type "exit" or Ctrl-D to quit the shell and resume operation
+  >>> add_cubes(('comment', 'tag'))
+  >>>
+
+Then restart the instance. Let's look at a blog entry:
+
+.. image:: ../../images/tutos-base_myblog-blogentry-taggable-commentable-primary_en.png
+   :alt: the primary view for a blog entry with comments and tags activated
+
+As you can see, we now have a box displaying tags and a section proposing to add
+a comment and displaying existing one below the post. All this without changing
+anything in our views, thanks to the design of generic views provided by the
+framework. Though if we take a look at a community, we won't see the tags box!
+That's because by default this box try to locate itself in the left column within
+the white frame, and this column is handled by the primary view we
+hijacked. Let's change our view to make it more extensible, by keeping both our
+custom rendering but also extension points provided by the default
+implementation.
+
+
+.. sourcecode:: python
+
+  class CommunityPrimaryView(primary.PrimaryView):
+      __select__ = is_instance('Community')
+
+      def render_entity_title(self, entity):
+	  self.w(u'<h1>Welcome to the "%s" community</h1>' % entity.printable_value('name'))
+
+      def render_entity_attributes(self, entity):
+	  if entity.display_cw_logo():
+	      self.w(u'<img src="http://www.cubicweb.org/doc/en/_static/cubicweb.png"/>')
+	  if entity.description:
+	      self.w(u'<p>%s</p>' % entity.printable_value('description'))
+
+It appears now properly:
+
+.. image:: ../../images/tutos-base_myblog-community-taggable-primary_en.png
+   :alt: the custom primary view for a community entry with tags activated
+
+You can control part of the interface independently from each others, piece by
+piece. Really.
+
+
+
+.. _`CubicWeb Forge`: http://www.cubicweb.org/project
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/tutorials/base/discovering-the-ui.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,161 @@
+
+.. _TutosBaseDiscoveringTheUI:
+
+Discovering the web interface
+-----------------------------
+
+You can now access your web instance to create blogs and post messages
+by visiting the URL http://localhost:8080/.
+
+By default, anonymous access is disabled, so a login form will appear. If you
+asked to allow anonymous access when initializing the instance, click on the
+'login' link in the top right hand corner. To login, you need then use the admin
+account you specified at the time you initialized the database with
+``cubicweb-ctl create``.
+
+.. image:: ../../images/tutos-base_login-form_en.png
+   :alt: the login form
+
+
+Once authenticated, you can start playing with your instance. The default index
+page looks like the following:
+
+.. image:: ../../images/tutos-base_index_en.png
+   :alt: the index page
+
+
+Minimal configuration
+~~~~~~~~~~~~~~~~~~~~~
+
+Before creating entities, let's change that 'unset title' thing that appears
+here and there. This comes from a |cubicweb| system properties. To set it,
+click on the 'site configuration link' in the pop-up menu behind your login name
+in the upper left-hand corner
+
+.. image:: ../../images/tutos-base_user-menu_en.png
+   :alt: the user pop-up menu
+
+The site title is in the 'Ui' section. Simply set it to the desired value and
+click the 'validate' button.
+
+.. image:: ../../images/tutos-base_siteconfig_en.png
+   :alt: the site configuration form
+
+You should see a 'changes applied' message. You can now go back to the
+index page by clicking on the |cubicweb| logo in the upper left-hand corner.
+
+You will much likely still see 'unset title' at this point. This is because by
+default the index page is cached. Force a refresh of the page (by typing Ctrl-R
+in Firefox for instance) and you should now see the title you entered.
+
+
+Adding entities
+~~~~~~~~~~~~~~~
+
+The ``blog`` cube defines several entity types, among them ``Blog`` which is a
+container for ``BlogEntry`` (i.e. posts) on a particular topic. We can get a
+graphical view of the schema by clicking on the 'site schema' link in the user
+pop-up menu we've already seen:
+
+.. image:: ../../images/tutos-base_schema_en.png
+   :alt: graphical view of the schema (aka data-model)
+
+Nice isn't it? Notice that this, as most other stuff we'll see in this tutorial,
+is generated by the framework according to the model of the application. In our
+case, the model defined by the ``blog`` cube.
+
+Now let us create a few of these entities.
+
+
+Add a blog
+**********
+
+Clicking on the `[+]` at the left of the 'Blog' link on the index page will lead
+you to an HTML form to create a blog.
+
+.. image:: ../../images/tutos-base_blog-form_en.png
+   :alt: the blog creation form
+
+For instance, call this new blog 'Tech-blog' and type in 'everything about
+technology' as the description , then validate the form by clicking on
+'Validate'. You will be redirected to the `primary` view of the newly created blog.
+
+.. image:: ../../images/tutos-base_blog-primary_en.png
+   :alt: the blog primary view
+
+
+Add a blog post
+***************
+
+There are several ways to add a blog entry. The simplest is to click on the 'add
+blog entry' link in the actions box on viewing the blog you have just created.
+You will then see a form to create a post, with a 'blog entry of' field preset
+to the blog we're coming from. Enter a title, some content, click the 'validate'
+button and you're done. You will be redirected to the blog primary view, though you
+now see that it contains the blog post you've just created.
+
+.. image:: ../../images/tutos-base_blog-primary-after-post-creation_en.png
+   :alt: the blog primary view after creation of a post
+
+Notice there are some new boxes that appears in the left column.
+
+You can achieve the same thing by following the same path as we did for the blog
+creation, e.g. by clicking on the `[+]` at the left of the 'Blog entry' link on
+the index page. The diffidence being that since there is no context information,
+the 'blog entry of' selector won't be preset to the blog.
+
+
+If you click on the 'modify' link of the action box, you are back to
+the form to edit the entity you just created, except that the form now
+has another section with a combo-box entitled 'add relation'. It
+provisos a generic way to edit relations which don't appears in the
+above form. Choose the relation you want to add and a second combo box
+appears where you can pick existing entities.  If there are too many
+of them, you will be offered to navigate to the target entity, that is
+go away from the form and go back to it later, once you've selected
+the entity you want to link with.
+
+.. image:: ../../images/tutos-base_form-generic-relations_en.png
+   :alt: the generic relations combo box
+
+This combo box can't appear until the entity is actually created. That's why you
+haven't seen it at creation time. You could also have hit 'Apply' instead of
+'validate' and it would have showed up.
+
+
+About ui auto-adaptation
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+One of the things that make |cubicweb| different of other frameworks is
+its automatic user interface that adapts itself according to the data being
+displayed. Let's see an example.
+
+If you go back to the home page an click on the 'Blog' link, you will be redirected
+to the primary view of the blog, the same we've seen earlier. Now, add another
+blog, go back to the index page, and click again on this link. You will see
+a very different view (namely the 'list' view).
+
+.. image:: ../../images/tutos-base_blogs-list_en.png
+   :alt: the list view when there are more than one blog to display
+
+This is because in the first case, the framework chose to use the 'primary'
+view since there was only one entity in the data to be displayed. Now that there
+are two entities, the 'list' view is more appropriate and hence is being used.
+
+There are various other places where |cubicweb| adapts to display data in the best
+way, the main being provided by the view *selection* mechanism that will be detailed
+later.
+
+
+Digging deeper
+~~~~~~~~~~~~~~
+
+By following principles explained below, you should now be able to
+create new users for your application, to configure with a finer
+grain, etc... You will notice that the index page lists a lot of types
+you don't know about. Most are built-in types provided by the framework
+to make the whole system work. You may ignore them in a first time and
+discover them as time goes.
+
+One thing that is worth playing with is the search box. It may be used in various
+way, from simple full text search to advanced queries using the :ref:`RQL` .
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/tutorials/base/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,48 @@
+.. -*- coding: utf-8 -*-
+
+.. _TutosBase:
+
+Building a simple blog with |cubicweb|
+======================================
+
+|cubicweb| is a semantic web application framework that favors reuse and
+object-oriented design.
+
+
+This tutorial is designed to help in your very first steps to start with
+|cubicweb|. We will tour through basic concepts such as:
+
+* getting an application running by using existing components
+* discovering the default user interface
+* basic extending and customizing the look and feel of that application
+
+More advanced concepts are covered in :ref:`TutosPhotoWebSite`.
+
+
+.. _TutosBaseVocab:
+
+Some vocabulary
+---------------
+
+|cubicweb| comes with a few words of vocabulary that you should know to
+understand what we're talking about. To follow this tutorial, you should at least
+know that:
+
+* a `cube` is a component that usually includes a model defining some data types
+  and a set of views to display them. A cube can be built by assembling other
+  cubes;
+
+* an `instance` is a specific installation of one or more cubes and includes
+  configuration files, a web server and a database.
+
+Reading :ref:`Concepts` for more vocabulary will be required at some point.
+
+Now, let's start the hot stuff!
+
+.. toctree::
+   :maxdepth: 2
+
+   blog-in-five-minutes
+   discovering-the-ui
+   customizing-the-application
+   conclusion
--- a/doc/tutorials/dataimport/data_import_tutorial.rst	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,646 +0,0 @@
-Importing relational data into a CubicWeb instance
-==================================================
-
-Introduction
-~~~~~~~~~~~~
-
-This tutorial explains how to import data from an external source (e.g. a collection of files) 
-into a CubicWeb cube instance.
-
-First, once we know the format of the data we wish to import, we devise a 
-*data model*, that is, a CubicWeb (Yams) schema which reflects the way the data
-is structured. This schema is implemented in the ``schema.py`` file.
-In this tutorial, we will describe such a schema for a particular data set, 
-the Diseasome data (see below).
-
-Once the schema is defined, we create a cube and an instance. 
-The cube is a specification of an application, whereas an instance 
-is the application per se. 
-
-Once the schema is defined and the instance is created, the import can be performed, via
-the following steps:
-
-1. Build a custom parser for the data to be imported. Thus, one obtains a Python
-   memory representation of the data.
-
-2. Map the parsed data to the data model defined in ``schema.py``.
-
-3. Perform the actual import of the data. This comes down to "populating"
-   the data model with the memory representation obtained at 1, according to
-   the mapping defined at 2.
-
-This tutorial illustrates all the above steps in the context of relational data
-stored in the RDF format.
-
-More specifically, we describe the import of Diseasome_ RDF/OWL data.
-
-.. _Diseasome: http://datahub.io/dataset/fu-berlin-diseasome
-
-Building a data model
-~~~~~~~~~~~~~~~~~~~~~
-
-The first thing to do when using CubicWeb for creating an application from scratch
-is to devise a *data model*, that is, a relational representation of the problem to be
-modeled or of the structure of the data to be imported. 
-
-In such a schema, we define
-an entity type (``EntityType`` objects) for each type of entity to import. Each such type
-has several attributes. If the attributes are of known CubicWeb (Yams) types, viz. numbers,
-strings or characters, then they are defined as attributes, as e.g. ``attribute = Int()``
-for an attribute named ``attribute`` which is an integer. 
-
-Each such type also has a set of
-relations, which are defined like the attributes, except that they represent, in fact,
-relations between the entities of the type under discussion and the objects of a type which
-is specified in the relation definition. 
-
-For example, for the Diseasome data, we have two types of entities, genes and diseases.
-Thus, we create two classes which inherit from ``EntityType``::
-
-    class Disease(EntityType):
-        # Corresponds to http://www.w3.org/2000/01/rdf-schema#label
-        label = String(maxsize=512, fulltextindexed=True)
-        ...
-
-        #Corresponds to http://www4.wiwiss.fu-berlin.de/diseasome/resource/diseasome/associatedGene
-        associated_genes = SubjectRelation('Gene', cardinality='**')
-        ...
-
-        #Corresponds to 'http://www4.wiwiss.fu-berlin.de/diseasome/resource/diseasome/chromosomalLocation'
-        chromosomal_location = SubjectRelation('ExternalUri', cardinality='?*', inlined=True)
-
-
-    class Gene(EntityType):
-        ...
-
-In this schema, there are attributes whose values are numbers or strings. Thus, they are 
-defined by using the CubicWeb / Yams primitive types, e.g., ``label = String(maxsize=12)``. 
-These types can have several constraints or attributes, such as ``maxsize``. 
-There are also relations, either between the entity types themselves, or between them
-and a CubicWeb type, ``ExternalUri``. The latter defines a class of URI objects in 
-CubicWeb. For instance, the ``chromosomal_location`` attribute is a relation between 
-a ``Disease`` entity and an ``ExternalUri`` entity. The relation is marked by the CubicWeb /
-Yams ``SubjectRelation`` method. The latter can have several optional keyword arguments, such as
-``cardinality`` which specifies the number of subjects and objects related by the relation type 
-specified. For example, the ``'?*'`` cardinality in the ``chromosomal_relation`` relation type says
-that zero or more ``Disease`` entities are related to zero or one ``ExternalUri`` entities.
-In other words, a ``Disease`` entity is related to at most one ``ExternalUri`` entity via the
-``chromosomal_location`` relation type, and that we can have zero or more ``Disease`` entities in the
-data base. 
-For a relation between the entity types themselves, the ``associated_genes`` between a ``Disease``
-entity and a ``Gene`` entity is defined, so that any number of ``Gene`` entities can be associated
-to a ``Disease``, and there can be any number of ``Disease`` s if a ``Gene`` exists.
-
-Of course, before being able to use the CubicWeb / Yams built-in objects, we need to import them::
-
-    
-    from yams.buildobjs import EntityType, SubjectRelation, String, Int
-    from cubicweb.schemas.base import ExternalUri
-
-Building a custom data parser
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The data we wish to import is structured in the RDF format,
-as a text file containing a set of lines. 
-On each line, there are three fields. 
-The first two fields are URIs ("Universal Resource Identifiers"). 
-The third field is either an URI or a string. Each field bares a particular meaning:
-
-- the leftmost field is an URI that holds the entity to be imported. 
-  Note that the entities defined in the data model (i.e., in ``schema.py``) should 
-  correspond to the entities whose URIs are specified in the import file.
-
-- the middle field is an URI that holds a relation whose subject is the  entity 
-  defined by the leftmost field. Note that this should also correspond
-  to the definitions in the data model.
-
-- the rightmost field is either an URI or a string. When this field is an URI, 
-  it gives the object of the relation defined by the middle field.
-  When the rightmost field is a string, the middle field is interpreted as an attribute
-  of the subject (introduced by the leftmost field) and the rightmost field is
-  interpreted as the value of the attribute.
-
-Note however that some attributes (i.e. relations whose objects are strings) 
-have their objects defined as strings followed by ``^^`` and by another URI;
-we ignore this part.
-
-Let us show some examples:
-
-- of line holding an attribute definition:
-  ``<http://www4.wiwiss.fu-berlin.de/diseasome/resource/genes/CYP17A1> 
-  <http://www.w3.org/2000/01/rdf-schema#label> "CYP17A1" .``
-  The line contains the definition of the ``label`` attribute of an
-  entity of type ``gene``. The value of ``label`` is '``CYP17A1``'.
-
-- of line holding a relation definition:
-  ``<http://www4.wiwiss.fu-berlin.de/diseasome/resource/diseases/1> 
-  <http://www4.wiwiss.fu-berlin.de/diseasome/resource/diseasome/associatedGene> 
-  <http://www4.wiwiss.fu-berlin.de/diseasome/resource/genes/HADH2> .``
-  The line contains the definition of the ``associatedGene`` relation between
-  a ``disease`` subject entity identified by ``1`` and a ``gene`` object 
-  entity defined by ``HADH2``.
-
-Thus, for parsing the data, we can (:note: see the ``diseasome_parser`` module):
-
-1. define a couple of regular expressions for parsing the two kinds of lines, 
-   ``RE_ATTS`` for parsing the attribute definitions, and ``RE_RELS`` for parsing
-   the relation definitions.
-
-2. define a function that iterates through the lines of the file and retrieves
-   (``yield`` s) a (subject, relation, object) tuple for each line.
-   We called it ``_retrieve_structure`` in the ``diseasome_parser`` module.
-   The function needs the file name and the types for which information
-   should be retrieved.
-
-Alternatively, instead of hand-making the parser, one could use the RDF parser provided
-in the ``dataio`` cube.
-
-.. XXX To further study and detail the ``dataio`` cube usage.
-
-Once we get to have the (subject, relation, object) triples, we need to map them into
-the data model.
-
-
-Mapping the data to the schema
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In the case of diseasome data, we can just define two dictionaries for mapping
-the names of the relations as extracted by the parser, to the names of the relations
-as defined in the ``schema.py`` data model. In the ``diseasome_parser`` module 
-they are called ``MAPPING_ATTS`` and ``MAPPING_RELS``. 
-Given that the relation and attribute names are given in CamelCase in the original data,
-mappings are necessary if we follow the PEP08 when naming the attributes in the data model.
-For example, the RDF relation ``chromosomalLocation`` is mapped into the schema relation 
-``chromosomal_location``.
-
-Once these mappings have been defined, we just iterate over the (subject, relation, object)
-tuples provided by the parser and we extract the entities, with their attributes and relations.
-For each entity, we thus have a dictionary with two keys, ``attributes`` and ``relations``.
-The value associated to the ``attributes`` key is a dictionary containing (attribute: value) 
-pairs, where "value" is a string, plus the ``cwuri`` key / attribute holding the URI of 
-the entity itself.
-The value associated to the ``relations`` key is a dictionary containing (relation: value)
-pairs, where "value" is an URI.
-This is implemented in the ``entities_from_rdf`` interface function of the module 
-``diseasome_parser``. This function provides an iterator on the dictionaries containing
-the ``attributes`` and ``relations`` keys for all entities.
-
-However, this is a simple case. In real life, things can get much more complicated, and the 
-mapping can be far from trivial, especially when several data sources (which can follow 
-different formatting and even structuring conventions) must be mapped into the same data model.
-
-Importing the data
-~~~~~~~~~~~~~~~~~~
-
-The data import code should be placed in a Python module. Let us call it 
-``diseasome_import.py``. Then, this module should be called via
-``cubicweb-ctl``, as follows::
-
-    cubicweb-ctl shell diseasome_import.py -- <other arguments e.g. data file>
-
-In the import module, we should use a *store* for doing the import.
-A store is an object which provides three kinds of methods for
-importing data:
-
-- a method for importing the entities, along with the values
-  of their attributes.
-- a method for importing the relations between the entities.
-- a method for committing the imports to the database.
-
-In CubicWeb, we have four stores:
-
-1. ``ObjectStore`` base class for the stores in CubicWeb.
-   It only provides a skeleton for all other stores and
-   provides the means for creating the memory structures
-   (dictionaries) that hold the entities and the relations
-   between them.
-
-2. ``RQLObjectStore``: store which uses the RQL language for performing
-   database insertions and updates. It relies on all the CubicWeb hooks 
-   machinery, especially for dealing with security issues (database access
-   permissions).
-
-2. ``NoHookRQLObjectStore``: store which uses the RQL language for
-   performing database insertions and updates, but for which 
-   all hooks are deactivated. This implies that 
-   certain checks with respect to the CubicWeb / Yams schema 
-   (data model) are not performed. However, all SQL queries 
-   obtained from the RQL ones are executed in a sequential
-   manner, one query per inserted entity.
-
-4. ``SQLGenObjectStore``: store which uses the SQL language directly. 
-   It inserts entities either sequentially, by executing an SQL query 
-   for each entity, or directly by using one PostGRES ``COPY FROM`` 
-   query for a set of similarly structured entities. 
-
-For really massive imports (millions or billions of entities), there
-is a cube ``dataio`` which contains another store, called 
-``MassiveObjectStore``. This store is similar to ``SQLGenObjectStore``,
-except that anything related to CubicWeb is bypassed. That is, even the
-CubicWeb EID entity identifiers are not handled. This store is the fastest,
-but has a slightly different API from the other four stores mentioned above.
-Moreover, it has an important limitation, in that it doesn't insert inlined [#]_
-relations in the database. 
-
-.. [#] An inlined relation is a relation defined in the schema
-       with the keyword argument ``inlined=True``. Such a relation
-       is inserted in the database as an attribute of the entity
-       whose subject it is.
-
-In the following section we will see how to import data by using the stores
-in CubicWeb's ``dataimport`` module.
-
-Using the stores in ``dataimport``
-++++++++++++++++++++++++++++++++++
-
-``ObjectStore`` is seldom used in real life for importing data, since it is
-only the base store for the other stores and it doesn't perform an actual
-import of the data. Nevertheless, the other three stores, which import data,
-are based on ``ObjectStore`` and provide the same API.
-
-All three stores ``RQLObjectStore``, ``NoHookRQLObjectStore`` and
-``SQLGenObjectStore`` provide exactly the same API for importing data, that is
-entities and relations, in an SQL database. 
-
-Before using a store, one must import the ``dataimport`` module and then initialize 
-the store, with the current ``session`` as a parameter::
-
-    import cubicweb.dataimport as cwdi
-    ...
-
-    store = cwdi.RQLObjectStore(session)
-
-Each such store provides three methods for data import:
-
-#. ``create_entity(Etype, **attributes)``, which allows us to add
-   an entity of the Yams type ``Etype`` to the database. This entity's attributes
-   are specified in the ``attributes`` dictionary. The method returns the entity 
-   created in the database. For example, we add two entities,
-   a person, of ``Person`` type, and a location, of ``Location`` type::
-
-        person = store.create_entity('Person', name='Toto', age='18', height='190')
-
-        location = store.create_entity('Location', town='Paris', arrondissement='13')
-
-#. ``relate(subject_eid, r_type, object_eid)``, which allows us to add a relation
-   of the Yams type ``r_type`` to the database. The relation's subject is an entity
-   whose EID is ``subject_eid``; its object is another entity, whose EID is 
-   ``object_eid``.  For example [#]_::
-
-        store.relate(person.eid(), 'lives_in', location.eid(), **kwargs)
-
-   ``kwargs`` is only used by the ``SQLGenObjectStore``'s ``relate`` method and is here
-   to allow us to specify the type of the subject of the relation, when the relation is
-   defined as inlined in the schema. 
-
-.. [#] The ``eid`` method of an entity defined via ``create_entity`` returns
-       the entity identifier as assigned by CubicWeb when creating the entity.
-       This only works for entities defined via the stores in the CubicWeb's
-       ``dataimport`` module.
-
-    The keyword argument that is understood by ``SQLGenObjectStore`` is called 
-   ``subjtype`` and holds the type of the subject entity. For the example considered here,
-   this comes to having [#]_::
-
-        store.relate(person.eid(), 'lives_in', location.eid(), subjtype=person.cw_etype)
-
-   If ``subjtype`` is not specified, then the store tries to infer the type of the subject.
-   However, this doesn't always work, e.g. when there are several possible subject types
-   for a given relation type. 
-
-.. [#] The ``cw_etype`` attribute of an entity defined via ``create_entity`` holds
-       the type of the entity just created. This only works for entities defined via
-       the stores in the CubicWeb's ``dataimport`` module. In the example considered
-       here, ``person.cw_etype`` holds ``'Person'``.
-    
-   All the other stores but ``SQLGenObjectStore`` ignore the ``kwargs`` parameters.
-
-#. ``flush()``, which allows us to perform the actual commit into the database, along
-   with some cleanup operations. Ideally, this method should be called as often as 
-   possible, that is after each insertion in the database, so that database sessions
-   are kept as atomic as possible. In practice, we usually call this method twice: 
-   first, after all the entities have been created, second, after all relations have
-   been created. 
-
-   Note however that before each commit the database insertions
-   have to be consistent with the schema. Thus, if, for instance,
-   an entity has an attribute defined through a relation (viz.
-   a ``SubjectRelation``) with a ``"1"`` or ``"+"`` object 
-   cardinality, we have to create the entity under discussion,
-   the object entity of the relation under discussion, and the
-   relation itself, before committing the additions to the database.
-
-   The ``flush`` method is simply called as::
-
-        store.flush().
-
-
-Using the ``MassiveObjectStore`` in the ``dataio`` cube
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-This store, available in the ``dataio`` cube, allows us to
-fully dispense with the CubicWeb import mechanisms and hence
-to interact directly with the database server, via SQL queries.
-
-Moreover, these queries rely on PostGreSQL's ``COPY FROM`` instruction
-to create several entities in a single query. This brings tremendous 
-performance improvements with respect to the RQL-based data insertion
-procedures.
-
-However, the API of this store is slightly different from the API of
-the stores in CubicWeb's ``dataimport`` module.
-
-Before using the store, one has to import the ``dataio`` cube's 
-``dataimport`` module, then initialize the store by giving it the
-``session`` parameter::
-
-    from cubes.dataio import dataimport as mcwdi
-    ...
-
-    store = mcwdi.MassiveObjectStore(session)
-
-The ``MassiveObjectStore`` provides six methods for inserting data
-into the database:
-
-#. ``init_rtype_table(SubjEtype, r_type, ObjEtype)``, which specifies the
-   creation of the tables associated to the relation types in the database.
-   Each such table has three column, the type of the subject entity, the
-   type of the relation (that is, the name of the attribute in the subject
-   entity which is defined via the relation), and the type of the object
-   entity. For example::
-
-        store.init_rtype_table('Person', 'lives_in', 'Location')
-
-   Please note that these tables can be created before the entities, since
-   they only specify their types, not their unique identifiers.
-
-#. ``create_entity(Etype, **attributes)``, which allows us to add new entities,
-   whose attributes are given in the ``attributes`` dictionary. 
-   Please note however that, by default, this method does *not* return 
-   the created entity. The method is called, for example, as in::
-
-        store.create_entity('Person', name='Toto', age='18', height='190', 
-                            uri='http://link/to/person/toto_18_190')
-        store.create_entity('Location', town='Paris', arrondissement='13',
-                            uri='http://link/to/location/paris_13')
-   
-   In order to be able to link these entities via the relations when needed,
-   we must provide ourselves a means for uniquely identifying the entities.
-   In general, this is done via URIs, stored in attributes like ``uri`` or
-   ``cwuri``. The name of the attribute is irrelevant as long as its value is
-   unique for each entity.
-
-#. ``relate_by_iid(subject_iid, r_type, object_iid)`` allows us to actually 
-   relate the entities uniquely identified by ``subject_iid`` and 
-   ``object_iid`` via a relation of type ``r_type``. For example::
-
-        store.relate_by_iid('http://link/to/person/toto_18_190',
-                            'lives_in',
-                            'http://link/to/location/paris_13')
-
-   Please note that this method does *not* work for inlined relations!
-
-#. ``convert_relations(SubjEtype, r_type, ObjEtype, subj_iid_attribute,
-   obj_iid_attribute)``
-   allows us to actually insert
-   the relations in the database. At one call of this method, one inserts
-   all the relations of type ``rtype`` between entities of given types.
-   ``subj_iid_attribute`` and ``object_iid_attribute`` are the names
-   of the attributes which store the unique identifiers of the entities,
-   as assigned by the user. These names can be identical, as long as
-   their values are unique. For example, for inserting all relations
-   of type ``lives_in`` between ``People`` and ``Location`` entities,
-   we write::
-        
-        store.convert_relations('Person', 'lives_in', 'Location', 'uri', 'uri')
-
-#. ``flush()`` performs the actual commit in the database. It only needs 
-   to be called after ``create_entity`` and ``relate_by_iid`` calls. 
-   Please note that ``relate_by_iid`` does *not* perform insertions into
-   the database, hence calling ``flush()`` for it would have no effect.
-
-#. ``cleanup()`` performs database cleanups, by removing temporary tables.
-   It should only be called at the end of the import.
-
-
-
-.. XXX to add smth on the store's parameter initialization.
-
-
-
-Application to the Diseasome data
-+++++++++++++++++++++++++++++++++
-
-Import setup
-############
-
-We define an import function, ``diseasome_import``, which does basically four things:
-
-#. creates and initializes the store to be used, via a line such as::
-    
-        store = cwdi.SQLGenObjectStore(session)
-   
-   where ``cwdi`` is the imported ``cubicweb.dataimport`` or 
-   ``cubes.dataio.dataimport``.
-
-#. calls the diseasome parser, that is, the ``entities_from_rdf`` function in the 
-   ``diseasome_parser`` module and iterates on its result, in a line such as::
-        
-        for entity, relations in parser.entities_from_rdf(filename, ('gene', 'disease')):
-        
-   where ``parser`` is the imported ``diseasome_parser`` module, and ``filename`` is the 
-   name of the file containing the data (with its path), e.g. ``../data/diseasome_dump.nt``.
-
-#. creates the entities to be inserted in the database; for Diseasome, there are two 
-   kinds of entities:
-   
-   #. entities defined in the data model, viz. ``Gene`` and ``Disease`` in our case.
-   #. entities which are built in CubicWeb / Yams, viz. ``ExternalUri`` which define
-      URIs.
-   
-   As we are working with RDF data, each entity is defined through a series of URIs. Hence,
-   each "relational attribute" [#]_ of an entity is defined via an URI, that is, in CubicWeb
-   terms, via an ``ExternalUri`` entity. The entities are created, in the loop presented above,
-   as such::
-        
-        ent = store.create_entity(etype, **entity)
-        
-   where ``etype`` is the appropriate entity type, either ``Gene`` or ``Disease``.
-
-.. [#] By "relational attribute" we denote an attribute (of an entity) which
-       is defined through a relation, e.g. the ``chromosomal_location`` attribute
-       of ``Disease`` entities, which is defined through a relation between a
-       ``Disease`` and an ``ExternalUri``.
-   
-   The ``ExternalUri`` entities are as many as URIs in the data file. For them, we define a unique
-   attribute, ``uri``, which holds the URI under discussion::
-        
-        extu = store.create_entity('ExternalUri', uri="http://path/of/the/uri")
-
-#. creates the relations between the entities. We have relations between:
-   
-   #. entities defined in the schema, e.g. between ``Disease`` and ``Gene``
-      entities, such as the ``associated_genes`` relation defined for 
-      ``Disease`` entities.
-   #. entities defined in the schema and ``ExternalUri`` entities, such as ``gene_id``.
-   
-   The way relations are added to the database depends on the store: 
-   
-   - for the stores in the CubicWeb ``dataimport`` module, we only use 
-     ``store.relate``, in 
-     another loop, on the relations (that is, a 
-     loop inside the preceding one, mentioned at step 2)::
-        
-        for rtype, rels in relations.iteritems():
-            ...
-            
-            store.relate(ent.eid(), rtype, extu.eid(), **kwargs)
-        
-     where ``kwargs`` is a dictionary designed to accommodate the need for specifying
-     the type of the subject entity of the relation, when the relation is inlined and
-     ``SQLGenObjectStore`` is used. For example::
-            
-            ...
-            store.relate(ent.eid(), 'chromosomal_location', extu.eid(), subjtype='Disease')
-   
-   - for the ``MassiveObjectStore`` in the ``dataio`` cube's ``dataimport`` module, 
-     the relations are created in three steps:
-     
-     #. first, a table is created for each relation type, as in::
-            
-            ...
-            store.init_rtype_table(ent.cw_etype, rtype, extu.cw_etype)
-            
-        which comes down to lines such as::
-            
-            store.init_rtype_table('Disease', 'associated_genes', 'Gene')
-            store.init_rtype_table('Gene', 'gene_id', 'ExternalUri')
-            
-     #. second, the URI of each entity will be used as its identifier, in the 
-        ``relate_by_iid`` method, such as::
-            
-            disease_uri = 'http://www4.wiwiss.fu-berlin.de/diseasome/resource/diseases/3'
-            gene_uri = '<http://www4.wiwiss.fu-berlin.de/diseasome/resource/genes/HSD3B2'
-            store.relate_by_iid(disease_uri, 'associated_genes', gene_uri)
-            
-     #. third, the relations for each relation type will be added to the database, 
-        via the ``convert_relations`` method, such as in::
-            
-            store.convert_relations('Disease', 'associated_genes', 'Gene', 'cwuri', 'cwuri')
-            
-        and::
-            
-            store.convert_relations('Gene', 'hgnc_id', 'ExternalUri', 'cwuri', 'uri')
-            
-        where ``cwuri`` and ``uri`` are the attributes which store the URIs of the entities
-        defined in the data model, and of the ``ExternalUri`` entities, respectively.
-
-#. flushes all relations and entities::
-    
-    store.flush()
-
-   which performs the actual commit of the inserted entities and relations in the database.
-
-If the ``MassiveObjectStore`` is used, then a cleanup of temporary SQL tables should be performed
-at the end of the import::
-
-    store.cleanup()
-
-Timing benchmarks
-#################
-
-In order to time the import script, we just decorate the import function with the ``timed``
-decorator::
-    
-    from logilab.common.decorators import timed
-    ...
-    
-    @timed
-    def diseasome_import(session, filename):
-        ...
-
-After running the import function as shown in the "Importing the data" section, we obtain two time measurements::
-
-    diseasome_import clock: ... / time: ...
-
-Here, the meanings of these measurements are [#]_:
-
-- ``clock`` is the time spent by CubicWeb, on the server side (i.e. hooks and data pre- / post-processing on SQL 
-  queries),
-
-- ``time`` is the sum between ``clock`` and the time spent in PostGreSQL.
-
-.. [#] The meanings of the ``clock`` and ``time`` measurements, when using the ``@timed``
-       decorators, were taken from `a blog post on massive data import in CubicWeb`_.
-
-.. _a blog post on massive data import in CubicWeb: http://www.cubicweb.org/blogentry/2116712
-
-The import function is put in an import module, named ``diseasome_import`` here. The module is called
-directly from the CubicWeb shell, as follows::
-
-    cubicweb-ctl shell diseasome_instance diseasome_import.py \
-    -- -df diseasome_import_file.nt -st StoreName
-
-The module accepts two arguments:
-
-- the data file, introduced by ``-df [--datafile]``, and
-- the store, introduced by ``-st [--store]``.
-
-The timings (in seconds) for different stores are given in the following table, for 
-importing 4213 ``Disease`` entities and 3919 ``Gene`` entities with the import module
-just described:
-
-+--------------------------+------------------------+--------------------------------+------------+
-| Store                    | CubicWeb time (clock)  | PostGreSQL time (time - clock) | Total time |
-+==========================+========================+================================+============+
-| ``RQLObjectStore``       | 225.98                 | 62.05                          | 288.03     |
-+--------------------------+------------------------+--------------------------------+------------+
-| ``NoHookRQLObjectStore`` | 62.73                  | 51.38                          | 114.11     |
-+--------------------------+------------------------+--------------------------------+------------+
-| ``SQLGenObjectStore``    | 20.41                  | 11.03                          | 31.44      |
-+--------------------------+------------------------+--------------------------------+------------+
-| ``MassiveObjectStore``   | 4.84                   | 6.93                           | 11.77      |
-+--------------------------+------------------------+--------------------------------+------------+
-
-
-Conclusions
-~~~~~~~~~~~
-
-In this tutorial we have seen how to import data in a CubicWeb application instance. We have first seen how to
-create a schema, then how to create a parser of the data and a mapping of the data to the schema.
-Finally, we have seen four ways of importing data into CubicWeb.
-
-Three of those are integrated into CubicWeb, namely the ``RQLObjectStore``, ``NoHookRQLObjectStore`` and
-``SQLGenObjectStore`` stores, which have a common API:
-
-- ``RQLObjectStore`` is by far the slowest, especially its time spent on the 
-  CubicWeb side, and so it should be used only for small amounts of 
-  "sensitive" data (i.e. where security is a concern).
-
-- ``NoHookRQLObjectStore`` slashes by almost four the time spent on the CubicWeb side, 
-  but is also quite slow; on the PostGres side it is as slow as the previous store. 
-  It should be used for data where security is not a concern,
-  but consistency (with the data model) is.
-
-- ``SQLGenObjectStore`` slashes by three the time spent on the CubicWeb side and by five the time 
-  spent on the PostGreSQL side. It should be used for relatively great amounts of data, where
-  security and data consistency are not a concern. Compared to the previous store, it has the
-  disadvantage that, for inlined relations, we must specify their subjects' types.
-
-For really huge amounts of data there is a fourth store, ``MassiveObjectStore``, available
-from the ``dataio`` cube. It provides a blazing performance with respect to all other stores:
-it is almost 25 times faster than ``RQLObjectStore`` and almost three times faster than 
-``SQLGenObjectStore``. However, it has a few usage caveats that should be taken into account:
-
-#. it cannot insert relations defined as inlined in the schema,
-#. no security or consistency check is performed on the data,
-#. its API is slightly different from the other stores.
-
-Hence, this store should be used when security and data consistency are not a concern,
-and there are no inlined relations in the schema.
-
-
-
-
-
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/tutorials/dataimport/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,646 @@
+Importing relational data into a CubicWeb instance
+==================================================
+
+Introduction
+~~~~~~~~~~~~
+
+This tutorial explains how to import data from an external source (e.g. a collection of files) 
+into a CubicWeb cube instance.
+
+First, once we know the format of the data we wish to import, we devise a 
+*data model*, that is, a CubicWeb (Yams) schema which reflects the way the data
+is structured. This schema is implemented in the ``schema.py`` file.
+In this tutorial, we will describe such a schema for a particular data set, 
+the Diseasome data (see below).
+
+Once the schema is defined, we create a cube and an instance. 
+The cube is a specification of an application, whereas an instance 
+is the application per se. 
+
+Once the schema is defined and the instance is created, the import can be performed, via
+the following steps:
+
+1. Build a custom parser for the data to be imported. Thus, one obtains a Python
+   memory representation of the data.
+
+2. Map the parsed data to the data model defined in ``schema.py``.
+
+3. Perform the actual import of the data. This comes down to "populating"
+   the data model with the memory representation obtained at 1, according to
+   the mapping defined at 2.
+
+This tutorial illustrates all the above steps in the context of relational data
+stored in the RDF format.
+
+More specifically, we describe the import of Diseasome_ RDF/OWL data.
+
+.. _Diseasome: http://datahub.io/dataset/fu-berlin-diseasome
+
+Building a data model
+~~~~~~~~~~~~~~~~~~~~~
+
+The first thing to do when using CubicWeb for creating an application from scratch
+is to devise a *data model*, that is, a relational representation of the problem to be
+modeled or of the structure of the data to be imported. 
+
+In such a schema, we define
+an entity type (``EntityType`` objects) for each type of entity to import. Each such type
+has several attributes. If the attributes are of known CubicWeb (Yams) types, viz. numbers,
+strings or characters, then they are defined as attributes, as e.g. ``attribute = Int()``
+for an attribute named ``attribute`` which is an integer. 
+
+Each such type also has a set of
+relations, which are defined like the attributes, except that they represent, in fact,
+relations between the entities of the type under discussion and the objects of a type which
+is specified in the relation definition. 
+
+For example, for the Diseasome data, we have two types of entities, genes and diseases.
+Thus, we create two classes which inherit from ``EntityType``::
+
+    class Disease(EntityType):
+        # Corresponds to http://www.w3.org/2000/01/rdf-schema#label
+        label = String(maxsize=512, fulltextindexed=True)
+        ...
+
+        #Corresponds to http://www4.wiwiss.fu-berlin.de/diseasome/resource/diseasome/associatedGene
+        associated_genes = SubjectRelation('Gene', cardinality='**')
+        ...
+
+        #Corresponds to 'http://www4.wiwiss.fu-berlin.de/diseasome/resource/diseasome/chromosomalLocation'
+        chromosomal_location = SubjectRelation('ExternalUri', cardinality='?*', inlined=True)
+
+
+    class Gene(EntityType):
+        ...
+
+In this schema, there are attributes whose values are numbers or strings. Thus, they are 
+defined by using the CubicWeb / Yams primitive types, e.g., ``label = String(maxsize=12)``. 
+These types can have several constraints or attributes, such as ``maxsize``. 
+There are also relations, either between the entity types themselves, or between them
+and a CubicWeb type, ``ExternalUri``. The latter defines a class of URI objects in 
+CubicWeb. For instance, the ``chromosomal_location`` attribute is a relation between 
+a ``Disease`` entity and an ``ExternalUri`` entity. The relation is marked by the CubicWeb /
+Yams ``SubjectRelation`` method. The latter can have several optional keyword arguments, such as
+``cardinality`` which specifies the number of subjects and objects related by the relation type 
+specified. For example, the ``'?*'`` cardinality in the ``chromosomal_relation`` relation type says
+that zero or more ``Disease`` entities are related to zero or one ``ExternalUri`` entities.
+In other words, a ``Disease`` entity is related to at most one ``ExternalUri`` entity via the
+``chromosomal_location`` relation type, and that we can have zero or more ``Disease`` entities in the
+data base. 
+For a relation between the entity types themselves, the ``associated_genes`` between a ``Disease``
+entity and a ``Gene`` entity is defined, so that any number of ``Gene`` entities can be associated
+to a ``Disease``, and there can be any number of ``Disease`` s if a ``Gene`` exists.
+
+Of course, before being able to use the CubicWeb / Yams built-in objects, we need to import them::
+
+    
+    from yams.buildobjs import EntityType, SubjectRelation, String, Int
+    from cubicweb.schemas.base import ExternalUri
+
+Building a custom data parser
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The data we wish to import is structured in the RDF format,
+as a text file containing a set of lines. 
+On each line, there are three fields. 
+The first two fields are URIs ("Universal Resource Identifiers"). 
+The third field is either an URI or a string. Each field bares a particular meaning:
+
+- the leftmost field is an URI that holds the entity to be imported. 
+  Note that the entities defined in the data model (i.e., in ``schema.py``) should 
+  correspond to the entities whose URIs are specified in the import file.
+
+- the middle field is an URI that holds a relation whose subject is the  entity 
+  defined by the leftmost field. Note that this should also correspond
+  to the definitions in the data model.
+
+- the rightmost field is either an URI or a string. When this field is an URI, 
+  it gives the object of the relation defined by the middle field.
+  When the rightmost field is a string, the middle field is interpreted as an attribute
+  of the subject (introduced by the leftmost field) and the rightmost field is
+  interpreted as the value of the attribute.
+
+Note however that some attributes (i.e. relations whose objects are strings) 
+have their objects defined as strings followed by ``^^`` and by another URI;
+we ignore this part.
+
+Let us show some examples:
+
+- of line holding an attribute definition:
+  ``<http://www4.wiwiss.fu-berlin.de/diseasome/resource/genes/CYP17A1> 
+  <http://www.w3.org/2000/01/rdf-schema#label> "CYP17A1" .``
+  The line contains the definition of the ``label`` attribute of an
+  entity of type ``gene``. The value of ``label`` is '``CYP17A1``'.
+
+- of line holding a relation definition:
+  ``<http://www4.wiwiss.fu-berlin.de/diseasome/resource/diseases/1> 
+  <http://www4.wiwiss.fu-berlin.de/diseasome/resource/diseasome/associatedGene> 
+  <http://www4.wiwiss.fu-berlin.de/diseasome/resource/genes/HADH2> .``
+  The line contains the definition of the ``associatedGene`` relation between
+  a ``disease`` subject entity identified by ``1`` and a ``gene`` object 
+  entity defined by ``HADH2``.
+
+Thus, for parsing the data, we can (:note: see the ``diseasome_parser`` module):
+
+1. define a couple of regular expressions for parsing the two kinds of lines, 
+   ``RE_ATTS`` for parsing the attribute definitions, and ``RE_RELS`` for parsing
+   the relation definitions.
+
+2. define a function that iterates through the lines of the file and retrieves
+   (``yield`` s) a (subject, relation, object) tuple for each line.
+   We called it ``_retrieve_structure`` in the ``diseasome_parser`` module.
+   The function needs the file name and the types for which information
+   should be retrieved.
+
+Alternatively, instead of hand-making the parser, one could use the RDF parser provided
+in the ``dataio`` cube.
+
+.. XXX To further study and detail the ``dataio`` cube usage.
+
+Once we get to have the (subject, relation, object) triples, we need to map them into
+the data model.
+
+
+Mapping the data to the schema
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In the case of diseasome data, we can just define two dictionaries for mapping
+the names of the relations as extracted by the parser, to the names of the relations
+as defined in the ``schema.py`` data model. In the ``diseasome_parser`` module 
+they are called ``MAPPING_ATTS`` and ``MAPPING_RELS``. 
+Given that the relation and attribute names are given in CamelCase in the original data,
+mappings are necessary if we follow the PEP08 when naming the attributes in the data model.
+For example, the RDF relation ``chromosomalLocation`` is mapped into the schema relation 
+``chromosomal_location``.
+
+Once these mappings have been defined, we just iterate over the (subject, relation, object)
+tuples provided by the parser and we extract the entities, with their attributes and relations.
+For each entity, we thus have a dictionary with two keys, ``attributes`` and ``relations``.
+The value associated to the ``attributes`` key is a dictionary containing (attribute: value) 
+pairs, where "value" is a string, plus the ``cwuri`` key / attribute holding the URI of 
+the entity itself.
+The value associated to the ``relations`` key is a dictionary containing (relation: value)
+pairs, where "value" is an URI.
+This is implemented in the ``entities_from_rdf`` interface function of the module 
+``diseasome_parser``. This function provides an iterator on the dictionaries containing
+the ``attributes`` and ``relations`` keys for all entities.
+
+However, this is a simple case. In real life, things can get much more complicated, and the 
+mapping can be far from trivial, especially when several data sources (which can follow 
+different formatting and even structuring conventions) must be mapped into the same data model.
+
+Importing the data
+~~~~~~~~~~~~~~~~~~
+
+The data import code should be placed in a Python module. Let us call it 
+``diseasome_import.py``. Then, this module should be called via
+``cubicweb-ctl``, as follows::
+
+    cubicweb-ctl shell diseasome_import.py -- <other arguments e.g. data file>
+
+In the import module, we should use a *store* for doing the import.
+A store is an object which provides three kinds of methods for
+importing data:
+
+- a method for importing the entities, along with the values
+  of their attributes.
+- a method for importing the relations between the entities.
+- a method for committing the imports to the database.
+
+In CubicWeb, we have four stores:
+
+1. ``ObjectStore`` base class for the stores in CubicWeb.
+   It only provides a skeleton for all other stores and
+   provides the means for creating the memory structures
+   (dictionaries) that hold the entities and the relations
+   between them.
+
+2. ``RQLObjectStore``: store which uses the RQL language for performing
+   database insertions and updates. It relies on all the CubicWeb hooks 
+   machinery, especially for dealing with security issues (database access
+   permissions).
+
+2. ``NoHookRQLObjectStore``: store which uses the RQL language for
+   performing database insertions and updates, but for which 
+   all hooks are deactivated. This implies that 
+   certain checks with respect to the CubicWeb / Yams schema 
+   (data model) are not performed. However, all SQL queries 
+   obtained from the RQL ones are executed in a sequential
+   manner, one query per inserted entity.
+
+4. ``SQLGenObjectStore``: store which uses the SQL language directly. 
+   It inserts entities either sequentially, by executing an SQL query 
+   for each entity, or directly by using one PostGRES ``COPY FROM`` 
+   query for a set of similarly structured entities. 
+
+For really massive imports (millions or billions of entities), there
+is a cube ``dataio`` which contains another store, called 
+``MassiveObjectStore``. This store is similar to ``SQLGenObjectStore``,
+except that anything related to CubicWeb is bypassed. That is, even the
+CubicWeb EID entity identifiers are not handled. This store is the fastest,
+but has a slightly different API from the other four stores mentioned above.
+Moreover, it has an important limitation, in that it doesn't insert inlined [#]_
+relations in the database. 
+
+.. [#] An inlined relation is a relation defined in the schema
+       with the keyword argument ``inlined=True``. Such a relation
+       is inserted in the database as an attribute of the entity
+       whose subject it is.
+
+In the following section we will see how to import data by using the stores
+in CubicWeb's ``dataimport`` module.
+
+Using the stores in ``dataimport``
+++++++++++++++++++++++++++++++++++
+
+``ObjectStore`` is seldom used in real life for importing data, since it is
+only the base store for the other stores and it doesn't perform an actual
+import of the data. Nevertheless, the other three stores, which import data,
+are based on ``ObjectStore`` and provide the same API.
+
+All three stores ``RQLObjectStore``, ``NoHookRQLObjectStore`` and
+``SQLGenObjectStore`` provide exactly the same API for importing data, that is
+entities and relations, in an SQL database. 
+
+Before using a store, one must import the ``dataimport`` module and then initialize 
+the store, with the current ``session`` as a parameter::
+
+    import cubicweb.dataimport as cwdi
+    ...
+
+    store = cwdi.RQLObjectStore(session)
+
+Each such store provides three methods for data import:
+
+#. ``create_entity(Etype, **attributes)``, which allows us to add
+   an entity of the Yams type ``Etype`` to the database. This entity's attributes
+   are specified in the ``attributes`` dictionary. The method returns the entity 
+   created in the database. For example, we add two entities,
+   a person, of ``Person`` type, and a location, of ``Location`` type::
+
+        person = store.create_entity('Person', name='Toto', age='18', height='190')
+
+        location = store.create_entity('Location', town='Paris', arrondissement='13')
+
+#. ``relate(subject_eid, r_type, object_eid)``, which allows us to add a relation
+   of the Yams type ``r_type`` to the database. The relation's subject is an entity
+   whose EID is ``subject_eid``; its object is another entity, whose EID is 
+   ``object_eid``.  For example [#]_::
+
+        store.relate(person.eid(), 'lives_in', location.eid(), **kwargs)
+
+   ``kwargs`` is only used by the ``SQLGenObjectStore``'s ``relate`` method and is here
+   to allow us to specify the type of the subject of the relation, when the relation is
+   defined as inlined in the schema. 
+
+.. [#] The ``eid`` method of an entity defined via ``create_entity`` returns
+       the entity identifier as assigned by CubicWeb when creating the entity.
+       This only works for entities defined via the stores in the CubicWeb's
+       ``dataimport`` module.
+
+   The keyword argument that is understood by ``SQLGenObjectStore`` is called 
+   ``subjtype`` and holds the type of the subject entity. For the example considered here,
+   this comes to having [#]_::
+
+        store.relate(person.eid(), 'lives_in', location.eid(), subjtype=person.cw_etype)
+
+   If ``subjtype`` is not specified, then the store tries to infer the type of the subject.
+   However, this doesn't always work, e.g. when there are several possible subject types
+   for a given relation type. 
+
+.. [#] The ``cw_etype`` attribute of an entity defined via ``create_entity`` holds
+       the type of the entity just created. This only works for entities defined via
+       the stores in the CubicWeb's ``dataimport`` module. In the example considered
+       here, ``person.cw_etype`` holds ``'Person'``.
+    
+   All the other stores but ``SQLGenObjectStore`` ignore the ``kwargs`` parameters.
+
+#. ``flush()``, which allows us to perform the actual commit into the database, along
+   with some cleanup operations. Ideally, this method should be called as often as 
+   possible, that is after each insertion in the database, so that database sessions
+   are kept as atomic as possible. In practice, we usually call this method twice: 
+   first, after all the entities have been created, second, after all relations have
+   been created. 
+
+   Note however that before each commit the database insertions
+   have to be consistent with the schema. Thus, if, for instance,
+   an entity has an attribute defined through a relation (viz.
+   a ``SubjectRelation``) with a ``"1"`` or ``"+"`` object 
+   cardinality, we have to create the entity under discussion,
+   the object entity of the relation under discussion, and the
+   relation itself, before committing the additions to the database.
+
+   The ``flush`` method is simply called as::
+
+        store.flush().
+
+
+Using the ``MassiveObjectStore`` in the ``dataio`` cube
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+This store, available in the ``dataio`` cube, allows us to
+fully dispense with the CubicWeb import mechanisms and hence
+to interact directly with the database server, via SQL queries.
+
+Moreover, these queries rely on PostGreSQL's ``COPY FROM`` instruction
+to create several entities in a single query. This brings tremendous 
+performance improvements with respect to the RQL-based data insertion
+procedures.
+
+However, the API of this store is slightly different from the API of
+the stores in CubicWeb's ``dataimport`` module.
+
+Before using the store, one has to import the ``dataio`` cube's 
+``dataimport`` module, then initialize the store by giving it the
+``session`` parameter::
+
+    from cubes.dataio import dataimport as mcwdi
+    ...
+
+    store = mcwdi.MassiveObjectStore(session)
+
+The ``MassiveObjectStore`` provides six methods for inserting data
+into the database:
+
+#. ``init_rtype_table(SubjEtype, r_type, ObjEtype)``, which specifies the
+   creation of the tables associated to the relation types in the database.
+   Each such table has three column, the type of the subject entity, the
+   type of the relation (that is, the name of the attribute in the subject
+   entity which is defined via the relation), and the type of the object
+   entity. For example::
+
+        store.init_rtype_table('Person', 'lives_in', 'Location')
+
+   Please note that these tables can be created before the entities, since
+   they only specify their types, not their unique identifiers.
+
+#. ``create_entity(Etype, **attributes)``, which allows us to add new entities,
+   whose attributes are given in the ``attributes`` dictionary. 
+   Please note however that, by default, this method does *not* return 
+   the created entity. The method is called, for example, as in::
+
+        store.create_entity('Person', name='Toto', age='18', height='190', 
+                            uri='http://link/to/person/toto_18_190')
+        store.create_entity('Location', town='Paris', arrondissement='13',
+                            uri='http://link/to/location/paris_13')
+   
+   In order to be able to link these entities via the relations when needed,
+   we must provide ourselves a means for uniquely identifying the entities.
+   In general, this is done via URIs, stored in attributes like ``uri`` or
+   ``cwuri``. The name of the attribute is irrelevant as long as its value is
+   unique for each entity.
+
+#. ``relate_by_iid(subject_iid, r_type, object_iid)`` allows us to actually 
+   relate the entities uniquely identified by ``subject_iid`` and 
+   ``object_iid`` via a relation of type ``r_type``. For example::
+
+        store.relate_by_iid('http://link/to/person/toto_18_190',
+                            'lives_in',
+                            'http://link/to/location/paris_13')
+
+   Please note that this method does *not* work for inlined relations!
+
+#. ``convert_relations(SubjEtype, r_type, ObjEtype, subj_iid_attribute,
+   obj_iid_attribute)``
+   allows us to actually insert
+   the relations in the database. At one call of this method, one inserts
+   all the relations of type ``rtype`` between entities of given types.
+   ``subj_iid_attribute`` and ``object_iid_attribute`` are the names
+   of the attributes which store the unique identifiers of the entities,
+   as assigned by the user. These names can be identical, as long as
+   their values are unique. For example, for inserting all relations
+   of type ``lives_in`` between ``People`` and ``Location`` entities,
+   we write::
+        
+        store.convert_relations('Person', 'lives_in', 'Location', 'uri', 'uri')
+
+#. ``flush()`` performs the actual commit in the database. It only needs 
+   to be called after ``create_entity`` and ``relate_by_iid`` calls. 
+   Please note that ``relate_by_iid`` does *not* perform insertions into
+   the database, hence calling ``flush()`` for it would have no effect.
+
+#. ``cleanup()`` performs database cleanups, by removing temporary tables.
+   It should only be called at the end of the import.
+
+
+
+.. XXX to add smth on the store's parameter initialization.
+
+
+
+Application to the Diseasome data
++++++++++++++++++++++++++++++++++
+
+Import setup
+############
+
+We define an import function, ``diseasome_import``, which does basically four things:
+
+#. creates and initializes the store to be used, via a line such as::
+    
+        store = cwdi.SQLGenObjectStore(session)
+   
+   where ``cwdi`` is the imported ``cubicweb.dataimport`` or 
+   ``cubes.dataio.dataimport``.
+
+#. calls the diseasome parser, that is, the ``entities_from_rdf`` function in the 
+   ``diseasome_parser`` module and iterates on its result, in a line such as::
+        
+        for entity, relations in parser.entities_from_rdf(filename, ('gene', 'disease')):
+        
+   where ``parser`` is the imported ``diseasome_parser`` module, and ``filename`` is the 
+   name of the file containing the data (with its path), e.g. ``../data/diseasome_dump.nt``.
+
+#. creates the entities to be inserted in the database; for Diseasome, there are two 
+   kinds of entities:
+   
+   #. entities defined in the data model, viz. ``Gene`` and ``Disease`` in our case.
+   #. entities which are built in CubicWeb / Yams, viz. ``ExternalUri`` which define
+      URIs.
+   
+   As we are working with RDF data, each entity is defined through a series of URIs. Hence,
+   each "relational attribute" [#]_ of an entity is defined via an URI, that is, in CubicWeb
+   terms, via an ``ExternalUri`` entity. The entities are created, in the loop presented above,
+   as such::
+        
+        ent = store.create_entity(etype, **entity)
+        
+   where ``etype`` is the appropriate entity type, either ``Gene`` or ``Disease``.
+
+.. [#] By "relational attribute" we denote an attribute (of an entity) which
+       is defined through a relation, e.g. the ``chromosomal_location`` attribute
+       of ``Disease`` entities, which is defined through a relation between a
+       ``Disease`` and an ``ExternalUri``.
+   
+   The ``ExternalUri`` entities are as many as URIs in the data file. For them, we define a unique
+   attribute, ``uri``, which holds the URI under discussion::
+        
+        extu = store.create_entity('ExternalUri', uri="http://path/of/the/uri")
+
+#. creates the relations between the entities. We have relations between:
+   
+   #. entities defined in the schema, e.g. between ``Disease`` and ``Gene``
+      entities, such as the ``associated_genes`` relation defined for 
+      ``Disease`` entities.
+   #. entities defined in the schema and ``ExternalUri`` entities, such as ``gene_id``.
+   
+   The way relations are added to the database depends on the store: 
+   
+   - for the stores in the CubicWeb ``dataimport`` module, we only use 
+     ``store.relate``, in 
+     another loop, on the relations (that is, a 
+     loop inside the preceding one, mentioned at step 2)::
+        
+        for rtype, rels in relations.iteritems():
+            ...
+            
+            store.relate(ent.eid(), rtype, extu.eid(), **kwargs)
+        
+     where ``kwargs`` is a dictionary designed to accommodate the need for specifying
+     the type of the subject entity of the relation, when the relation is inlined and
+     ``SQLGenObjectStore`` is used. For example::
+            
+            ...
+            store.relate(ent.eid(), 'chromosomal_location', extu.eid(), subjtype='Disease')
+   
+   - for the ``MassiveObjectStore`` in the ``dataio`` cube's ``dataimport`` module, 
+     the relations are created in three steps:
+     
+     #. first, a table is created for each relation type, as in::
+            
+            ...
+            store.init_rtype_table(ent.cw_etype, rtype, extu.cw_etype)
+            
+        which comes down to lines such as::
+            
+            store.init_rtype_table('Disease', 'associated_genes', 'Gene')
+            store.init_rtype_table('Gene', 'gene_id', 'ExternalUri')
+            
+     #. second, the URI of each entity will be used as its identifier, in the 
+        ``relate_by_iid`` method, such as::
+            
+            disease_uri = 'http://www4.wiwiss.fu-berlin.de/diseasome/resource/diseases/3'
+            gene_uri = '<http://www4.wiwiss.fu-berlin.de/diseasome/resource/genes/HSD3B2'
+            store.relate_by_iid(disease_uri, 'associated_genes', gene_uri)
+            
+     #. third, the relations for each relation type will be added to the database, 
+        via the ``convert_relations`` method, such as in::
+            
+            store.convert_relations('Disease', 'associated_genes', 'Gene', 'cwuri', 'cwuri')
+            
+        and::
+            
+            store.convert_relations('Gene', 'hgnc_id', 'ExternalUri', 'cwuri', 'uri')
+            
+        where ``cwuri`` and ``uri`` are the attributes which store the URIs of the entities
+        defined in the data model, and of the ``ExternalUri`` entities, respectively.
+
+#. flushes all relations and entities::
+    
+    store.flush()
+
+   which performs the actual commit of the inserted entities and relations in the database.
+
+If the ``MassiveObjectStore`` is used, then a cleanup of temporary SQL tables should be performed
+at the end of the import::
+
+    store.cleanup()
+
+Timing benchmarks
+#################
+
+In order to time the import script, we just decorate the import function with the ``timed``
+decorator::
+    
+    from logilab.common.decorators import timed
+    ...
+    
+    @timed
+    def diseasome_import(session, filename):
+        ...
+
+After running the import function as shown in the "Importing the data" section, we obtain two time measurements::
+
+    diseasome_import clock: ... / time: ...
+
+Here, the meanings of these measurements are [#]_:
+
+- ``clock`` is the time spent by CubicWeb, on the server side (i.e. hooks and data pre- / post-processing on SQL 
+  queries),
+
+- ``time`` is the sum between ``clock`` and the time spent in PostGreSQL.
+
+.. [#] The meanings of the ``clock`` and ``time`` measurements, when using the ``@timed``
+       decorators, were taken from `a blog post on massive data import in CubicWeb`_.
+
+.. _a blog post on massive data import in CubicWeb: http://www.cubicweb.org/blogentry/2116712
+
+The import function is put in an import module, named ``diseasome_import`` here. The module is called
+directly from the CubicWeb shell, as follows::
+
+    cubicweb-ctl shell diseasome_instance diseasome_import.py \
+    -- -df diseasome_import_file.nt -st StoreName
+
+The module accepts two arguments:
+
+- the data file, introduced by ``-df [--datafile]``, and
+- the store, introduced by ``-st [--store]``.
+
+The timings (in seconds) for different stores are given in the following table, for 
+importing 4213 ``Disease`` entities and 3919 ``Gene`` entities with the import module
+just described:
+
++--------------------------+------------------------+--------------------------------+------------+
+| Store                    | CubicWeb time (clock)  | PostGreSQL time (time - clock) | Total time |
++==========================+========================+================================+============+
+| ``RQLObjectStore``       | 225.98                 | 62.05                          | 288.03     |
++--------------------------+------------------------+--------------------------------+------------+
+| ``NoHookRQLObjectStore`` | 62.73                  | 51.38                          | 114.11     |
++--------------------------+------------------------+--------------------------------+------------+
+| ``SQLGenObjectStore``    | 20.41                  | 11.03                          | 31.44      |
++--------------------------+------------------------+--------------------------------+------------+
+| ``MassiveObjectStore``   | 4.84                   | 6.93                           | 11.77      |
++--------------------------+------------------------+--------------------------------+------------+
+
+
+Conclusions
+~~~~~~~~~~~
+
+In this tutorial we have seen how to import data in a CubicWeb application instance. We have first seen how to
+create a schema, then how to create a parser of the data and a mapping of the data to the schema.
+Finally, we have seen four ways of importing data into CubicWeb.
+
+Three of those are integrated into CubicWeb, namely the ``RQLObjectStore``, ``NoHookRQLObjectStore`` and
+``SQLGenObjectStore`` stores, which have a common API:
+
+- ``RQLObjectStore`` is by far the slowest, especially its time spent on the 
+  CubicWeb side, and so it should be used only for small amounts of 
+  "sensitive" data (i.e. where security is a concern).
+
+- ``NoHookRQLObjectStore`` slashes by almost four the time spent on the CubicWeb side, 
+  but is also quite slow; on the PostGres side it is as slow as the previous store. 
+  It should be used for data where security is not a concern,
+  but consistency (with the data model) is.
+
+- ``SQLGenObjectStore`` slashes by three the time spent on the CubicWeb side and by five the time 
+  spent on the PostGreSQL side. It should be used for relatively great amounts of data, where
+  security and data consistency are not a concern. Compared to the previous store, it has the
+  disadvantage that, for inlined relations, we must specify their subjects' types.
+
+For really huge amounts of data there is a fourth store, ``MassiveObjectStore``, available
+from the ``dataio`` cube. It provides a blazing performance with respect to all other stores:
+it is almost 25 times faster than ``RQLObjectStore`` and almost three times faster than 
+``SQLGenObjectStore``. However, it has a few usage caveats that should be taken into account:
+
+#. it cannot insert relations defined as inlined in the schema,
+#. no security or consistency check is performed on the data,
+#. its API is slightly different from the other stores.
+
+Hence, this store should be used when security and data consistency are not a concern,
+and there are no inlined relations in the schema.
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/tutorials/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,18 @@
+Tutorials
+~~~~~~~~~
+
+We present a few tutorials of different levels. The blog building
+tutorial introduces one smoothly to the basic concepts.
+
+Then there is a photo gallery construction tutorial which highlights
+more advanced concepts such as unit tests, security settings,
+migration scripts.
+
+.. toctree::
+   :maxdepth: 1
+
+   base/index
+   advanced/index
+   tools/windmill.rst
+   textreports/index
+   dataimport/index
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/tutorials/textreports/index.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,13 @@
+.. -*- coding: utf-8 -*-
+
+Writing text reports with RestructuredText
+==========================================
+
+|cubicweb| offers several text formats for the RichString type used in schemas,
+including restructuredtext.
+
+Three additional restructuredtext roles are defined by |cubicweb|:
+
+.. autofunction:: cubicweb.ext.rest.eid_reference_role
+.. autofunction:: cubicweb.ext.rest.rql_role
+.. autofunction:: cubicweb.ext.rest.bookmark_role
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/tutorials/tools/windmill.rst	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,229 @@
+==========================
+Use Windmill with CubicWeb
+==========================
+
+Windmill_ implements cross browser testing, in-browser recording and playback,
+and functionality for fast accurate debugging and test environment integration.
+
+.. _Windmill: http://www.getwindmill.com/
+
+`Online features list <http://www.getwindmill.com/features>`_ is available.
+
+
+Installation
+============
+
+Windmill
+--------
+
+You have to install Windmill manually for now. If you're using Debian, there is
+no binary package (`yet <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=579109>`_).
+
+The simplest solution is to use a *setuptools/pip* command (for a clean
+environment, take a look to the `virtualenv
+<http://pypi.python.org/pypi/virtualenv>`_ project as well)::
+
+    $ pip install windmill
+    $ curl -O http://github.com/windmill/windmill/tarball/master
+
+However, the Windmill project doesn't release frequently. Our recommandation is
+to used the last snapshot of the Git repository::
+
+    $ git clone git://github.com/windmill/windmill.git HEAD
+    $ cd windmill
+    $ python setup.py develop
+
+Install instructions are `available <http://wiki.github.com/windmill/windmill/installing>`_.
+
+Be sure to have the windmill module in your PYTHONPATH afterwards::
+
+    $ python -c "import windmill"
+
+X dummy
+-------
+
+In order to reduce unecessary system load from your test machines, It's
+recommended to use X dummy server for testing the Unix web clients, you need a
+dummy video X driver (as xserver-xorg-video-dummy package in Debian) coupled
+with a light X server as `Xvfb <http://en.wikipedia.org/wiki/Xvfb>`_.
+
+    The dummy driver is a special driver available with the XFree86 DDX. To use
+    the dummy driver, simply substitue it for your normal card driver in the
+    Device section of your xorg.conf configuration file. For example, if you
+    normally uses an ati driver, then you will have a Device section with
+    Driver "ati" to let the X server know that you want it to load and use the
+    ati driver; however, for these conformance tests, you would change that
+    line to Driver "dummy" and remove any other ati specific options from the
+    Device section.
+
+    *From: http://www.x.org/wiki/XorgTesting*
+
+Then, you can run the X server with the following command ::
+
+    $ /usr/bin/X11/Xvfb :1 -ac -screen 0 1280x1024x8 -fbdir /tmp
+
+
+Windmill usage
+==============
+
+Record your use case
+--------------------
+
+- start your instance manually
+- start Windmill_ with url site as last argument (read Usage_ or use *'-h'*
+  option to find required command line arguments)
+- use the record button
+- click on save to obtain python code of your use case
+- copy the content to a new file in a *windmill* directory
+
+.. _Usage: http://wiki.github.com/windmill/windmill/running-tests
+
+If you are using firefox as client, consider the "firebug" option.
+
+If you have a running instance, you can refine the test by the *loadtest* windmill option::
+
+    $ windmill -m firebug loadtest=<test_file.py> <instance url>
+
+Or use the internal windmill shell to explore available commands::
+
+    $ windmill -m firebug shell <instance url>
+
+And enter python commands:
+
+.. sourcecode:: python
+
+    >>> load_test(<your test file>)
+    >>> run_test(<your test file>)
+
+
+
+Integrate Windmill tests into CubicWeb
+======================================
+
+Set environment
+---------------
+
+You have to create a new unit test file and a `windmill` directory and copy all
+your windmill use case into it.
+
+.. sourcecode:: python
+
+    # test_windmill.py
+
+    # Run all scenarii found in windmill directory
+    from cubicweb.devtools.cwwindmill import (CubicWebWindmillUseCase,
+                                              unittest_main)
+
+    if __name__ == '__main__':
+        unittest_main()
+
+Run your tests
+--------------
+
+You can easily run your windmill test suite through `pytest` or :mod:`unittest`.
+You have to copy a *test_windmill.py* file from :mod:`web.test`.
+
+To run your test series::
+
+    $ pytest test/test_windmill.py
+
+By default, CubicWeb will use **firefox** as the default browser and will try
+to run test instance server on localhost. In the general case, You've no need
+to change anything.
+
+Check :class:`cubicweb.devtools.cwwindmill.CubicWebWindmillUseCase` for
+Windmill configuration. You can edit windmill settings with following class attributes:
+
+* browser
+  identification string (firefox|ie|safari|chrome) (firefox by default)
+* test_dir
+  testing file path or directory (windmill directory under your unit case
+  file by default)
+* edit_test
+  load and edit test for debugging (False by default)
+
+Examples:
+
+.. sourcecode:: python
+
+    browser = 'firefox'
+    test_dir = osp.join(__file__, 'windmill')
+    edit_test = False
+
+If you want to change cubicweb test server parameters, you can check class
+variables from :class:`CubicWebServerConfig` or inherit it with overriding the
+:attr:`configcls` attribute in :class:`CubicWebServerTC` ::
+
+.. sourcecode:: python
+
+    class OtherCubicWebServerConfig(CubicWebServerConfig):
+        port = 9999
+
+    class NewCubicWebServerTC(CubicWebServerTC):
+        configcls = OtherCubicWebServerConfig
+
+For instance, CubicWeb framework windmill tests can be manually run by::
+
+    $ pytest web/test/test_windmill.py
+
+Edit your tests
+---------------
+
+You can toggle the `edit_test` variable to enable test edition.
+
+But if you are using `pytest` as test runner, use the `-i` option directly.
+The test series will be loaded and you can run assertions step-by-step::
+
+    $ pytest -i test/test_windmill.py
+
+In this case, the `firebug` extension will be loaded automatically for you.
+
+Afterwards, don't forget to save your edited test into the right file (no autosave feature).
+
+Best practises
+--------------
+
+Don't run another instance on the same port. You risk to silence some
+regressions (test runner will automatically fail in further versions).
+
+Start your use case by using an assert on the expected primary url page.
+Otherwise all your tests could fail without clear explanation of the used
+navigation.
+
+In the same location of the *test_windmill.py*, create a *windmill/* with your
+windmill recorded use cases.
+
+
+Caveats
+=======
+
+File Upload
+-----------
+
+Windmill can't do file uploads. This is a limitation of browser Javascript
+support / sandboxing, not of Windmill per se.  It would be nice if there were
+some command that would prime the Windmill HTTP proxy to add a particular file
+to the next HTTP request that comes through, so that uploads could at least be
+faked.
+
+.. http://groups.google.com/group/windmill-dev/browse_thread/thread/cf9dc969722bd6bb/01aa18fdd652f7ff?lnk=gst&q=input+type+file#01aa18fdd652f7ff
+
+.. http://davisagli.com/blog/in-browser-integration-testing-with-windmill
+
+.. http://groups.google.com/group/windmill-dev/browse_thread/thread/b7bebcc38ed30dc7
+
+
+Preferences
+===========
+
+A *.windmill/prefs.py* could be used to redefine default configuration values.
+
+.. define CubicWeb preferences in the parent test case instead with a dedicated firefox profile
+
+For managing browser extensions, read `advanced topic chapter
+<http://wiki.github.com/windmill/windmill/advanced-topics>`_.
+
+More configuration examples could be seen in *windmill/conf/global_settings.py*
+as template.
+
+
--- a/entities/__init__.py	Mon May 09 17:24:03 2016 +0200
+++ b/entities/__init__.py	Tue Jun 21 07:42:30 2016 +0200
@@ -53,7 +53,7 @@
         """
         restrictions = ['X is %s' % cls.__regid__]
         selected = ['X']
-        for attrschema in cls.e_schema.indexable_attributes():
+        for attrschema in sorted(cls.e_schema.indexable_attributes()):
             varname = attrschema.type.upper()
             restrictions.append('X %s %s' % (attrschema, varname))
             selected.append(varname)
--- a/entities/adapters.py	Mon May 09 17:24:03 2016 +0200
+++ b/entities/adapters.py	Tue Jun 21 07:42:30 2016 +0200
@@ -24,11 +24,13 @@
 
 from itertools import chain
 from warnings import warn
+from hashlib import md5
 
 from logilab.mtconverter import TransformError
 from logilab.common.decorators import cached
 
-from cubicweb import ValidationError, view
+from cubicweb import ValidationError, view, ViolatedConstraint
+from cubicweb.schema import CONSTRAINTS
 from cubicweb.predicates import is_instance, relation_possible, match_exception
 
 
@@ -79,6 +81,8 @@
         itree = self.entity.cw_adapt_to('ITree')
         if itree is not None:
             return itree.path()[:-1]
+        if view.msgid_timestamp:
+            return (self.entity.eid,)
         return ()
 
 
@@ -139,7 +143,7 @@
                 continue
             weight = self.attr_weight.get(rschema, 'C')
             try:
-                value = entity.printable_value(rschema, format='text/plain')
+                value = entity.printable_value(rschema, format=u'text/plain')
             except TransformError:
                 continue
             except Exception:
@@ -370,3 +374,25 @@
             i18nvalues.append(rtype + '-rtype')
         errors[''] = _('some relations violate a unicity constraint')
         raise ValidationError(self.entity.eid, errors, msgargs=msgargs, i18nvalues=i18nvalues)
+
+
+class IUserFriendlyCheckConstraint(IUserFriendlyError):
+    __select__ = match_exception(ViolatedConstraint)
+
+    def raise_user_exception(self):
+        _ = self._cw._
+        cstrname = self.exc.cstrname
+        eschema = self.entity.e_schema
+        for rschema, attrschema in eschema.attribute_definitions():
+            rdef = rschema.rdef(eschema, attrschema)
+            for constraint in rdef.constraints:
+                if cstrname == 'cstr' + md5(eschema.type + rschema.type + constraint.type() + (constraint.serialize() or '')).hexdigest():
+                    break
+            else:
+                continue
+            break
+        else:
+            assert 0
+        key = rschema.type + '-subject'
+        msg, args = constraint.failed_message(key, self.entity.cw_edited[rschema.type])
+        raise ValidationError(self.entity.eid, {key: msg}, args)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/entities/test/requirements.txt	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,1 @@
+docutils
--- a/entities/test/unittest_base.py	Mon May 09 17:24:03 2016 +0200
+++ b/entities/test/unittest_base.py	Tue Jun 21 07:42:30 2016 +0200
@@ -66,8 +66,8 @@
     def test_fti_rql_method(self):
         with self.admin_access.web_request() as req:
             eclass = self.vreg['etypes'].etype_class('EmailAddress')
-            self.assertEqual(['Any X, ALIAS, ADDRESS WHERE X is EmailAddress, '
-                              'X alias ALIAS, X address ADDRESS'],
+            self.assertEqual(['Any X, ADDRESS, ALIAS WHERE X is EmailAddress, '
+                              'X address ADDRESS, X alias ALIAS'],
                              eclass.cw_fti_index_rql_queries(req))
 
 
--- a/entities/test/unittest_wfobjs.py	Mon May 09 17:24:03 2016 +0200
+++ b/entities/test/unittest_wfobjs.py	Tue Jun 21 07:42:30 2016 +0200
@@ -87,12 +87,12 @@
             shell.rollback()
             # no pb if not in the same workflow
             wf2 = add_wf(shell, 'Company')
-            foo = wf.add_state(u'foo', initial=True)
-            bar = wf.add_state(u'bar')
-            wf.add_transition(u'baz', (foo,), bar, ('managers',))
+            foo = wf2.add_state(u'foo', initial=True)
+            bar = wf2.add_state(u'bar')
+            wf2.add_transition(u'baz', (foo,), bar, ('managers',))
             shell.commit()
             # gnark gnark
-            biz = wf.add_transition(u'biz', (bar,), foo)
+            biz = wf2.add_transition(u'biz', (bar,), foo)
             shell.commit()
             with self.assertRaises(ValidationError) as cm:
                 biz.cw_set(name=u'baz')
@@ -416,6 +416,32 @@
                 group.cw_clear_all_caches()
                 self.assertEqual(iworkflowable.state, nextstate)
 
+    def test_replace_state(self):
+        with self.admin_access.shell() as shell:
+            wf = add_wf(shell, 'CWGroup', name='groupwf', default=True)
+            s_new = wf.add_state('new', initial=True)
+            s_state1 = wf.add_state('state1')
+            wf.add_transition('tr', (s_new,), s_state1)
+            shell.commit()
+
+        with self.admin_access.repo_cnx() as cnx:
+            group = cnx.create_entity('CWGroup', name=u'grp1')
+            cnx.commit()
+
+            iwf = group.cw_adapt_to('IWorkflowable')
+            iwf.fire_transition('tr')
+            cnx.commit()
+            group.cw_clear_all_caches()
+
+            wf = cnx.entity_from_eid(wf.eid)
+            wf.add_state('state2')
+            with cnx.security_enabled(write=False):
+                wf.replace_state('state1', 'state2')
+            cnx.commit()
+
+            self.assertEqual(iwf.state, 'state2')
+            self.assertEqual(iwf.latest_trinfo().to_state[0].name, 'state2')
+
 
 class CustomWorkflowTC(CubicWebTC):
 
@@ -569,8 +595,9 @@
         with self.admin_access.web_request() as req:
             user = self.create_user(req, 'member', surname=u'toto')
             req.execute('SET X custom_workflow WF WHERE X eid %(x)s, WF eid %(wf)s',
-                     {'wf': wf.eid, 'x': user.eid})
+                        {'wf': wf.eid, 'x': user.eid})
             req.cnx.commit()
+            user.cw_clear_all_caches()
             iworkflowable = user.cw_adapt_to('IWorkflowable')
             self.assertEqual(iworkflowable.state, 'dead')
 
--- a/entities/wfobjs.py	Mon May 09 17:24:03 2016 +0200
+++ b/entities/wfobjs.py	Tue Jun 21 07:42:30 2016 +0200
@@ -174,12 +174,14 @@
             todelstate = self.state_by_name(todelstate)
         if not hasattr(replacement, 'eid'):
             replacement = self.state_by_name(replacement)
+        args = {'os': todelstate.eid, 'ns': replacement.eid}
         execute = self._cw.execute
-        execute('SET X in_state S WHERE S eid %(s)s', {'s': todelstate.eid})
-        execute('SET X from_state NS WHERE X to_state OS, OS eid %(os)s, NS eid %(ns)s',
-                {'os': todelstate.eid, 'ns': replacement.eid})
-        execute('SET X to_state NS WHERE X to_state OS, OS eid %(os)s, NS eid %(ns)s',
-                {'os': todelstate.eid, 'ns': replacement.eid})
+        execute('SET X in_state NS WHERE X in_state OS, '
+                'NS eid %(ns)s, OS eid %(os)s', args)
+        execute('SET X from_state NS WHERE X from_state OS, '
+                'OS eid %(os)s, NS eid %(ns)s', args)
+        execute('SET X to_state NS WHERE X to_state OS, '
+                'OS eid %(os)s, NS eid %(ns)s', args)
         todelstate.cw_delete()
 
 
@@ -412,7 +414,7 @@
         """return the default workflow for entities of this type"""
         # XXX CWEType method
         wfrset = self._cw.execute('Any WF WHERE ET default_workflow WF, '
-                                  'ET name %(et)s', {'et': self.entity.cw_etype})
+                                  'ET name %(et)s', {'et': unicode(self.entity.cw_etype)})
         if wfrset:
             return wfrset.get_entity(0, 0)
         self.warning("can't find any workflow for %s", self.entity.cw_etype)
--- a/entity.py	Mon May 09 17:24:03 2016 +0200
+++ b/entity.py	Tue Jun 21 07:42:30 2016 +0200
@@ -336,7 +336,7 @@
         else:
             visited.add(eschema.type)
         _fetchattrs = []
-        for attr in fetchattrs:
+        for attr in sorted(fetchattrs):
             try:
                 rschema = eschema.subjrels[attr]
             except KeyError:
@@ -425,7 +425,7 @@
         else:
             for rschema in cls.e_schema.subject_relations():
                 if (rschema.final
-                    and rschema != 'eid'
+                    and rschema not in ('eid', 'cwuri')
                     and cls.e_schema.has_unique_values(rschema)
                     and cls.e_schema.rdef(rschema.type).cardinality[0] == '1'):
                     mainattr = str(rschema)
@@ -514,7 +514,7 @@
         prefixing the relation name by 'reverse_'. Also, relation values may be
         an entity or eid, a list of entities or eids.
         """
-        rql, qargs, pendingrels, attrcache = cls._cw_build_entity_query(kwargs)
+        rql, qargs, pendingrels, _attrcache = cls._cw_build_entity_query(kwargs)
         if rql:
             rql = 'INSERT %s X: %s' % (cls.__regid__, rql)
         else:
@@ -524,7 +524,6 @@
         except IndexError:
             raise Exception('could not create a %r with %r (%r)' %
                             (cls.__regid__, rql, qargs))
-        created._cw_update_attr_cache(attrcache)
         cls._cw_handle_pending_relations(created.eid, pendingrels, execute)
         return created
 
@@ -555,43 +554,6 @@
             return self.eid
         return super(Entity, self).__hash__()
 
-    def _cw_update_attr_cache(self, attrcache):
-        # if context is a repository session, don't consider dont-cache-attrs as
-        # the instance already holds modified values and loosing them could
-        # introduce severe problems
-        trdata = self._cw.transaction_data
-        uncached_attrs = trdata.get('%s.storage-special-process-attrs' % self.eid, set())
-        if self._cw.is_request:
-            uncached_attrs.update(trdata.get('%s.dont-cache-attrs' % self.eid, set()))
-        for attr in uncached_attrs:
-            attrcache.pop(attr, None)
-            self.cw_attr_cache.pop(attr, None)
-        self.cw_attr_cache.update(attrcache)
-
-    def _cw_dont_cache_attribute(self, attr, repo_side=False):
-        """Repository side method called when some attribute has been
-        transformed by a hook, hence original value should not be cached by
-        the client.
-
-        If repo_side is True, this means that the attribute has been
-        transformed by a *storage*, hence the original value should
-        not be cached **by anyone**.
-
-        This only applies to a storage special case where the value
-        specified in creation or update is **not** the value that will
-        be transparently exposed later.
-
-        For example we have a special "fs_importing" mode in BFSS
-        where a file path is given as attribute value and stored as is
-        in the data base. Later access to the attribute will provide
-        the content of the file at the specified path. We do not want
-        the "filepath" value to be cached.
-        """
-        self._cw.transaction_data.setdefault('%s.dont-cache-attrs' % self.eid, set()).add(attr)
-        if repo_side:
-            trdata = self._cw.transaction_data
-            trdata.setdefault('%s.storage-special-process-attrs' % self.eid, set()).add(attr)
-
     def __json_encode__(self):
         """custom json dumps hook to dump the entity's eid
         which is not part of dict structure itself
@@ -836,7 +798,6 @@
 
     # data fetching methods ###################################################
 
-    @cached
     def as_rset(self): # XXX .cw_as_rset
         """returns a resultset containing `self` information"""
         rset = ResultSet([(self.eid,)], 'Any X WHERE X eid %(x)s',
@@ -865,7 +826,7 @@
             attr = rschema.type
             if attr == 'eid':
                 continue
-            # password retreival is blocked at the repository server level
+            # password retrieval is blocked at the repository server level
             rdef = rschema.rdef(self.e_schema, attrschema)
             if not self._cw.user.matching_groups(rdef.get_groups('read')) \
                    or (attrschema.type == 'Password' and skip_pwd):
@@ -1329,10 +1290,6 @@
             else:
                 rql += ' WHERE X eid %(x)s'
             self._cw.execute(rql, qargs)
-        # update current local object _after_ the rql query to avoid
-        # interferences between the query execution itself and the cw_edited /
-        # skip_security machinery
-        self._cw_update_attr_cache(attrcache)
         self._cw_handle_pending_relations(self.eid, pendingrels, self._cw.execute)
         # XXX update relation cache
 
--- a/etwist/server.py	Mon May 09 17:24:03 2016 +0200
+++ b/etwist/server.py	Tue Jun 21 07:42:30 2016 +0200
@@ -24,6 +24,7 @@
 import threading
 from urlparse import urlsplit, urlunsplit
 from cgi import FieldStorage, parse_header
+from cubicweb.statsd_logger import statsd_timeit
 
 from twisted.internet import reactor, task, threads
 from twisted.web import http, server
@@ -65,14 +66,6 @@
         # when we have an in-memory repository, clean unused sessions every XX
         # seconds and properly shutdown the server
         if config['repository-uri'] == 'inmemory://':
-            if config.pyro_enabled():
-                # if pyro is enabled, we have to register to the pyro name
-                # server, create a pyro daemon, and create a task to handle pyro
-                # requests
-                self.appli.repo.warning('remote repository access through pyro is deprecated')
-                self.pyro_daemon = self.appli.repo.pyro_register()
-                self.pyro_listen_timeout = 0.02
-                self.appli.repo.looping_task(1, self.pyro_loop_event)
             if config.mode != 'test':
                 reactor.addSystemEventTrigger('before', 'shutdown',
                                               self.shutdown_event)
@@ -93,13 +86,6 @@
         """
         self.appli.repo.shutdown()
 
-    def pyro_loop_event(self):
-        """listen for pyro events"""
-        try:
-            self.pyro_daemon.handleRequests(self.pyro_listen_timeout)
-        except select.error:
-            return
-
     def getChild(self, path, request):
         """Indicate which resource to use to process down the URL's path"""
         return self
@@ -118,6 +104,7 @@
             deferred = threads.deferToThread(self.render_request, request)
             return NOT_DONE_YET
 
+    @statsd_timeit
     def render_request(self, request):
         try:
             # processing HUGE files (hundred of megabytes) in http.processReceived
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/etwist/test/requirements.txt	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,1 @@
+Twisted
--- a/etwist/twconfig.py	Mon May 09 17:24:03 2016 +0200
+++ b/etwist/twconfig.py	Tue Jun 21 07:42:30 2016 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -17,9 +17,6 @@
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
 """twisted server configurations:
 
-* the "twisted" configuration to get a web instance running in a standalone
-  twisted web server which talk to a repository server using Pyro
-
 * the "all-in-one" configuration to get a web instance 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
@@ -82,13 +79,6 @@
 the repository rather than the user running the command',
           'group': 'main', 'level': WebConfiguration.mode == 'system'
           }),
-        ('pyro-server',
-         {'type' : 'yn',
-          # pyro is only a recommends by default, so don't activate it here
-          'default': False,
-          'help': 'run a pyro server',
-          'group': 'main', 'level': 1,
-          }),
         ('webserver-threadpool-size',
          {'type': 'int',
           'default': 4,
@@ -117,9 +107,6 @@
 
         cubicweb_appobject_path = WebConfigurationBase.cubicweb_appobject_path | ServerConfiguration.cubicweb_appobject_path
         cube_appobject_path = WebConfigurationBase.cube_appobject_path | ServerConfiguration.cube_appobject_path
-        def pyro_enabled(self):
-            """tell if pyro is activated for the in memory repository"""
-            return self['pyro-server']
 
 
     CONFIGURATIONS.append(AllInOneConfiguration)
--- a/etwist/twctl.py	Mon May 09 17:24:03 2016 +0200
+++ b/etwist/twctl.py	Tue Jun 21 07:42:30 2016 +0200
@@ -71,11 +71,14 @@
         cfgname = 'all-in-one'
         subcommand = 'cubicweb-twisted'
 
-    class AllInOneStopHandler(serverctl.RepositoryStopHandler):
+    class AllInOneStopHandler(CommandHandler):
         cmdname = 'stop'
         cfgname = 'all-in-one'
         subcommand = 'cubicweb-twisted'
 
+        def poststop(self):
+            pass
+
     class AllInOneUpgradeHandler(TWUpgradeHandler):
         cfgname = 'all-in-one'
 
--- a/ext/rest.py	Mon May 09 17:24:03 2016 +0200
+++ b/ext/rest.py	Tue Jun 21 07:42:30 2016 +0200
@@ -35,7 +35,6 @@
 __docformat__ = "restructuredtext en"
 
 import sys
-from cStringIO import StringIO
 from itertools import chain
 from logging import getLogger
 from os.path import join
@@ -411,7 +410,7 @@
         # remove unprintable characters unauthorized in xml
         data = data.translate(ESC_CAR_TABLE)
     settings = {'input_encoding': encoding, 'output_encoding': 'unicode',
-                'warning_stream': StringIO(),
+                'warning_stream': False,
                 'traceback': True, # don't sys.exit
                 'stylesheet': None, # don't try to embed stylesheet (may cause
                                     # obscure bug due to docutils computing
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ext/test/requirements.txt	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,1 @@
+docutils
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hooks/logstats.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,59 @@
+# copyright 2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+
+"""looping task for dumping instance's stats in a file
+"""
+
+__docformat__ = "restructuredtext en"
+
+from datetime import datetime
+import json
+
+from cubicweb.server import hook
+
+class LogStatsStartHook(hook.Hook):
+    """register task to regularly dump instance's stats in a file
+
+    data are stored as one json entry per row
+    """
+    __regid__ = 'cubicweb.hook.logstats.start'
+    events = ('server_startup',)
+
+    def __call__(self):
+        interval = self.repo.config.get('logstat-interval', 0)
+        if interval <= 0:
+            return            
+
+        def dump_stats(repo):
+            statsfile = repo.config.get('logstat-file')
+            with repo.internal_cnx() as cnx:
+                stats = cnx.call_service('repo_stats')
+                gcstats = cnx.call_service('repo_gc_stats', nmax=5)
+                
+            allstats = {'resources': stats,
+                        'memory': gcstats,
+                        'timestamp': datetime.utcnow().isoformat(),
+                       }
+            try:
+                with open(statsfile, 'ab') as ofile:
+                    json.dump(allstats, ofile)
+                    ofile.write('\n')
+            except IOError:
+                repo.warning('Cannot open stats file for writing: %s', statsfile)
+                    
+        self.repo.looping_task(interval, dump_stats, self.repo)
--- a/hooks/metadata.py	Mon May 09 17:24:03 2016 +0200
+++ b/hooks/metadata.py	Tue Jun 21 07:42:30 2016 +0200
@@ -20,6 +20,7 @@
 __docformat__ = "restructuredtext en"
 
 from datetime import datetime
+from base64 import b64encode
 
 from cubicweb.predicates import is_instance
 from cubicweb.server import hook
@@ -199,17 +200,17 @@
             oldsource = self._cw.entity_from_eid(schange[self.eidfrom])
             entity = self._cw.entity_from_eid(self.eidfrom)
             # we don't want the moved entity to be reimported later.  To
-            # distinguish this state, the trick is to change the associated
-            # record in the 'entities' system table with eid=-eid while leaving
-            # other fields unchanged, and to add a new record with eid=eid,
-            # source='system'. External source will then have consider case
-            # where `extid2eid` return a negative eid as 'this entity was known
-            # but has been moved, ignore it'.
-            self._cw.system_sql('UPDATE entities SET eid=-eid WHERE eid=%(eid)s',
-                                {'eid': self.eidfrom})
+            # distinguish this state, move the record from the 'entities' table
+            # to 'moved_entities'.  External source will then have consider
+            # case where `extid2eid` returns a negative eid as 'this entity was
+            # known but has been moved, ignore it'.
+            extid = self._cw.entity_metas(entity.eid)['extid']
+            assert extid is not None
+            attrs = {'eid': entity.eid, 'extid': b64encode(extid).decode('ascii')}
+            self._cw.system_sql(syssource.sqlgen.insert('moved_entities', attrs), attrs)
             attrs = {'type': entity.cw_etype, 'eid': entity.eid, 'extid': None,
                      'asource': 'system'}
-            self._cw.system_sql(syssource.sqlgen.insert('entities', attrs), attrs)
+            self._cw.system_sql(syssource.sqlgen.update('entities', attrs, ['eid']), attrs)
             # register an operation to update repository/sources caches
             ChangeEntitySourceUpdateCaches(self._cw, entity=entity,
                                            oldsource=oldsource.repo_source,
--- a/hooks/notification.py	Mon May 09 17:24:03 2016 +0200
+++ b/hooks/notification.py	Tue Jun 21 07:42:30 2016 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -165,7 +165,7 @@
 class EntityUpdateHook(NotificationHook):
     __regid__ = 'notifentityupdated'
     __abstract__ = True # do not register by default
-    __select__ = NotificationHook.__select__ & hook.from_dbapi_query()
+    __select__ = NotificationHook.__select__ & hook.issued_from_user_query()
     events = ('before_update_entity',)
     skip_attrs = set()
 
@@ -200,7 +200,7 @@
 
 class SomethingChangedHook(NotificationHook):
     __regid__ = 'supervising'
-    __select__ = NotificationHook.__select__ & hook.from_dbapi_query()
+    __select__ = NotificationHook.__select__ & hook.issued_from_user_query()
     events = ('before_add_relation', 'before_delete_relation',
               'after_add_entity', 'before_update_entity')
 
--- a/hooks/syncschema.py	Mon May 09 17:24:03 2016 +0200
+++ b/hooks/syncschema.py	Tue Jun 21 07:42:30 2016 +0200
@@ -27,9 +27,10 @@
 _ = unicode
 
 from copy import copy
+from hashlib import md5
 from yams.schema import (BASE_TYPES, BadSchemaDefinition,
                          RelationSchema, RelationDefinitionSchema)
-from yams import buildobjs as ybo, schema2sql as y2sql, convert_default_value
+from yams import buildobjs as ybo, convert_default_value
 
 from logilab.common.decorators import clear_cache
 
@@ -37,7 +38,7 @@
 from cubicweb.predicates import is_instance
 from cubicweb.schema import (SCHEMA_TYPES, META_RTYPES, VIRTUAL_RTYPES,
                              CONSTRAINTS, ETYPE_NAME_MAP, display_name)
-from cubicweb.server import hook, schemaserial as ss
+from cubicweb.server import hook, schemaserial as ss, schema2sql as y2sql
 from cubicweb.server.sqlutils import SQL_PREFIX
 from cubicweb.hooks.synccomputed import RecomputeAttributeOperation
 
@@ -72,7 +73,7 @@
     table = SQL_PREFIX + etype
     column = SQL_PREFIX + rtype
     try:
-        cnx.system_sql(str('ALTER TABLE %s ADD %s integer' % (table, column)),
+        cnx.system_sql(str('ALTER TABLE %s ADD %s integer REFERENCES entities (eid)' % (table, column)),
                        rollback_on_failure=False)
         cnx.info('added column %s to table %s', column, table)
     except Exception:
@@ -319,8 +320,12 @@
         if 'fulltext_container' in self.values:
             op = UpdateFTIndexOp.get_instance(cnx)
             for subjtype, objtype in rschema.rdefs:
-                op.add_data(subjtype)
-                op.add_data(objtype)
+                if self.values['fulltext_container'] == 'subject':
+                    op.add_data(subjtype)
+                    op.add_data(objtype)
+                else:
+                    op.add_data(objtype)
+                    op.add_data(subjtype)
         # update the in-memory schema first
         self.oldvalues = dict( (attr, getattr(rschema, attr)) for attr in self.values)
         self.rschema.__dict__.update(self.values)
@@ -711,6 +716,10 @@
         elif cstrtype == 'UniqueConstraint':
             syssource.update_rdef_unique(cnx, rdef)
             self.unique_changed = True
+        if cstrtype in ('BoundaryConstraint', 'IntervalBoundConstraint', 'StaticVocabularyConstraint'):
+            cstrname = 'cstr' + md5(rdef.subject.type + rdef.rtype.type + cstrtype +
+                                    (self.oldcstr.serialize() or '')).hexdigest()
+            cnx.system_sql('ALTER TABLE %s%s DROP CONSTRAINT %s' % (SQL_PREFIX, rdef.subject.type, cstrname))
 
     def revertprecommit_event(self):
         # revert changes on in memory schema
@@ -758,6 +767,16 @@
         elif cstrtype == 'UniqueConstraint' and oldcstr is None:
             syssource.update_rdef_unique(cnx, rdef)
             self.unique_changed = True
+        if cstrtype in ('BoundaryConstraint', 'IntervalBoundConstraint', 'StaticVocabularyConstraint'):
+            if oldcstr is not None:
+                oldcstrname = 'cstr' + md5(rdef.subject.type + rdef.rtype.type + cstrtype +
+                                           (self.oldcstr.serialize() or '')).hexdigest()
+                cnx.system_sql('ALTER TABLE %s%s DROP CONSTRAINT %s' %
+                               (SQL_PREFIX, rdef.subject.type, oldcstrname))
+            cstrname, check = y2sql.check_constraint(rdef.subject, rdef.object, rdef.rtype.type,
+                    newcstr, syssource.dbhelper, prefix=SQL_PREFIX)
+            cnx.system_sql('ALTER TABLE %s%s ADD CONSTRAINT %s CHECK(%s)' %
+                           (SQL_PREFIX, rdef.subject.type, cstrname, check))
 
 
 class CWUniqueTogetherConstraintAddOp(MemSchemaOperation):
@@ -1335,6 +1354,7 @@
     We wait after the commit to as the schema in memory is only updated after
     the commit.
     """
+    containercls = list
 
     def postcommit_event(self):
         cnx = self.cnx
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hooks/test/requirements.txt	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,1 @@
+psycopg2
--- a/hooks/test/unittest_hooks.py	Mon May 09 17:24:03 2016 +0200
+++ b/hooks/test/unittest_hooks.py	Tue Jun 21 07:42:30 2016 +0200
@@ -146,20 +146,6 @@
 
 class UserGroupHooksTC(CubicWebTC):
 
-    def test_user_synchronization(self):
-        with self.admin_access.repo_cnx() as cnx:
-            self.create_user(cnx, 'toto', password='hop', commit=False)
-            self.assertRaises(AuthenticationError,
-                              self.repo.connect, u'toto', password='hop')
-            cnx.commit()
-            cnxid = self.repo.connect(u'toto', password='hop')
-            self.assertNotEqual(cnxid, cnx.sessionid)
-            cnx.execute('DELETE CWUser X WHERE X login "toto"')
-            self.repo.execute(cnxid, 'State X')
-            cnx.commit()
-            self.assertRaises(BadConnectionId,
-                              self.repo.execute, cnxid, 'State X')
-
     def test_user_group_synchronization(self):
         with self.admin_access.repo_cnx() as cnx:
             user = cnx.user
--- a/hooks/test/unittest_syncschema.py	Mon May 09 17:24:03 2016 +0200
+++ b/hooks/test/unittest_syncschema.py	Tue Jun 21 07:42:30 2016 +0200
@@ -21,15 +21,23 @@
 
 from cubicweb import ValidationError, Binary
 from cubicweb.schema import META_RTYPES
+from cubicweb.devtools import startpgcluster, stoppgcluster, PostgresApptestConfiguration
 from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.server.sqlutils import SQL_PREFIX
 from cubicweb.devtools.repotest import schema_eids_idx
 
 
+def setUpModule():
+    startpgcluster(__file__)
+
+
 def tearDownModule(*args):
+    stoppgcluster(__file__)
     del SchemaModificationHooksTC.schema_eids
 
+
 class SchemaModificationHooksTC(CubicWebTC):
+    configcls = PostgresApptestConfiguration
 
     def setUp(self):
         super(SchemaModificationHooksTC, self).setUp()
@@ -38,12 +46,11 @@
 
     def index_exists(self, cnx, etype, attr, unique=False):
         dbhelper = self.repo.system_source.dbhelper
-        with cnx.ensure_cnx_set:
-            sqlcursor = cnx.cnxset.cu
-            return dbhelper.index_exists(sqlcursor,
-                                         SQL_PREFIX + etype,
-                                         SQL_PREFIX + attr,
-                                         unique=unique)
+        sqlcursor = cnx.cnxset.cu
+        return dbhelper.index_exists(sqlcursor,
+                                     SQL_PREFIX + etype,
+                                     SQL_PREFIX + attr,
+                                     unique=unique)
 
     def _set_perms(self, cnx, eid):
         cnx.execute('SET X read_permission G WHERE X eid %(x)s, G is CWGroup',
@@ -296,6 +303,7 @@
             cnx.execute('SET DEF cardinality "11" '
                          'WHERE DEF relation_type RT, DEF from_entity E,'
                          'RT name "surname", E name "CWUser"')
+            cnx.execute('SET U surname "Doe" WHERE U surname NULL')
             cnx.commit()
             # should not be able anymore to add cwuser without surname
             self.assertRaises(ValidationError, self.create_user, cnx, "toto")
--- a/hooks/zmq.py	Mon May 09 17:24:03 2016 +0200
+++ b/hooks/zmq.py	Tue Jun 21 07:42:30 2016 +0200
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# copyright 2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -50,30 +50,3 @@
         self.repo.app_instances_bus.start()
 
 
-class ZMQRepositoryServerStopHook(hook.Hook):
-    __regid__ = 'zmqrepositoryserverstop'
-    events = ('server_shutdown',)
-
-    def __call__(self):
-        server = getattr(self.repo, 'zmq_repo_server', None)
-        if server:
-            self.repo.zmq_repo_server.quit()
-
-class ZMQRepositoryServerStartHook(hook.Hook):
-    __regid__ = 'zmqrepositoryserverstart'
-    events = ('server_startup',)
-
-    def __call__(self):
-        config = self.repo.config
-        if config.name == 'repository':
-            # start-repository command already starts a zmq repo
-            return
-        address = config.get('zmq-repository-address')
-        if not address:
-            return
-        self.repo.warning('remote access to the repository via zmq/pickle is deprecated')
-        from cubicweb.server import cwzmq
-        self.repo.zmq_repo_server = server = cwzmq.ZMQRepositoryServer(self.repo)
-        server.connect(address)
-        self.repo.threaded_task(server.run)
-
--- a/i18n/de.po	Mon May 09 17:24:03 2016 +0200
+++ b/i18n/de.po	Tue Jun 21 07:42:30 2016 +0200
@@ -642,10 +642,6 @@
 msgid "New WorkflowTransition"
 msgstr "Neuer workflow-Ãœbergang"
 
-#, python-format
-msgid "No account? Try public access at %s"
-msgstr "Kein Konto? Zur öffentlichen Website: %s"
-
 msgid "No result matching query"
 msgstr "Ihre Suche ergab keine Treffer."
 
@@ -1132,6 +1128,9 @@
 msgid "add a CWCache"
 msgstr ""
 
+msgid "add a CWComputedRType"
+msgstr ""
+
 msgid "add a CWConstraint"
 msgstr ""
 
@@ -4158,9 +4157,6 @@
 msgid "thursday"
 msgstr "Donnerstag"
 
-msgid "timeline"
-msgstr "Zeitleiste"
-
 msgid "timestamp"
 msgstr "Datum"
 
@@ -4509,7 +4505,7 @@
 msgstr "Wert"
 
 #, python-format
-msgid "value %(KEY-value)s must be %(KEY-op)s %(KEY-boundary)s"
+msgid "value %(KEY-value)s must be < %(KEY-boundary)s"
 msgstr ""
 
 #, python-format
@@ -4517,6 +4513,10 @@
 msgstr ""
 
 #, python-format
+msgid "value %(KEY-value)s must be > %(KEY-boundary)s"
+msgstr ""
+
+#, python-format
 msgid "value %(KEY-value)s must be >= %(KEY-boundary)s"
 msgstr ""
 
@@ -4663,6 +4663,9 @@
 #~ msgid "Browse by category"
 #~ msgstr "nach Kategorien navigieren"
 
+#~ msgid "No account? Try public access at %s"
+#~ msgstr "Kein Konto? Zur öffentlichen Website: %s"
+
 #~ msgid "anonymous"
 #~ msgstr "anonym"
 
@@ -4682,3 +4685,6 @@
 
 #~ msgid "no edited fields specified for entity %s"
 #~ msgstr "kein Eingabefeld spezifiziert Für Entität %s"
+
+#~ msgid "timeline"
+#~ msgstr "Zeitleiste"
--- a/i18n/en.po	Mon May 09 17:24:03 2016 +0200
+++ b/i18n/en.po	Tue Jun 21 07:42:30 2016 +0200
@@ -620,10 +620,6 @@
 msgid "New WorkflowTransition"
 msgstr "New workflow-transition"
 
-#, python-format
-msgid "No account? Try public access at %s"
-msgstr ""
-
 msgid "No result matching query"
 msgstr ""
 
@@ -1094,6 +1090,9 @@
 msgid "add a CWCache"
 msgstr ""
 
+msgid "add a CWComputedRType"
+msgstr ""
+
 msgid "add a CWConstraint"
 msgstr ""
 
@@ -4060,9 +4059,6 @@
 msgid "thursday"
 msgstr ""
 
-msgid "timeline"
-msgstr ""
-
 msgid "timestamp"
 msgstr ""
 
@@ -4402,7 +4398,7 @@
 msgstr ""
 
 #, python-format
-msgid "value %(KEY-value)s must be %(KEY-op)s %(KEY-boundary)s"
+msgid "value %(KEY-value)s must be < %(KEY-boundary)s"
 msgstr ""
 
 #, python-format
@@ -4410,6 +4406,10 @@
 msgstr ""
 
 #, python-format
+msgid "value %(KEY-value)s must be > %(KEY-boundary)s"
+msgstr ""
+
+#, python-format
 msgid "value %(KEY-value)s must be >= %(KEY-boundary)s"
 msgstr ""
 
--- a/i18n/es.po	Mon May 09 17:24:03 2016 +0200
+++ b/i18n/es.po	Tue Jun 21 07:42:30 2016 +0200
@@ -651,10 +651,6 @@
 msgid "New WorkflowTransition"
 msgstr "Agregar transición de Workflow"
 
-#, python-format
-msgid "No account? Try public access at %s"
-msgstr "No esta registrado? Use el acceso público en %s"
-
 msgid "No result matching query"
 msgstr "Ningún resultado corresponde a su búsqueda"
 
@@ -1151,6 +1147,9 @@
 msgid "add a CWCache"
 msgstr ""
 
+msgid "add a CWComputedRType"
+msgstr ""
+
 msgid "add a CWConstraint"
 msgstr ""
 
@@ -4222,9 +4221,6 @@
 msgid "thursday"
 msgstr "Jueves"
 
-msgid "timeline"
-msgstr "Escala de Tiempo"
-
 msgid "timestamp"
 msgstr "Fecha"
 
@@ -4573,14 +4569,18 @@
 msgstr "Vampr"
 
 #, python-format
-msgid "value %(KEY-value)s must be %(KEY-op)s %(KEY-boundary)s"
-msgstr "El valor %(KEY-value)s debe ser %(KEY-op)s %(KEY-boundary)s"
+msgid "value %(KEY-value)s must be < %(KEY-boundary)s"
+msgstr ""
 
 #, python-format
 msgid "value %(KEY-value)s must be <= %(KEY-boundary)s"
 msgstr "el valor %(KEY-value)s debe ser <= %(KEY-boundary)s"
 
 #, python-format
+msgid "value %(KEY-value)s must be > %(KEY-boundary)s"
+msgstr ""
+
+#, python-format
 msgid "value %(KEY-value)s must be >= %(KEY-boundary)s"
 msgstr "el valor %(KEY-value)s debe ser >= %(KEY-boundary)s"
 
@@ -4729,6 +4729,9 @@
 #~ msgid "Browse by category"
 #~ msgstr "Busca por categoría"
 
+#~ msgid "No account? Try public access at %s"
+#~ msgstr "No esta registrado? Use el acceso público en %s"
+
 #~ msgid "anonymous"
 #~ msgstr "anónimo"
 
@@ -4765,9 +4768,15 @@
 #~ msgid "no edited fields specified for entity %s"
 #~ msgstr "Ningún campo editable especificado para la entidad %s"
 
+#~ msgid "timeline"
+#~ msgstr "Escala de Tiempo"
+
 #~ msgid "unknown option(s): %s"
 #~ msgstr "opcion(es) desconocida(s): %s"
 
+#~ msgid "value %(KEY-value)s must be %(KEY-op)s %(KEY-boundary)s"
+#~ msgstr "El valor %(KEY-value)s debe ser %(KEY-op)s %(KEY-boundary)s"
+
 #~ msgid "web sessions without CNX"
 #~ msgstr "sesiones web sin conexión asociada"
 
--- a/i18n/fr.po	Mon May 09 17:24:03 2016 +0200
+++ b/i18n/fr.po	Tue Jun 21 07:42:30 2016 +0200
@@ -647,10 +647,6 @@
 msgid "New WorkflowTransition"
 msgstr "Nouvelle transition workflow"
 
-#, python-format
-msgid "No account? Try public access at %s"
-msgstr "Pas de compte ? Accédez au site public : %s"
-
 msgid "No result matching query"
 msgstr "Aucun résultat ne correspond à la requête"
 
@@ -1147,6 +1143,9 @@
 msgid "add a CWCache"
 msgstr ""
 
+msgid "add a CWComputedRType"
+msgstr ""
+
 msgid "add a CWConstraint"
 msgstr ""
 
@@ -4221,9 +4220,6 @@
 msgid "thursday"
 msgstr "jeudi"
 
-msgid "timeline"
-msgstr "échelle de temps"
-
 msgid "timestamp"
 msgstr "date"
 
@@ -4570,18 +4566,22 @@
 msgstr "valeur"
 
 #, python-format
-msgid "value %(KEY-value)s must be %(KEY-op)s %(KEY-boundary)s"
-msgstr "la valeur %(KEY-value)s n'est pas %(KEY-op)s %(KEY-boundary)s"
+msgid "value %(KEY-value)s must be < %(KEY-boundary)s"
+msgstr "la valeur %(KEY-value)s doit être strictement inférieure à %(KEY-boundary)s"
 
 #, python-format
 msgid "value %(KEY-value)s must be <= %(KEY-boundary)s"
 msgstr ""
-"la valeur %(KEY-value)s n'est pas inférieure ou égale à %(KEY-boundary)s"
+"la valeur %(KEY-value)s doit être inférieure ou égale à %(KEY-boundary)s"
+
+#, python-format
+msgid "value %(KEY-value)s must be > %(KEY-boundary)s"
+msgstr "la valeur %(KEY-value)s doit être strictement supérieure à %(KEY-boundary)s"
 
 #, python-format
 msgid "value %(KEY-value)s must be >= %(KEY-boundary)s"
 msgstr ""
-"la valeur %(KEY-value)s n'est pas supérieure ou égale à %(KEY-boundary)s"
+"la valeur %(KEY-value)s doit être supérieure ou égale à %(KEY-boundary)s"
 
 msgid "value associated to this key is not editable manually"
 msgstr "la valeur associée à cette clé n'est pas éditable manuellement"
@@ -4723,69 +4723,3 @@
 
 msgid "you should probably delete that property"
 msgstr "vous devriez probablement supprimer cette propriété"
-
-#~ msgid "%s relation should not be in mapped"
-#~ msgstr "la relation %s ne devrait pas ếtre mappé"
-
-#~ msgid "Any"
-#~ msgstr "Tous"
-
-#~ msgid "Browse by category"
-#~ msgstr "Naviguer par catégorie"
-
-#~ msgid "anonymous"
-#~ msgstr "anonyme"
-
-#~ msgid "attribute/relation can't be mapped, only entity and relation types"
-#~ msgstr ""
-#~ "les attributs et relations ne peuvent être mappés, uniquement les types "
-#~ "d'entité et de relation"
-
-#~ msgid "can't connect to source %s, some data may be missing"
-#~ msgstr "ne peut se connecter à la source %s, des données peuvent manquer"
-
-#~ msgid "can't mix dontcross and maycross options"
-#~ msgstr "ne peut mélanger dontcross et maycross options"
-
-#~ msgid "can't mix dontcross and write options"
-#~ msgstr "ne peut mélanger dontcross et write options"
-
-#~ msgid "components_etypenavigation"
-#~ msgstr "filtrage par type"
-
-#~ msgid "components_etypenavigation_description"
-#~ msgstr "permet de filtrer par type d'entité les résultats d'une recherche"
-
-#~ msgid "error while querying source %s, some data may be missing"
-#~ msgstr ""
-#~ "une erreur est survenue en interrogeant %s, il est possible que les\n"
-#~ "données affichées soient incomplètes"
-
-#~ msgid "inlined relation %(rtype)s of %(etype)s should be supported"
-#~ msgstr ""
-#~ "la relation %(rtype)s du type d'entité %(etype)s doit être supportée "
-#~ "('inlined')"
-
-#~ msgid "no edited fields specified for entity %s"
-#~ msgstr "aucun champ à éditer spécifié pour l'entité %s"
-
-#~ msgid "unknown option(s): %s"
-#~ msgstr "option(s) inconnue(s) : %s"
-
-#~ msgid "web sessions without CNX"
-#~ msgstr "sessions web sans connexion associée"
-
-#~ msgid "workflow already has a state of that name"
-#~ msgstr "le workflow a déja un état du même nom"
-
-#~ msgid "workflow already has a transition of that name"
-#~ msgstr "le workflow a déja une transition du même nom"
-
-#~ msgid "you may want to specify something for %s"
-#~ msgstr "vous désirez peut-être spécifié quelque chose pour la relation %s"
-
-#~ msgid ""
-#~ "you should un-inline relation %s which is supported and may be crossed "
-#~ msgstr ""
-#~ "vous devriez enlevé la mise en ligne de la relation %s qui est supportée "
-#~ "et peut-être croisée"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/migration/3.21.0_Any.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,172 @@
+from cubicweb.schema import PURE_VIRTUAL_RTYPES
+from cubicweb.server.schema2sql import rschema_has_table
+
+
+def add_foreign_keys():
+    source = repo.system_source
+    if not source.dbhelper.alter_column_support:
+        return
+    for rschema in schema.relations():
+        if rschema.inlined:
+            add_foreign_keys_inlined(rschema)
+        elif rschema_has_table(rschema, skip_relations=PURE_VIRTUAL_RTYPES):
+            add_foreign_keys_relation(rschema)
+    for eschema in schema.entities():
+        if eschema.final:
+            continue
+        add_foreign_key_etype(eschema)
+
+
+def add_foreign_keys_relation(rschema):
+    args = {'r': rschema.type}
+    count = sql('SELECT COUNT(*) FROM ('
+                '    SELECT eid_from FROM %(r)s_relation'
+                '  UNION'
+                '    SELECT eid_to FROM %(r)s_relation'
+                '  EXCEPT'
+                '    SELECT eid FROM entities) AS eids' % args,
+                ask_confirm=False)[0][0]
+    if count:
+        print '%s references %d unknown entities, deleting' % (rschema, count)
+        sql('DELETE FROM %(r)s_relation '
+            'WHERE eid_from IN (SELECT eid_from FROM %(r)s_relation EXCEPT SELECT eid FROM entities)' % args)
+        sql('DELETE FROM %(r)s_relation '
+            'WHERE eid_to IN (SELECT eid_to FROM %(r)s_relation EXCEPT SELECT eid FROM entities)' % args)
+
+    args['from_fk'] = '%(r)s_relation_eid_from_fkey' % args
+    args['to_fk'] = '%(r)s_relation_eid_to_fkey' % args
+    args['table'] = '%(r)s_relation' % args
+    if repo.system_source.dbdriver == 'postgres':
+        sql('ALTER TABLE %(table)s DROP CONSTRAINT IF EXISTS %(from_fk)s' % args,
+            ask_confirm=False)
+        sql('ALTER TABLE %(table)s DROP CONSTRAINT IF EXISTS %(to_fk)s' % args,
+            ask_confirm=False)
+    elif repo.system_source.dbdriver.startswith('sqlserver'):
+        sql("IF OBJECT_ID('%(from_fk)s', 'F') IS NOT NULL "
+            "ALTER TABLE %(table)s DROP CONSTRAINT %(from_fk)s" % args,
+            ask_confirm=False)
+        sql("IF OBJECT_ID('%(to_fk)s', 'F') IS NOT NULL "
+            "ALTER TABLE %(table)s DROP CONSTRAINT %(to_fk)s" % args,
+            ask_confirm=False)
+    sql('ALTER TABLE %(table)s ADD CONSTRAINT %(from_fk)s '
+        'FOREIGN KEY (eid_from) REFERENCES entities (eid)' % args,
+        ask_confirm=False)
+    sql('ALTER TABLE %(table)s ADD CONSTRAINT %(to_fk)s '
+        'FOREIGN KEY (eid_to) REFERENCES entities (eid)' % args,
+        ask_confirm=False)
+
+
+def add_foreign_keys_inlined(rschema):
+    for eschema in rschema.subjects():
+        args = {'e': eschema.type, 'r': rschema.type}
+        args['c'] = 'cw_%(e)s_cw_%(r)s_fkey' % args
+
+        if eschema.rdef(rschema).cardinality[0] == '1':
+            broken_eids = sql('SELECT cw_eid FROM cw_%(e)s WHERE cw_%(r)s IS NULL' % args,
+                              ask_confirm=False)
+            if broken_eids:
+                print 'Required relation %(e)s.%(r)s missing' % args
+                args['eids'] = ', '.join(str(eid) for eid, in broken_eids)
+                rql('DELETE %(e)s X WHERE X eid IN (%(eids)s)' % args)
+            broken_eids = sql('SELECT cw_eid FROM cw_%(e)s WHERE cw_%(r)s IN (SELECT cw_%(r)s FROM cw_%(e)s '
+                              'EXCEPT SELECT eid FROM entities)' % args,
+                              ask_confirm=False)
+            if broken_eids:
+                print 'Required relation %(e)s.%(r)s references unknown objects, deleting subject entities' % args
+                args['eids'] = ', '.join(str(eid) for eid, in broken_eids)
+                rql('DELETE %(e)s X WHERE X eid IN (%(eids)s)' % args)
+        else:
+            if sql('SELECT COUNT(*) FROM ('
+                   '    SELECT cw_%(r)s FROM cw_%(e)s WHERE cw_%(r)s IS NOT NULL'
+                   '  EXCEPT'
+                   '    SELECT eid FROM entities) AS eids' % args,
+                   ask_confirm=False)[0][0]:
+                print '%(e)s.%(r)s references unknown entities, deleting relation' % args
+                sql('UPDATE cw_%(e)s SET cw_%(r)s = NULL WHERE cw_%(r)s IS NOT NULL AND cw_%(r)s IN '
+                    '(SELECT cw_%(r)s FROM cw_%(e)s EXCEPT SELECT eid FROM entities)' % args)
+
+        if repo.system_source.dbdriver == 'postgres':
+            sql('ALTER TABLE cw_%(e)s DROP CONSTRAINT IF EXISTS %(c)s' % args,
+                ask_confirm=False)
+        elif repo.system_source.dbdriver.startswith('sqlserver'):
+            sql("IF OBJECT_ID('%(c)s', 'F') IS NOT NULL "
+                "ALTER TABLE cw_%(e)s DROP CONSTRAINT %(c)s" % args,
+                ask_confirm=False)
+        sql('ALTER TABLE cw_%(e)s ADD CONSTRAINT %(c)s '
+            'FOREIGN KEY (cw_%(r)s) references entities(eid)' % args,
+            ask_confirm=False)
+
+
+def add_foreign_key_etype(eschema):
+    args = {'e': eschema.type}
+    if sql('SELECT COUNT(*) FROM ('
+           '    SELECT cw_eid FROM cw_%(e)s'
+           '  EXCEPT'
+           '    SELECT eid FROM entities) AS eids' % args,
+           ask_confirm=False)[0][0]:
+        print '%(e)s has nonexistent entities, deleting' % args
+        sql('DELETE FROM cw_%(e)s WHERE cw_eid IN '
+            '(SELECT cw_eid FROM cw_%(e)s EXCEPT SELECT eid FROM entities)' % args)
+    args['c'] = 'cw_%(e)s_cw_eid_fkey' % args
+    if repo.system_source.dbdriver == 'postgres':
+        sql('ALTER TABLE cw_%(e)s DROP CONSTRAINT IF EXISTS %(c)s' % args,
+            ask_confirm=False)
+    elif repo.system_source.dbdriver.startswith('sqlserver'):
+        sql("IF OBJECT_ID('%(c)s', 'F') IS NOT NULL "
+            "ALTER TABLE cw_%(e)s DROP CONSTRAINT %(c)s" % args,
+            ask_confirm=False)
+    sql('ALTER TABLE cw_%(e)s ADD CONSTRAINT %(c)s '
+        'FOREIGN KEY (cw_eid) REFERENCES entities (eid)' % args,
+        ask_confirm=False)
+
+
+add_foreign_keys()
+
+cu = session.cnxset.cu
+helper = repo.system_source.dbhelper
+
+helper.drop_index(cu, 'entities', 'extid', False)
+# don't use create_index because it doesn't work for columns that may be NULL
+# on sqlserver
+for query in helper.sqls_create_multicol_unique_index('entities', ['extid']):
+    cu.execute(query)
+
+if 'moved_entities' not in helper.list_tables(cu):
+    sql('''
+    CREATE TABLE moved_entities (
+      eid INTEGER PRIMARY KEY NOT NULL,
+      extid VARCHAR(256) UNIQUE
+    )
+    ''')
+
+moved_entities = sql('SELECT -eid, extid FROM entities WHERE eid < 0',
+                     ask_confirm=False)
+if moved_entities:
+    cu.executemany('INSERT INTO moved_entities (eid, extid) VALUES (%s, %s)',
+                   moved_entities)
+    sql('DELETE FROM entities WHERE eid < 0')
+
+commit()
+
+sync_schema_props_perms('CWEType')
+
+sync_schema_props_perms('cwuri')
+
+from cubicweb.server.schema2sql import check_constraint
+
+for cwconstraint in rql('Any C WHERE R constrained_by C').entities():
+    cwrdef = cwconstraint.reverse_constrained_by[0]
+    rdef = cwrdef.yams_schema()
+    cstr = rdef.constraint_by_eid(cwconstraint.eid)
+    if cstr.type() not in ('BoundaryConstraint', 'IntervalBoundConstraint', 'StaticVocabularyConstraint'):
+        continue
+    cstrname, check = check_constraint(rdef.subject, rdef.object, rdef.rtype.type,
+            cstr, helper, prefix='cw_')
+    args = {'e': rdef.subject.type, 'c': cstrname, 'v': check}
+    if repo.system_source.dbdriver == 'postgres':
+        sql('ALTER TABLE cw_%(e)s DROP CONSTRAINT IF EXISTS %(c)s' % args)
+    elif repo.system_source.dbdriver.startswith('sqlserver'):
+        sql("IF OBJECT_ID('%(c)s', 'C') IS NOT NULL "
+            "ALTER TABLE cw_%(e)s DROP CONSTRAINT %(c)s" % args)
+    sql('ALTER TABLE cw_%(e)s ADD CONSTRAINT %(c)s CHECK(%(v)s)' % args)
+commit()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/migration/3.21.1_Any.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,4 @@
+# re-read ComputedRelation permissions from schema.py now that we're
+# able to serialize them
+for computedrtype in schema.iter_computed_relations():
+    sync_schema_props_perms(computedrtype.type)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/migration/3.21.2_Any.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,7 @@
+sync_schema_props_perms('cwuri')
+
+helper = repo.system_source.dbhelper
+cu = session.cnxset.cu
+helper.set_null_allowed(cu, 'moved_entities', 'extid', 'VARCHAR(256)', False)
+
+commit()
--- a/misc/migration/bootstrapmigration_repository.py	Mon May 09 17:24:03 2016 +0200
+++ b/misc/migration/bootstrapmigration_repository.py	Tue Jun 21 07:42:30 2016 +0200
@@ -57,7 +57,7 @@
     commit()
 
 if applcubicwebversion <= (3, 14, 4) and cubicwebversion >= (3, 14, 4):
-    from yams import schema2sql as y2sql
+    from cubicweb.server import schema2sql as y2sql
     dbhelper = repo.system_source.dbhelper
     rdefdef = schema['CWSource'].rdef('name')
     attrtype = y2sql.type_from_constraints(dbhelper, rdefdef.object, rdefdef.constraints).split()[0]
@@ -434,6 +434,12 @@
 if applcubicwebversion < (3, 2, 0) and cubicwebversion >= (3, 2, 0):
     add_cube('card', update_database=False)
 
+
+if applcubicwebversion < (3, 21, 1) and cubicwebversion >= (3, 21, 1):
+    add_relation_definition('CWComputedRType', 'read_permission', 'CWGroup')
+    add_relation_definition('CWComputedRType', 'read_permission', 'RQLExpression')
+
+
 def sync_constraint_types():
     """Make sure the repository knows about all constraint types defined in the code"""
     from cubicweb.schema import CONSTRAINTS
--- a/misc/migration/postcreate.py	Mon May 09 17:24:03 2016 +0200
+++ b/misc/migration/postcreate.py	Tue Jun 21 07:42:30 2016 +0200
@@ -38,9 +38,9 @@
 activated = userwf.add_state(_('activated'), initial=True)
 deactivated = userwf.add_state(_('deactivated'))
 userwf.add_transition(_('deactivate'), (activated,), deactivated,
-                      requiredgroups=('managers',))
+                      requiredgroups=(u'managers',))
 userwf.add_transition(_('activate'), (deactivated,), activated,
-                      requiredgroups=('managers',))
+                      requiredgroups=(u'managers',))
 
 # create anonymous user if all-in-one config and anonymous user has been specified
 if hasattr(config, 'anonymous_user'):
@@ -50,7 +50,7 @@
         print 'Hopefully this is not a production instance...'
     elif anonlogin:
         from cubicweb.server import create_user
-        create_user(session, unicode(anonlogin), anonpwd, 'guests')
+        create_user(session, unicode(anonlogin), anonpwd, u'guests')
 
 # need this since we already have at least one user in the database (the default admin)
 for user in rql('Any X WHERE X is CWUser').entities():
@@ -63,7 +63,7 @@
     cfg.input_config(inputlevel=0)
     for section, options in cfg.options_by_section():
         for optname, optdict, value in options:
-            key = '%s.%s' % (section, optname)
+            key = u'%s.%s' % (section, optname)
             default = cfg.option_default(optname, optdict)
             # only record values differing from default
             if value != default:
--- a/misc/scripts/ldapuser2ldapfeed.py	Mon May 09 17:24:03 2016 +0200
+++ b/misc/scripts/ldapuser2ldapfeed.py	Tue Jun 21 07:42:30 2016 +0200
@@ -31,8 +31,6 @@
 from cubicweb.server.edition import EditedEntity
 
 
-session.mode = 'write' # hold on the connections set
-
 print '******************** backport entity content ***************************'
 
 todelete = defaultdict(list)
--- a/misc/scripts/pyroforge2datafeed.py	Mon May 09 17:24:03 2016 +0200
+++ b/misc/scripts/pyroforge2datafeed.py	Tue Jun 21 07:42:30 2016 +0200
@@ -39,8 +39,6 @@
         ))
 
 
-session.mode = 'write' # hold on the connections set
-
 print '******************** backport entity content ***************************'
 
 from cubicweb.server import debugged
--- a/predicates.py	Mon May 09 17:24:03 2016 +0200
+++ b/predicates.py	Tue Jun 21 07:42:30 2016 +0200
@@ -15,171 +15,7 @@
 #
 # You should have received a copy of the GNU Lesser General Public License along
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-""".. _Selectors:
-
-Predicates and selectors
-------------------------
-
-A predicate is a class testing a particular aspect of a context. A selector is
-built by combining existant predicates or even selectors.
-
-Using and combining existant predicates
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-You can combine predicates using the `&`, `|` and `~` operators.
-
-When two predicates are combined using the `&` operator, it means that
-both should return a positive score. On success, the sum of scores is
-returned.
-
-When two predicates are combined using the `|` operator, it means that
-one of them should return a positive score. On success, the first
-positive score is returned.
-
-You can also "negate" a predicate by precedeing it by the `~` unary operator.
-
-Of course you can use parenthesis to balance expressions.
-
-Example
-~~~~~~~
-
-The goal: when on a blog, one wants the RSS link to refer to blog entries, not to
-the blog entity itself.
-
-To do that, one defines a method on entity classes that returns the
-RSS stream url for a given entity. The default implementation on
-:class:`~cubicweb.entities.AnyEntity` (the generic entity class used
-as base for all others) and a specific implementation on `Blog` will
-do what we want.
-
-But when we have a result set containing several `Blog` entities (or
-different entities), we don't know on which entity to call the
-aforementioned method. In this case, we keep the generic behaviour.
-
-Hence we have two cases here, one for a single-entity rsets, the other for
-multi-entities rsets.
-
-In web/views/boxes.py lies the RSSIconBox class. Look at its selector:
-
-.. sourcecode:: python
-
-  class RSSIconBox(box.Box):
-    ''' just display the RSS icon on uniform result set '''
-    __select__ = box.Box.__select__ & non_final_entity()
-
-It takes into account:
-
-* the inherited selection criteria (one has to look them up in the class
-  hierarchy to know the details)
-
-* :class:`~cubicweb.predicates.non_final_entity`, which filters on result sets
-  containing non final entities (a 'final entity' being synonym for entity
-  attributes type, eg `String`, `Int`, etc)
-
-This matches our second case. Hence we have to provide a specific component for
-the first case:
-
-.. sourcecode:: python
-
-  class EntityRSSIconBox(RSSIconBox):
-    '''just display the RSS icon on uniform result set for a single entity'''
-    __select__ = RSSIconBox.__select__ & one_line_rset()
-
-Here, one adds the :class:`~cubicweb.predicates.one_line_rset` predicate, which
-filters result sets of size 1. Thus, on a result set containing multiple
-entities, :class:`one_line_rset` makes the EntityRSSIconBox class non
-selectable. However for a result set with one entity, the `EntityRSSIconBox`
-class will have a higher score than `RSSIconBox`, which is what we wanted.
-
-Of course, once this is done, you have to:
-
-* fill in the call method of `EntityRSSIconBox`
-
-* provide the default implementation of the method returning the RSS stream url
-  on :class:`~cubicweb.entities.AnyEntity`
-
-* redefine this method on `Blog`.
-
-
-When to use selectors?
-~~~~~~~~~~~~~~~~~~~~~~
-
-Selectors are to be used whenever arises the need of dispatching on the shape or
-content of a result set or whatever else context (value in request form params,
-authenticated user groups, etc...). That is, almost all the time.
-
-Here is a quick example:
-
-.. sourcecode:: python
-
-    class UserLink(component.Component):
-	'''if the user is the anonymous user, build a link to login else a link
-	to the connected user object with a logout link
-	'''
-	__regid__ = 'loggeduserlink'
-
-	def call(self):
-	    if self._cw.session.anonymous_session:
-		# display login link
-		...
-	    else:
-		# display a link to the connected user object with a loggout link
-		...
-
-The proper way to implement this with |cubicweb| is two have two different
-classes sharing the same identifier but with different selectors so you'll get
-the correct one according to the context.
-
-.. sourcecode:: python
-
-    class UserLink(component.Component):
-	'''display a link to the connected user object with a loggout link'''
-	__regid__ = 'loggeduserlink'
-	__select__ = component.Component.__select__ & authenticated_user()
-
-	def call(self):
-            # display useractions and siteactions
-	    ...
-
-    class AnonUserLink(component.Component):
-	'''build a link to login'''
-	__regid__ = 'loggeduserlink'
-	__select__ = component.Component.__select__ & anonymous_user()
-
-	def call(self):
-	    # display login link
-            ...
-
-The big advantage, aside readability once you're familiar with the
-system, is that your cube becomes much more easily customizable by
-improving componentization.
-
-
-.. _CustomPredicates:
-
-Defining your own predicates
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. autodocstring:: cubicweb.appobject::objectify_predicate
-
-In other cases, you can take a look at the following abstract base classes:
-
-.. autoclass:: cubicweb.predicates.ExpectedValuePredicate
-.. autoclass:: cubicweb.predicates.EClassPredicate
-.. autoclass:: cubicweb.predicates.EntityPredicate
-
-.. _DebuggingSelectors:
-
-Debugging selection
-~~~~~~~~~~~~~~~~~~~
-
-Once in a while, one needs to understand why a view (or any application object)
-is, or is not selected appropriately. Looking at which predicates fired (or did
-not) is the way. The :class:`logilab.common.registry.traced_selection` context
-manager to help with that, *if you're running your instance in debug mode*.
-
-.. autoclass:: logilab.common.registry.traced_selection
-
+"""Predicate classes
 """
 
 __docformat__ = "restructuredtext en"
@@ -394,7 +230,7 @@
     """
     def __init__(self, *expected, **kwargs):
         assert expected, self
-        if len(expected) == 1 and isinstance(expected[0], set):
+        if len(expected) == 1 and isinstance(expected[0], (set, dict)):
             self.expected = expected[0]
         else:
             self.expected = frozenset(expected)
@@ -409,7 +245,21 @@
 
     def __call__(self, cls, req, **kwargs):
         values = self._values_set(cls, req, **kwargs)
-        matching = len(values & self.expected)
+        if isinstance(values, dict):
+            if isinstance(self.expected, dict):
+                matching = 0
+                for key, expected_value in self.expected.items():
+                    if key in values:
+                        if (isinstance(expected_value, (list, tuple, frozenset, set))
+                            and values[key] in expected_value):
+                            matching += 1
+                        elif values[key] == expected_value:
+                            matching += 1
+            if isinstance(self.expected, (set, frozenset)):
+                values = frozenset(values)
+                matching = len(values & self.expected)
+        else:
+            matching = len(values & self.expected)
         if self.once_is_enough:
             return matching
         if matching == len(self.expected):
@@ -438,7 +288,7 @@
     """
 
     def _values_set(self, cls, req, **kwargs):
-        return frozenset(kwargs)
+        return kwargs
 
 
 class appobject_selectable(Predicate):
@@ -1278,31 +1128,29 @@
 def no_cnx(cls, req, **kwargs):
     """Return 1 if the web session has no connection set. This occurs when
     anonymous access is not allowed and user isn't authenticated.
-
-    May only be used on the web side, not on the data repository side.
     """
     if not req.cnx:
         return 1
     return 0
 
+
 @objectify_predicate
 def authenticated_user(cls, req, **kwargs):
     """Return 1 if the user is authenticated (i.e. not the anonymous user).
-
-    May only be used on the web side, not on the data repository side.
     """
     if req.session.anonymous_session:
         return 0
     return 1
 
 
-# XXX == ~ authenticated_user()
-def anonymous_user():
+@objectify_predicate
+def anonymous_user(cls, req, **kwargs):
     """Return 1 if the user is not authenticated (i.e. is the anonymous user).
+    """
+    if req.session.anonymous_session:
+        return 1
+    return 0
 
-    May only be used on the web side, not on the data repository side.
-    """
-    return ~ authenticated_user()
 
 class match_user_groups(ExpectedValuePredicate):
     """Return a non-zero score if request's user is in at least one of the
@@ -1435,8 +1283,23 @@
     in which case a single matching parameter is enough.
     """
 
+    def __init__(self, *expected, **kwargs):
+        """override default __init__ to allow either named or positional
+        parameters.
+        """
+        if kwargs and expected:
+            raise ValueError("match_form_params() can't be called with both "
+                             "positional and named arguments")
+        if expected:
+            if len(expected) == 1 and not isinstance(expected[0], basestring):
+                raise ValueError("match_form_params() positional arguments "
+                                 "must be strings")
+            super(match_form_params, self).__init__(*expected)
+        else:
+            super(match_form_params, self).__init__(kwargs)
+
     def _values_set(self, cls, req, **kwargs):
-        return frozenset(req.form)
+        return req.form
 
 
 class match_http_method(ExpectedValuePredicate):
--- a/repoapi.py	Mon May 09 17:24:03 2016 +0200
+++ b/repoapi.py	Tue Jun 21 07:42:30 2016 +0200
@@ -1,4 +1,4 @@
-# copyright 2013-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2013-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -17,14 +17,12 @@
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
 """Official API to access the content of a repository
 """
-from logilab.common.deprecation import deprecated
+from logilab.common.deprecation import class_deprecated
 
 from cubicweb.utils import parse_repo_uri
-from cubicweb import ConnectionError, ProgrammingError, AuthenticationError
-from uuid import uuid4
-from contextlib import contextmanager
-from cubicweb.req import RequestSessionBase
-from functools import wraps
+from cubicweb import ConnectionError, AuthenticationError
+from cubicweb.server.session import Connection
+
 
 ### private function for specific method ############################
 
@@ -41,7 +39,7 @@
     loading the repository for a client, eg web server, configuration).
 
     The returned repository may be an in-memory repository or a proxy object
-    using a specific RPC method, depending on the given URI (pyro or zmq).
+    using a specific RPC method, depending on the given URI.
     """
     if uri is None:
         return _get_inmemory_repo(config, vreg)
@@ -52,49 +50,20 @@
         # me may have been called with a dummy 'inmemory://' uri ...
         return _get_inmemory_repo(config, vreg)
 
-    if protocol == 'pyroloc':  # direct connection to the instance
-        from logilab.common.pyro_ext import get_proxy
-        uri = uri.replace('pyroloc', 'PYRO')
-        return get_proxy(uri)
-
-    if protocol == 'pyro':  # connection mediated through the pyro ns
-        from logilab.common.pyro_ext import ns_get_proxy
-        path = appid.strip('/')
-        if not path:
-            raise ConnectionError(
-                "can't find instance name in %s (expected to be the path component)"
-                % uri)
-        if '.' in path:
-            nsgroup, nsid = path.rsplit('.', 1)
-        else:
-            nsgroup = 'cubicweb'
-            nsid = path
-        return ns_get_proxy(nsid, defaultnsgroup=nsgroup, nshost=hostport)
-
-    if protocol.startswith('zmqpickle-'):
-        from cubicweb.zmqclient import ZMQRepositoryClient
-        return ZMQRepositoryClient(uri)
-    else:
-        raise ConnectionError('unknown protocol: `%s`' % protocol)
+    raise ConnectionError('unknown protocol: `%s`' % protocol)
 
 def connect(repo, login, **kwargs):
-    """Take credential and return associated ClientConnection.
-
-    The ClientConnection is associated to a new Session object that will be
-    closed when the ClientConnection is closed.
+    """Take credential and return associated Connection.
 
     raise AuthenticationError if the credential are invalid."""
     sessionid = repo.connect(login, **kwargs)
     session = repo._get_session(sessionid)
     # XXX the autoclose_session should probably be handle on the session directly
     # this is something to consider once we have proper server side Connection.
-    return ClientConnection(session, autoclose_session=True)
+    return Connection(session)
 
 def anonymous_cnx(repo):
-    """return a ClientConnection for Anonymous user.
-
-    The ClientConnection is associated to a new Session object that will be
-    closed when the ClientConnection is closed.
+    """return a Connection for Anonymous user.
 
     raises an AuthenticationError if anonymous usage is not allowed
     """
@@ -105,292 +74,7 @@
     # use vreg's repository cache
     return connect(repo, anon_login, password=anon_password)
 
-def _srv_cnx_func(name):
-    """Decorate ClientConnection method blindly forward to Connection
-    THIS TRANSITIONAL PURPOSE
 
-    will be dropped when we have standalone connection"""
-    def proxy(clt_cnx, *args, **kwargs):
-        # the ``with`` dance is transitional. We do not have Standalone
-        # Connection yet so we use this trick to unsure the session have the
-        # proper cnx loaded. This can be simplified one we have Standalone
-        # Connection object
-        if not clt_cnx._open:
-            raise ProgrammingError('Closed client connection')
-        return getattr(clt_cnx._cnx, name)(*args, **kwargs)
-    return proxy
-
-def _open_only(func):
-    """decorator for ClientConnection method that check it is open"""
-    @wraps(func)
-    def check_open(clt_cnx, *args, **kwargs):
-        if not clt_cnx._open:
-            raise ProgrammingError('Closed client connection')
-        return func(clt_cnx, *args, **kwargs)
-    return check_open
-
-
-class ClientConnection(RequestSessionBase):
-    """A Connection object to be used Client side.
-
-    This object is aimed to be used client side (so potential communication
-    with the repo through RPC) and aims to offer some compatibility with the
-    cubicweb.dbapi.Connection interface.
-
-    The autoclose_session parameter informs the connection that this session
-    has been opened explicitly and only for this client connection. The
-    connection will close the session on exit.
-    """
-    # make exceptions available through the connection object
-    ProgrammingError = ProgrammingError
-    # attributes that may be overriden per connection instance
-    anonymous_connection = False # XXX really needed ?
-    is_repo_in_memory = True # BC, always true
-
-    def __init__(self, session, autoclose_session=False):
-        super(ClientConnection, self).__init__(session.vreg)
-        self._session = session # XXX there is no real reason to keep the
-                                # session around function still using it should
-                                # be rewritten and migrated.
-        self._cnx = None
-        self._open = None
-        self._web_request = False
-        #: cache entities built during the connection
-        self._eid_cache = {}
-        self._set_user(session.user)
-        self._autoclose_session = autoclose_session
-
-    def __enter__(self):
-        assert self._open is None
-        self._open = True
-        self._cnx = self._session.new_cnx()
-        self._cnx.__enter__()
-        self._cnx.ctx_count += 1
-        return self
-
-    def __exit__(self, exc_type, exc_val, exc_tb):
-        self._open = False
-        self._cnx.ctx_count -= 1
-        self._cnx.__exit__(exc_type, exc_val, exc_tb)
-        self._cnx = None
-        if self._autoclose_session:
-            # we have to call repo.close to ensure the repo properly forgets the
-            # session; calling session.close() is not enough :-(
-            self._session.repo.close(self._session.sessionid)
-
-
-    # begin silly BC
-    @property
-    def _closed(self):
-        return not self._open
-
-    def close(self):
-        if self._open:
-            self.__exit__(None, None, None)
-
-    def __repr__(self):
-        # XXX we probably want to reference the user of the session here
-        if self._open is None:
-            return '<ClientConnection (not open yet)>'
-        elif not self._open:
-            return '<ClientConnection (closed)>'
-        elif self.anonymous_connection:
-            return '<ClientConnection %s (anonymous)>' % self._cnx.connectionid
-        else:
-            return '<ClientConnection %s>' % self._cnx.connectionid
-    # end silly BC
-
-    # Main Connection purpose in life #########################################
-
-    call_service = _srv_cnx_func('call_service')
-
-    @_open_only
-    def execute(self, *args, **kwargs):
-        # the ``with`` dance is transitional. We do not have Standalone
-        # Connection yet so we use this trick to unsure the session have the
-        # proper cnx loaded. This can be simplified one we have Standalone
-        # Connection object
-        rset = self._cnx.execute(*args, **kwargs)
-        rset.req = self
-        return rset
-
-    @_open_only
-    def commit(self, *args, **kwargs):
-        try:
-            return self._cnx.commit(*args, **kwargs)
-        finally:
-            self.drop_entity_cache()
-
-    @_open_only
-    def rollback(self, *args, **kwargs):
-        try:
-            return self._cnx.rollback(*args, **kwargs)
-        finally:
-            self.drop_entity_cache()
-
-    # security #################################################################
-
-    allow_all_hooks_but = _srv_cnx_func('allow_all_hooks_but')
-    deny_all_hooks_but = _srv_cnx_func('deny_all_hooks_but')
-    security_enabled = _srv_cnx_func('security_enabled')
-
-    # direct sql ###############################################################
-
-    system_sql = _srv_cnx_func('system_sql')
-
-    # session data methods #####################################################
-
-    get_shared_data = _srv_cnx_func('get_shared_data')
-    set_shared_data = _srv_cnx_func('set_shared_data')
-
-    @property
-    def transaction_data(self):
-        return self._cnx.transaction_data
-
-    # meta-data accessors ######################################################
-
-    @_open_only
-    def source_defs(self):
-        """Return the definition of sources used by the repository."""
-        return self._session.repo.source_defs()
-
-    @_open_only
-    def get_schema(self):
-        """Return the schema currently used by the repository."""
-        return self._session.repo.source_defs()
-
-    @_open_only
-    def get_option_value(self, option):
-        """Return the value for `option` in the configuration."""
-        return self._session.repo.get_option_value(option)
-
-    entity_metas = _srv_cnx_func('entity_metas')
-    describe = _srv_cnx_func('describe') # XXX deprecated in 3.19
-
-    # undo support ############################################################
-
-    @_open_only
-    def undoable_transactions(self, ueid=None, req=None, **actionfilters):
-        """Return a list of undoable transaction objects by the connection's
-        user, ordered by descendant transaction time.
-
-        Managers may filter according to user (eid) who has done the transaction
-        using the `ueid` argument. Others will only see their own transactions.
-
-        Additional filtering capabilities is provided by using the following
-        named arguments:
-
-        * `etype` to get only transactions creating/updating/deleting entities
-          of the given type
-
-        * `eid` to get only transactions applied to entity of the given eid
-
-        * `action` to get only transactions doing the given action (action in
-          'C', 'U', 'D', 'A', 'R'). If `etype`, action can only be 'C', 'U' or
-          'D'.
-
-        * `public`: when additional filtering is provided, their are by default
-          only searched in 'public' actions, unless a `public` argument is given
-          and set to false.
-        """
-        # the ``with`` dance is transitional. We do not have Standalone
-        # Connection yet so we use this trick to unsure the session have the
-        # proper cnx loaded. This can be simplified one we have Standalone
-        # Connection object
-        source = self._cnx.repo.system_source
-        txinfos = source.undoable_transactions(self._cnx, ueid, **actionfilters)
-        for txinfo in txinfos:
-            txinfo.req = req or self  # XXX mostly wrong
-        return txinfos
-
-    @_open_only
-    def transaction_info(self, txuuid, req=None):
-        """Return transaction object for the given uid.
-
-        raise `NoSuchTransaction` if not found or if session's user is not
-        allowed (eg not in managers group and the transaction doesn't belong to
-        him).
-        """
-        # the ``with`` dance is transitional. We do not have Standalone
-        # Connection yet so we use this trick to unsure the session have the
-        # proper cnx loaded. This can be simplified one we have Standalone
-        # Connection object
-        txinfo = self._cnx.repo.system_source.tx_info(self._cnx, txuuid)
-        if req:
-            txinfo.req = req
-        else:
-            txinfo.cnx = self
-        return txinfo
-
-    @_open_only
-    def transaction_actions(self, txuuid, public=True):
-        """Return an ordered list of action effectued during that transaction.
-
-        If public is true, return only 'public' actions, eg not ones triggered
-        under the cover by hooks, else return all actions.
-
-        raise `NoSuchTransaction` if the transaction is not found or if
-        session's user is not allowed (eg not in managers group and the
-        transaction doesn't belong to him).
-        """
-        # the ``with`` dance is transitional. We do not have Standalone
-        # Connection yet so we use this trick to unsure the session have the
-        # proper cnx loaded. This can be simplified one we have Standalone
-        # Connection object
-        return self._cnx.repo.system_source.tx_actions(self._cnx, txuuid, public)
-
-    @_open_only
-    def undo_transaction(self, txuuid):
-        """Undo the given transaction. Return potential restoration errors.
-
-        raise `NoSuchTransaction` if not found or if session's user is not
-        allowed (eg not in managers group and the transaction doesn't belong to
-        him).
-        """
-        # the ``with`` dance is transitional. We do not have Standalone
-        # Connection yet so we use this trick to unsure the session have the
-        # proper cnx loaded. This can be simplified one we have Standalone
-        # Connection object
-        return self._cnx.repo.system_source.undo_transaction(self._cnx, txuuid)
-
-    # cache management
-
-    def entity_cache(self, eid):
-        return self._eid_cache[eid]
-
-    def set_entity_cache(self, entity):
-        self._eid_cache[entity.eid] = entity
-
-    def cached_entities(self):
-        return self._eid_cache.values()
-
-    def drop_entity_cache(self, eid=None):
-        if eid is None:
-            self._eid_cache = {}
-        else:
-            del self._eid_cache[eid]
-
-    # deprecated stuff
-
-    @deprecated('[3.19] This is a repoapi.ClientConnection object not a dbapi one')
-    def request(self):
-        return self
-
-    @deprecated('[3.19] This is a repoapi.ClientConnection object not a dbapi one')
-    def cursor(self):
-        return self
-
-    @property
-    @deprecated('[3.19] This is a repoapi.ClientConnection object not a dbapi one')
-    def sessionid(self):
-        return self._session.sessionid
-
-    @property
-    @deprecated('[3.19] This is a repoapi.ClientConnection object not a dbapi one')
-    def connection(self):
-        return self
-
-    @property
-    @deprecated('[3.19] This is a repoapi.ClientConnection object not a dbapi one')
-    def _repo(self):
-        return self._session.repo
+class ClientConnection(Connection):
+    __metaclass__ = class_deprecated
+    __deprecation_warning__ = '[3.20] %(cls)s is deprecated, use Connection instead'
--- a/req.py	Mon May 09 17:24:03 2016 +0200
+++ b/req.py	Tue Jun 21 07:42:30 2016 +0200
@@ -81,7 +81,6 @@
         A special method is needed to ensure the linked user is linked to the
         connection too.
         """
-        # cnx validity is checked by the call to .user_info
         rset = self.eid_rset(orig_user.eid, 'CWUser')
         user_cls = self.vreg['etypes'].etype_class('CWUser')
         user = user_cls(self, rset, row=0, groups=orig_user.groups,
@@ -357,7 +356,7 @@
         for key, val in sorted(newparams.iteritems()):
             query[key] = (self.url_quote(val),)
         query = '&'.join(u'%s=%s' % (param, value)
-                         for param, values in query.items()
+                         for param, values in sorted(query.items())
                          for value in values)
         return urlunsplit((schema, netloc, path, query, fragment))
 
--- a/rqlrewrite.py	Mon May 09 17:24:03 2016 +0200
+++ b/rqlrewrite.py	Tue Jun 21 07:42:30 2016 +0200
@@ -89,7 +89,7 @@
                 mytyperel.r_type = 'is'
                 if len(possibletypes) > 1:
                     node = n.Function('IN')
-                    for etype in possibletypes:
+                    for etype in sorted(possibletypes):
                         node.append(n.Constant(etype, 'etype'))
                 else:
                     etype = iter(possibletypes).next()
--- a/rset.py	Mon May 09 17:24:03 2016 +0200
+++ b/rset.py	Tue Jun 21 07:42:30 2016 +0200
@@ -21,13 +21,16 @@
 
 from warnings import warn
 
+from logilab.common import nullobject
 from logilab.common.decorators import cached, clear_cache, copy_cache
-
 from rql import nodes, stmts
 
 from cubicweb import NotAnEntity, NoResultError, MultipleResultsError
 
 
+_MARKER = nullobject()
+
+
 class ResultSet(object):
     """A result set wraps a RQL query result. This object implements
     partially the list protocol to allow direct use as a list of
@@ -362,12 +365,14 @@
         rset.limited = (limit, offset)
         return rset
 
-    def printable_rql(self, encoded=False):
+    def printable_rql(self, encoded=_MARKER):
         """return the result set's origin rql as a string, with arguments
         substitued
         """
+        if encoded is not _MARKER:
+            warn('[3.21] the "encoded" argument is deprecated', DeprecationWarning)
         encoding = self.req.encoding
-        rqlstr = self.syntax_tree().as_string(encoding, self.args)
+        rqlstr = self.syntax_tree().as_string(kwargs=self.args)
         # sounds like we get encoded or unicode string due to a bug in as_string
         if not encoded:
             if isinstance(rqlstr, unicode):
@@ -478,6 +483,7 @@
         #     new attributes found in this resultset ?
         try:
             entity = req.entity_cache(eid)
+            entity._cw = req
         except KeyError:
             pass
         else:
--- a/schema.py	Mon May 09 17:24:03 2016 +0200
+++ b/schema.py	Tue Jun 21 07:42:30 2016 +0200
@@ -32,13 +32,14 @@
 from logilab.common.textutils import splitstrip
 from logilab.common.graph import get_cycles
 
+import yams
 from yams import BadSchemaDefinition, buildobjs as ybo
 from yams.schema import Schema, ERSchema, EntitySchema, RelationSchema, \
      RelationDefinitionSchema, PermissionMixIn, role_name
-from yams.constraints import BaseConstraint, FormatConstraint
+from yams.constraints import (BaseConstraint, FormatConstraint, BoundaryConstraint,
+                              IntervalBoundConstraint, StaticVocabularyConstraint)
 from yams.reader import (CONSTRAINTS, PyFileReader, SchemaLoader,
-                         obsolete as yobsolete, cleanup_sys_modules,
-                         fill_schema_from_namespace)
+                         cleanup_sys_modules, fill_schema_from_namespace)
 
 from rql import parse, nodes, RQLSyntaxError, TypeResolverException
 from rql.analyze import ETypeResolver
@@ -462,6 +463,13 @@
 ybo.DEFAULT_ATTRPERMS['update'] = ('managers', ERQLExpression('U has_update_permission X'))
 ybo.DEFAULT_ATTRPERMS['add'] = ('managers', ERQLExpression('U has_add_permission X'))
 
+# we don't want 'add' or 'delete' permissions on computed relation types
+# (they're hardcoded to '()' on computed relation definitions)
+if 'add' in yams.DEFAULT_COMPUTED_RELPERMS:
+    del yams.DEFAULT_COMPUTED_RELPERMS['add']
+if 'delete' in yams.DEFAULT_COMPUTED_RELPERMS:
+    del yams.DEFAULT_COMPUTED_RELPERMS['delete']
+
 
 PUB_SYSTEM_ENTITY_PERMS = {
     'read':   ('managers', 'users', 'guests',),
@@ -657,7 +665,7 @@
     groups = self.get_groups(action)
     if _cw.user.matching_groups(groups):
         if DBG:
-            print 'check_perm: %r %r: user matches %s' % (action, _self_str, groups)
+            print ('check_perm: %r %r: user matches %s' % (action, _self_str, groups))
         return
     # if 'owners' in allowed groups, check if the user actually owns this
     # object, if so that's enough
@@ -859,7 +867,9 @@
         return ERQLExpression(expression, mainvars, eid)
 
 
-class CubicWebRelationSchema(RelationSchema):
+class CubicWebRelationSchema(PermissionMixIn, RelationSchema):
+    permissions = {}
+    ACTIONS = ()
 
     def __init__(self, schema=None, rdef=None, eid=None, **kwargs):
         if rdef is not None:
@@ -870,6 +880,17 @@
             eid = getattr(rdef, 'eid', None)
         self.eid = eid
 
+    def init_computed_relation(self, rdef):
+        self.ACTIONS = ('read',)
+        super(CubicWebRelationSchema, self).init_computed_relation(rdef)
+
+    def advertise_new_add_permission(self):
+        pass
+
+    def check_permission_definitions(self):
+        RelationSchema.check_permission_definitions(self)
+        PermissionMixIn.check_permission_definitions(self)
+
     @property
     def meta(self):
         return self.type in META_RTYPES
@@ -1097,7 +1118,7 @@
                     subjtype, rschema.type, objtype,
                     __permissions__={'add': (),
                                      'delete': (),
-                                     'read': ('managers', 'users', 'guests')})
+                                     'read': rschema.permissions['read']})
                 rdef.infered = True
                 self.add_relation_def(rdef)
 
@@ -1108,6 +1129,12 @@
 
 # additional cw specific constraints ###########################################
 
+# these are implemented as CHECK constraints in sql, don't do the work
+# twice
+StaticVocabularyConstraint.check = lambda *args: True
+IntervalBoundConstraint.check = lambda *args: True
+BoundaryConstraint.check = lambda *args: True
+
 class BaseRQLConstraint(RRQLExpression, BaseConstraint):
     """base class for rql constraints"""
     distinct_query = None
@@ -1396,13 +1423,6 @@
             return self.regular_formats + tuple(NEED_PERM_FORMATS)
     return self.regular_formats
 
-# XXX monkey patch PyFileReader.import_erschema until bw_normalize_etype is
-# necessary
-orig_import_erschema = PyFileReader.import_erschema
-def bw_import_erschema(self, ertype, schemamod=None, instantiate=True):
-    return orig_import_erschema(self, bw_normalize_etype(ertype), schemamod, instantiate)
-PyFileReader.import_erschema = bw_import_erschema
-
 # XXX itou for some Statement methods
 from rql import stmts
 orig_get_etype = stmts.ScopeNode.get_etype
@@ -1424,16 +1444,3 @@
 def bw_set_statement_type(self, etype):
     return orig_set_statement_type(self, bw_normalize_etype(etype))
 stmts.Select.set_statement_type = bw_set_statement_type
-
-# XXX deprecated
-
-from yams.constraints import StaticVocabularyConstraint
-
-RichString = moved('yams.buildobjs', 'RichString')
-
-StaticVocabularyConstraint = class_moved(StaticVocabularyConstraint)
-FormatConstraint = class_moved(FormatConstraint)
-
-PyFileReader.context['ERQLExpression'] = yobsolete(ERQLExpression)
-PyFileReader.context['RRQLExpression'] = yobsolete(RRQLExpression)
-PyFileReader.context['WorkflowableEntityType'] = WorkflowableEntityType
--- a/schemas/base.py	Mon May 09 17:24:03 2016 +0200
+++ b/schemas/base.py	Tue Jun 21 07:42:30 2016 +0200
@@ -23,7 +23,7 @@
 from yams.buildobjs import (EntityType, RelationType, RelationDefinition,
                             SubjectRelation,
                             String, TZDatetime, Datetime, Password, Interval,
-                            Boolean)
+                            Boolean, UniqueConstraint)
 from cubicweb.schema import (
     RQLConstraint, WorkflowableEntityType, ERQLExpression, RRQLExpression,
     PUB_SYSTEM_ENTITY_PERMS, PUB_SYSTEM_REL_PERMS, PUB_SYSTEM_ATTR_PERMS,
@@ -184,7 +184,6 @@
     cardinality = '?*'
 
 
-
 class ExternalUri(EntityType):
     """a URI representing an object in external data store"""
     uri = String(required=True, unique=True, maxsize=256,
--- a/schemas/bootstrap.py	Mon May 09 17:24:03 2016 +0200
+++ b/schemas/bootstrap.py	Tue Jun 21 07:42:30 2016 +0200
@@ -38,7 +38,7 @@
     description = RichString(internationalizable=True,
                              description=_('semantic description of this entity type'))
     # necessary to filter using RQL
-    final = Boolean(description=_('automatic'))
+    final = Boolean(default=False, description=_('automatic'))
 
 
 class CWRType(EntityType):
@@ -239,7 +239,7 @@
     """groups allowed to read entities/relations of this type"""
     __permissions__ = PUB_SYSTEM_REL_PERMS
     name = 'read_permission'
-    subject = ('CWEType', 'CWAttribute', 'CWRelation')
+    subject = ('CWEType', 'CWAttribute', 'CWRelation', 'CWComputedRType')
     object = 'CWGroup'
     cardinality = '**'
 
@@ -271,7 +271,7 @@
     """rql expression allowing to read entities/relations of this type"""
     __permissions__ = PUB_SYSTEM_REL_PERMS
     name = 'read_permission'
-    subject = ('CWEType', 'CWAttribute', 'CWRelation')
+    subject = ('CWEType', 'CWAttribute', 'CWRelation', 'CWComputedRType')
     object = 'RQLExpression'
     cardinality = '*?'
     composite = 'subject'
--- a/server/__init__.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/__init__.py	Tue Jun 21 07:42:30 2016 +0200
@@ -280,7 +280,7 @@
         # sort for eid predicatability as expected in some server tests
         for group in sorted(BASE_GROUPS):
             cnx.create_entity('CWGroup', name=unicode(group))
-        admin = create_user(cnx, login, pwd, 'managers')
+        admin = create_user(cnx, login, pwd, u'managers')
         cnx.execute('SET X owned_by U WHERE X is IN (CWGroup,CWSource), U eid %(u)s',
                         {'u': admin.eid})
         cnx.commit()
--- a/server/checkintegrity.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/checkintegrity.py	Tue Jun 21 07:42:30 2016 +0200
@@ -88,11 +88,10 @@
     # to be updated due to the reindexation
     repo = cnx.repo
     dbhelper = repo.system_source.dbhelper
-    with cnx.ensure_cnx_set:
-        cursor = cnx.cnxset.cu
-        if not dbhelper.has_fti_table(cursor):
-            print 'no text index table'
-            dbhelper.init_fti(cursor)
+    cursor = cnx.cnxset.cu
+    if not dbhelper.has_fti_table(cursor):
+        print 'no text index table'
+        dbhelper.init_fti(cursor)
     repo.system_source.do_fti = True  # ensure full-text indexation is activated
     if etypes is None:
         print 'Reindexing entities'
@@ -208,7 +207,7 @@
                                 '  WHERE cs.eid_from=e.eid AND cs.eid_to=s.cw_eid) '
                                 'ORDER BY e.eid')
     for row in cursor.fetchall():
-        sys.stderr.write(msg % row)
+        sys.stderr.write(msg % tuple(row))
     if fix:
         cnx.system_sql('INSERT INTO is_relation (eid_from, eid_to) '
                            'SELECT e.eid, s.cw_eid FROM entities as e, cw_CWEType as s '
@@ -222,7 +221,7 @@
                                 '  WHERE cs.eid_from=e.eid AND cs.eid_to=s.cw_eid) '
                                 'ORDER BY e.eid')
     for row in cursor.fetchall():
-        sys.stderr.write(msg % row)
+        sys.stderr.write(msg % tuple(row))
     if fix:
         cnx.system_sql('INSERT INTO is_instance_of_relation (eid_from, eid_to) '
                            'SELECT e.eid, s.cw_eid FROM entities as e, cw_CWEType as s '
@@ -400,8 +399,7 @@
         with cnx.security_enabled(read=False, write=False): # ensure no read security
             for check in checks:
                 check_func = globals()['check_%s' % check]
-                with cnx.ensure_cnx_set:
-                    check_func(repo.schema, cnx, eids_cache, fix=fix)
+                check_func(repo.schema, cnx, eids_cache, fix=fix)
         if fix:
             cnx.commit()
         else:
@@ -410,6 +408,5 @@
             print 'WARNING: Diagnostic run, nothing has been corrected'
     if reindex:
         cnx.rollback()
-        with cnx.ensure_cnx_set:
-            reindex_entities(repo.schema, cnx, withpb=withpb)
+        reindex_entities(repo.schema, cnx, withpb=withpb)
         cnx.commit()
--- a/server/cwzmq.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/cwzmq.py	Tue Jun 21 07:42:30 2016 +0200
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# copyright 2012-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2012-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -17,8 +17,6 @@
 # You should have received a copy of the GNU Lesser General Public License along
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
 
-import cPickle
-import traceback
 from threading import Thread
 from logging import getLogger
 
@@ -27,16 +25,10 @@
 import zmq.eventloop.zmqstream
 
 from cubicweb import set_log_methods
-from cubicweb.server.server import QuitEvent, Finished
+
 
 ctx = zmq.Context()
 
-def cwproto_to_zmqaddr(address):
-    """ converts a cw-zmq address (like zmqpickle-tcp://<ip>:<port>)
-    into a proper zmq address (tcp://<ip>:<port>)
-    """
-    assert address.startswith('zmqpickle-'), 'bad protocol string %s' % address
-    return address.split('-', 1)[1] # chop the `zmqpickle-` prefix
 
 class ZMQComm(object):
     """
@@ -134,132 +126,5 @@
         self.ioloop.add_callback(lambda: self.stream.setsockopt(zmq.SUBSCRIBE, topic))
 
 
-class ZMQRepositoryServer(object):
-
-    def __init__(self, repository):
-        """make the repository available as a PyRO object"""
-        self.address = None
-        self.repo = repository
-        self.socket = None
-        self.stream = None
-        self.loop = ioloop.IOLoop()
-
-        # event queue
-        self.events = []
-
-    def connect(self, address):
-        self.address = cwproto_to_zmqaddr(address)
-
-    def run(self):
-        """enter the service loop"""
-        # start repository looping tasks
-        self.socket = ctx.socket(zmq.REP)
-        self.stream = zmq.eventloop.zmqstream.ZMQStream(self.socket, io_loop=self.loop)
-        self.stream.bind(self.address)
-        self.info('ZMQ server bound on: %s', self.address)
-
-        self.stream.on_recv(self.process_cmds)
-
-        try:
-            self.loop.start()
-        except zmq.ZMQError:
-            self.warning('ZMQ event loop killed')
-        self.quit()
-
-    def trigger_events(self):
-        """trigger ready events"""
-        for event in self.events[:]:
-            if event.is_ready():
-                self.info('starting event %s', event)
-                event.fire(self)
-                try:
-                    event.update()
-                except Finished:
-                    self.events.remove(event)
-
-    def process_cmd(self, cmd):
-        """Delegate the given command to the repository.
-
-        ``cmd`` is a list of (method_name, args, kwargs)
-        where ``args`` is a list of positional arguments
-        and ``kwargs`` is a dictionnary of named arguments.
-
-        >>> rset = delegate_to_repo(["execute", [sessionid], {'rql': rql}])
-
-        :note1: ``kwargs`` may be ommited
-
-            >>> rset = delegate_to_repo(["execute", [sessionid, rql]])
-
-        :note2: both ``args`` and ``kwargs`` may be omitted
-
-            >>> schema = delegate_to_repo(["get_schema"])
-            >>> schema = delegate_to_repo("get_schema") # also allowed
-
-        """
-        cmd = cPickle.loads(cmd)
-        if not cmd:
-            raise AttributeError('function name required')
-        if isinstance(cmd, basestring):
-            cmd = [cmd]
-        if len(cmd) < 2:
-            cmd.append(())
-        if len(cmd) < 3:
-            cmd.append({})
-        cmd  = list(cmd) + [(), {}]
-        funcname, args, kwargs = cmd[:3]
-        result = getattr(self.repo, funcname)(*args, **kwargs)
-        return result
-
-    def process_cmds(self, cmds):
-        """Callback intended to be used with ``on_recv``.
-
-        Call ``delegate_to_repo`` on each command and send a pickled of
-        each result recursively.
-
-        Any exception are catched, pickled and sent.
-        """
-        try:
-            for cmd in cmds:
-                result = self.process_cmd(cmd)
-                self.send_data(result)
-        except Exception as exc:
-            traceback.print_exc()
-            self.send_data(exc)
-
-    def send_data(self, data):
-        self.socket.send_pyobj(data)
-
-    def quit(self, shutdown_repo=False):
-        """stop the server"""
-        self.info('Quitting ZMQ server')
-        try:
-            self.loop.add_callback(self.loop.stop)
-            self.stream.on_recv(None)
-            self.stream.close()
-        except Exception as e:
-            print e
-            pass
-        if shutdown_repo and not self.repo.shutting_down:
-            event = QuitEvent()
-            event.fire(self)
-
-    # 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(shutdown_repo=True))
-        signal.signal(signal.SIGTERM, lambda x, y, s=self: s.quit(shutdown_repo=True))
-
-
-    # these are overridden by set_log_methods below
-    # only defining here to prevent pylint from complaining
-    @classmethod
-    def info(cls, msg, *a, **kw):
-        pass
-
-
 set_log_methods(Publisher, getLogger('cubicweb.zmq.pub'))
 set_log_methods(Subscriber, getLogger('cubicweb.zmq.sub'))
-set_log_methods(ZMQRepositoryServer, getLogger('cubicweb.zmq.repo'))
--- a/server/edition.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/edition.py	Tue Jun 21 07:42:30 2016 +0200
@@ -103,8 +103,6 @@
         assert not self.saved, 'too late to modify edited attributes'
         super(EditedEntity, self).__setitem__(attr, value)
         self.entity.cw_attr_cache[attr] = value
-        # mark attribute as needing purge by the client
-        self.entity._cw_dont_cache_attribute(attr)
 
     def oldnewvalue(self, attr):
         """returns the couple (old attr value, new attr value)
--- a/server/hook.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/hook.py	Tue Jun 21 07:42:30 2016 +0200
@@ -320,6 +320,7 @@
                 eids_from_to = []
             pruned = self.get_pruned_hooks(cnx, event,
                                            entities, eids_from_to, kwargs)
+
             # by default, hooks are executed with security turned off
             with cnx.security_enabled(read=False):
                 for _kwargs in _iter_kwargs(entities, eids_from_to, kwargs):
@@ -327,10 +328,11 @@
                                    key=lambda x: x.order)
                     debug = server.DEBUG & server.DBG_HOOKS
                     with cnx.security_enabled(write=False):
-                        for hook in hooks:
-                            if debug:
-                                print event, _kwargs, hook
-                            hook()
+                        with cnx.running_hooks_ops():
+                            for hook in hooks:
+                                if debug:
+                                    print event, _kwargs, hook
+                                hook()
 
     def get_pruned_hooks(self, cnx, event, entities, eids_from_to, kwargs):
         """return a set of hooks that should not be considered by filtered_possible objects
@@ -425,10 +427,13 @@
     return req.is_hook_activated(cls)
 
 @objectify_predicate
-def from_dbapi_query(cls, req, **kwargs):
-    if req.running_dbapi_query:
-        return 1
-    return 0
+def issued_from_user_query(cls, req, **kwargs):
+    return 0 if req.hooks_in_progress else 1
+
+from_dbapi_query = class_renamed('from_dbapi_query',
+                                 issued_from_user_query,
+                                 message='[3.21] ')
+
 
 class rechain(object):
     def __init__(self, *iterators):
--- a/server/migractions.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/migractions.py	Tue Jun 21 07:42:30 2016 +0200
@@ -44,7 +44,6 @@
 from logilab.common.decorators import cached, clear_cache
 
 from yams.constraints import SizeConstraint
-from yams.schema2sql import eschema2sql, rschema2sql, unique_index_name
 from yams.schema import RelationDefinitionSchema
 
 from cubicweb import CW_SOFTWARE_ROOT, AuthenticationError, ExecutionError
@@ -56,13 +55,11 @@
 from cubicweb import repoapi
 from cubicweb.migration import MigrationHelper, yes
 from cubicweb.server import hook, schemaserial as ss
+from cubicweb.server.schema2sql import eschema2sql, rschema2sql, unique_index_name
 from cubicweb.server.utils import manager_userpasswd
 from cubicweb.server.sqlutils import sqlexec, SQL_PREFIX
 
 
-def mock_object(**params):
-    return type('Mock', (), params)()
-
 class ClearGroupMap(hook.Hook):
     __regid__ = 'cw.migration.clear_group_mapping'
     __select__ = hook.Hook.__select__ & is_instance('CWGroup')
@@ -94,10 +91,10 @@
             assert repo
             self.cnx = cnx
             self.repo = repo
-            self.session = cnx._session
+            self.session = cnx.session
         elif connect:
             self.repo_connect()
-            self.set_session()
+            self.set_cnx()
         else:
             self.session = None
         # no config on shell to a remote instance
@@ -125,7 +122,9 @@
         self.fs_schema = schema
         self._synchronized = set()
 
-    def set_session(self):
+    # overriden from base MigrationHelper ######################################
+
+    def set_cnx(self):
         try:
             login = self.repo.config.default_admin_config['login']
             pwd = self.repo.config.default_admin_config['password']
@@ -149,9 +148,7 @@
                 print 'aborting...'
                 sys.exit(0)
         self.session = self.repo._get_session(self.cnx.sessionid)
-        self.session.keep_cnxset_mode('transaction')
 
-    # overriden from base MigrationHelper ######################################
 
     @cached
     def repo_connect(self):
@@ -178,15 +175,14 @@
             super(ServerMigrationHelper, self).migrate(vcconf, toupgrade, options)
 
     def cmd_process_script(self, migrscript, funcname=None, *args, **kwargs):
-        with self.cnx._cnx.ensure_cnx_set:
-            try:
-                return super(ServerMigrationHelper, self).cmd_process_script(
-                      migrscript, funcname, *args, **kwargs)
-            except ExecutionError as err:
-                sys.stderr.write("-> %s\n" % err)
-            except BaseException:
-                self.rollback()
-                raise
+        try:
+            return super(ServerMigrationHelper, self).cmd_process_script(
+                  migrscript, funcname, *args, **kwargs)
+        except ExecutionError as err:
+            sys.stderr.write("-> %s\n" % err)
+        except BaseException:
+            self.rollback()
+            raise
 
     # Adjust docstring
     cmd_process_script.__doc__ = MigrationHelper.cmd_process_script.__doc__
@@ -287,12 +283,10 @@
         print '-> database restored.'
 
     def commit(self):
-        if hasattr(self, 'cnx'):
-            self.cnx.commit(free_cnxset=False)
+        self.cnx.commit()
 
     def rollback(self):
-        if hasattr(self, 'cnx'):
-            self.cnx.rollback(free_cnxset=False)
+        self.cnx.rollback()
 
     def rqlexecall(self, rqliter, ask_confirm=False):
         for rql, kwargs in rqliter:
@@ -310,7 +304,7 @@
                         'schema': self.repo.get_schema(),
                         'cnx': self.cnx,
                         'fsschema': self.fs_schema,
-                        'session' : self.cnx._cnx,
+                        'session' : self.cnx,
                         'repo' : self.repo,
                         })
         return context
@@ -961,7 +955,6 @@
                              % (rtype, new.eid, oldeid), ask_confirm=False)
             # delete relations using SQL to avoid relations content removal
             # triggered by schema synchronization hooks.
-            session = self.session
             for rdeftype in ('CWRelation', 'CWAttribute'):
                 thispending = set( (eid for eid, in self.sqlexec(
                     'SELECT cw_eid FROM cw_%s WHERE cw_from_entity=%%(eid)s OR '
@@ -971,10 +964,10 @@
                 # get some validation error on commit since integrity hooks
                 # may think some required relation is missing... This also ensure
                 # repository caches are properly cleanup
-                hook.CleanupDeletedEidsCacheOp.get_instance(session).union(thispending)
+                hook.CleanupDeletedEidsCacheOp.get_instance(self.cnx).union(thispending)
                 # and don't forget to remove record from system tables
                 entities = [self.cnx.entity_from_eid(eid, rdeftype) for eid in thispending]
-                self.repo.system_source.delete_info_multi(self.cnx._cnx, entities)
+                self.repo.system_source.delete_info_multi(self.cnx, entities)
                 self.sqlexec('DELETE FROM cw_%s WHERE cw_from_entity=%%(eid)s OR '
                              'cw_to_entity=%%(eid)s' % rdeftype,
                              {'eid': oldeid}, ask_confirm=False)
@@ -1027,7 +1020,8 @@
             print 'warning: relation type %s is already known, skip addition' % (
                 rtype)
         elif rschema.rule:
-            ss.execschemarql(execute, rschema, ss.crschema2rql(rschema))
+            gmap = self.group_mapping()
+            ss.execschemarql(execute, rschema, ss.crschema2rql(rschema, gmap))
         else:
             # register the relation into CWRType and insert necessary relation
             # definitions
@@ -1086,7 +1080,7 @@
             if not self.confirm('Relation %s is still present in the filesystem schema,'
                                 ' do you really want to drop it?' % oldname,
                                 default='n'):
-                raise SystemExit(1)
+                return
         self.cmd_add_relation_type(newname, commit=True)
         if not self.repo.schema[oldname].rule:
             self.rqlexec('SET X %s Y WHERE X %s Y' % (newname, oldname),
@@ -1284,6 +1278,7 @@
             return 'missing workflow relations, see make_workflowable(%s)' % etype
         for etype in wfof:
             eschema = self.repo.schema[etype]
+            etype = unicode(etype)
             if ensure_workflowable:
                 assert 'in_state' in eschema.subjrels, _missing_wf_rel(etype)
                 assert 'custom_workflow' in eschema.subjrels, _missing_wf_rel(etype)
@@ -1396,7 +1391,7 @@
         indexable entity types
         """
         from cubicweb.server.checkintegrity import reindex_entities
-        reindex_entities(self.repo.schema, self.cnx._cnx, etypes=etypes)
+        reindex_entities(self.repo.schema, self.cnx, etypes=etypes)
 
     @contextmanager
     def cmd_dropped_constraints(self, etype, attrname, cstrtype=None,
@@ -1486,19 +1481,28 @@
         * the actual schema won't be updated until next startup
         """
         rschema = self.repo.schema.rschema(attr)
-        oldtype = rschema.objects(etype)[0]
-        rdefeid = rschema.rdef(etype, oldtype).eid
-        allownull = rschema.rdef(etype, oldtype).cardinality[0] != '1'
+        oldschema = rschema.objects(etype)[0]
+        rdef = rschema.rdef(etype, oldschema)
         sql = ("UPDATE cw_CWAttribute "
                "SET cw_to_entity=(SELECT cw_eid FROM cw_CWEType WHERE cw_name='%s')"
-               "WHERE cw_eid=%s") % (newtype, rdefeid)
+               "WHERE cw_eid=%s") % (newtype, rdef.eid)
         self.sqlexec(sql, ask_confirm=False)
         dbhelper = self.repo.system_source.dbhelper
         sqltype = dbhelper.TYPE_MAPPING[newtype]
-        cursor = self.cnx._cnx.cnxset.cu
-        dbhelper.change_col_type(cursor, 'cw_%s'  % etype, 'cw_%s' % attr, sqltype, allownull)
+        cursor = self.cnx.cnxset.cu
+        allownull = rdef.cardinality[0] != '1'
+        dbhelper.change_col_type(cursor, 'cw_%s' % etype, 'cw_%s' % attr, sqltype, allownull)
         if commit:
             self.commit()
+            # manually update live schema
+            eschema = self.repo.schema[etype]
+            rschema._subj_schemas[eschema].remove(oldschema)
+            rschema._obj_schemas[oldschema].remove(eschema)
+            newschema = self.repo.schema[newtype]
+            rschema._update(eschema, newschema)
+            rdef.object = newschema
+            del rschema.rdefs[(eschema, oldschema)]
+            rschema.rdefs[(eschema, newschema)] = rdef
 
     def cmd_add_entity_type_table(self, etype, commit=True):
         """low level method to create the sql table for an existing entity.
--- a/server/querier.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/querier.py	Tue Jun 21 07:42:30 2016 +0200
@@ -37,6 +37,7 @@
 from cubicweb.server.ssplanner import READ_ONLY_RTYPES, add_types_restriction
 from cubicweb.server.edition import EditedEntity
 from cubicweb.server.ssplanner import SSPlanner
+from cubicweb.statsd_logger import statsd_timeit, statsd_c
 
 ETYPE_PYOBJ_MAP[Binary] = 'Bytes'
 
@@ -75,7 +76,7 @@
 
 def check_relations_read_access(cnx, select, args):
     """Raise :exc:`Unauthorized` if the given user doesn't have credentials to
-    read relations used in the givel syntaxt tree
+    read relations used in the given syntax tree
     """
     # use `term_etype` since we've to deal with rewritten constants here,
     # when used as an external source by another repository.
@@ -516,6 +517,7 @@
             return InsertPlan(self, rqlst, args, cnx)
         return ExecutionPlan(self, rqlst, args, cnx)
 
+    @statsd_timeit
     def execute(self, cnx, rql, args=None, build_descr=True):
         """execute a rql query, return resulting rows and their description in
         a `ResultSet` object
@@ -558,8 +560,10 @@
                         return empty_rset(rql, args)
             rqlst = self._rql_cache[cachekey]
             self.cache_hit += 1
+            statsd_c('cache_hit')
         except KeyError:
             self.cache_miss += 1
+            statsd_c('cache_miss')
             rqlst = self.parse(rql)
             try:
                 # compute solutions for rqlst and return named args in query
@@ -570,7 +574,7 @@
             except UnknownEid:
                 # we want queries such as "Any X WHERE X eid 9999" return an
                 # empty result instead of raising UnknownEid
-                return empty_rset(rql, args, rqlst)
+                return empty_rset(rql, args)
             if args and rql not in self._rql_ck_cache:
                 self._rql_ck_cache[rql] = eidkeys
                 if eidkeys:
@@ -580,9 +584,6 @@
         if rqlst.TYPE != 'select':
             if cnx.read_security:
                 check_no_password_selected(rqlst)
-            # write query, ensure connection's mode is 'write' so connections
-            # won't be released until commit/rollback
-            cnx.mode = 'write'
             cachekey = None
         else:
             if cnx.read_security:
--- a/server/repository.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/repository.py	Tue Jun 21 07:42:30 2016 +0200
@@ -24,34 +24,29 @@
 * brings these classes all together to provide a single access
   point to a cubicweb instance.
 * handles session management
-* provides method for pyro registration, to call if pyro is enabled
 """
 __docformat__ = "restructuredtext en"
 
-import sys
 import threading
 import Queue
 from warnings import warn
 from itertools import chain
 from time import time, localtime, strftime
 from contextlib import contextmanager
-from warnings import warn
 
 from logilab.common.decorators import cached, clear_cache
 from logilab.common.deprecation import deprecated
 
 from yams import BadSchemaDefinition
-from rql import RQLSyntaxError
 from rql.utils import rqlvar_maker
 
 from cubicweb import (CW_MIGRATION_MAP, QueryError,
                       UnknownEid, AuthenticationError, ExecutionError,
-                      BadConnectionId, Unauthorized, ValidationError,
-                      UniqueTogetherError, onevent)
+                      BadConnectionId, ValidationError, Unauthorized,
+                      UniqueTogetherError, onevent, ViolatedConstraint)
 from cubicweb import cwvreg, schema, server
 from cubicweb.server import ShuttingDown, utils, hook, querier, sources
-from cubicweb.server.session import Session, InternalSession, InternalManager
-from cubicweb.server.ssplanner import EditedEntity
+from cubicweb.server.session import Session, InternalManager
 
 NO_CACHE_RELATIONS = set( [('owned_by', 'object'),
                            ('created_by', 'object'),
@@ -59,7 +54,7 @@
                            ])
 
 def prefill_entity_caches(entity):
-    session = entity._cw
+    cnx = entity._cw
     # prefill entity relation caches
     for rschema in entity.e_schema.subject_relations():
         rtype = str(rschema)
@@ -69,14 +64,14 @@
             entity.cw_attr_cache.setdefault(rtype, None)
         else:
             entity.cw_set_relation_cache(rtype, 'subject',
-                                         session.empty_rset())
+                                         cnx.empty_rset())
     for rschema in entity.e_schema.object_relations():
         rtype = str(rschema)
         if rtype in schema.VIRTUAL_RTYPES or (rtype, 'object') in NO_CACHE_RELATIONS:
             continue
-        entity.cw_set_relation_cache(rtype, 'object', session.empty_rset())
+        entity.cw_set_relation_cache(rtype, 'object', cnx.empty_rset())
 
-def del_existing_rel_if_needed(session, eidfrom, rtype, eidto):
+def del_existing_rel_if_needed(cnx, eidfrom, rtype, eidto):
     """delete existing relation when adding a new one if card is 1 or ?
 
     have to be done once the new relation has been inserted to avoid having
@@ -86,9 +81,9 @@
     hooks order hazardness
     """
     # skip that if integrity explicitly disabled
-    if not session.is_hook_category_activated('activeintegrity'):
+    if not cnx.is_hook_category_activated('activeintegrity'):
         return
-    rdef = session.rtype_eids_rdef(rtype, eidfrom, eidto)
+    rdef = cnx.rtype_eids_rdef(rtype, eidfrom, eidto)
     card = rdef.cardinality
     # one may be tented to check for neweids but this may cause more than one
     # relation even with '1?'  cardinality if thoses relations are added in the
@@ -102,34 +97,34 @@
     # * we don't want read permissions to be applied but we want delete
     #   permission to be checked
     if card[0] in '1?':
-        with session.security_enabled(read=False):
-            session.execute('DELETE X %s Y WHERE X eid %%(x)s, '
-                            'NOT Y eid %%(y)s' % rtype,
-                                {'x': eidfrom, 'y': eidto})
+        with cnx.security_enabled(read=False):
+            cnx.execute('DELETE X %s Y WHERE X eid %%(x)s, '
+                        'NOT Y eid %%(y)s' % rtype,
+                        {'x': eidfrom, 'y': eidto})
     if card[1] in '1?':
-        with session.security_enabled(read=False):
-            session.execute('DELETE X %s Y WHERE Y eid %%(y)s, '
-                            'NOT X eid %%(x)s' % rtype,
-                            {'x': eidfrom, 'y': eidto})
+        with cnx.security_enabled(read=False):
+            cnx.execute('DELETE X %s Y WHERE Y eid %%(y)s, '
+                        'NOT X eid %%(x)s' % rtype,
+                        {'x': eidfrom, 'y': eidto})
 
 
-def preprocess_inlined_relations(session, entity):
+def preprocess_inlined_relations(cnx, entity):
     """when an entity is added, check if it has some inlined relation which
     requires to be extrated for proper call hooks
     """
     relations = []
-    activeintegrity = session.is_hook_category_activated('activeintegrity')
+    activeintegrity = cnx.is_hook_category_activated('activeintegrity')
     eschema = entity.e_schema
     for attr in entity.cw_edited:
         rschema = eschema.subjrels[attr]
         if not rschema.final: # inlined relation
             value = entity.cw_edited[attr]
             relations.append((attr, value))
-            session.update_rel_cache_add(entity.eid, attr, value)
-            rdef = session.rtype_eids_rdef(attr, entity.eid, value)
+            cnx.update_rel_cache_add(entity.eid, attr, value)
+            rdef = cnx.rtype_eids_rdef(attr, entity.eid, value)
             if rdef.cardinality[1] in '1?' and activeintegrity:
-                with session.security_enabled(read=False):
-                    session.execute('DELETE X %s Y WHERE Y eid %%(y)s' % attr,
+                with cnx.security_enabled(read=False):
+                    cnx.execute('DELETE X %s Y WHERE Y eid %%(y)s' % attr,
                                     {'x': entity.eid, 'y': value})
     return relations
 
@@ -151,8 +146,6 @@
 class Repository(object):
     """a repository provides access to a set of persistent storages for
     entities and relations
-
-    XXX protect pyro access
     """
 
     def __init__(self, config, tasks_manager=None, vreg=None):
@@ -162,17 +155,11 @@
         self.vreg = vreg
         self._tasks_manager = tasks_manager
 
-        self.pyro_registered = False
-        self.pyro_uri = None
-        # every pyro client is handled in its own thread; map these threads to
-        # the session we opened for them so we can clean up when they go away
-        self._pyro_sessions = {}
         self.app_instances_bus = NullEventBus()
         self.info('starting repository from %s', self.config.apphome)
         # dictionary of opened sessions
         self._sessions = {}
 
-
         # list of functions to be called at regular interval
         # list of running threads
         self._running_threads = []
@@ -234,7 +221,7 @@
         if config.quick_start or config.creating or not config.read_instance_schema:
             # load schema from the file system
             if not config.creating:
-                self.warning("set fs instance'schema")
+                self.info("set fs instance'schema")
             self.set_schema(config.load_schema(expand_cubes=True))
         else:
             # normal start: load the instance schema from the database
@@ -435,10 +422,6 @@
             except Exception:
                 self.exception('error while closing %s' % cnxset)
                 continue
-        if self.pyro_registered:
-            if self._use_pyrons():
-                pyro_unregister(self.config)
-            self.pyro_uri = None
         hits, misses = self.querier.cache_hit, self.querier.cache_miss
         try:
             self.info('rql st cache hit/miss: %s/%s (%s%% hits)', hits, misses,
@@ -461,8 +444,7 @@
         for source in self.sources_by_uri.itervalues():
             if self.config.source_enabled(source) and source.support_entity('CWUser'):
                 try:
-                    with cnx.ensure_cnx_set:
-                        return source.authenticate(cnx, login, **authinfo)
+                    return source.authenticate(cnx, login, **authinfo)
                 except AuthenticationError:
                     continue
         else:
@@ -481,19 +463,18 @@
 
     def _build_user(self, cnx, eid):
         """return a CWUser entity for user with the given eid"""
-        with cnx.ensure_cnx_set:
-            cls = self.vreg['etypes'].etype_class('CWUser')
-            st = cls.fetch_rqlst(cnx.user, ordermethod=None)
-            st.add_eid_restriction(st.get_variable('X'), 'x', 'Substitute')
-            rset = cnx.execute(st.as_string(), {'x': eid})
-            assert len(rset) == 1, rset
-            cwuser = rset.get_entity(0, 0)
-            # pylint: disable=W0104
-            # prefetch / cache cwuser's groups and properties. This is especially
-            # useful for internal sessions to avoid security insertions
-            cwuser.groups
-            cwuser.properties
-            return cwuser
+        cls = self.vreg['etypes'].etype_class('CWUser')
+        st = cls.fetch_rqlst(cnx.user, ordermethod=None)
+        st.add_eid_restriction(st.get_variable('X'), 'x', 'Substitute')
+        rset = cnx.execute(st.as_string(), {'x': eid})
+        assert len(rset) == 1, rset
+        cwuser = rset.get_entity(0, 0)
+        # pylint: disable=W0104
+        # prefetch / cache cwuser's groups and properties. This is especially
+        # useful for internal sessions to avoid security insertions
+        cwuser.groups
+        cwuser.properties
+        return cwuser
 
     # public (dbapi) interface ################################################
 
@@ -640,14 +621,14 @@
         for k in chain(fetch_attrs, query_attrs):
             if k not in cwuserattrs:
                 raise Exception('bad input for find_user')
-        with self.internal_session() as session:
+        with self.internal_cnx() as cnx:
             varmaker = rqlvar_maker()
             vars = [(attr, varmaker.next()) for attr in fetch_attrs]
             rql = 'Any %s WHERE X is CWUser, ' % ','.join(var[1] for var in vars)
             rql += ','.join('X %s %s' % (var[0], var[1]) for var in vars) + ','
-            rset = session.execute(rql + ','.join('X %s %%(%s)s' % (attr, attr)
-                                                  for attr in query_attrs),
-                                   query_attrs)
+            rset = cnx.execute(rql + ','.join('X %s %%(%s)s' % (attr, attr)
+                                              for attr in query_attrs),
+                               query_attrs)
             return rset.rows
 
     def new_session(self, login, **kwargs):
@@ -662,12 +643,6 @@
             # try to get a user object
             user = self.authenticate_user(cnx, login, **kwargs)
         session = Session(user, self, cnxprops)
-        if threading.currentThread() in self._pyro_sessions:
-            # assume no pyro client does one get_repository followed by
-            # multiple repo.connect
-            assert self._pyro_sessions[threading.currentThread()] == None
-            self.debug('record session %s', session)
-            self._pyro_sessions[threading.currentThread()] = session
         user._cw = user.cw_rset.req = session
         user.cw_clear_relation_cache()
         self._sessions[session.sessionid] = session
@@ -683,190 +658,26 @@
         """open a new session for a given user and return its sessionid """
         return self.new_session(login, **kwargs).sessionid
 
-    def execute(self, sessionid, rqlstring, args=None, build_descr=True,
-                txid=None):
-        """execute a RQL query
-
-        * rqlstring should be a unicode string or a plain ascii string
-        * args the optional parameters used in the query
-        * build_descr is a flag indicating if the description should be
-          built on select queries
-        """
-        session = self._get_session(sessionid, setcnxset=True, txid=txid)
-        try:
-            try:
-                rset = self.querier.execute(session, rqlstring, args,
-                                            build_descr)
-                # NOTE: the web front will (re)build it when needed
-                #       e.g in facets
-                #       Zeroed to avoid useless overhead with pyro
-                rset._rqlst = None
-                return rset
-            except (ValidationError, Unauthorized, RQLSyntaxError):
-                raise
-            except Exception:
-                # FIXME: check error to catch internal errors
-                self.exception('unexpected error while executing %s with %s', rqlstring, args)
-                raise
-        finally:
-            session.free_cnxset()
-
-    @deprecated('[3.19] use .entity_metas(sessionid, eid, txid) instead')
-    def describe(self, sessionid, eid, txid=None):
-        """return a tuple `(type, physical source uri, extid, actual source
-        uri)` for the entity of the given `eid`
-
-        As of 3.19, physical source uri is always the system source.
-        """
-        session = self._get_session(sessionid, setcnxset=True, txid=txid)
-        try:
-            etype, extid, source = self.type_and_source_from_eid(eid, session)
-            return etype, source, extid, source
-        finally:
-            session.free_cnxset()
-
-    def entity_metas(self, sessionid, eid, txid=None):
-        """return a dictionary containing meta-datas for the entity of the given
-        `eid`. Available keys are:
-
-        * 'type', the entity's type name,
-
-        * 'source', the name of the source from which this entity's coming from,
-
-        * 'extid', the identifierfor this entity in its originating source, as
-          an encoded string or `None` for entities from the 'system' source.
-        """
-        session = self._get_session(sessionid, setcnxset=True, txid=txid)
-        try:
-            etype, extid, source = self.type_and_source_from_eid(eid, session)
-            return {'type': etype, 'source': source, 'extid': extid}
-        finally:
-            session.free_cnxset()
-
     def check_session(self, sessionid):
         """raise `BadConnectionId` if the connection is no more valid, else
         return its latest activity timestamp.
         """
-        return self._get_session(sessionid, setcnxset=False).timestamp
-
-    @deprecated('[3.19] use session or transaction data')
-    def get_shared_data(self, sessionid, key, default=None, pop=False, txdata=False):
-        """return value associated to key in the session's data dictionary or
-        session's transaction's data if `txdata` is true.
-
-        If pop is True, value will be removed from the dictionary.
-
-        If key isn't defined in the dictionary, value specified by the
-        `default` argument will be returned.
-        """
-        session = self._get_session(sessionid, setcnxset=False)
-        return session.get_shared_data(key, default, pop, txdata)
-
-    @deprecated('[3.19] use session or transaction data')
-    def set_shared_data(self, sessionid, key, value, txdata=False):
-        """set value associated to `key` in shared data
-
-        if `txdata` is true, the value will be added to the repository session's
-        transaction's data which are cleared on commit/rollback of the current
-        transaction.
-        """
-        session = self._get_session(sessionid, setcnxset=False)
-        session.set_shared_data(key, value, txdata)
-
-    def commit(self, sessionid, txid=None):
-        """commit transaction for the session with the given id"""
-        self.debug('begin commit for session %s', sessionid)
-        try:
-            session = self._get_session(sessionid)
-            session.set_cnx(txid)
-            return session.commit()
-        except (ValidationError, Unauthorized):
-            raise
-        except Exception:
-            self.exception('unexpected error')
-            raise
-
-    def rollback(self, sessionid, txid=None):
-        """commit transaction for the session with the given id"""
-        self.debug('begin rollback for session %s', sessionid)
-        try:
-            session = self._get_session(sessionid)
-            session.set_cnx(txid)
-            session.rollback()
-        except Exception:
-            self.exception('unexpected error')
-            raise
+        return self._get_session(sessionid).timestamp
 
     def close(self, sessionid, txid=None, checkshuttingdown=True):
         """close the session with the given id"""
         session = self._get_session(sessionid, txid=txid,
                                     checkshuttingdown=checkshuttingdown)
         # operation uncommited before close are rolled back before hook is called
-        if session._cnx._session_handled:
-            session._cnx.rollback(free_cnxset=False)
         with session.new_cnx() as cnx:
             self.hm.call_hooks('session_close', cnx)
             # commit connection at this point in case write operation has been
             # done during `session_close` hooks
             cnx.commit()
         session.close()
-        if threading.currentThread() in self._pyro_sessions:
-            self._pyro_sessions[threading.currentThread()] = None
         del self._sessions[sessionid]
         self.info('closed session %s for user %s', sessionid, session.user.login)
 
-    def call_service(self, sessionid, regid, **kwargs):
-        """
-        See :class:`cubicweb.dbapi.Connection.call_service`
-        and :class:`cubicweb.server.Service`
-        """
-        # XXX lack a txid
-        session = self._get_session(sessionid)
-        return session._cnx.call_service(regid, **kwargs)
-
-    def user_info(self, sessionid, props=None):
-        """this method should be used by client to:
-        * check session id validity
-        * update user information on each user's request (i.e. groups and
-          custom properties)
-        """
-        user = self._get_session(sessionid, setcnxset=False).user
-        return user.eid, user.login, user.groups, user.properties
-
-    def undoable_transactions(self, sessionid, ueid=None, txid=None,
-                              **actionfilters):
-        """See :class:`cubicweb.dbapi.Connection.undoable_transactions`"""
-        session = self._get_session(sessionid, setcnxset=True, txid=txid)
-        try:
-            return self.system_source.undoable_transactions(session, ueid,
-                                                            **actionfilters)
-        finally:
-            session.free_cnxset()
-
-    def transaction_info(self, sessionid, txuuid, txid=None):
-        """See :class:`cubicweb.dbapi.Connection.transaction_info`"""
-        session = self._get_session(sessionid, setcnxset=True, txid=txid)
-        try:
-            return self.system_source.tx_info(session, txuuid)
-        finally:
-            session.free_cnxset()
-
-    def transaction_actions(self, sessionid, txuuid, public=True, txid=None):
-        """See :class:`cubicweb.dbapi.Connection.transaction_actions`"""
-        session = self._get_session(sessionid, setcnxset=True, txid=txid)
-        try:
-            return self.system_source.tx_actions(session, txuuid, public)
-        finally:
-            session.free_cnxset()
-
-    def undo_transaction(self, sessionid, txuuid, txid=None):
-        """See :class:`cubicweb.dbapi.Connection.undo_transaction`"""
-        session = self._get_session(sessionid, setcnxset=True, txid=txid)
-        try:
-            return self.system_source.undo_transaction(session, txuuid)
-        finally:
-            session.free_cnxset()
-
     # session handling ########################################################
 
     def close_sessions(self):
@@ -891,27 +702,6 @@
                 nbclosed += 1
         return nbclosed
 
-    @deprecated("[3.19] use internal_cnx now\n"
-                "(Beware that integrity hook are now enabled by default)")
-    def internal_session(self, cnxprops=None, safe=False):
-        """return a dbapi like connection/cursor using internal user which have
-        every rights on the repository. The `safe` argument is a boolean flag
-        telling if integrity hooks should be activated or not.
-
-        /!\ the safe argument is False by default.
-
-        *YOU HAVE TO* commit/rollback or close (rollback implicitly) the
-        session once the job's done, else you'll leak connections set up to the
-        time where no one is available, causing irremediable freeze...
-        """
-        session = InternalSession(self, cnxprops)
-        if not safe:
-            session.disable_hook_categories('integrity')
-        session.disable_hook_categories('security')
-        session._cnx.ctx_count += 1
-        session.set_cnxset()
-        return session
-
     @contextmanager
     def internal_cnx(self):
         """Context manager returning a Connection using internal user which have
@@ -920,14 +710,14 @@
         Beware that unlike the older :meth:`internal_session`, internal
         connections have all hooks beside security enabled.
         """
-        with InternalSession(self) as session:
+        with Session(InternalManager(), self) as session:
             with session.new_cnx() as cnx:
+                cnx.user._cw = cnx  # XXX remove when "vreg = user._cw.vreg"
+                                    # hack in entity.py is gone
                 with cnx.security_enabled(read=False, write=False):
-                    with cnx.ensure_cnx_set:
-                        yield cnx
+                    yield cnx
 
-    def _get_session(self, sessionid, setcnxset=False, txid=None,
-                     checkshuttingdown=True):
+    def _get_session(self, sessionid, txid=None, checkshuttingdown=True):
         """return the session associated with the given session identifier"""
         if checkshuttingdown and self.shutting_down:
             raise ShuttingDown('Repository is shutting down')
@@ -935,9 +725,6 @@
             session = self._sessions[sessionid]
         except KeyError:
             raise BadConnectionId('No such session %s' % sessionid)
-        if setcnxset:
-            session.set_cnx(txid) # must be done before set_cnxset
-            session.set_cnxset()
         return session
 
     # data sources handling ###################################################
@@ -977,11 +764,11 @@
         """return the type of the entity with id <eid>"""
         return self.type_and_source_from_eid(eid, cnx)[0]
 
-    def querier_cache_key(self, session, rql, args, eidkeys):
+    def querier_cache_key(self, cnx, rql, args, eidkeys):
         cachekey = [rql]
         for key in sorted(eidkeys):
             try:
-                etype = self.type_from_eid(args[key], session)
+                etype = self.type_from_eid(args[key], cnx)
             except KeyError:
                 raise QueryError('bad cache key %s (no value)' % key)
             except TypeError:
@@ -1020,13 +807,7 @@
             return self._extid_cache[extid]
         except KeyError:
             pass
-        try:
-            # bw compat: cnx may be a session, get at the Connection
-            cnx = cnx._cnx
-        except AttributeError:
-            pass
-        with cnx.ensure_cnx_set:
-            eid = self.system_source.extid2eid(cnx, extid)
+        eid = self.system_source.extid2eid(cnx, extid)
         if eid is not None:
             self._extid_cache[extid] = eid
             self._type_source_cache[eid] = (etype, extid, source.uri)
@@ -1034,123 +815,80 @@
         if not insert:
             return
         # no link between extid and eid, create one
-        with cnx.ensure_cnx_set:
-            # write query, ensure connection's mode is 'write' so connections
-            # won't be released until commit/rollback
-            cnx.mode = 'write'
-            try:
-                eid = self.system_source.create_eid(cnx)
-                self._extid_cache[extid] = eid
-                self._type_source_cache[eid] = (etype, extid, source.uri)
-                entity = source.before_entity_insertion(
-                    cnx, extid, etype, eid, sourceparams)
+        # write query, ensure connection's mode is 'write' so connections
+        # won't be released until commit/rollback
+        try:
+            eid = self.system_source.create_eid(cnx)
+            self._extid_cache[extid] = eid
+            self._type_source_cache[eid] = (etype, extid, source.uri)
+            entity = source.before_entity_insertion(
+                cnx, extid, etype, eid, sourceparams)
+            if source.should_call_hooks:
+                # get back a copy of operation for later restore if
+                # necessary, see below
+                pending_operations = cnx.pending_operations[:]
+                self.hm.call_hooks('before_add_entity', cnx, entity=entity)
+            self.add_info(cnx, entity, source, extid)
+            source.after_entity_insertion(cnx, extid, entity, sourceparams)
+            if source.should_call_hooks:
+                self.hm.call_hooks('after_add_entity', cnx, entity=entity)
+            return eid
+        except Exception:
+            # XXX do some cleanup manually so that the transaction has a
+            # chance to be commited, with simply this entity discarded
+            self._extid_cache.pop(extid, None)
+            self._type_source_cache.pop(eid, None)
+            if 'entity' in locals():
+                hook.CleanupDeletedEidsCacheOp.get_instance(cnx).add_data(entity.eid)
+                self.system_source.delete_info_multi(cnx, [entity])
                 if source.should_call_hooks:
-                    # get back a copy of operation for later restore if
-                    # necessary, see below
-                    pending_operations = cnx.pending_operations[:]
-                    self.hm.call_hooks('before_add_entity', cnx, entity=entity)
-                self.add_info(cnx, entity, source, extid)
-                source.after_entity_insertion(cnx, extid, entity, sourceparams)
-                if source.should_call_hooks:
-                    self.hm.call_hooks('after_add_entity', cnx, entity=entity)
-                return eid
-            except Exception:
-                # XXX do some cleanup manually so that the transaction has a
-                # chance to be commited, with simply this entity discarded
-                self._extid_cache.pop(extid, None)
-                self._type_source_cache.pop(eid, None)
-                if 'entity' in locals():
-                    hook.CleanupDeletedEidsCacheOp.get_instance(cnx).add_data(entity.eid)
-                    self.system_source.delete_info_multi(cnx, [entity])
-                    if source.should_call_hooks:
-                        cnx.pending_operations = pending_operations
-                raise
+                    cnx.pending_operations = pending_operations
+            raise
 
-    def add_info(self, session, entity, source, extid=None):
+    def add_info(self, cnx, entity, source, extid=None):
         """add type and source info for an eid into the system table,
         and index the entity with the full text index
         """
         # begin by inserting eid/type/source/extid into the entities table
-        hook.CleanupNewEidsCacheOp.get_instance(session).add_data(entity.eid)
-        self.system_source.add_info(session, entity, source, extid)
+        hook.CleanupNewEidsCacheOp.get_instance(cnx).add_data(entity.eid)
+        self.system_source.add_info(cnx, entity, source, extid)
 
-    def delete_info(self, session, entity, sourceuri):
-        """called by external source when some entity known by the system source
-        has been deleted in the external source
+    def _delete_cascade_multi(self, cnx, entities):
+        """same as _delete_cascade but accepts a list of entities with
+        the same etype and belonging to the same source.
         """
-        # mark eid as being deleted in session info and setup cache update
-        # operation
-        hook.CleanupDeletedEidsCacheOp.get_instance(session).add_data(entity.eid)
-        self._delete_info(session, entity, sourceuri)
-
-    def _delete_info(self, session, entity, sourceuri):
-        """delete system information on deletion of an entity:
-
-        * delete all remaining relations from/to this entity
-        * call delete info on the system source
-        """
-        pendingrtypes = session.transaction_data.get('pendingrtypes', ())
+        pendingrtypes = cnx.transaction_data.get('pendingrtypes', ())
         # delete remaining relations: if user can delete the entity, he can
         # delete all its relations without security checking
-        with session.security_enabled(read=False, write=False):
-            eid = entity.eid
-            for rschema, _, role in entity.e_schema.relation_definitions():
-                if rschema.rule:
-                    continue # computed relation
-                rtype = rschema.type
-                if rtype in schema.VIRTUAL_RTYPES or rtype in pendingrtypes:
-                    continue
-                if role == 'subject':
-                    # don't skip inlined relation so they are regularly
-                    # deleted and so hooks are correctly called
-                    rql = 'DELETE X %s Y WHERE X eid %%(x)s' % rtype
-                else:
-                    rql = 'DELETE Y %s X WHERE X eid %%(x)s' % rtype
-                try:
-                    session.execute(rql, {'x': eid}, build_descr=False)
-                except Exception:
-                    if self.config.mode == 'test':
+        with cnx.security_enabled(read=False, write=False):
+            in_eids = ','.join([str(_e.eid) for _e in entities])
+            with cnx.running_hooks_ops():
+                for rschema, _, role in entities[0].e_schema.relation_definitions():
+                    if rschema.rule:
+                        continue # computed relation
+                    rtype = rschema.type
+                    if rtype in schema.VIRTUAL_RTYPES or rtype in pendingrtypes:
+                        continue
+                    if role == 'subject':
+                        # don't skip inlined relation so they are regularly
+                        # deleted and so hooks are correctly called
+                        rql = 'DELETE X %s Y WHERE X eid IN (%s)' % (rtype, in_eids)
+                    else:
+                        rql = 'DELETE Y %s X WHERE X eid IN (%s)' % (rtype, in_eids)
+                    try:
+                        cnx.execute(rql, build_descr=False)
+                    except ValidationError:
                         raise
-                    self.exception('error while cascading delete for entity %s '
-                                   'from %s. RQL: %s', entity, sourceuri, rql)
-        self.system_source.delete_info_multi(session, [entity])
-
-    def _delete_info_multi(self, session, entities):
-        """same as _delete_info but accepts a list of entities with
-        the same etype and belinging to the same source.
-        """
-        pendingrtypes = session.transaction_data.get('pendingrtypes', ())
-        # delete remaining relations: if user can delete the entity, he can
-        # delete all its relations without security checking
-        with session.security_enabled(read=False, write=False):
-            in_eids = ','.join([str(_e.eid) for _e in entities])
-            for rschema, _, role in entities[0].e_schema.relation_definitions():
-                if rschema.rule:
-                    continue # computed relation
-                rtype = rschema.type
-                if rtype in schema.VIRTUAL_RTYPES or rtype in pendingrtypes:
-                    continue
-                if role == 'subject':
-                    # don't skip inlined relation so they are regularly
-                    # deleted and so hooks are correctly called
-                    rql = 'DELETE X %s Y WHERE X eid IN (%s)' % (rtype, in_eids)
-                else:
-                    rql = 'DELETE Y %s X WHERE X eid IN (%s)' % (rtype, in_eids)
-                try:
-                    session.execute(rql, build_descr=False)
-                except ValidationError:
-                    raise
-                except Unauthorized:
-                    self.exception('Unauthorized exception while cascading delete for entity %s. '
-                                   'RQL: %s.\nThis should not happen since security is disabled here.',
-                                   entities, rql)
-                    raise
-                except Exception:
-                    if self.config.mode == 'test':
+                    except Unauthorized:
+                        self.exception('Unauthorized exception while cascading delete for entity %s. '
+                                       'RQL: %s.\nThis should not happen since security is disabled here.',
+                                       entities, rql)
                         raise
-                    self.exception('error while cascading delete for entity %s. RQL: %s',
-                                   entities, rql)
-        self.system_source.delete_info_multi(session, entities)
+                    except Exception:
+                        if self.config.mode == 'test':
+                            raise
+                        self.exception('error while cascading delete for entity %s. RQL: %s',
+                                       entities, rql)
 
     def init_entity_caches(self, cnx, entity, source):
         """add entity to connection entities cache and repo's extid cache.
@@ -1188,13 +926,13 @@
         edited.set_defaults()
         if cnx.is_hook_category_activated('integrity'):
             edited.check(creation=True)
+        self.add_info(cnx, entity, source, extid)
         try:
             source.add_entity(cnx, entity)
-        except UniqueTogetherError as exc:
+        except (UniqueTogetherError, ViolatedConstraint) as exc:
             userhdlr = cnx.vreg['adapters'].select(
                 'IUserFriendlyError', cnx, entity=entity, exc=exc)
             userhdlr.raise_user_exception()
-        self.add_info(cnx, entity, source, extid)
         edited.saved = entity._cw_is_saved = True
         # trigger after_add_entity after after_add_relation
         self.hm.call_hooks('after_add_entity', cnx, entity=entity)
@@ -1254,7 +992,7 @@
             try:
                 source.update_entity(cnx, entity)
                 edited.saved = True
-            except UniqueTogetherError as exc:
+            except (UniqueTogetherError, ViolatedConstraint) as exc:
                 userhdlr = cnx.vreg['adapters'].select(
                     'IUserFriendlyError', cnx, entity=entity, exc=exc)
                 userhdlr.raise_user_exception()
@@ -1309,8 +1047,9 @@
             if server.DEBUG & server.DBG_REPO:
                 print 'DELETE entities', etype, [entity.eid for entity in entities]
             self.hm.call_hooks('before_delete_entity', cnx, entities=entities)
-            self._delete_info_multi(cnx, entities)
+            self._delete_cascade_multi(cnx, entities)
             source.delete_entities(cnx, entities)
+            source.delete_info_multi(cnx, entities)
             self.hm.call_hooks('after_delete_entity', cnx, entities=entities)
         # don't clear cache here, it is done in a hook on commit
 
@@ -1341,7 +1080,7 @@
                     continue
                 # take care to relation of cardinality '?1', as all eids will
                 # be inserted later, we've remove duplicated eids since they
-                # won't be catched by `del_existing_rel_if_needed`
+                # won't be caught by `del_existing_rel_if_needed`
                 rdef = cnx.rtype_eids_rdef(rtype, subjeid, objeid)
                 card = rdef.cardinality
                 if card[0] in '?1':
@@ -1392,79 +1131,12 @@
                            eidfrom=subject, rtype=rtype, eidto=object)
 
 
-    # pyro handling ###########################################################
-
-    @property
-    @cached
-    def pyro_appid(self):
-        from logilab.common import pyro_ext as pyro
-        config = self.config
-        appid = '%s.%s' % pyro.ns_group_and_id(
-            config['pyro-instance-id'] or config.appid,
-            config['pyro-ns-group'])
-        # ensure config['pyro-instance-id'] is a full qualified pyro name
-        config['pyro-instance-id'] = appid
-        return appid
-
-    def _use_pyrons(self):
-        """return True if the pyro-ns-host is set to something else
-        than NO_PYRONS, meaning we want to go through a pyro
-        nameserver"""
-        return self.config['pyro-ns-host'] != 'NO_PYRONS'
-
-    def pyro_register(self, host=''):
-        """register the repository as a pyro object"""
-        from logilab.common import pyro_ext as pyro
-        daemon = pyro.register_object(self, self.pyro_appid,
-                                      daemonhost=self.config['pyro-host'],
-                                      nshost=self.config['pyro-ns-host'],
-                                      use_pyrons=self._use_pyrons())
-        self.info('repository registered as a pyro object %s', self.pyro_appid)
-        self.pyro_uri =  pyro.get_object_uri(self.pyro_appid)
-        self.info('pyro uri is: %s', self.pyro_uri)
-        self.pyro_registered = True
-        # register a looping task to regularly ensure we're still registered
-        # into the pyro name server
-        if self._use_pyrons():
-            self.looping_task(60*10, self._ensure_pyro_ns)
-        pyro_sessions = self._pyro_sessions
-        # install hacky function to free cnxset
-        def handleConnection(conn, tcpserver, sessions=pyro_sessions):
-            sessions[threading.currentThread()] = None
-            return tcpserver.getAdapter().__class__.handleConnection(tcpserver.getAdapter(), conn, tcpserver)
-        daemon.getAdapter().handleConnection = handleConnection
-        def removeConnection(conn, sessions=pyro_sessions):
-            daemon.__class__.removeConnection(daemon, conn)
-            session = sessions.pop(threading.currentThread(), None)
-            if session is None:
-                # client was not yet connected to the repo
-                return
-            if not session.closed:
-                self.close(session.sessionid)
-        daemon.removeConnection = removeConnection
-        return daemon
-
-    def _ensure_pyro_ns(self):
-        if not self._use_pyrons():
-            return
-        from logilab.common import pyro_ext as pyro
-        pyro.ns_reregister(self.pyro_appid, nshost=self.config['pyro-ns-host'])
-        self.info('repository re-registered as a pyro object %s',
-                  self.pyro_appid)
 
 
     # these are overridden by set_log_methods below
     # only defining here to prevent pylint from complaining
     info = warning = error = critical = exception = debug = lambda msg, *a, **kw: None
 
-
-def pyro_unregister(config):
-    """unregister the repository from the pyro name server"""
-    from logilab.common.pyro_ext import ns_unregister
-    appid = config['pyro-instance-id'] or config.appid
-    ns_unregister(appid, config['pyro-ns-group'], config['pyro-ns-host'])
-
-
 from logging import getLogger
 from cubicweb import set_log_methods
 set_log_methods(Repository, getLogger('cubicweb.repository'))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/schema2sql.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,293 @@
+# copyright 2004-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of cubicweb.
+#
+# yams is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# yams is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with yams. If not, see <http://www.gnu.org/licenses/>.
+"""write a schema as sql"""
+
+__docformat__ = "restructuredtext en"
+
+from hashlib import md5
+
+from six import string_types
+from six.moves import range
+
+from yams.constraints import (SizeConstraint, UniqueConstraint, Attribute,
+                              NOW, TODAY)
+
+# default are usually not handled at the sql level. If you want them, set
+# SET_DEFAULT to True
+SET_DEFAULT = False
+
+def rschema_has_table(rschema, skip_relations):
+    """Return True if the given schema should have a table in the database"""
+    return not (rschema.final or rschema.inlined or rschema.rule or rschema.type in skip_relations)
+
+
+def schema2sql(dbhelper, schema, skip_entities=(), skip_relations=(), prefix=''):
+    """write to the output stream a SQL schema to store the objects
+    corresponding to the given schema
+    """
+    output = []
+    w = output.append
+    for etype in sorted(schema.entities()):
+        eschema = schema.eschema(etype)
+        if eschema.final or eschema.type in skip_entities:
+            continue
+        w(eschema2sql(dbhelper, eschema, skip_relations, prefix=prefix))
+    for rtype in sorted(schema.relations()):
+        rschema = schema.rschema(rtype)
+        if rschema_has_table(rschema, skip_relations):
+            w(rschema2sql(rschema))
+    return '\n'.join(output)
+
+
+def dropschema2sql(dbhelper, schema, skip_entities=(), skip_relations=(), prefix=''):
+    """write to the output stream a SQL schema to store the objects
+    corresponding to the given schema
+    """
+    output = []
+    w = output.append
+    for etype in sorted(schema.entities()):
+        eschema = schema.eschema(etype)
+        if eschema.final or eschema.type in skip_entities:
+            continue
+        stmts = dropeschema2sql(dbhelper, eschema, skip_relations, prefix=prefix)
+        for stmt in stmts:
+            w(stmt)
+    for rtype in sorted(schema.relations()):
+        rschema = schema.rschema(rtype)
+        if rschema_has_table(rschema, skip_relations):
+            w(droprschema2sql(rschema))
+    return '\n'.join(output)
+
+
+def eschema_attrs(eschema, skip_relations):
+    attrs = [attrdef for attrdef in eschema.attribute_definitions()
+             if not attrdef[0].type in skip_relations]
+    attrs += [(rschema, None)
+              for rschema in eschema.subject_relations()
+              if not rschema.final and rschema.inlined]
+    return attrs
+
+def unique_index_name(eschema, columns):
+    return u'unique_%s' % md5((eschema.type +
+                              ',' +
+                              ','.join(sorted(columns))).encode('ascii')).hexdigest()
+
+def iter_unique_index_names(eschema):
+    for columns in eschema._unique_together or ():
+        yield columns, unique_index_name(eschema, columns)
+
+def dropeschema2sql(dbhelper, eschema, skip_relations=(), prefix=''):
+    """return sql to drop an entity type's table"""
+    # not necessary to drop indexes, that's implictly done when
+    # dropping the table, but we need to drop SQLServer views used to
+    # create multicol unique indices
+    statements = []
+    tablename = prefix + eschema.type
+    if eschema._unique_together is not None:
+        for columns, index_name in iter_unique_index_names(eschema):
+            cols  = ['%s%s' % (prefix, col) for col in columns]
+            sqls = dbhelper.sqls_drop_multicol_unique_index(tablename, cols, index_name)
+            statements += sqls
+    statements += ['DROP TABLE %s;' % (tablename)]
+    return statements
+
+
+def eschema2sql(dbhelper, eschema, skip_relations=(), prefix=''):
+    """write an entity schema as SQL statements to stdout"""
+    output = []
+    w = output.append
+    table = prefix + eschema.type
+    w('CREATE TABLE %s(' % (table))
+    attrs = eschema_attrs(eschema, skip_relations)
+    # XXX handle objectinline physical mode
+    for i in range(len(attrs)):
+        rschema, attrschema = attrs[i]
+        if attrschema is not None:
+            sqltype = aschema2sql(dbhelper, eschema, rschema, attrschema,
+                                  indent=' ')
+        else: # inline relation
+            sqltype = 'integer REFERENCES entities (eid)'
+        if i == len(attrs) - 1:
+            w(' %s%s %s' % (prefix, rschema.type, sqltype))
+        else:
+            w(' %s%s %s,' % (prefix, rschema.type, sqltype))
+    for rschema, aschema in attrs:
+        if aschema is None:  # inline relation
+            continue
+        attr = rschema.type
+        rdef = rschema.rdef(eschema.type, aschema.type)
+        for constraint in rdef.constraints:
+            cstrname, check = check_constraint(eschema, aschema, attr, constraint, dbhelper, prefix=prefix)
+            if cstrname is not None:
+                w(', CONSTRAINT %s CHECK(%s)' % (cstrname, check))
+    w(');')
+    # create indexes
+    for i in range(len(attrs)):
+        rschema, attrschema = attrs[i]
+        if attrschema is None or eschema.rdef(rschema).indexed:
+            w(dbhelper.sql_create_index(table, prefix + rschema.type))
+    for columns, index_name in iter_unique_index_names(eschema):
+        cols  = ['%s%s' % (prefix, col) for col in columns]
+        sqls = dbhelper.sqls_create_multicol_unique_index(table, cols, index_name)
+        for sql in sqls:
+            w(sql)
+    w('')
+    return '\n'.join(output)
+
+def as_sql(value, dbhelper, prefix):
+    if isinstance(value, Attribute):
+        return prefix + value.attr
+    elif isinstance(value, TODAY):
+        return dbhelper.sql_current_date()
+    elif isinstance(value, NOW):
+        return dbhelper.sql_current_timestamp()
+    else:
+        # XXX more quoting for literals?
+        return value
+
+def check_constraint(eschema, aschema, attr, constraint, dbhelper, prefix=''):
+    # XXX should find a better name
+    cstrname = 'cstr' + md5(eschema.type + attr + constraint.type() +
+                            (constraint.serialize() or '')).hexdigest()
+    if constraint.type() == 'BoundaryConstraint':
+        value = as_sql(constraint.boundary, dbhelper, prefix)
+        return cstrname, '%s%s %s %s' % (prefix, attr, constraint.operator, value)
+    elif constraint.type() == 'IntervalBoundConstraint':
+        condition = []
+        if constraint.minvalue is not None:
+            value = as_sql(constraint.minvalue, dbhelper, prefix)
+            condition.append('%s%s >= %s' % (prefix, attr, value))
+        if constraint.maxvalue is not None:
+            value = as_sql(constraint.maxvalue, dbhelper, prefix)
+            condition.append('%s%s <= %s' % (prefix, attr, value))
+        return cstrname, ' AND '.join(condition)
+    elif constraint.type() == 'StaticVocabularyConstraint':
+        sample = next(iter(constraint.vocabulary()))
+        if not isinstance(sample, string_types):
+            values = ', '.join(str(word) for word in constraint.vocabulary())
+        else:
+            # XXX better quoting?
+            values = ', '.join("'%s'" % word.replace("'", "''") for word in constraint.vocabulary())
+        return cstrname, '%s%s IN (%s)' % (prefix, attr, values)
+    return None, None
+
+def aschema2sql(dbhelper, eschema, rschema, aschema, creating=True, indent=''):
+    """write an attribute schema as SQL statements to stdout"""
+    attr = rschema.type
+    rdef = rschema.rdef(eschema.type, aschema.type)
+    sqltype = type_from_constraints(dbhelper, aschema.type, rdef.constraints,
+                                    creating)
+    if SET_DEFAULT:
+        default = eschema.default(attr)
+        if default is not None:
+            if aschema.type == 'Boolean':
+                sqltype += ' DEFAULT %s' % dbhelper.boolean_value(default)
+            elif aschema.type == 'String':
+                sqltype += ' DEFAULT %r' % str(default)
+            elif aschema.type in ('Int', 'BigInt', 'Float'):
+                sqltype += ' DEFAULT %s' % default
+            # XXX ignore default for other type
+            # this is expected for NOW / TODAY
+    if creating:
+        if rdef.uid:
+            sqltype += ' PRIMARY KEY REFERENCES entities (eid)'
+        elif rdef.cardinality[0] == '1':
+            # don't set NOT NULL if backend isn't able to change it later
+            if dbhelper.alter_column_support:
+                sqltype += ' NOT NULL'
+    # else we're getting sql type to alter a column, we don't want key / indexes
+    # / null modifiers
+    return sqltype
+
+
+def type_from_constraints(dbhelper, etype, constraints, creating=True):
+    """return a sql type string corresponding to the constraints"""
+    constraints = list(constraints)
+    unique, sqltype = False, None
+    size_constrained_string = dbhelper.TYPE_MAPPING.get('SizeConstrainedString', 'varchar(%s)')
+    if etype == 'String':
+        for constraint in constraints:
+            if isinstance(constraint, SizeConstraint):
+                if constraint.max is not None:
+                    sqltype = size_constrained_string % constraint.max
+            elif isinstance(constraint, UniqueConstraint):
+                unique = True
+    if sqltype is None:
+        sqltype = dbhelper.TYPE_MAPPING[etype]
+    if creating and unique:
+        sqltype += ' UNIQUE'
+    return sqltype
+
+
+_SQL_SCHEMA = """
+CREATE TABLE %(table)s (
+  eid_from INTEGER NOT NULL REFERENCES entities (eid),
+  eid_to INTEGER NOT NULL REFERENCES entities (eid),
+  CONSTRAINT %(table)s_p_key PRIMARY KEY(eid_from, eid_to)
+);
+
+CREATE INDEX %(table)s_from_idx ON %(table)s(eid_from);
+CREATE INDEX %(table)s_to_idx ON %(table)s(eid_to);"""
+
+
+def rschema2sql(rschema):
+    assert not rschema.rule
+    return _SQL_SCHEMA % {'table': '%s_relation' % rschema.type}
+
+
+def droprschema2sql(rschema):
+    """return sql to drop a relation type's table"""
+    # not necessary to drop indexes, that's implictly done when dropping
+    # the table
+    return 'DROP TABLE %s_relation;' % rschema.type
+
+
+def grant_schema(schema, user, set_owner=True, skip_entities=(), prefix=''):
+    """write to the output stream a SQL schema to store the objects
+    corresponding to the given schema
+    """
+    output = []
+    w = output.append
+    for etype in sorted(schema.entities()):
+        eschema = schema.eschema(etype)
+        if eschema.final or etype in skip_entities:
+            continue
+        w(grant_eschema(eschema, user, set_owner, prefix=prefix))
+    for rtype in sorted(schema.relations()):
+        rschema = schema.rschema(rtype)
+        if rschema_has_table(rschema, skip_relations=()):  # XXX skip_relations should be specified
+            w(grant_rschema(rschema, user, set_owner))
+    return '\n'.join(output)
+
+
+def grant_eschema(eschema, user, set_owner=True, prefix=''):
+    output = []
+    w = output.append
+    etype = eschema.type
+    if set_owner:
+        w('ALTER TABLE %s%s OWNER TO %s;' % (prefix, etype, user))
+    w('GRANT ALL ON %s%s TO %s;' % (prefix, etype, user))
+    return '\n'.join(output)
+
+
+def grant_rschema(rschema, user, set_owner=True):
+    output = []
+    if set_owner:
+        output.append('ALTER TABLE %s_relation OWNER TO %s;' % (rschema.type, user))
+    output.append('GRANT ALL ON %s_relation TO %s;' % (rschema.type, user))
+    return '\n'.join(output)
--- a/server/schemaserial.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/schemaserial.py	Tue Jun 21 07:42:30 2016 +0200
@@ -25,13 +25,12 @@
 
 from logilab.common.shellutils import ProgressBar, DummyProgressBar
 
-from yams import (BadSchemaDefinition, schema as schemamod, buildobjs as ybo,
-                  schema2sql as y2sql)
+from yams import BadSchemaDefinition, schema as schemamod, buildobjs as ybo
 
 from cubicweb import Binary
 from cubicweb.schema import (KNOWN_RPROPERTIES, CONSTRAINTS, ETYPE_NAME_MAP,
                              VIRTUAL_RTYPES)
-from cubicweb.server import sqlutils
+from cubicweb.server import sqlutils, schema2sql as y2sql
 
 
 def group_mapping(cnx, interactive=True):
@@ -93,14 +92,6 @@
     with cnx.ensure_cnx_set:
         tables = set(t.lower() for t in dbhelper.list_tables(cnx.cnxset.cu))
         has_computed_relations = 'cw_cwcomputedrtype' in tables
-    if has_computed_relations:
-        rset = cnx.execute(
-            'Any X, N, R, D WHERE X is CWComputedRType, X name N, '
-            'X rule R, X description D')
-        for eid, rule_name, rule, description in rset.rows:
-            rtype = ybo.ComputedRelation(name=rule_name, rule=rule, eid=eid,
-                                         description=description)
-            schema.add_relation_type(rtype)
     # computed attribute
     try:
         cnx.system_sql("SELECT cw_formula FROM cw_CWAttribute")
@@ -110,14 +101,13 @@
         has_computed_attributes = False
 
     # XXX bw compat (3.6 migration)
-    with cnx.ensure_cnx_set:
-        sqlcu = cnx.system_sql("SELECT * FROM cw_CWRType WHERE cw_name='symetric'")
-        if sqlcu.fetchall():
-            sql = dbhelper.sql_rename_col('cw_CWRType', 'cw_symetric', 'cw_symmetric',
-                                          dbhelper.TYPE_MAPPING['Boolean'], True)
-            sqlcu.execute(sql)
-            sqlcu.execute("UPDATE cw_CWRType SET cw_name='symmetric' WHERE cw_name='symetric'")
-            cnx.commit(False)
+    sqlcu = cnx.system_sql("SELECT * FROM cw_CWRType WHERE cw_name='symetric'")
+    if sqlcu.fetchall():
+        sql = dbhelper.sql_rename_col('cw_CWRType', 'cw_symetric', 'cw_symmetric',
+                                      dbhelper.TYPE_MAPPING['Boolean'], True)
+        sqlcu.execute(sql)
+        sqlcu.execute("UPDATE cw_CWRType SET cw_name='symmetric' WHERE cw_name='symetric'")
+        cnx.commit()
     ertidx = {}
     copiedeids = set()
     permsidx = deserialize_ertype_permissions(cnx)
@@ -179,6 +169,15 @@
         stype = ETYPE_NAME_MAP.get(stype, stype)
         schema.eschema(etype)._specialized_type = stype
         schema.eschema(stype)._specialized_by.append(etype)
+    if has_computed_relations:
+        rset = cnx.execute(
+            'Any X, N, R, D WHERE X is CWComputedRType, X name N, '
+            'X rule R, X description D')
+        for eid, rule_name, rule, description in rset.rows:
+            rtype = ybo.ComputedRelation(name=rule_name, rule=rule, eid=eid,
+                                         description=description)
+            rschema = schema.add_relation_type(rtype)
+            set_perms(rschema, permsidx)
     # load every relation types
     for eid, rtype, desc, sym, il, ftc in cnx.execute(
         'Any X,N,D,S,I,FTC WHERE X is CWRType, X name N, X description D, '
@@ -377,7 +376,7 @@
             pb.update()
             continue
         if rschema.rule:
-            execschemarql(execute, rschema, crschema2rql(rschema))
+            execschemarql(execute, rschema, crschema2rql(rschema, groupmap))
             pb.update()
             continue
         execschemarql(execute, rschema, rschema2rql(rschema, addrdef=False))
@@ -527,9 +526,12 @@
     relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)]
     return relations, values
 
-def crschema2rql(crschema):
+def crschema2rql(crschema, groupmap):
     relations, values = crschema_relations_values(crschema)
     yield 'INSERT CWComputedRType X: %s' % ','.join(relations), values
+    if groupmap:
+        for rql, args in _erperms2rql(crschema, groupmap):
+            yield rql, args
 
 def crschema_relations_values(crschema):
     values = _ervalues(crschema)
--- a/server/server.py	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +0,0 @@
-# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of CubicWeb.
-#
-# CubicWeb is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""Pyro RQL server"""
-
-__docformat__ = "restructuredtext en"
-
-import select
-from time import localtime, mktime
-
-from cubicweb.server.utils import TasksManager
-from cubicweb.server.repository import Repository
-
-class Finished(Exception):
-    """raise to remove an event from the event loop"""
-
-class TimeEvent:
-    """base event"""
-    # timefunc = staticmethod(localtime)
-    timefunc = localtime
-
-    def __init__(self, absolute=None, period=None):
-        # local time tuple
-        if absolute is None:
-            absolute = self.timefunc()
-        self.absolute = absolute
-        # optional period in seconds
-        self.period = period
-
-    def is_ready(self):
-        """return  true if the event is ready to be fired"""
-        now = self.timefunc()
-        if self.absolute <= now:
-            return True
-        return False
-
-    def fire(self, server):
-        """fire the event
-        must be overridden by concrete events
-        """
-        raise NotImplementedError()
-
-    def update(self):
-        """update the absolute date for the event or raise a finished exception
-        """
-        if self.period is None:
-            raise Finished
-        self.absolute = localtime(mktime(self.absolute) + self.period)
-
-
-class QuitEvent(TimeEvent):
-    """stop the server"""
-    def fire(self, server):
-        server.repo.shutdown()
-        server.quiting = True
-
-
-class RepositoryServer(object):
-
-    def __init__(self, config):
-        """make the repository available as a PyRO object"""
-        self.config = config
-        self.repo = Repository(config, TasksManager())
-        self.ns = None
-        self.quiting = None
-        # event queue
-        self.events = []
-
-    def add_event(self, event):
-        """add an event to the loop"""
-        self.info('adding event %s', event)
-        self.events.append(event)
-
-    def trigger_events(self):
-        """trigger ready events"""
-        for event in self.events[:]:
-            if event.is_ready():
-                self.info('starting event %s', event)
-                event.fire(self)
-                try:
-                    event.update()
-                except Finished:
-                    self.events.remove(event)
-
-    def run(self, req_timeout=5.0):
-        """enter the service loop"""
-        # start repository looping tasks
-        self.repo.start_looping_tasks()
-        while self.quiting is None:
-            try:
-                self.daemon.handleRequests(req_timeout)
-            except select.error:
-                continue
-            finally:
-                self.trigger_events()
-
-    def quit(self):
-        """stop the server"""
-        self.add_event(QuitEvent())
-
-    def connect(self, host='', port=0):
-        """the connect method on the repository only register to pyro if
-        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())
-
-
-    # these are overridden by set_log_methods below
-    # only defining here to prevent pylint from complaining
-    @classmethod
-    def info(cls, msg, *a, **kw):
-        pass
-
-from logging import getLogger
-from cubicweb import set_log_methods
-LOGGER = getLogger('cubicweb.reposerver')
-set_log_methods(RepositoryServer, LOGGER)
--- a/server/serverconfig.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/serverconfig.py	Tue Jun 21 07:42:30 2016 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -197,44 +197,6 @@
 notified of every changes.',
           'group': 'email', 'level': 2,
           }),
-        # pyro services config
-        ('pyro-host',
-         {'type' : 'string',
-          'default': None,
-          'help': 'Pyro server host, if not detectable correctly through \
-gethostname(). It may contains port information using <host>:<port> notation, \
-and if not set, it will be choosen randomly',
-          'group': 'pyro', 'level': 3,
-          }),
-        ('pyro-instance-id',
-         {'type' : 'string',
-          'default': lgconfig.Method('default_instance_id'),
-          'help': 'identifier of the CubicWeb instance in the Pyro name server',
-          'group': 'pyro', 'level': 1,
-          }),
-        ('pyro-ns-host',
-         {'type' : 'string',
-          'default': '',
-          'help': 'Pyro name server\'s host. If not set, will be detected by a \
-broadcast query. It may contains port information using <host>:<port> notation. \
-Use "NO_PYRONS" to create a Pyro server but not register to a pyro nameserver',
-          'group': 'pyro', 'level': 1,
-          }),
-        ('pyro-ns-group',
-         {'type' : 'string',
-          'default': 'cubicweb',
-          'help': 'Pyro name server\'s group where the repository will be \
-registered.',
-          'group': 'pyro', 'level': 1,
-          }),
-        # zmq services config
-        ('zmq-repository-address',
-         {'type' : 'string',
-          'default': None,
-          'help': ('ZMQ URI on which the repository will be bound '
-                   'to (of the form `zmqpickle-tcp://<ipaddr>:<port>`).'),
-          'group': 'zmq', 'level': 3,
-          }),
          ('zmq-address-sub',
           {'type' : 'csv',
            'default' : (),
@@ -350,10 +312,6 @@
             stream.write('[%s]\n%s\n' % (section, generate_source_config(sconfig)))
         restrict_perms_to_user(sourcesfile)
 
-    def pyro_enabled(self):
-        """pyro is always enabled in standalone repository configuration"""
-        return True
-
     def load_schema(self, expand_cubes=False, **kwargs):
         from cubicweb.schema import CubicWebSchemaLoader
         if expand_cubes:
@@ -387,6 +345,3 @@
         return ServerMigrationHelper(self, schema, interactive=interactive,
                                      cnx=cnx, repo=repo, connect=connect,
                                      verbosity=verbosity)
-
-
-CONFIGURATIONS.append(ServerConfiguration)
--- a/server/serverctl.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/serverctl.py	Tue Jun 21 07:42:30 2016 +0200
@@ -38,7 +38,6 @@
 from cubicweb.toolsutils import Command, CommandHandler, underline_title
 from cubicweb.cwctl import CWCTL, check_options_consistency, ConfigureInstanceCommand
 from cubicweb.server import SOURCE_TYPES
-from cubicweb.server.repository import Repository
 from cubicweb.server.serverconfig import (
     USER_OPTIONS, ServerConfiguration, SourceConfiguration,
     ask_source_config, generate_source_config)
@@ -167,11 +166,6 @@
         if not automatic:
             print underline_title('Configuring the repository')
             config.input_config('email', inputlevel)
-            # ask for pyro configuration if pyro is activated and we're not
-            # using a all-in-one config, in which case this is done by the web
-            # side command handler
-            if config.pyro_enabled() and config.name != 'all-in-one':
-                config.input_config('pyro', inputlevel)
             print '\n'+underline_title('Configuring the sources')
         sourcesfile = config.sources_file()
         # hack to make Method('default_instance_id') usable in db option defs
@@ -301,33 +295,6 @@
                     raise ExecutionError(str(exc))
 
 
-class RepositoryStartHandler(CommandHandler):
-    cmdname = 'start'
-    cfgname = 'repository'
-
-    def start_server(self, config):
-        command = ['cubicweb-ctl', 'start-repository']
-        if config.debugmode:
-            command.append('--debug')
-        command.append('--loglevel')
-        command.append(config['log-threshold'].lower())
-        command.append(config.appid)
-        subprocess.call(command)
-        return 1
-
-
-class RepositoryStopHandler(CommandHandler):
-    cmdname = 'stop'
-    cfgname = 'repository'
-
-    def poststop(self):
-        """if pyro is enabled, ensure the repository is correctly unregistered
-        """
-        if self.config.pyro_enabled():
-            from cubicweb.server.repository import pyro_unregister
-            pyro_unregister(self.config)
-
-
 # repository specific commands ################################################
 
 def createdb(helper, source, dbcnx, cursor, **kwargs):
@@ -530,51 +497,55 @@
         appid = args[0]
         config = ServerConfiguration.config_for(appid)
         repo, cnx = repo_cnx(config)
-        with cnx:
-            used = set(n for n, in cnx.execute('Any SN WHERE S is CWSource, S name SN'))
-            cubes = repo.get_cubes()
-            while True:
-                type = raw_input('source type (%s): '
-                                    % ', '.join(sorted(SOURCE_TYPES)))
-                if type not in SOURCE_TYPES:
-                    print '-> unknown source type, use one of the available types.'
-                    continue
-                sourcemodule = SOURCE_TYPES[type].module
-                if not sourcemodule.startswith('cubicweb.'):
-                    # module names look like cubes.mycube.themodule
-                    sourcecube = SOURCE_TYPES[type].module.split('.', 2)[1]
-                    # if the source adapter is coming from an external component,
-                    # ensure it's specified in used cubes
-                    if not sourcecube in cubes:
-                        print ('-> this source type require the %s cube which is '
-                               'not used by the instance.')
+        repo.hm.call_hooks('server_maintenance', repo=repo)
+        try:
+            with cnx:
+                used = set(n for n, in cnx.execute('Any SN WHERE S is CWSource, S name SN'))
+                cubes = repo.get_cubes()
+                while True:
+                    type = raw_input('source type (%s): '
+                                        % ', '.join(sorted(SOURCE_TYPES)))
+                    if type not in SOURCE_TYPES:
+                        print '-> unknown source type, use one of the available types.'
                         continue
-                break
-            while True:
-                parser = raw_input('parser type (%s): '
-                                    % ', '.join(sorted(repo.vreg['parsers'])))
-                if parser in repo.vreg['parsers']:
+                    sourcemodule = SOURCE_TYPES[type].module
+                    if not sourcemodule.startswith('cubicweb.'):
+                        # module names look like cubes.mycube.themodule
+                        sourcecube = SOURCE_TYPES[type].module.split('.', 2)[1]
+                        # if the source adapter is coming from an external component,
+                        # ensure it's specified in used cubes
+                        if not sourcecube in cubes:
+                            print ('-> this source type require the %s cube which is '
+                                   'not used by the instance.')
+                            continue
                     break
-                print '-> unknown parser identifier, use one of the available types.'
-            while True:
-                sourceuri = raw_input('source identifier (a unique name used to '
-                                      'tell sources apart): ').strip()
-                if not sourceuri:
-                    print '-> mandatory.'
-                else:
-                    sourceuri = unicode(sourceuri, sys.stdin.encoding)
-                    if sourceuri in used:
-                        print '-> uri already used, choose another one.'
+                while True:
+                    parser = raw_input('parser type (%s): '
+                                        % ', '.join(sorted(repo.vreg['parsers'])))
+                    if parser in repo.vreg['parsers']:
+                        break
+                    print '-> unknown parser identifier, use one of the available types.'
+                while True:
+                    sourceuri = raw_input('source identifier (a unique name used to '
+                                          'tell sources apart): ').strip()
+                    if not sourceuri:
+                        print '-> mandatory.'
                     else:
-                        break
-            url = raw_input('source URL (leave empty for none): ').strip()
-            url = unicode(url) if url else None
-            # XXX configurable inputlevel
-            sconfig = ask_source_config(config, type, inputlevel=self.config.config_level)
-            cfgstr = unicode(generate_source_config(sconfig), sys.stdin.encoding)
-            cnx.create_entity('CWSource', name=sourceuri, type=unicode(type),
-                              config=cfgstr, parser=unicode(parser), url=unicode(url))
-            cnx.commit()
+                        sourceuri = unicode(sourceuri, sys.stdin.encoding)
+                        if sourceuri in used:
+                            print '-> uri already used, choose another one.'
+                        else:
+                            break
+                url = raw_input('source URL (leave empty for none): ').strip()
+                url = unicode(url) if url else None
+                # XXX configurable inputlevel
+                sconfig = ask_source_config(config, type, inputlevel=self.config.config_level)
+                cfgstr = unicode(generate_source_config(sconfig), sys.stdin.encoding)
+                cnx.create_entity('CWSource', name=sourceuri, type=unicode(type),
+                                  config=cfgstr, parser=unicode(parser), url=unicode(url))
+                cnx.commit()
+        finally:
+            repo.hm.call_hooks('server_shutdown')
 
 
 class GrantUserOnInstanceCommand(Command):
@@ -686,77 +657,6 @@
         cnx.close()
 
 
-class StartRepositoryCommand(Command):
-    """Start a CubicWeb RQL server for a given instance.
-
-    The server will be remotely accessible through pyro or ZMQ
-
-    <instance>
-      the identifier of the instance to initialize.
-    """
-    name = 'start-repository'
-    arguments = '<instance>'
-    min_args = max_args = 1
-    options = (
-        ('debug',
-         {'short': 'D', 'action' : 'store_true',
-          'help': 'start server in debug mode.'}),
-        ('loglevel',
-         {'short': 'l', 'type' : 'choice', 'metavar': '<log level>',
-          'default': None, 'choices': ('debug', 'info', 'warning', 'error'),
-          'help': 'debug if -D is set, error otherwise',
-          }),
-        ('address',
-         {'short': 'a', 'type': 'string', 'metavar': '<protocol>://<host>:<port>',
-          'default': '',
-          'help': ('specify a ZMQ URI on which to bind, or use "pyro://"'
-                   'to create a pyro-based repository'),
-          }),
-        )
-
-    def create_repo(self, config):
-        address = self['address']
-        if not address:
-            address = config.get('zmq-repository-address') or 'pyro://'
-        if address.startswith('pyro://'):
-            from cubicweb.server.server import RepositoryServer
-            return RepositoryServer(config), config['host']
-        else:
-            from cubicweb.server.utils import TasksManager
-            from cubicweb.server.cwzmq import ZMQRepositoryServer
-            repo = Repository(config, TasksManager())
-            return ZMQRepositoryServer(repo), address
-
-    def run(self, args):
-        from logilab.common.daemon import daemonize, setugid
-        from cubicweb.cwctl import init_cmdline_log_threshold
-        print 'WARNING: Standalone repository with pyro or zmq access is deprecated'
-        appid = args[0]
-        debug = self['debug']
-        if sys.platform == 'win32' and not debug:
-            logger = logging.getLogger('cubicweb.ctl')
-            logger.info('Forcing debug mode on win32 platform')
-            debug = True
-        config = ServerConfiguration.config_for(appid, debugmode=debug)
-        init_cmdline_log_threshold(config, self['loglevel'])
-        # create the server
-        server, address = self.create_repo(config)
-        # ensure the directory where the pid-file should be set exists (for
-        # instance /var/run/cubicweb may be deleted on computer restart)
-        pidfile = config['pid-file']
-        piddir = os.path.dirname(pidfile)
-        # go ! (don't daemonize in debug mode)
-        if not os.path.exists(piddir):
-            os.makedirs(piddir)
-        if not debug and daemonize(pidfile, umask=config['umask']):
-            return
-        uid = config['uid']
-        if uid is not None:
-            setugid(uid)
-        server.install_sig_handlers()
-        server.connect(address)
-        server.run()
-
 
 def _remote_dump(host, appid, output, sudo=False):
     # XXX generate unique/portable file name
@@ -1061,7 +961,7 @@
         config = ServerConfiguration.config_for(appid)
         repo, cnx = repo_cnx(config)
         with cnx:
-            reindex_entities(repo.schema, cnx._cnx, etypes=etypes)
+            reindex_entities(repo.schema, cnx, etypes=etypes)
             cnx.commit()
 
 
@@ -1084,20 +984,23 @@
     )
 
     def run(self, args):
+        from cubicweb import repoapi
         from cubicweb.cwctl import init_cmdline_log_threshold
         config = ServerConfiguration.config_for(args[0])
         config.global_set_option('log-file', None)
         config.log_format = '%(levelname)s %(name)s: %(message)s'
         init_cmdline_log_threshold(config, self['loglevel'])
-        # only retrieve cnx to trigger authentication, close it right away
-        repo, cnx = repo_cnx(config)
-        cnx.close()
+        repo = repoapi.get_repository(config=config)
+        repo.hm.call_hooks('server_maintenance', repo=repo)
         try:
-            source = repo.sources_by_uri[args[1]]
-        except KeyError:
-            raise ExecutionError('no source named %r' % args[1])
-        session = repo.internal_session()
-        stats = source.pull_data(session, force=True, raise_on_error=True)
+            try:
+                source = repo.sources_by_uri[args[1]]
+            except KeyError:
+                raise ExecutionError('no source named %r' % args[1])
+            with repo.internal_cnx() as cnx:
+                stats = source.pull_data(cnx, force=True, raise_on_error=True)
+        finally:
+            repo.shutdown()
         for key, val in stats.iteritems():
             if val:
                 print key, ':', val
@@ -1135,18 +1038,17 @@
 
     def run(self, args):
         from yams.diff import schema_diff
+        from cubicweb import repoapi
         appid = args.pop(0)
         diff_tool = args.pop(0)
         config = ServerConfiguration.config_for(appid)
-        repo, cnx = repo_cnx(config)
-        cnx.close()
+        repo = repoapi.get_repository(config=config)
         fsschema = config.load_schema(expand_cubes=True)
         schema_diff(fsschema, repo.schema, permissionshandler, diff_tool, ignore=('eid',))
 
 
 for cmdclass in (CreateInstanceDBCommand, InitInstanceCommand,
                  GrantUserOnInstanceCommand, ResetAdminPasswordCommand,
-                 StartRepositoryCommand,
                  DBDumpCommand, DBRestoreCommand, DBCopyCommand,
                  AddSourceCommand, CheckRepositoryCommand, RebuildFTICommand,
                  SynchronizeSourceCommand, SchemaDiffCommand,
--- a/server/session.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/session.py	Tue Jun 21 07:42:30 2016 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -19,7 +19,6 @@
 __docformat__ = "restructuredtext en"
 
 import sys
-import threading
 from time import time
 from uuid import uuid4
 from warnings import warn
@@ -34,7 +33,6 @@
 from cubicweb.req import RequestSessionBase
 from cubicweb.utils import make_uid
 from cubicweb.rqlrewrite import RQLRewriter
-from cubicweb.server import ShuttingDown
 from cubicweb.server.edition import EditedEntity
 
 
@@ -68,27 +66,6 @@
     return req.vreg.config.repairing
 
 
-class transaction(object):
-    """Ensure that the transaction is either commited or rolled back at exit
-
-    Context manager to enter a transaction for a session: when exiting the
-    `with` block on exception, call `session.rollback()`, else call
-    `session.commit()` on normal exit
-    """
-    def __init__(self, session, free_cnxset=True):
-        self.session = session
-        self.free_cnxset = free_cnxset
-
-    def __enter__(self):
-        # ensure session has a cnxset
-        self.session.set_cnxset()
-
-    def __exit__(self, exctype, exc, traceback):
-        if exctype:
-            self.session.rollback(free_cnxset=self.free_cnxset)
-        else:
-            self.session.commit(free_cnxset=self.free_cnxset)
-
 @deprecated('[3.17] use <object>.allow/deny_all_hooks_but instead')
 def hooks_control(obj, mode, *categories):
     assert mode in  (HOOKS_ALLOW_ALL, HOOKS_DENY_ALL)
@@ -98,8 +75,7 @@
         return obj.deny_all_hooks_but(*categories)
 
 
-class _hooks_control(object): # XXX repoapi: remove me when
-                              # session stop being connection
+class _hooks_control(object):
     """context manager to control activated hooks categories.
 
     If mode is `HOOKS_DENY_ALL`, given hooks categories will
@@ -149,24 +125,6 @@
         finally:
             self.cnx.hooks_mode = self.oldmode
 
-class _session_hooks_control(_hooks_control): # XXX repoapi: remove me when
-                                              # session stop being connection
-    """hook control context manager for session
-
-    Necessary to handle some unholy transaction scope logic."""
-
-
-    def __init__(self, session, mode, *categories):
-        self.session = session
-        super_init = super(_session_hooks_control, self).__init__
-        super_init(session._cnx, mode, *categories)
-
-    def __exit__(self, exctype, exc, traceback):
-        super_exit = super(_session_hooks_control, self).__exit__
-        ret = super_exit(exctype, exc, traceback)
-        if self.cnx.ctx_count == 0:
-            self.session._close_cnx(self.cnx)
-        return ret
 
 @deprecated('[3.17] use <object>.security_enabled instead')
 def security_enabled(obj, *args, **kwargs):
@@ -205,24 +163,6 @@
         if self.oldwrite is not None:
             self.cnx.write_security = self.oldwrite
 
-class _session_security_enabled(_security_enabled):
-    """hook security context manager for session
-
-    Necessary To handle some unholy transaction scope logic."""
-
-
-    def __init__(self, session, read=None, write=None):
-        self.session = session
-        super_init = super(_session_security_enabled, self).__init__
-        super_init(session._cnx, read=read, write=write)
-
-    def __exit__(self, exctype, exc, traceback):
-        super_exit = super(_session_security_enabled, self).__exit__
-        ret = super_exit(exctype, exc, traceback)
-        if self.cnx.ctx_count == 0:
-            self.session._close_cnx(self.cnx)
-        return ret
-
 HOOKS_ALLOW_ALL = object()
 HOOKS_DENY_ALL = object()
 DEFAULT_SECURITY = object() # evaluated to true by design
@@ -230,146 +170,6 @@
 class SessionClosedError(RuntimeError):
     pass
 
-class CnxSetTracker(object):
-    """Keep track of which connection use which cnxset.
-
-    There should be one of these objects per session (including internal sessions).
-
-    Session objects are responsible for creating their CnxSetTracker object.
-
-    Connections should use the :meth:`record` and :meth:`forget` to inform the
-    tracker of cnxsets they have acquired.
-
-    .. automethod:: cubicweb.server.session.CnxSetTracker.record
-    .. automethod:: cubicweb.server.session.CnxSetTracker.forget
-
-    Sessions use the :meth:`close` and :meth:`wait` methods when closing.
-
-    .. automethod:: cubicweb.server.session.CnxSetTracker.close
-    .. automethod:: cubicweb.server.session.CnxSetTracker.wait
-
-    This object itself is threadsafe. It also requires caller to acquired its
-    lock in some situation.
-    """
-
-    def __init__(self):
-        self._active = True
-        self._condition = threading.Condition()
-        self._record = {}
-
-    def __enter__(self):
-        return self._condition.__enter__()
-
-    def __exit__(self, *args):
-        return self._condition.__exit__(*args)
-
-    def record(self, cnxid, cnxset):
-        """Inform the tracker that a cnxid has acquired a cnxset
-
-        This method is to be used by Connection objects.
-
-        This method fails when:
-        - The cnxid already has a recorded cnxset.
-        - The tracker is not active anymore.
-
-        Notes about the caller:
-        (1) It is responsible for retrieving a cnxset.
-        (2) It must be prepared to release the cnxset if the
-            `cnxsettracker.forget` call fails.
-        (3) It should acquire the tracker lock until the very end of the operation.
-        (4) However it must only lock the CnxSetTracker object after having
-            retrieved the cnxset to prevent deadlock.
-
-        A typical usage look like::
-
-        cnxset = repo._get_cnxset() # (1)
-        try:
-            with cnxset_tracker: # (3) and (4)
-                cnxset_tracker.record(caller.id, cnxset)
-                # (3') operation ends when caller is in expected state only
-                caller.cnxset = cnxset
-        except Exception:
-            repo._free_cnxset(cnxset) # (2)
-            raise
-        """
-        # dubious since the caller is supposed to have acquired it anyway.
-        with self._condition:
-            if not self._active:
-                raise SessionClosedError('Closed')
-            old = self._record.get(cnxid)
-            if old is not None:
-                raise ValueError('connection "%s" already has a cnx_set (%r)'
-                                 % (cnxid, old))
-            self._record[cnxid] = cnxset
-
-    def forget(self, cnxid, cnxset):
-        """Inform the tracker that a cnxid have release a cnxset
-
-        This methode is to be used by Connection object.
-
-        This method fails when:
-        - The cnxset for the cnxid does not match the recorded one.
-
-        Notes about the caller:
-        (1) It is responsible for releasing the cnxset.
-        (2) It should acquire the tracker lock during the operation to ensure
-            the internal tracker state is always accurate regarding its own state.
-
-        A typical usage look like::
-
-        cnxset = caller.cnxset
-        try:
-            with cnxset_tracker:
-                # (2) you can not have caller.cnxset out of sync with
-                #     cnxset_tracker state while unlocked
-                caller.cnxset = None
-                cnxset_tracker.forget(caller.id, cnxset)
-        finally:
-            cnxset = repo._free_cnxset(cnxset) # (1)
-        """
-        with self._condition:
-            old = self._record.get(cnxid, None)
-            if old is not cnxset:
-                raise ValueError('recorded cnxset for "%s" mismatch: %r != %r'
-                                 % (cnxid, old, cnxset))
-            self._record.pop(cnxid)
-            self._condition.notify_all()
-
-    def close(self):
-        """Marks the tracker as inactive.
-
-        This method is to be used by Session objects.
-
-        An inactive tracker does not accept new records anymore.
-        """
-        with self._condition:
-            self._active = False
-
-    def wait(self, timeout=10):
-        """Wait for all recorded cnxsets to be released
-
-        This method is to be used by Session objects.
-
-        Returns a tuple of connection ids that remain open.
-        """
-        with self._condition:
-            if  self._active:
-                raise RuntimeError('Cannot wait on active tracker.'
-                                   ' Call tracker.close() first')
-            while self._record and timeout > 0:
-                start = time()
-                self._condition.wait(timeout)
-                timeout -= time() - start
-            return tuple(self._record)
-
-
-def _with_cnx_set(func):
-    """decorator for Connection method that ensure they run with a cnxset """
-    @functools.wraps(func)
-    def wrapper(cnx, *args, **kwargs):
-        with cnx.ensure_cnx_set:
-            return func(cnx, *args, **kwargs)
-    return wrapper
 
 def _open_only(func):
     """decorator for Connection method that check it is open"""
@@ -389,8 +189,9 @@
 
     Database connection resources:
 
-      :attr:`running_dbapi_query`, boolean flag telling if the executing query
-      is coming from a dbapi connection or is a query from within the repository
+      :attr:`hooks_in_progress`, boolean flag telling if the executing
+      query is coming from a repoapi connection or is a query from
+      within the repository (e.g. started by hooks)
 
       :attr:`cnxset`, the connections set to use to execute queries on sources.
       If the transaction is read only, the connection set may be freed between
@@ -406,12 +207,18 @@
       'transaction' (we want to keep the connections set during all the
       transaction, with or without writing)
 
-    Internal transaction data:
+    Shared data:
 
-      :attr:`data` is a dictionary containing some shared data
-      cleared at the end of the transaction. Hooks and operations may put
-      arbitrary data in there, and this may also be used as a communication
-      channel between the client and the repository.
+      :attr:`data` is a dictionary bound to the underlying session,
+      who will be present for the life time of the session. This may
+      be useful for web clients that rely on the server for managing
+      bits of session-scoped data.
+
+      :attr:`transaction_data` is a dictionary cleared at the end of
+      the transaction. Hooks and operations may put arbitrary data in
+      there.
+
+    Internal state:
 
       :attr:`pending_operations`, ordered list of operations to be processed on
       commit/rollback
@@ -438,33 +245,20 @@
       read/write security is currently activated.
 
     """
+    is_request = False
+    hooks_in_progress = False
+    is_repo_in_memory = True # bw compat
 
-    is_request = False
-
-    def __init__(self, session, cnxid=None, session_handled=False):
+    def __init__(self, session):
         # using super(Connection, self) confuse some test hack
         RequestSessionBase.__init__(self, session.vreg)
-        # only the session provide explicite
-        if cnxid is not None:
-            assert session_handled # only session profive explicite cnxid
         #: connection unique id
         self._open = None
-        if cnxid is None:
-            cnxid = '%s-%s' % (session.sessionid, uuid4().hex)
-        self.connectionid = cnxid
+        self.connectionid = '%s-%s' % (session.sessionid, uuid4().hex)
+        self.session = session
         self.sessionid = session.sessionid
-        #: self._session_handled
-        #: are the life cycle of this Connection automatically controlled by the
-        #: Session This is the old backward compatibility mode
-        self._session_handled = session_handled
         #: reentrance handling
         self.ctx_count = 0
-        #: count the number of entry in a context needing a cnxset
-        self._cnxset_count = 0
-        #: Boolean for compat with the older explicite set_cnxset/free_cnx API
-        #: When a call set_cnxset is done, no automatic freeing will be done
-        #: until free_cnx is called.
-        self._auto_free_cnx_set = True
 
         #: server.Repository object
         self.repo = session.repo
@@ -474,16 +268,8 @@
         # other session utility
         self._session_timestamp = session._timestamp
 
-        #: connection handling mode
-        self.mode = session.default_mode
-        #: connection set used to execute queries on sources
-        self._cnxset = None
-        #: CnxSetTracker used to report cnxset usage
-        self._cnxset_tracker = session._cnxset_tracker
-        #: is this connection from a client or internal to the repo
-        self.running_dbapi_query = True
         # internal (root) session
-        self.is_internal_session = session.is_internal_session
+        self.is_internal_session = isinstance(session.user, InternalManager)
 
         #: dict containing arbitrary data cleared at the end of the transaction
         self.transaction_data = {}
@@ -506,7 +292,7 @@
 
         # undo control
         config = session.repo.config
-        if config.creating or config.repairing or session.is_internal_session:
+        if config.creating or config.repairing or self.is_internal_session:
             self.undo_actions = False
         else:
             self.undo_actions = config['undo-enabled']
@@ -521,21 +307,109 @@
         else:
             self._set_user(session.user)
 
+    @_open_only
+    def source_defs(self):
+        """Return the definition of sources used by the repository."""
+        return self.session.repo.source_defs()
 
-    # live cycle handling ####################################################
+    @_open_only
+    def get_schema(self):
+        """Return the schema currently used by the repository."""
+        return self.session.repo.source_defs()
+
+    @_open_only
+    def get_option_value(self, option):
+        """Return the value for `option` in the configuration."""
+        return self.session.repo.get_option_value(option)
+
+    # transaction api
+
+    @_open_only
+    def undoable_transactions(self, ueid=None, **actionfilters):
+        """Return a list of undoable transaction objects by the connection's
+        user, ordered by descendant transaction time.
+
+        Managers may filter according to user (eid) who has done the transaction
+        using the `ueid` argument. Others will only see their own transactions.
+
+        Additional filtering capabilities is provided by using the following
+        named arguments:
+
+        * `etype` to get only transactions creating/updating/deleting entities
+          of the given type
+
+        * `eid` to get only transactions applied to entity of the given eid
+
+        * `action` to get only transactions doing the given action (action in
+          'C', 'U', 'D', 'A', 'R'). If `etype`, action can only be 'C', 'U' or
+          'D'.
+
+        * `public`: when additional filtering is provided, they are by default
+          only searched in 'public' actions, unless a `public` argument is given
+          and set to false.
+        """
+        return self.repo.system_source.undoable_transactions(self, ueid,
+                                                             **actionfilters)
+
+    @_open_only
+    def transaction_info(self, txuuid):
+        """Return transaction object for the given uid.
+
+        raise `NoSuchTransaction` if not found or if session's user is
+        not allowed (eg not in managers group and the transaction
+        doesn't belong to him).
+        """
+        return self.repo.system_source.tx_info(self, txuuid)
+
+    @_open_only
+    def transaction_actions(self, txuuid, public=True):
+        """Return an ordered list of actions effectued during that transaction.
+
+        If public is true, return only 'public' actions, i.e. not ones
+        triggered under the cover by hooks, else return all actions.
+
+        raise `NoSuchTransaction` if the transaction is not found or
+        if the user is not allowed (eg not in managers group).
+        """
+        return self.repo.system_source.tx_actions(self, txuuid, public)
+
+    @_open_only
+    def undo_transaction(self, txuuid):
+        """Undo the given transaction. Return potential restoration errors.
+
+        raise `NoSuchTransaction` if not found or if user is not
+        allowed (eg not in managers group).
+        """
+        return self.repo.system_source.undo_transaction(self, txuuid)
+
+    # life cycle handling ####################################################
 
     def __enter__(self):
         assert self._open is None # first opening
         self._open = True
+        self.cnxset = self.repo._get_cnxset()
         return self
 
     def __exit__(self, exctype=None, excvalue=None, tb=None):
         assert self._open # actually already open
-        assert self._cnxset_count == 0
         self.rollback()
         self._open = False
+        self.cnxset.cnxset_freed()
+        self.repo._free_cnxset(self.cnxset)
+        self.cnxset = None
 
+    @contextmanager
+    def running_hooks_ops(self):
+        """this context manager should be called whenever hooks or operations
+        are about to be run (but after hook selection)
 
+        It will help the undo logic record pertinent metadata or some
+        hooks to run (or not) depending on who/what issued the query.
+        """
+        prevmode = self.hooks_in_progress
+        self.hooks_in_progress = True
+        yield
+        self.hooks_in_progress = prevmode
 
     # shared data handling ###################################################
 
@@ -580,83 +454,27 @@
         self.local_perm_cache.clear()
         self.rewriter = RQLRewriter(self)
 
-    # Connection Set Management ###############################################
-    @property
-    @_open_only
-    def cnxset(self):
-        return self._cnxset
-
-    @cnxset.setter
-    @_open_only
-    def cnxset(self, new_cnxset):
-        with self._cnxset_tracker:
-            old_cnxset = self._cnxset
-            if new_cnxset is old_cnxset:
-                return #nothing to do
-            if old_cnxset is not None:
-                old_cnxset.rollback()
-                self._cnxset = None
-                self.ctx_count -= 1
-                self._cnxset_tracker.forget(self.connectionid, old_cnxset)
-            if new_cnxset is not None:
-                self._cnxset_tracker.record(self.connectionid, new_cnxset)
-                self._cnxset = new_cnxset
-                self.ctx_count += 1
-
-    @_open_only
-    def _set_cnxset(self):
-        """the connection need a connections set to execute some queries"""
-        if self.cnxset is None:
-            cnxset = self.repo._get_cnxset()
-            try:
-                self.cnxset = cnxset
-            except:
-                self.repo._free_cnxset(cnxset)
-                raise
-        return self.cnxset
-
-    @_open_only
-    def _free_cnxset(self, ignoremode=False):
-        """the connection is no longer using its connections set, at least for some time"""
-        # cnxset may be none if no operation has been done since last commit
-        # or rollback
-        cnxset = self.cnxset
-        if cnxset is not None and (ignoremode or self.mode == 'read'):
-            assert self._cnxset_count == 0
-            try:
-                self.cnxset = None
-            finally:
-                cnxset.cnxset_freed()
-                self.repo._free_cnxset(cnxset)
-
     @deprecated('[3.19] cnxset are automatically managed now.'
                 ' stop using explicit set and free.')
     def set_cnxset(self):
-        self._auto_free_cnx_set = False
-        return self._set_cnxset()
+        pass
 
     @deprecated('[3.19] cnxset are automatically managed now.'
                 ' stop using explicit set and free.')
     def free_cnxset(self, ignoremode=False):
-        self._auto_free_cnx_set = True
-        return self._free_cnxset(ignoremode=ignoremode)
-
+        pass
 
     @property
     @contextmanager
     @_open_only
+    @deprecated('[3.21] a cnxset is automatically set on __enter__ call now.'
+                ' stop using .ensure_cnx_set')
     def ensure_cnx_set(self):
-        assert self._cnxset_count >= 0
-        if self._cnxset_count == 0:
-            self._set_cnxset()
-        try:
-            self._cnxset_count += 1
-            yield
-        finally:
-            self._cnxset_count = max(self._cnxset_count - 1, 0)
-            if self._cnxset_count == 0 and self._auto_free_cnx_set:
-                self._free_cnxset()
+        yield
 
+    @property
+    def anonymous_connection(self):
+        return self.session.anonymous_session
 
     # Entity cache management #################################################
     #
@@ -939,27 +757,7 @@
     @read_security.setter
     @_open_only
     def read_security(self, activated):
-        oldmode = self._read_security
         self._read_security = activated
-        # running_dbapi_query used to detect hooks triggered by a 'dbapi' query
-        # (eg not issued on the session). This is tricky since we the execution
-        # model of a (write) user query is:
-        #
-        # repository.execute (security enabled)
-        #  \-> querier.execute
-        #       \-> repo.glob_xxx (add/update/delete entity/relation)
-        #            \-> deactivate security before calling hooks
-        #                 \-> WE WANT TO CHECK QUERY NATURE HERE
-        #                      \-> potentially, other calls to querier.execute
-        #
-        # so we can't rely on simply checking session.read_security, but
-        # recalling the first transition from DEFAULT_SECURITY to something
-        # else (False actually) is not perfect but should be enough
-        #
-        # also reset running_dbapi_query to true when we go back to
-        # DEFAULT_SECURITY
-        self.running_dbapi_query = (oldmode is DEFAULT_SECURITY
-                                    or activated is DEFAULT_SECURITY)
 
     # undo support ############################################################
 
@@ -971,7 +769,7 @@
     def transaction_uuid(self, set=True):
         uuid = self.transaction_data.get('tx_uuid')
         if set and uuid is None:
-            self.transaction_data['tx_uuid'] = uuid = uuid4().hex
+            self.transaction_data['tx_uuid'] = uuid = unicode(uuid4().hex)
             self.repo.system_source.start_undoable_transaction(self, uuid)
         return uuid
 
@@ -988,7 +786,6 @@
         return self.repo.source_defs()
 
     @deprecated('[3.19] use .entity_metas(eid) instead')
-    @_with_cnx_set
     @_open_only
     def describe(self, eid, asdict=False):
         """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
@@ -999,7 +796,6 @@
             return metas
         return etype, source, extid
 
-    @_with_cnx_set
     @_open_only
     def entity_metas(self, eid):
         """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
@@ -1008,7 +804,6 @@
 
     # core method #############################################################
 
-    @_with_cnx_set
     @_open_only
     def execute(self, rql, kwargs=None, build_descr=True):
         """db-api like method directly linked to the querier execute method.
@@ -1022,21 +817,16 @@
         return rset
 
     @_open_only
-    def rollback(self, free_cnxset=True, reset_pool=None):
+    def rollback(self, free_cnxset=None, reset_pool=None):
         """rollback the current transaction"""
-        if reset_pool is not None:
-            warn('[3.13] use free_cnxset argument instead for reset_pool',
+        if free_cnxset is not None:
+            warn('[3.21] free_cnxset is now unneeded',
                  DeprecationWarning, stacklevel=2)
-            free_cnxset = reset_pool
-        if self._cnxset_count != 0:
-            # we are inside ensure_cnx_set, don't lose it
-            free_cnxset = False
+        if reset_pool is not None:
+            warn('[3.13] reset_pool is now unneeded',
+                 DeprecationWarning, stacklevel=2)
         cnxset = self.cnxset
-        if cnxset is None:
-            self.clear()
-            self._session_timestamp.touch()
-            self.debug('rollback transaction %s done (no db activity)', self.connectionid)
-            return
+        assert cnxset is not None
         try:
             # by default, operations are executed with security turned off
             with self.security_enabled(False, False):
@@ -1051,26 +841,18 @@
                 self.debug('rollback for transaction %s done', self.connectionid)
         finally:
             self._session_timestamp.touch()
-            if free_cnxset:
-                self._free_cnxset(ignoremode=True)
             self.clear()
 
     @_open_only
-    def commit(self, free_cnxset=True, reset_pool=None):
+    def commit(self, free_cnxset=None, reset_pool=None):
         """commit the current session's transaction"""
-        if reset_pool is not None:
-            warn('[3.13] use free_cnxset argument instead for reset_pool',
+        if free_cnxset is not None:
+            warn('[3.21] free_cnxset is now unneeded',
                  DeprecationWarning, stacklevel=2)
-            free_cnxset = reset_pool
-        if self.cnxset is None:
-            assert not self.pending_operations
-            self.clear()
-            self._session_timestamp.touch()
-            self.debug('commit transaction %s done (no db activity)', self.connectionid)
-            return
-        if self._cnxset_count != 0:
-            # we are inside ensure_cnx_set, don't lose it
-            free_cnxset = False
+        if reset_pool is not None:
+            warn('[3.13] reset_pool is now unneeded',
+                 DeprecationWarning, stacklevel=2)
+        assert self.cnxset is not None
         cstate = self.commit_state
         if cstate == 'uncommitable':
             raise QueryError('transaction must be rolled back')
@@ -1094,13 +876,14 @@
                 if debug:
                     print self.commit_state, '*' * 20
                 try:
-                    while self.pending_operations:
-                        operation = self.pending_operations.pop(0)
-                        operation.processed = 'precommit'
-                        processed.append(operation)
-                        if debug:
-                            print operation
-                        operation.handle_event('precommit_event')
+                    with self.running_hooks_ops():
+                        while self.pending_operations:
+                            operation = self.pending_operations.pop(0)
+                            operation.processed = 'precommit'
+                            processed.append(operation)
+                            if debug:
+                                print operation
+                            operation.handle_event('precommit_event')
                     self.pending_operations[:] = processed
                     self.debug('precommit transaction %s done', self.connectionid)
                 except BaseException:
@@ -1117,56 +900,52 @@
                     operation.failed = True
                     if debug:
                         print self.commit_state, '*' * 20
-                    for operation in reversed(processed):
-                        if debug:
-                            print operation
-                        try:
-                            operation.handle_event('revertprecommit_event')
-                        except BaseException:
-                            self.critical('error while reverting precommit',
-                                          exc_info=True)
+                    with self.running_hooks_ops():
+                        for operation in reversed(processed):
+                            if debug:
+                                print operation
+                            try:
+                                operation.handle_event('revertprecommit_event')
+                            except BaseException:
+                                self.critical('error while reverting precommit',
+                                              exc_info=True)
                     # XXX use slice notation since self.pending_operations is a
                     # read-only property.
                     self.pending_operations[:] = processed + self.pending_operations
-                    self.rollback(free_cnxset)
+                    self.rollback()
                     raise
                 self.cnxset.commit()
                 self.commit_state = 'postcommit'
                 if debug:
                     print self.commit_state, '*' * 20
-                while self.pending_operations:
-                    operation = self.pending_operations.pop(0)
-                    if debug:
-                        print operation
-                    operation.processed = 'postcommit'
-                    try:
-                        operation.handle_event('postcommit_event')
-                    except BaseException:
-                        self.critical('error while postcommit',
-                                      exc_info=sys.exc_info())
+                with self.running_hooks_ops():
+                    while self.pending_operations:
+                        operation = self.pending_operations.pop(0)
+                        if debug:
+                            print operation
+                        operation.processed = 'postcommit'
+                        try:
+                            operation.handle_event('postcommit_event')
+                        except BaseException:
+                            self.critical('error while postcommit',
+                                          exc_info=sys.exc_info())
                 self.debug('postcommit transaction %s done', self.connectionid)
                 return self.transaction_uuid(set=False)
         finally:
             self._session_timestamp.touch()
-            if free_cnxset:
-                self._free_cnxset(ignoremode=True)
             self.clear()
 
     # resource accessors ######################################################
 
-    @_with_cnx_set
     @_open_only
     def call_service(self, regid, **kwargs):
         self.debug('calling service %s', regid)
         service = self.vreg['services'].select(regid, self, **kwargs)
         return service.call(**kwargs)
 
-    @_with_cnx_set
     @_open_only
     def system_sql(self, sql, args=None, rollback_on_failure=True):
         """return a sql cursor on the system database"""
-        if sql.split(None, 1)[0].upper() != 'SELECT':
-            self.mode = 'write'
         source = self.repo.system_source
         try:
             return source.doexec(self, sql, args, rollback=rollback_on_failure)
@@ -1202,18 +981,6 @@
         args['fset'] = write_attr
     return property(**args)
 
-def cnx_meth(meth_name):
-    """return a function forwarding calls to connection.
-
-    This is to be used by session"""
-    @deprecated('[3.19] use a Connection object instead')
-    def meth_from_cnx(session, *args, **kwargs):
-        result = getattr(session._cnx, meth_name)(*args, **kwargs)
-        if getattr(result, '_cw', None) is not None:
-            result._cw = session
-        return result
-    meth_from_cnx.__doc__ = getattr(Connection, meth_name).__doc__
-    return meth_from_cnx
 
 class Timestamp(object):
 
@@ -1227,149 +994,37 @@
         return float(self.value)
 
 
-class Session(RequestSessionBase): # XXX repoapi: stop being a
-                                   # RequestSessionBase at some point
+class Session(object):
     """Repository user session
 
     This ties all together:
      * session id,
      * user,
-     * connections set,
      * other session data.
-
-    **About session storage / transactions**
-
-    Here is a description of internal session attributes. Besides :attr:`data`
-    and :attr:`transaction_data`, you should not have to use attributes
-    described here but higher level APIs.
-
-      :attr:`data` is a dictionary containing shared data, used to communicate
-      extra information between the client and the repository
-
-      :attr:`_cnxs` is a dictionary of :class:`Connection` instance, one
-      for each running connection. The key is the connection id. By default
-      the connection id is the thread name but it can be otherwise (per dbapi
-      cursor for instance, or per thread name *from another process*).
-
-      :attr:`__threaddata` is a thread local storage whose `cnx` attribute
-      refers to the proper instance of :class:`Connection` according to the
-      connection.
-
-    You should not have to use neither :attr:`_cnx` nor :attr:`__threaddata`,
-    simply access connection data transparently through the :attr:`_cnx`
-    property. Also, you usually don't have to access it directly since current
-    connection's data may be accessed/modified through properties / methods:
-
-      :attr:`connection_data`, similarly to :attr:`data`, is a dictionary
-      containing some shared data that should be cleared at the end of the
-      connection. Hooks and operations may put arbitrary data in there, and
-      this may also be used as a communication channel between the client and
-      the repository.
-
-    .. automethod:: cubicweb.server.session.Session.get_shared_data
-    .. automethod:: cubicweb.server.session.Session.set_shared_data
-    .. automethod:: cubicweb.server.session.Session.added_in_transaction
-    .. automethod:: cubicweb.server.session.Session.deleted_in_transaction
-
-    Connection state information:
-
-      :attr:`running_dbapi_query`, boolean flag telling if the executing query
-      is coming from a dbapi connection or is a query from within the repository
-
-      :attr:`cnxset`, the connections set to use to execute queries on sources.
-      During a transaction, the connection set may be freed so that is may be
-      used by another session as long as no writing is done. This means we can
-      have multiple sessions with a reasonably low connections set pool size.
-
-      .. automethod:: cubicweb.server.session.Session.set_cnxset
-      .. automethod:: cubicweb.server.session.Session.free_cnxset
-
-      :attr:`mode`, string telling the connections set handling mode, may be one
-      of 'read' (connections set may be freed), 'write' (some write was done in
-      the connections set, it can't be freed before end of the transaction),
-      'transaction' (we want to keep the connections set during all the
-      transaction, with or without writing)
-
-      :attr:`pending_operations`, ordered list of operations to be processed on
-      commit/rollback
-
-      :attr:`commit_state`, describing the transaction commit state, may be one
-      of None (not yet committing), 'precommit' (calling precommit event on
-      operations), 'postcommit' (calling postcommit event on operations),
-      'uncommitable' (some :exc:`ValidationError` or :exc:`Unauthorized` error
-      has been raised during the transaction and so it must be rolled back).
-
-    .. automethod:: cubicweb.server.session.Session.commit
-    .. automethod:: cubicweb.server.session.Session.rollback
-    .. automethod:: cubicweb.server.session.Session.close
-    .. automethod:: cubicweb.server.session.Session.closed
-
-    Security level Management:
-
-      :attr:`read_security` and :attr:`write_security`, boolean flags telling if
-      read/write security is currently activated.
-
-    .. automethod:: cubicweb.server.session.Session.security_enabled
-
-    Hooks Management:
-
-      :attr:`hooks_mode`, may be either `HOOKS_ALLOW_ALL` or `HOOKS_DENY_ALL`.
-
-      :attr:`enabled_hook_categories`, when :attr:`hooks_mode` is
-      `HOOKS_DENY_ALL`, this set contains hooks categories that are enabled.
-
-      :attr:`disabled_hook_categories`, when :attr:`hooks_mode` is
-      `HOOKS_ALLOW_ALL`, this set contains hooks categories that are disabled.
-
-    .. automethod:: cubicweb.server.session.Session.deny_all_hooks_but
-    .. automethod:: cubicweb.server.session.Session.allow_all_hooks_but
-    .. automethod:: cubicweb.server.session.Session.is_hook_category_activated
-    .. automethod:: cubicweb.server.session.Session.is_hook_activated
-
-    Data manipulation:
-
-    .. automethod:: cubicweb.server.session.Session.add_relation
-    .. automethod:: cubicweb.server.session.Session.add_relations
-    .. automethod:: cubicweb.server.session.Session.delete_relation
-
-    Other:
-
-    .. automethod:: cubicweb.server.session.Session.call_service
-
-
-
     """
-    is_request = False
-    is_internal_session = False
 
     def __init__(self, user, repo, cnxprops=None, _id=None):
-        super(Session, self).__init__(repo.vreg)
         self.sessionid = _id or make_uid(unormalize(user.login).encode('UTF8'))
         self.user = user # XXX repoapi: deprecated and store only a login.
         self.repo = repo
+        self.vreg = repo.vreg
         self._timestamp = Timestamp()
-        self.default_mode = 'read'
-        # short cut to querier .execute method
-        self._execute = repo.querier.execute
-        # shared data, used to communicate extra information between the client
-        # and the rql server
         self.data = {}
-        # i18n initialization
-        self.set_language(user.prefered_language())
-        ### internals
-        # Connection of this section
-        self._cnxs = {} # XXX repoapi: remove this when nobody use the session
-                        # as a Connection
-        # Data local to the thread
-        self.__threaddata = threading.local() # XXX repoapi: remove this when
-                                              # nobody use the session as a Connection
-        self._cnxset_tracker = CnxSetTracker()
-        self._closed = False
-        self._lock = threading.RLock()
+        self.closed = False
+
+    def close(self):
+        self.closed = True
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, *args):
+        pass
 
     def __unicode__(self):
         return '<session %s (%s 0x%x)>' % (
             unicode(self.user.login), self.sessionid, id(self))
+
     @property
     def timestamp(self):
         return float(self._timestamp)
@@ -1390,55 +1045,6 @@
         """
         return Connection(self)
 
-    def _get_cnx(self, cnxid):
-        """return the <cnxid> connection attached to this session
-
-        Connection is created if necessary"""
-        with self._lock: # no connection exist with the same id
-            try:
-                if self.closed:
-                    raise SessionClosedError('try to access connections set on'
-                                             ' a closed session %s' % self.id)
-                cnx = self._cnxs[cnxid]
-                assert cnx._session_handled
-            except KeyError:
-                cnx = Connection(self, cnxid=cnxid, session_handled=True)
-                self._cnxs[cnxid] = cnx
-                cnx.__enter__()
-        return cnx
-
-    def _close_cnx(self, cnx):
-        """Close a Connection related to a session"""
-        assert cnx._session_handled
-        cnx.__exit__()
-        self._cnxs.pop(cnx.connectionid, None)
-        try:
-            if self.__threaddata.cnx is cnx:
-                del self.__threaddata.cnx
-        except AttributeError:
-            pass
-
-    def set_cnx(self, cnxid=None):
-        # XXX repoapi: remove this when nobody use the session as a Connection
-        """set the default connection of the current thread to <cnxid>
-
-        Connection is created if necessary"""
-        if cnxid is None:
-            cnxid = threading.currentThread().getName()
-        cnx = self._get_cnx(cnxid)
-        # New style session should not be accesed through the session.
-        assert cnx._session_handled
-        self.__threaddata.cnx = cnx
-
-    @property
-    def _cnx(self):
-        """default connection for current session in current thread"""
-        try:
-            return self.__threaddata.cnx
-        except AttributeError:
-            self.set_cnx()
-            return self.__threaddata.cnx
-
     @deprecated('[3.19] use a Connection object instead')
     def get_option_value(self, option, foreid=None):
         if foreid is not None:
@@ -1446,108 +1052,6 @@
                  stacklevel=2)
         return self.repo.get_option_value(option)
 
-    @deprecated('[3.19] use a Connection object instead')
-    def transaction(self, free_cnxset=True):
-        """return context manager to enter a transaction for the session: when
-        exiting the `with` block on exception, call `session.rollback()`, else
-        call `session.commit()` on normal exit.
-
-        The `free_cnxset` will be given to rollback/commit methods to indicate
-        whether the connections set should be freed or not.
-        """
-        return transaction(self, free_cnxset)
-
-    add_relation = cnx_meth('add_relation')
-    add_relations = cnx_meth('add_relations')
-    delete_relation = cnx_meth('delete_relation')
-
-    # relations cache handling #################################################
-
-    update_rel_cache_add = cnx_meth('update_rel_cache_add')
-    update_rel_cache_del = cnx_meth('update_rel_cache_del')
-
-    # resource accessors ######################################################
-
-    system_sql = cnx_meth('system_sql')
-    deleted_in_transaction = cnx_meth('deleted_in_transaction')
-    added_in_transaction = cnx_meth('added_in_transaction')
-    rtype_eids_rdef = cnx_meth('rtype_eids_rdef')
-
-    # security control #########################################################
-
-    @deprecated('[3.19] use a Connection object instead')
-    def security_enabled(self, read=None, write=None):
-        return _session_security_enabled(self, read=read, write=write)
-
-    read_security = cnx_attr('read_security', writable=True)
-    write_security = cnx_attr('write_security', writable=True)
-    running_dbapi_query = cnx_attr('running_dbapi_query')
-
-    # hooks activation control #################################################
-    # all hooks should be activated during normal execution
-
-
-    @deprecated('[3.19] use a Connection object instead')
-    def allow_all_hooks_but(self, *categories):
-        return _session_hooks_control(self, HOOKS_ALLOW_ALL, *categories)
-    @deprecated('[3.19] use a Connection object instead')
-    def deny_all_hooks_but(self, *categories):
-        return _session_hooks_control(self, HOOKS_DENY_ALL, *categories)
-
-    hooks_mode = cnx_attr('hooks_mode')
-
-    disabled_hook_categories = cnx_attr('disabled_hook_cats')
-    enabled_hook_categories = cnx_attr('enabled_hook_cats')
-    disable_hook_categories = cnx_meth('disable_hook_categories')
-    enable_hook_categories = cnx_meth('enable_hook_categories')
-    is_hook_category_activated = cnx_meth('is_hook_category_activated')
-    is_hook_activated = cnx_meth('is_hook_activated')
-
-    # connection management ###################################################
-
-    @deprecated('[3.19] use a Connection object instead')
-    def keep_cnxset_mode(self, mode):
-        """set `mode`, e.g. how the session will keep its connections set:
-
-        * if mode == 'write', the connections set is freed after each read
-          query, but kept until the transaction's end (eg commit or rollback)
-          when a write query is detected (eg INSERT/SET/DELETE queries)
-
-        * if mode == 'transaction', the connections set is only freed after the
-          transaction's end
-
-        notice that a repository has a limited set of connections sets, and a
-        session has to wait for a free connections set to run any rql query
-        (unless it already has one set).
-        """
-        assert mode in ('transaction', 'write')
-        if mode == 'transaction':
-            self.default_mode = 'transaction'
-        else: # mode == 'write'
-            self.default_mode = 'read'
-
-    mode = cnx_attr('mode', writable=True)
-    commit_state = cnx_attr('commit_state', writable=True)
-
-    @property
-    @deprecated('[3.19] use a Connection object instead')
-    def cnxset(self):
-        """connections set, set according to transaction mode for each query"""
-        if self._closed:
-            self.free_cnxset(True)
-            raise SessionClosedError('try to access connections set on a closed session %s' % self.id)
-        return self._cnx.cnxset
-
-    def set_cnxset(self):
-        """the session need a connections set to execute some queries"""
-        with self._lock: # can probably be removed
-            if self._closed:
-                self.free_cnxset(True)
-                raise SessionClosedError('try to set connections set on a closed session %s' % self.id)
-            return self._cnx.set_cnxset()
-    free_cnxset = cnx_meth('free_cnxset')
-    ensure_cnx_set = cnx_attr('ensure_cnx_set')
-
     def _touch(self):
         """update latest session usage timestamp and reset mode to read"""
         self._timestamp.touch()
@@ -1559,156 +1063,6 @@
         assert value == {}
         pass
 
-    # shared data handling ###################################################
-
-    @deprecated('[3.19] use session or transaction data')
-    def get_shared_data(self, key, default=None, pop=False, txdata=False):
-        """return value associated to `key` in session data"""
-        if txdata:
-            return self._cnx.get_shared_data(key, default, pop, txdata=True)
-        else:
-            data = self.data
-        if pop:
-            return data.pop(key, default)
-        else:
-            return data.get(key, default)
-
-    @deprecated('[3.19] use session or transaction data')
-    def set_shared_data(self, key, value, txdata=False):
-        """set value associated to `key` in session data"""
-        if txdata:
-            return self._cnx.set_shared_data(key, value, txdata=True)
-        else:
-            self.data[key] = value
-
-    # server-side service call #################################################
-
-    call_service = cnx_meth('call_service')
-
-    # request interface #######################################################
-
-    @property
-    @deprecated('[3.19] use a Connection object instead')
-    def cursor(self):
-        """return a rql cursor"""
-        return self
-
-    set_entity_cache  = cnx_meth('set_entity_cache')
-    entity_cache      = cnx_meth('entity_cache')
-    cache_entities    = cnx_meth('cached_entities')
-    drop_entity_cache = cnx_meth('drop_entity_cache')
-
-    source_defs = cnx_meth('source_defs')
-    entity_metas = cnx_meth('entity_metas')
-    describe = cnx_meth('describe') # XXX deprecated in 3.19
-
-
-    @deprecated('[3.19] use a Connection object instead')
-    def execute(self, *args, **kwargs):
-        """db-api like method directly linked to the querier execute method.
-
-        See :meth:`cubicweb.dbapi.Cursor.execute` documentation.
-        """
-        rset = self._cnx.execute(*args, **kwargs)
-        rset.req = self
-        return rset
-
-    def _clear_thread_data(self, free_cnxset=True):
-        """remove everything from the thread local storage, except connections set
-        which is explicitly removed by free_cnxset, and mode which is set anyway
-        by _touch
-        """
-        try:
-            cnx = self.__threaddata.cnx
-        except AttributeError:
-            pass
-        else:
-            if free_cnxset:
-                cnx._free_cnxset()
-                if cnx.ctx_count == 0:
-                    self._close_cnx(cnx)
-                else:
-                    cnx.clear()
-            else:
-                cnx.clear()
-
-    @deprecated('[3.19] use a Connection object instead')
-    def commit(self, free_cnxset=True, reset_pool=None):
-        """commit the current session's transaction"""
-        cstate = self._cnx.commit_state
-        if cstate == 'uncommitable':
-            raise QueryError('transaction must be rolled back')
-        try:
-            return self._cnx.commit(free_cnxset, reset_pool)
-        finally:
-            self._clear_thread_data(free_cnxset)
-
-    @deprecated('[3.19] use a Connection object instead')
-    def rollback(self, *args, **kwargs):
-        """rollback the current session's transaction"""
-        return self._rollback(*args, **kwargs)
-
-    def _rollback(self, free_cnxset=True, **kwargs):
-        try:
-            return self._cnx.rollback(free_cnxset, **kwargs)
-        finally:
-            self._clear_thread_data(free_cnxset)
-
-    def close(self):
-        # do not close connections set on session close, since they are shared now
-        tracker = self._cnxset_tracker
-        with self._lock:
-            self._closed = True
-        tracker.close()
-        if self._cnx._session_handled:
-            self._rollback()
-        self.debug('waiting for open connection of session: %s', self)
-        timeout = 10
-        pendings = tracker.wait(timeout)
-        if pendings:
-            self.error('%i connection still alive after 10 seconds, will close '
-                       'session anyway', len(pendings))
-            for cnxid in pendings:
-                cnx = self._cnxs.get(cnxid)
-                if cnx is not None:
-                    # drop cnx.cnxset
-                    with tracker:
-                        try:
-                            cnxset = cnx.cnxset
-                            if cnxset is None:
-                                continue
-                            cnx.cnxset = None
-                        except RuntimeError:
-                            msg = 'issue while force free of cnxset in %s'
-                            self.error(msg, cnx)
-                    # cnxset.reconnect() do an hard reset of the cnxset
-                    # it force it to be freed
-                    cnxset.reconnect()
-                    self.repo._free_cnxset(cnxset)
-        del self.__threaddata
-        del self._cnxs
-
-    @property
-    def closed(self):
-        return not hasattr(self, '_cnxs')
-
-    # transaction data/operations management ##################################
-
-    transaction_data = cnx_attr('transaction_data')
-    pending_operations = cnx_attr('pending_operations')
-    pruned_hooks_cache = cnx_attr('pruned_hooks_cache')
-    add_operation      = cnx_meth('add_operation')
-
-    # undo support ############################################################
-
-    ertype_supports_undo = cnx_meth('ertype_supports_undo')
-    transaction_inc_action_counter = cnx_meth('transaction_inc_action_counter')
-    transaction_uuid = cnx_meth('transaction_uuid')
-
-    # querier helpers #########################################################
-
-    rql_rewriter = cnx_attr('_rewriter')
-
     # deprecated ###############################################################
 
     @property
@@ -1725,52 +1079,10 @@
     def schema_rproperty(self, rtype, eidfrom, eidto, rprop):
         return getattr(self.rtype_eids_rdef(rtype, eidfrom, eidto), rprop)
 
-    @property
-    @deprecated("[3.13] use .cnxset attribute instead of .pool")
-    def pool(self):
-        return self.cnxset
-
-    @deprecated("[3.13] use .set_cnxset() method instead of .set_pool()")
-    def set_pool(self):
-        return self.set_cnxset()
-
-    @deprecated("[3.13] use .free_cnxset() method instead of .reset_pool()")
-    def reset_pool(self):
-        return self.free_cnxset()
-
     # these are overridden by set_log_methods below
     # only defining here to prevent pylint from complaining
     info = warning = error = critical = exception = debug = lambda msg,*a,**kw: None
 
-Session.HOOKS_ALLOW_ALL = HOOKS_ALLOW_ALL
-Session.HOOKS_DENY_ALL = HOOKS_DENY_ALL
-Session.DEFAULT_SECURITY = DEFAULT_SECURITY
-
-
-
-class InternalSession(Session):
-    """special session created internally by the repository"""
-    is_internal_session = True
-    running_dbapi_query = False
-
-    def __init__(self, repo, cnxprops=None, safe=False):
-        super(InternalSession, self).__init__(InternalManager(), repo, cnxprops,
-                                              _id='internal')
-        self.user._cw = self # XXX remove when "vreg = user._cw.vreg" hack in entity.py is gone
-
-    def __enter__(self):
-        return self
-
-    def __exit__(self, exctype, excvalue, tb):
-        self.close()
-
-    @property
-    def cnxset(self):
-        """connections set, set according to transaction mode for each query"""
-        if self.repo.shutting_down:
-            self.free_cnxset(True)
-            raise ShuttingDown('repository is shutting down')
-        return self._cnx.cnxset
 
 
 class InternalManager(object):
@@ -1778,6 +1090,7 @@
     bootstrapping the repository or creating regular users according to
     repository content
     """
+
     def __init__(self, lang='en'):
         self.eid = -1
         self.login = u'__internal_manager__'
--- a/server/sources/__init__.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/sources/__init__.py	Tue Jun 21 07:42:30 2016 +0200
@@ -128,6 +128,9 @@
     def __eq__(self, other):
         return self.uri == other.uri
 
+    def __ne__(self, other):
+        return not (self == other)
+
     def backup(self, backupfile, confirm, format='native'):
         """method called to create a backup of source's data"""
         pass
@@ -395,7 +398,7 @@
     # system source interface #################################################
 
     def eid_type_source(self, cnx, eid):
-        """return a tuple (type, source, extid) for the entity with id <eid>"""
+        """return a tuple (type, extid, source) for the entity with id <eid>"""
         raise NotImplementedError(self)
 
     def create_eid(self, cnx):
--- a/server/sources/datafeed.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/sources/datafeed.py	Tue Jun 21 07:42:30 2016 +0200
@@ -25,7 +25,7 @@
 from datetime import datetime, timedelta
 from base64 import b64decode
 from cookielib import CookieJar
-
+import urlparse
 from lxml import etree
 
 from cubicweb import RegistryNotFound, ObjectNotFound, ValidationError, UnknownEid
@@ -319,24 +319,45 @@
                 return url.replace(mappedurl, URL_MAPPING[mappedurl], 1)
         return url
 
-    def retrieve_url(self, url, data=None, headers=None):
+    def retrieve_url(self, url):
         """Return stream linked by the given url:
         * HTTP urls will be normalized (see :meth:`normalize_url`)
         * handle file:// URL
         * other will be considered as plain content, useful for testing purpose
+
+        For http URLs, it will try to find a cwclientlib config entry
+        (if available) and use it as requester.
         """
-        if headers is None:
-            headers = {}
-        if url.startswith('http'):
-            url = self.normalize_url(url)
-            if data:
-                self.source.info('POST %s %s', url, data)
-            else:
-                self.source.info('GET %s', url)
-            req = urllib2.Request(url, data, headers)
+        purl = urlparse.urlparse(url)
+        if purl.scheme == 'file':
+            return URLLibResponseAdapter(open(url[7:]), url)
+
+        url = self.normalize_url(url)
+
+        # first, try to use cwclientlib if it's available and if the
+        # url matches a configuration entry in ~/.config/cwclientlibrc
+        try:
+            from cwclientlib import cwproxy_for
+            # parse url again since it has been normalized
+            cnx = cwproxy_for(url)
+            cnx.timeout = self.source.http_timeout
+            self.source.info('Using cwclientlib for %s' % url)
+            resp = cnx.get(url)
+            resp.raise_for_status()
+            return URLLibResponseAdapter(StringIO.StringIO(resp.text), url)
+        except (ImportError, ValueError, EnvironmentError) as exc:
+            # ImportError: not available
+            # ValueError: no config entry found
+            # EnvironmentError: no cwclientlib config file found
+            self.source.debug(str(exc))
+
+        # no chance with cwclientlib, fall back to former implementation
+        if purl.scheme in ('http', 'https'):
+            self.source.info('GET %s', url)
+            req = urllib2.Request(url)
             return _OPENER.open(req, timeout=self.source.http_timeout)
-        if url.startswith('file://'):
-            return URLLibResponseAdapter(open(url[7:]), url)
+
+        # url is probably plain content
         return URLLibResponseAdapter(StringIO.StringIO(url), url)
 
     def add_schema_config(self, schemacfg, checkonly=False):
@@ -489,15 +510,32 @@
         raise NotImplementedError
 
     def is_deleted(self, extid, etype, eid):
-        if extid.startswith('http'):
+        if extid.startswith('file://'):
+            return exists(extid[7:])
+
+        url = self.normalize_url(extid)
+        # first, try to use cwclientlib if it's available and if the
+        # url matches a configuration entry in ~/.config/cwclientlibrc
+        try:
+            from cwclientlib import cwproxy_for
+            # parse url again since it has been normalized
+            cnx = cwproxy_for(url)
+            cnx.timeout = self.source.http_timeout
+            self.source.info('Using cwclientlib for checking %s' % url)
+            return cnx.get(url).status_code == 404
+        except (ImportError, ValueError, EnvironmentError) as exc:
+            # ImportError: not available
+            # ValueError: no config entry found
+            # EnvironmentError: no cwclientlib config file found
+            self.source.debug(str(exc))
+
+        # no chance with cwclientlib, fall back to former implementation
+        if urlparse.urlparse(url).scheme in ('http', 'https'):
             try:
-                _OPENER.open(self.normalize_url(extid), # XXX HTTP HEAD request
-                             timeout=self.source.http_timeout)
+                _OPENER.open(url, timeout=self.source.http_timeout)
             except urllib2.HTTPError as ex:
                 if ex.code == 404:
                     return True
-        elif extid.startswith('file://'):
-            return exists(extid[7:])
         return False
 
 
--- a/server/sources/native.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/sources/native.py	Tue Jun 21 07:42:30 2016 +0200
@@ -43,16 +43,16 @@
 from logilab.common.shellutils import getlogin, ASK
 from logilab.database import get_db_helper, sqlgen
 
-from yams import schema2sql as y2sql
 from yams.schema import role_name
 
 from cubicweb import (UnknownEid, AuthenticationError, ValidationError, Binary,
-                      UniqueTogetherError, UndoTransactionException)
+                      UniqueTogetherError, UndoTransactionException, ViolatedConstraint)
 from cubicweb import transaction as tx, server, neg_role
 from cubicweb.utils import QueryCache
 from cubicweb.schema import VIRTUAL_RTYPES
 from cubicweb.cwconfig import CubicWebNoAppConfiguration
 from cubicweb.server import hook
+from cubicweb.server import schema2sql as y2sql
 from cubicweb.server.utils import crypt_password, eschema_eid, verify_and_update
 from cubicweb.server.sqlutils import SQL_PREFIX, SQLAdapterMixIn
 from cubicweb.server.rqlannotation import set_qdata
@@ -60,6 +60,7 @@
 from cubicweb.server.edition import EditedEntity
 from cubicweb.server.sources import AbstractSource, dbg_st_search, dbg_results
 from cubicweb.server.sources.rql2sql import SQLGenerator
+from cubicweb.statsd_logger import statsd_timeit
 
 
 ATTR_MAP = {}
@@ -272,7 +273,7 @@
          {'type' : 'string',
           'default': 'postgres',
           # XXX use choice type
-          'help': 'database driver (postgres, mysql, sqlite, sqlserver2005)',
+          'help': 'database driver (postgres, sqlite, sqlserver2005)',
           'group': 'native-source', 'level': 0,
           }),
         ('db-host',
@@ -376,6 +377,7 @@
         self._cache.pop('Any X WHERE X eid %s' % eid, None)
         self._cache.pop('Any %s' % eid, None)
 
+    @statsd_timeit
     def sqlexec(self, cnx, sql, args=None):
         """execute the query and return its result"""
         return self.process_result(self.doexec(cnx, sql, args))
@@ -480,6 +482,7 @@
 
     # ISource interface #######################################################
 
+    @statsd_timeit
     def compile_rql(self, rql, sols):
         rqlst = self.repo.vreg.rqlhelper.parse(rql)
         rqlst.restricted_vars = ()
@@ -517,6 +520,7 @@
         # can't claim not supporting a relation
         return True #not rtype == 'content_for'
 
+    @statsd_timeit
     def authenticate(self, cnx, login, **kwargs):
         """return CWUser eid for the given login and other authentication
         information found in kwargs, else raise `AuthenticationError`
@@ -553,31 +557,22 @@
                 self._cache[cachekey] = sql, qargs, cbs
         args = self.merge_args(args, qargs)
         assert isinstance(sql, basestring), repr(sql)
-        try:
-            cursor = self.doexec(cnx, sql, args)
-        except (self.OperationalError, self.InterfaceError):
-            if cnx.mode == 'write':
-                # do not attempt to reconnect if there has been some write
-                # during the transaction
-                raise
-            # FIXME: better detection of deconnection pb
-            self.warning("trying to reconnect")
-            cnx.cnxset.reconnect()
-            cursor = self.doexec(cnx, sql, args)
-        except self.DbapiError as exc:
-            # We get this one with pyodbc and SQL Server when connection was reset
-            if exc.args[0] == '08S01' and cnx.mode != 'write':
-                self.warning("trying to reconnect")
-                cnx.cnxset.reconnect()
-                cursor = self.doexec(cnx, sql, args)
-            else:
-                raise
+        cursor = self.doexec(cnx, sql, args)
         results = self.process_result(cursor, cnx, cbs)
         assert dbg_results(results)
         return results
 
     @contextmanager
-    def _storage_handler(self, entity, event):
+    def _fixup_cw(self, cnx, entity):
+        _cw = entity._cw
+        entity._cw = cnx
+        try:
+            yield
+        finally:
+            entity._cw = _cw
+
+    @contextmanager
+    def _storage_handler(self, cnx, entity, event):
         # 1/ memorize values as they are before the storage is called.
         #    For instance, the BFSStorage will replace the `data`
         #    binary value with a Binary containing the destination path
@@ -592,14 +587,15 @@
         etype = entities[0].__regid__
         for attr, storage in self._storages.get(etype, {}).items():
             for entity in entities:
-                if event == 'deleted':
-                    storage.entity_deleted(entity, attr)
-                else:
-                    edited = entity.cw_edited
-                    if attr in edited:
-                        handler = getattr(storage, 'entity_%s' % event)
-                        to_restore = handler(entity, attr)
-                        restore_values.append((entity, attr, to_restore))
+                with self._fixup_cw(cnx, entity):
+                    if event == 'deleted':
+                        storage.entity_deleted(entity, attr)
+                    else:
+                        edited = entity.cw_edited
+                        if attr in edited:
+                            handler = getattr(storage, 'entity_%s' % event)
+                            to_restore = handler(entity, attr)
+                            restore_values.append((entity, attr, to_restore))
         try:
             yield # 2/ execute the source's instructions
         finally:
@@ -609,22 +605,22 @@
 
     def add_entity(self, cnx, entity):
         """add a new entity to the source"""
-        with self._storage_handler(entity, 'added'):
+        with self._storage_handler(cnx, entity, 'added'):
             attrs = self.preprocess_entity(entity)
             sql = self.sqlgen.insert(SQL_PREFIX + entity.cw_etype, attrs)
             self.doexec(cnx, sql, attrs)
             if cnx.ertype_supports_undo(entity.cw_etype):
-                self._record_tx_action(cnx, 'tx_entity_actions', 'C',
-                                       etype=entity.cw_etype, eid=entity.eid)
+                self._record_tx_action(cnx, 'tx_entity_actions', u'C',
+                                       etype=unicode(entity.cw_etype), eid=entity.eid)
 
     def update_entity(self, cnx, entity):
         """replace an entity in the source"""
-        with self._storage_handler(entity, 'updated'):
+        with self._storage_handler(cnx, entity, 'updated'):
             attrs = self.preprocess_entity(entity)
             if cnx.ertype_supports_undo(entity.cw_etype):
                 changes = self._save_attrs(cnx, entity, attrs)
-                self._record_tx_action(cnx, 'tx_entity_actions', 'U',
-                                       etype=entity.cw_etype, eid=entity.eid,
+                self._record_tx_action(cnx, 'tx_entity_actions', u'U',
+                                       etype=unicode(entity.cw_etype), eid=entity.eid,
                                        changes=self._binary(dumps(changes)))
             sql = self.sqlgen.update(SQL_PREFIX + entity.cw_etype, attrs,
                                      ['cw_eid'])
@@ -632,14 +628,14 @@
 
     def delete_entity(self, cnx, entity):
         """delete an entity from the source"""
-        with self._storage_handler(entity, 'deleted'):
+        with self._storage_handler(cnx, entity, 'deleted'):
             if cnx.ertype_supports_undo(entity.cw_etype):
                 attrs = [SQL_PREFIX + r.type
                          for r in entity.e_schema.subject_relations()
                          if (r.final or r.inlined) and not r in VIRTUAL_RTYPES]
                 changes = self._save_attrs(cnx, entity, attrs)
-                self._record_tx_action(cnx, 'tx_entity_actions', 'D',
-                                       etype=entity.cw_etype, eid=entity.eid,
+                self._record_tx_action(cnx, 'tx_entity_actions', u'D',
+                                       etype=unicode(entity.cw_etype), eid=entity.eid,
                                        changes=self._binary(dumps(changes)))
             attrs = {'cw_eid': entity.eid}
             sql = self.sqlgen.delete(SQL_PREFIX + entity.cw_etype, attrs)
@@ -649,16 +645,16 @@
         """add a relation to the source"""
         self._add_relations(cnx,  rtype, [(subject, object)], inlined)
         if cnx.ertype_supports_undo(rtype):
-            self._record_tx_action(cnx, 'tx_relation_actions', 'A',
-                                   eid_from=subject, rtype=rtype, eid_to=object)
+            self._record_tx_action(cnx, 'tx_relation_actions', u'A',
+                                   eid_from=subject, rtype=unicode(rtype), eid_to=object)
 
     def add_relations(self, cnx,  rtype, subj_obj_list, inlined=False):
         """add a relations to the source"""
         self._add_relations(cnx, rtype, subj_obj_list, inlined)
         if cnx.ertype_supports_undo(rtype):
             for subject, object in subj_obj_list:
-                self._record_tx_action(cnx, 'tx_relation_actions', 'A',
-                                       eid_from=subject, rtype=rtype, eid_to=object)
+                self._record_tx_action(cnx, 'tx_relation_actions', u'A',
+                                       eid_from=subject, rtype=unicode(rtype), eid_to=object)
 
     def _add_relations(self, cnx, rtype, subj_obj_list, inlined=False):
         """add a relation to the source"""
@@ -689,8 +685,8 @@
         rschema = self.schema.rschema(rtype)
         self._delete_relation(cnx, subject, rtype, object, rschema.inlined)
         if cnx.ertype_supports_undo(rtype):
-            self._record_tx_action(cnx, 'tx_relation_actions', 'R',
-                                   eid_from=subject, rtype=rtype, eid_to=object)
+            self._record_tx_action(cnx, 'tx_relation_actions', u'R',
+                                   eid_from=subject, rtype=unicode(rtype), eid_to=object)
 
     def _delete_relation(self, cnx, subject, rtype, object, inlined=False):
         """delete a relation from the source"""
@@ -705,6 +701,7 @@
             sql = self.sqlgen.delete('%s_relation' % rtype, attrs)
         self.doexec(cnx, sql, attrs)
 
+    @statsd_timeit
     def doexec(self, cnx, query, args=None, rollback=True):
         """Execute a query.
         it's a function just so that it shows up in profiling
@@ -749,15 +746,28 @@
                         columns = arg.split(':', 1)[1].split(',')
                         rtypes = [c.split('.', 1)[1].strip()[3:] for c in columns]
                         raise UniqueTogetherError(cnx, rtypes=rtypes)
+
+                    mo = re.search('"cstr[a-f0-9]{32}"', arg)
+                    if mo is not None:
+                        # postgresql
+                        raise ViolatedConstraint(cnx, cstrname=mo.group(0)[1:-1])
+                    if arg.startswith('CHECK constraint failed:'):
+                        # sqlite3 (new)
+                        raise ViolatedConstraint(cnx, cstrname=arg.split(':', 1)[1].strip())
+                    mo = re.match('^constraint (cstr.*) failed$', arg)
+                    if mo is not None:
+                        # sqlite3 (old)
+                        raise ViolatedConstraint(cnx, cstrname=mo.group(1))
             raise
         return cursor
 
+    @statsd_timeit
     def doexecmany(self, cnx, query, args):
         """Execute a query.
         it's a function just so that it shows up in profiling
         """
         if server.DEBUG & server.DBG_SQL:
-            print 'execmany', query, 'with', len(args), 'arguments'
+            print 'execmany', query, 'with', len(args), 'arguments', cnx.cnxset.cnx
         cursor = cnx.cnxset.cu
         try:
             # str(query) to avoid error if it's a unicode string
@@ -829,23 +839,17 @@
 
     # system source interface #################################################
 
-    def _eid_type_source(self, cnx, eid, sql, _retry=True):
+    def _eid_type_source(self, cnx, eid, sql):
         try:
             res = self.doexec(cnx, sql).fetchone()
             if res is not None:
                 return res
-        except (self.OperationalError, self.InterfaceError):
-            if cnx.mode == 'read' and _retry:
-                self.warning("trying to reconnect (eid_type_source())")
-                cnx.cnxset.reconnect()
-                return self._eid_type_source(cnx, eid, sql, _retry=False)
         except Exception:
-            assert cnx.cnxset, 'connection has no connections set'
             self.exception('failed to query entities table for eid %s', eid)
         raise UnknownEid(eid)
 
     def eid_type_source(self, cnx, eid): # pylint: disable=E0202
-        """return a tuple (type, source, extid) for the entity with id <eid>"""
+        """return a tuple (type, extid, source) for the entity with id <eid>"""
         sql = 'SELECT type, extid, asource FROM entities WHERE eid=%s' % eid
         res = self._eid_type_source(cnx, eid, sql)
         if res[-2] is not None:
@@ -855,7 +859,7 @@
         return res
 
     def eid_type_source_pre_131(self, cnx, eid):
-        """return a tuple (type, source, extid) for the entity with id <eid>"""
+        """return a tuple (type, extid, source) for the entity with id <eid>"""
         sql = 'SELECT type, extid FROM entities WHERE eid=%s' % eid
         res = self._eid_type_source(cnx, eid, sql)
         if not isinstance(res, list):
@@ -868,9 +872,10 @@
     def extid2eid(self, cnx, extid):
         """get eid from an external id. Return None if no record found."""
         assert isinstance(extid, str)
+        args = {'x': b64encode(extid)}
         cursor = self.doexec(cnx,
                              'SELECT eid FROM entities WHERE extid=%(x)s',
-                             {'x': b64encode(extid)})
+                             args)
         # XXX testing rowcount cause strange bug with sqlite, results are there
         #     but rowcount is 0
         #if cursor.rowcount > 0:
@@ -880,6 +885,17 @@
                 return result[0]
         except Exception:
             pass
+        cursor = self.doexec(cnx,
+                             'SELECT eid FROM moved_entities WHERE extid=%(x)s',
+                             args)
+        try:
+            result = cursor.fetchone()
+            if result:
+                # entity was moved to the system source, return negative
+                # number to tell the external source to ignore it
+                return -result[0]
+        except Exception:
+            pass
         return None
 
     def _handle_is_relation_sql(self, cnx, sql, attrs):
@@ -897,7 +913,7 @@
         if extid is not None:
             assert isinstance(extid, str)
             extid = b64encode(extid)
-        attrs = {'type': entity.cw_etype, 'eid': entity.eid, 'extid': extid and unicode(extid),
+        attrs = {'type': unicode(entity.cw_etype), 'eid': entity.eid, 'extid': extid and unicode(extid),
                  'asource': unicode(source.uri)}
         self._handle_insert_entity_sql(cnx, self.sqlgen.insert('entities', attrs), attrs)
         # insert core relations: is, is_instance_of and cw_source
@@ -940,7 +956,7 @@
     # undo support #############################################################
 
     def undoable_transactions(self, cnx, ueid=None, **actionfilters):
-        """See :class:`cubicweb.repoapi.ClientConnection.undoable_transactions`"""
+        """See :class:`cubicweb.repoapi.Connection.undoable_transactions`"""
         # force filtering to connection's user if not a manager
         if not cnx.user.is_in_group('managers'):
             ueid = cnx.user.eid
@@ -965,7 +981,7 @@
                     # only, and with no eid specified
                     assert actionfilters.get('action', 'C') in 'CUD'
                     assert not 'eid' in actionfilters
-                    tearestr['etype'] = val
+                    tearestr['etype'] = unicode(val)
                 elif key == 'eid':
                     # eid filter may apply to 'eid' of tx_entity_actions or to
                     # 'eid_from' OR 'eid_to' of tx_relation_actions
@@ -976,10 +992,10 @@
                         trarestr['eid_to'] = val
                 elif key == 'action':
                     if val in 'CUD':
-                        tearestr['txa_action'] = val
+                        tearestr['txa_action'] = unicode(val)
                     else:
                         assert val in 'AR'
-                        trarestr['txa_action'] = val
+                        trarestr['txa_action'] = unicode(val)
                 else:
                     raise AssertionError('unknow filter %s' % key)
             assert trarestr or tearestr, "can't only filter on 'public'"
@@ -1007,17 +1023,17 @@
             restr.update(tearestr)
         # we want results ordered by transaction's time descendant
         sql += ' ORDER BY tx_time DESC'
-        with cnx.ensure_cnx_set:
-            cu = self.doexec(cnx, sql, restr)
-            # turn results into transaction objects
-            return [tx.Transaction(*args) for args in cu.fetchall()]
+        cu = self.doexec(cnx, sql, restr)
+        # turn results into transaction objects
+        return [tx.Transaction(cnx, *args) for args in cu.fetchall()]
 
     def tx_info(self, cnx, txuuid):
-        """See :class:`cubicweb.repoapi.ClientConnection.transaction_info`"""
-        return tx.Transaction(txuuid, *self._tx_info(cnx, txuuid))
+        """See :class:`cubicweb.repoapi.Connection.transaction_info`"""
+        return tx.Transaction(cnx, txuuid, *self._tx_info(cnx, unicode(txuuid)))
 
     def tx_actions(self, cnx, txuuid, public):
-        """See :class:`cubicweb.repoapi.ClientConnection.transaction_actions`"""
+        """See :class:`cubicweb.repoapi.Connection.transaction_actions`"""
+        txuuid = unicode(txuuid)
         self._tx_info(cnx, txuuid)
         restr = {'tx_uuid': txuuid}
         if public:
@@ -1039,13 +1055,11 @@
         return sorted(actions, key=lambda x: x.order)
 
     def undo_transaction(self, cnx, txuuid):
-        """See :class:`cubicweb.repoapi.ClientConnection.undo_transaction`
+        """See :class:`cubicweb.repoapi.Connection.undo_transaction`
 
         important note: while undoing of a transaction, only hooks in the
         'integrity', 'activeintegrity' and 'undo' categories are called.
         """
-        # set mode so connections set isn't released subsquently until commit/rollback
-        cnx.mode = 'write'
         errors = []
         cnx.transaction_data['undoing_uuid'] = txuuid
         with cnx.deny_all_hooks_but('integrity', 'activeintegrity', 'undo'):
@@ -1097,7 +1111,7 @@
         kwargs['tx_uuid'] = cnx.transaction_uuid()
         kwargs['txa_action'] = action
         kwargs['txa_order'] = cnx.transaction_inc_action_counter()
-        kwargs['txa_public'] = cnx.running_dbapi_query
+        kwargs['txa_public'] = not cnx.hooks_in_progress
         self.doexec(cnx, self.sqlgen.insert(table, kwargs), kwargs)
 
     def _tx_info(self, cnx, txuuid):
@@ -1106,19 +1120,18 @@
         raise `NoSuchTransaction` if there is no such transaction of if the
         connection's user isn't allowed to see it.
         """
-        with cnx.ensure_cnx_set:
-            restr = {'tx_uuid': txuuid}
-            sql = self.sqlgen.select('transactions', restr,
-                                     ('tx_time', 'tx_user'))
-            cu = self.doexec(cnx, sql, restr)
-            try:
-                time, ueid = cu.fetchone()
-            except TypeError:
-                raise tx.NoSuchTransaction(txuuid)
-            if not (cnx.user.is_in_group('managers')
-                    or cnx.user.eid == ueid):
-                raise tx.NoSuchTransaction(txuuid)
-            return time, ueid
+        restr = {'tx_uuid': txuuid}
+        sql = self.sqlgen.select('transactions', restr,
+                                 ('tx_time', 'tx_user'))
+        cu = self.doexec(cnx, sql, restr)
+        try:
+            time, ueid = cu.fetchone()
+        except TypeError:
+            raise tx.NoSuchTransaction(txuuid)
+        if not (cnx.user.is_in_group('managers')
+                or cnx.user.eid == ueid):
+            raise tx.NoSuchTransaction(txuuid)
+        return time, ueid
 
     def _reedit_entity(self, entity, changes, err):
         cnx = entity._cw
@@ -1151,6 +1164,7 @@
                         err(cnx._("can't restore entity %(eid)s of type %(eschema)s, "
                                       "target of %(rtype)s (eid %(value)s) does not exist any longer")
                             % locals())
+                        changes[column] = None
             elif eschema.destination(rtype) in ('Bytes', 'Password'):
                 changes[column] = self._binary(value)
                 edited[rtype] = Binary(value)
@@ -1182,10 +1196,10 @@
         self.repo.hm.call_hooks('before_add_entity', cnx, entity=entity)
         # restore the entity
         action.changes['cw_eid'] = eid
+        # restore record in entities (will update fti if needed)
+        self.add_info(cnx, entity, self, None)
         sql = self.sqlgen.insert(SQL_PREFIX + etype, action.changes)
         self.doexec(cnx, sql, action.changes)
-        # restore record in entities (will update fti if needed)
-        self.add_info(cnx, entity, self, None)
         self.repo.hm.call_hooks('after_add_entity', cnx, entity=entity)
         return errors
 
@@ -1390,7 +1404,10 @@
   extid VARCHAR(256)
 );;
 CREATE INDEX entities_type_idx ON entities(type);;
-CREATE INDEX entities_extid_idx ON entities(extid);;
+CREATE TABLE moved_entities (
+  eid INTEGER PRIMARY KEY NOT NULL,
+  extid VARCHAR(256) UNIQUE NOT NULL
+);;
 
 CREATE TABLE transactions (
   tx_uuid CHAR(32) PRIMARY KEY NOT NULL,
@@ -1442,18 +1459,22 @@
     DELETE FROM tx_relation_actions WHERE tx_uuid=OLD.tx_uuid;
 END;;
 '''
+    schema += ';;'.join(helper.sqls_create_multicol_unique_index('entities', ['extid']))
+    schema += ';;\n'
     return schema
 
 
 def sql_drop_schema(driver):
     helper = get_db_helper(driver)
     return """
+%s;
 %s
 DROP TABLE entities;
 DROP TABLE tx_entity_actions;
 DROP TABLE tx_relation_actions;
 DROP TABLE transactions;
-""" % helper.sql_drop_numrange('entities_id_seq')
+""" % (';'.join(helper.sqls_drop_multicol_unique_index('entities', ['extid'])),
+       helper.sql_drop_numrange('entities_id_seq'))
 
 
 def grant_schema(user, set_owner=True):
@@ -1530,7 +1551,7 @@
                                         SQL_PREFIX + 'login'),
                                        {'newhash': self.source._binary(newhash),
                                         'login': login})
-                    cnx.commit(free_cnxset=False)
+                    cnx.commit()
             return user
         except IndexError:
             raise AuthenticationError('bad password')
--- a/server/sources/rql2sql.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/sources/rql2sql.py	Tue Jun 21 07:42:30 2016 +0200
@@ -50,9 +50,7 @@
 __docformat__ = "restructuredtext en"
 
 import threading
-from datetime import datetime, time
 
-from logilab.common.date import utcdatetime, utctime
 from logilab.database import FunctionDescr, SQL_FUNCTIONS_REGISTRY
 
 from rql import BadRQLQuery, CoercionError
@@ -397,11 +395,14 @@
             yield 1
             return
         thisexistssols, thisexistsvars = self.existssols[exists]
+        notdone_outside_vars = set()
         # when iterating other solutions inner to an EXISTS subquery, we should
         # reset variables which have this exists node as scope at each iteration
         for var in exists.stmt.defined_vars.itervalues():
             if var.scope is exists:
                 thisexistsvars.add(var.name)
+            elif var.name not in self.done:
+                notdone_outside_vars.add(var)
         origsol = self.solution
         origtables = self.tables
         done = self.done
@@ -417,6 +418,10 @@
                 for var in thisexistsvars:
                     if var in done:
                         done.remove(var)
+                for var in list(notdone_outside_vars):
+                    if var.name in done and var._q_sqltable in self.tables:
+                        origtables[var._q_sqltable] = self.tables[var._q_sqltable]
+                        notdone_outside_vars.remove(var)
                 for rel in exists.iget_nodes(Relation):
                     if rel in done:
                         done.remove(rel)
@@ -1509,14 +1514,6 @@
                 _id = value
                 if isinstance(_id, unicode):
                     _id = _id.encode()
-                # convert timestamp to utc.
-                # expect SET TiME ZONE to UTC at connection opening time.
-                # This shouldn't change anything for datetime without TZ.
-                value = self._args[_id]
-                if isinstance(value, datetime) and value.tzinfo is not None:
-                    self._query_attrs[_id] = utcdatetime(value)
-                elif isinstance(value, time) and value.tzinfo is not None:
-                    self._query_attrs[_id] = utctime(value)
         else:
             _id = str(id(constant)).replace('-', '', 1)
             self._query_attrs[_id] = value
--- a/server/sources/storages.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/sources/storages.py	Tue Jun 21 07:42:30 2016 +0200
@@ -141,7 +141,6 @@
         """an entity using this storage for attr has been added"""
         if entity._cw.transaction_data.get('fs_importing'):
             binary = Binary.from_file(entity.cw_edited[attr].getvalue())
-            entity._cw_dont_cache_attribute(attr, repo_side=True)
         else:
             binary = entity.cw_edited.pop(attr)
             fd, fpath = self.new_fs_path(entity, attr)
@@ -160,7 +159,6 @@
             # We do not need to create it but we need to fetch the content of
             # the file as the actual content of the attribute
             fpath = entity.cw_edited[attr].getvalue()
-            entity._cw_dont_cache_attribute(attr, repo_side=True)
             assert fpath is not None
             binary = Binary.from_file(fpath)
         else:
--- a/server/sqlutils.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/sqlutils.py	Tue Jun 21 07:42:30 2016 +0200
@@ -20,17 +20,18 @@
 __docformat__ = "restructuredtext en"
 
 import sys
-import os
 import re
 import subprocess
 from os.path import abspath
 from itertools import ifilter
 from logging import getLogger
+from datetime import time, datetime
 
 from logilab import database as db, common as lgc
 from logilab.common.shellutils import ProgressBar, DummyProgressBar
 from logilab.common.deprecation import deprecated
 from logilab.common.logging_ext import set_log_methods
+from logilab.common.date import utctime, utcdatetime
 from logilab.database.sqlgen import SQLGenerator
 
 from cubicweb import Binary, ConfigurationError
@@ -106,7 +107,7 @@
     """return sql to give all access privileges to the given user on the system
     schema
     """
-    from yams.schema2sql import grant_schema
+    from cubicweb.server.schema2sql import grant_schema
     from cubicweb.server.sources import native
     output = []
     w = output.append
@@ -124,7 +125,7 @@
               user=None, set_owner=False,
               skip_relations=PURE_VIRTUAL_RTYPES, skip_entities=()):
     """return the system sql schema, according to the given parameters"""
-    from yams.schema2sql import schema2sql
+    from cubicweb.server.schema2sql import schema2sql
     from cubicweb.server.sources import native
     if set_owner:
         assert user, 'user is argument required when set_owner is true'
@@ -149,7 +150,7 @@
 def sqldropschema(schema, driver, text_index=True,
                   skip_relations=PURE_VIRTUAL_RTYPES, skip_entities=()):
     """return the sql to drop the schema, according to the given parameters"""
-    from yams.schema2sql import dropschema2sql
+    from cubicweb.server.schema2sql import dropschema2sql
     from cubicweb.server.sources import native
     output = []
     w = output.append
@@ -373,8 +374,17 @@
                 # convert cubicweb binary into db binary
                 if isinstance(val, Binary):
                     val = self._binary(val.getvalue())
+                # convert timestamp to utc.
+                # expect SET TiME ZONE to UTC at connection opening time.
+                # This shouldn't change anything for datetime without TZ.
+                elif isinstance(val, datetime) and val.tzinfo is not None:
+                    val = utcdatetime(val)
+                elif isinstance(val, time) and val.tzinfo is not None:
+                    val = utctime(val)
                 newargs[key] = val
             # should not collide
+            assert not (frozenset(newargs) & frozenset(query_args)), \
+                'unexpected collision: %s' % (frozenset(newargs) & frozenset(query_args))
             newargs.update(query_args)
             return newargs
         return query_args
@@ -503,6 +513,8 @@
         return (dt.weekday() + 1) % 7
     cnx.create_function("WEEKDAY", 1, weekday)
 
+    cnx.cursor().execute("pragma foreign_keys = on")
+
     import yams.constraints
     yams.constraints.patch_sqlite_decimal()
 
--- a/server/ssplanner.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/ssplanner.py	Tue Jun 21 07:42:30 2016 +0200
@@ -249,7 +249,7 @@
                 raise QueryError('can not assign to %r relation'
                                  % relation.r_type)
             lhs, rhs = relation.get_variable_parts()
-            lhskey = lhs.as_string('utf-8')
+            lhskey = lhs.as_string()
             if not lhskey in selectedidx:
                 if lhs.variable in eidconsts:
                     eid = eidconsts[lhs.variable]
@@ -262,7 +262,7 @@
                 selectedidx[lhskey] = lhsinfo
             else:
                 lhsinfo = selectedidx[lhskey][:-1] + (None,)
-            rhskey = rhs.as_string('utf-8')
+            rhskey = rhs.as_string()
             if not rhskey in selectedidx:
                 if isinstance(rhs, Constant):
                     rhsinfo = (_CONSTANT, rhs.eval(plan.args), residx)
--- a/server/test/data-cwep002/schema.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/test/data-cwep002/schema.py	Tue Jun 21 07:42:30 2016 +0200
@@ -32,4 +32,5 @@
 
 class has_employee(ComputedRelation):
     rule = 'O works_for S'
+    __permissions__ = {'read': ('managers',)}
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/data-migractions/bootstrap_cubes	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,1 @@
+card,comment,tag,basket,file,localperms,fakeemail
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/data-migractions/cubes/__init__.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,1 @@
+__import__('pkg_resources').declare_namespace(__name__)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/data-migractions/cubes/fakeemail/__pkginfo__.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,53 @@
+# pylint: disable-msg=W0622
+"""cubicweb-fakeemail packaging information"""
+
+modname = 'fakeemail'
+distname = "cubicweb-%s" % modname
+
+numversion = (1, 10, 0)
+version = '.'.join(str(num) for num in numversion)
+
+license = 'LGPL'
+author = "Logilab"
+author_email = "contact@logilab.fr"
+web = 'http://www.cubicweb.org/project/%s' % distname
+description = "email component for the CubicWeb framework"
+classifiers = [
+           'Environment :: Web Environment',
+           'Framework :: CubicWeb',
+           'Programming Language :: Python',
+           'Programming Language :: JavaScript',
+]
+
+# used packages
+__depends__ = {'cubicweb': '>= 3.19.0',
+               'cubicweb-file': '>= 1.9.0',
+               'logilab-common': '>= 0.58.3',
+               }
+__recommends__ = {'cubicweb-comment': None}
+
+
+# packaging ###
+
+from os import listdir as _listdir
+from os.path import join, isdir
+from glob import glob
+
+THIS_CUBE_DIR = join('share', 'cubicweb', 'cubes', modname)
+
+def listdir(dirpath):
+    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))]
+
+data_files = [
+    # common files
+    [THIS_CUBE_DIR, [fname for fname in glob('*.py') if fname != 'setup.py']],
+    ]
+# check for possible extended cube layout
+for dirname in ('entities', 'views', 'sobjects', 'hooks', 'schema', 'data', 'i18n', 'migration', 'wdoc'):
+    if isdir(dirname):
+        data_files.append([join(THIS_CUBE_DIR, dirname), listdir(dirname)])
+# Note: here, you'll need to add subdirectories if you want
+# them to be included in the debian package
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/data-migractions/cubes/fakeemail/schema.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,88 @@
+"""entity/relation schemas to store email in an cubicweb instance
+
+:organization: Logilab
+:copyright: 2006-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+_ = unicode
+
+# pylint: disable-msg=E0611,F0401
+from yams.buildobjs import (SubjectRelation, RelationType, EntityType,
+                            String, Datetime, Int, RelationDefinition)
+from yams.reader import context
+
+from cubicweb.schema import ERQLExpression
+
+
+class Email(EntityType):
+    """electronic mail"""
+    subject   = String(fulltextindexed=True)
+    date      = Datetime(description=_('UTC time on which the mail was sent'))
+    messageid = String(required=True, indexed=True)
+    headers   = String(description=_('raw headers'))
+
+    sender     = SubjectRelation('EmailAddress', cardinality='?*')
+    # an email with only Bcc is acceptable, don't require any recipients
+    recipients = SubjectRelation('EmailAddress')
+    cc         = SubjectRelation('EmailAddress')
+
+    parts       = SubjectRelation('EmailPart', cardinality='*1', composite='subject')
+    attachment  = SubjectRelation('File')
+
+    reply_to    = SubjectRelation('Email', cardinality='?*')
+    cites       = SubjectRelation('Email')
+    in_thread   = SubjectRelation('EmailThread', cardinality='?*')
+
+
+class EmailPart(EntityType):
+    """an email attachment"""
+    __permissions__ = {
+        'read':   ('managers', 'users', 'guests',), # XXX if E parts X, U has_read_permission E
+        'add':    ('managers', ERQLExpression('E parts X, U has_update_permission E'),),
+        'delete': ('managers', ERQLExpression('E parts X, U has_update_permission E')),
+        'update': ('managers', 'owners',),
+        }
+
+    content  = String(fulltextindexed=True)
+    content_format = String(required=True, maxsize=50)
+    ordernum = Int(required=True)
+    alternative = SubjectRelation('EmailPart', symmetric=True)
+
+
+class EmailThread(EntityType):
+    """discussion thread"""
+    title = String(required=True, indexed=True, fulltextindexed=True)
+    see_also = SubjectRelation('EmailThread')
+    forked_from = SubjectRelation('EmailThread', cardinality='?*')
+
+class parts(RelationType):
+    """ """
+    fulltext_container = 'subject'
+
+class sender(RelationType):
+    """ """
+    inlined = True
+
+class in_thread(RelationType):
+    """ """
+    inlined = True
+
+class reply_to(RelationType):
+    """ """
+    inlined = True
+
+class generated_by(RelationType):
+    """mark an entity as generated from an email"""
+    cardinality = '?*'
+    subject = ('TrInfo',)
+    object = 'Email'
+
+# if comment is installed
+if 'Comment' in context.defined:
+    class comment_generated_by(RelationDefinition):
+        subject = 'Comment'
+        name = 'generated_by'
+        object = 'Email'
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/data-migractions/migratedapp/__init__.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,17 @@
+# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/data-migractions/migratedapp/bootstrap_cubes	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,1 @@
+card,comment,tag,basket,fakeemail,file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/data-migractions/migratedapp/schema.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,212 @@
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+"""cw.server.migraction test"""
+import datetime as dt
+from yams.buildobjs import (EntityType, RelationType, RelationDefinition,
+                            SubjectRelation, Bytes,
+                            RichString, String, Int, Boolean, Datetime, Date, Float)
+from yams.constraints import SizeConstraint, UniqueConstraint
+from cubicweb.schema import (WorkflowableEntityType, RQLConstraint,
+                             RQLVocabularyConstraint,
+                             ERQLExpression, RRQLExpression)
+
+class Affaire(EntityType):
+    __permissions__ = {
+        'read':   ('managers', 'users', 'guests'),
+        'add':    ('managers', ERQLExpression('X concerne S, S owned_by U')),
+        '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,
+                 constraints=[SizeConstraint(256)])
+    concerne = SubjectRelation('Societe')
+    opt_attr = Bytes()
+
+class Societe(WorkflowableEntityType):
+    __permissions__ = {
+        'read': ('managers', 'users', 'guests'),
+        'update': ('managers', 'owners'),
+        'delete': ('managers', 'owners'),
+        'add': ('managers', 'users',)
+        }
+    nom  = String(maxsize=64, fulltextindexed=True)
+    web  = String(maxsize=128)
+    tel  = Float()
+    fax  = Int()
+    rncs = String(maxsize=128)
+    ad1  = String(maxsize=128)
+    ad2  = String(maxsize=128)
+    ad3  = String(maxsize=128)
+    cp   = String(maxsize=12)
+    ville= String(maxsize=32)
+
+# Division and SubDivision are gone
+
+# New
+class Para(EntityType):
+    para = String(maxsize=512)
+    newattr = String()
+    newinlined = SubjectRelation('Affaire', cardinality='?*', inlined=True)
+    newnotinlined = SubjectRelation('Affaire', cardinality='?*')
+
+class Note(Para):
+    __specializes_schema__ = True
+
+    __permissions__ = {'read':   ('managers', 'users', 'guests',),
+                   'update': ('managers', 'owners',),
+                   'delete': ('managers', ),
+                   'add':    ('managers',
+                              ERQLExpression('X ecrit_part PE, U in_group G, '
+                                             'PE require_permission P, P name "add_note", '
+                                             'P require_group G'),)}
+
+    whatever = Int(default=0)  # keep it before `date` for unittest_migraction.test_add_attribute_int
+    yesno = Boolean(default=False)
+    date = Datetime()
+    type = String(maxsize=1)
+    unique_id = String(maxsize=1, required=True, unique=True)
+    mydate = Date(default='TODAY')
+    oldstyledefaultdate = Date(default='2013/01/01')
+    newstyledefaultdate = Date(default=dt.date(2013, 1, 1))
+    shortpara = String(maxsize=64, default='hop')
+    ecrit_par = SubjectRelation('Personne', constraints=[RQLConstraint('S concerne A, O concerne A')])
+    attachment = SubjectRelation('File')
+
+
+class Frozable(EntityType):
+    __permissions__ = {
+        'read':   ('managers', 'users'),
+        'add':    ('managers', 'users'),
+        'update': ('managers', ERQLExpression('X frozen False'),),
+        'delete': ('managers', ERQLExpression('X frozen False'),)
+    }
+    name = String()
+    frozen = Boolean(default=False,
+                     __permissions__ = {
+                         'read':   ('managers', 'users'),
+                         'add':    ('managers', 'users'),
+                         'update': ('managers', 'owners')
+                         })
+
+
+class Personne(EntityType):
+    __permissions__ = {
+        'read':   ('managers', 'users'), # 'guests' was removed
+        'add':    ('managers', 'users'),
+        'update': ('managers', 'owners'),
+        'delete': ('managers', 'owners')
+    }
+    __unique_together__ = [('nom', 'prenom', 'datenaiss')]
+    nom    = String(fulltextindexed=True, required=True, maxsize=64)
+    prenom = String(fulltextindexed=True, maxsize=64)
+    civility   = String(maxsize=1, default='M', fulltextindexed=True)
+    promo  = String(vocabulary=('bon','pasbon'))
+    titre  = String(fulltextindexed=True, maxsize=128)
+    adel   = String(maxsize=128)
+    ass    = String(maxsize=128)
+    web    = String(maxsize=128)
+    tel    = Int()
+    fax    = Int()
+    datenaiss = Datetime()
+    test   = Boolean()
+
+    travaille = SubjectRelation('Societe')
+    concerne = SubjectRelation('Affaire')
+    concerne2 = SubjectRelation(('Affaire', 'Note'), cardinality='1*')
+    connait = SubjectRelation('Personne', symmetric=True)
+
+class concerne(RelationType):
+    __permissions__ = {
+        'read':   ('managers', 'users', 'guests'),
+        'add':    ('managers', RRQLExpression('U has_update_permission S')),
+        'delete': ('managers', RRQLExpression('O owned_by U')),
+        }
+
+# `Old` entity type is gonce
+# `comments` is gone
+# `fiche` is gone
+# `multisource_*` rdefs are gone
+# `see_also_*` rdefs are gone
+
+class evaluee(RelationDefinition):
+    subject = ('Personne', 'CWUser', 'Societe')
+    object = ('Note')
+    constraints = [RQLVocabularyConstraint('S owned_by U')]
+
+class ecrit_par(RelationType):
+    __permissions__ = {'read':   ('managers', 'users', 'guests',),
+                   'delete': ('managers', ),
+                   'add':    ('managers',
+                              RRQLExpression('O require_permission P, P name "add_note", '
+                                             'U in_group G, P require_group G'),)
+                   }
+    inlined = True
+    cardinality = '?*'
+
+# `copain` rdef is gone
+# `tags` rdef is gone
+# `filed_under` rdef is gone
+# `require_permission` rdef is gone
+# `require_state` rdef is gone
+# `personne_composite` rdef is gone
+# `personne_inlined` rdef is gone
+# `login_user` rdef is gone
+# `ambiguous_inlined` rdef is gone
+
+class Folder(EntityType):
+    """folders are used to classify entities. They may be defined as a tree.
+    """
+    name = String(required=True, indexed=True, internationalizable=True,
+                  maxsize=64)
+    description = RichString(fulltextindexed=True)
+    filed_under = SubjectRelation('Folder', description=_('parent folder'))
+
+
+# New
+class Text(Para):
+    __specializes_schema__ = True
+    summary = String(maxsize=512)
+
+
+# New
+class Folder2(EntityType):
+    """folders are used to classify entities. They may be defined as a tree.
+    When you include the Folder entity, all application specific entities
+    may then be classified using the "filed_under" relation.
+    """
+    name = String(required=True, indexed=True, internationalizable=True,
+                  constraints=[UniqueConstraint(), SizeConstraint(64)])
+    description = RichString(fulltextindexed=True)
+
+# New
+class filed_under2(RelationDefinition):
+    subject ='*'
+    object = 'Folder2'
+
+
+# New
+class New(EntityType):
+    new_name = String()
+
+# New
+class same_as(RelationDefinition):
+    subject = ('Societe',)
+    object = 'ExternalUri'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/data-migractions/schema.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,288 @@
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+
+from yams.buildobjs import (EntityType, RelationType, RelationDefinition, ComputedRelation,
+                            SubjectRelation, RichString, String, Int, Float,
+                            Boolean, Datetime, TZDatetime, Bytes)
+from yams.constraints import SizeConstraint
+from cubicweb.schema import (WorkflowableEntityType,
+                             RQLConstraint, RQLUniqueConstraint,
+                             RQLVocabularyConstraint,
+                             ERQLExpression, RRQLExpression)
+
+class Affaire(WorkflowableEntityType):
+    __permissions__ = {
+        '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,
+                   constraints=[SizeConstraint(256)])
+    descr = RichString(fulltextindexed=True,
+                       description=_('more detailed description'))
+
+    duration = Int()
+    invoiced = Float()
+    opt_attr = Bytes()
+
+    depends_on = SubjectRelation('Affaire')
+    require_permission = SubjectRelation('CWPermission')
+    concerne = SubjectRelation(('Societe', 'Note'))
+    todo_by = SubjectRelation('Personne', cardinality='?*')
+    documented_by = SubjectRelation('Card')
+
+
+class Societe(EntityType):
+    __unique_together__ = [('nom', 'type', 'cp')]
+    __permissions__ = {
+        'read': ('managers', 'users', 'guests'),
+        'update': ('managers', 'owners', ERQLExpression('U login L, X nom L')),
+        '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
+    tel  = Int()
+    fax  = Int()
+    rncs = String(maxsize=128)
+    ad1  = String(maxsize=128)
+    ad2  = String(maxsize=128)
+    ad3  = String(maxsize=128)
+    cp   = String(maxsize=12)
+    ville= String(maxsize=32)
+
+
+class Division(Societe):
+    __specializes_schema__ = True
+
+class SubDivision(Division):
+    __specializes_schema__ = True
+
+class travaille_subdivision(RelationDefinition):
+    subject = 'Personne'
+    object = 'SubDivision'
+
+from cubicweb.schemas.base import CWUser
+CWUser.get_relations('login').next().fulltextindexed = True
+
+class Note(WorkflowableEntityType):
+    date = String(maxsize=10)
+    type = String(vocabulary=[u'todo', u'a', u'b', u'T', u'lalala'])
+    para = String(maxsize=512,
+                  __permissions__ = {
+                      'add': ('managers', ERQLExpression('X in_state S, S name "todo"')),
+                      'read':   ('managers', 'users', 'guests'),
+                      'update': ('managers', ERQLExpression('X in_state S, S name "todo"')),
+                      })
+    something = String(maxsize=1,
+                      __permissions__ = {
+                          'read': ('managers', 'users', 'guests'),
+                          'add': (ERQLExpression('NOT X para NULL'),),
+                          'update': ('managers', 'owners')
+                      })
+    migrated_from = SubjectRelation('Note')
+    attachment = SubjectRelation('File')
+    inline1 = SubjectRelation('Affaire', inlined=True, cardinality='?*',
+                              constraints=[RQLUniqueConstraint('S type T, S inline1 A1, A1 todo_by C, '
+                                                              'Y type T, Y inline1 A2, A2 todo_by C',
+                                                               'S,Y')])
+    todo_by = SubjectRelation('CWUser')
+
+
+class Frozable(EntityType):
+    __permissions__ = {
+        'read':   ('managers', 'users'),
+        'add':    ('managers', 'users'),
+        'update': ('managers', ERQLExpression('X frozen False'),),
+        'delete': ('managers', ERQLExpression('X frozen False'),)
+    }
+    name = String()
+    frozen = Boolean(default=False,
+                     __permissions__ = {
+                         'read':   ('managers', 'users'),
+                         'add':    ('managers', 'users'),
+                         'update': ('managers', 'owners')
+                         })
+
+
+class Personne(EntityType):
+    __unique_together__ = [('nom', 'prenom', 'inline2')]
+    nom    = String(fulltextindexed=True, required=True, maxsize=64)
+    prenom = String(fulltextindexed=True, maxsize=64)
+    sexe   = String(maxsize=1, default='M', fulltextindexed=True)
+    promo  = String(vocabulary=('bon','pasbon'))
+    titre  = String(fulltextindexed=True, maxsize=128)
+    adel   = String(maxsize=128)
+    ass    = String(maxsize=128)
+    web    = String(maxsize=128)
+    tel    = Int()
+    fax    = Int()
+    datenaiss = Datetime()
+    tzdatenaiss = TZDatetime()
+    test   = Boolean(__permissions__={
+        'read': ('managers', 'users', 'guests'),
+        'add': ('managers',),
+        'update': ('managers',),
+        })
+    description = String()
+    firstname = String(fulltextindexed=True, maxsize=64)
+
+    concerne = SubjectRelation('Affaire')
+    connait = SubjectRelation('Personne')
+    inline2 = SubjectRelation('Affaire', inlined=True, cardinality='?*')
+
+
+class Old(EntityType):
+    name = String(__permissions__ = {
+        'read'   : ('managers', 'users', 'guests'),
+        'add'    : ('managers', 'users', 'guests'),
+        'update' : ()
+    })
+
+
+class connait(RelationType):
+    symmetric = True
+
+class concerne(RelationType):
+    __permissions__ = {
+        'read':   ('managers', 'users', 'guests'),
+        'add':    ('managers', RRQLExpression('U has_update_permission S')),
+        'delete': ('managers', RRQLExpression('O owned_by U')),
+        }
+
+class travaille(RelationDefinition):
+    __permissions__ = {
+        'read':   ('managers', 'users', 'guests'),
+        'add':    ('managers', RRQLExpression('U has_update_permission S')),
+        'delete': ('managers', RRQLExpression('O owned_by U')),
+        }
+    subject = 'Personne'
+    object = 'Societe'
+    constraints = [RQLVocabularyConstraint('S owned_by U'),
+                   RQLVocabularyConstraint('S created_by U')]
+
+class comments(RelationDefinition):
+    subject = 'Comment'
+    object = 'Personne'
+
+class fiche(RelationDefinition):
+    inlined = True
+    subject = 'Personne'
+    object = 'Card'
+    cardinality = '??'
+
+class multisource_inlined_rel(RelationDefinition):
+    inlined = True
+    cardinality = '?*'
+    subject = ('Card', 'Note')
+    object = ('Affaire', 'Note')
+
+
+class see_also_1(RelationDefinition):
+    name = 'see_also'
+    subject = object = 'Folder'
+
+class see_also_2(RelationDefinition):
+    name = 'see_also'
+    subject = ('Bookmark', 'Note')
+    object = ('Bookmark', 'Note')
+
+class evaluee(RelationDefinition):
+    subject = ('Personne', 'CWUser', 'Societe')
+    object = ('Note')
+    constraints = [
+        RQLVocabularyConstraint('S created_by U'),
+        RQLVocabularyConstraint('S owned_by U'),
+    ]
+
+class ecrit_par(RelationType):
+    inlined = True
+
+class ecrit_par_1(RelationDefinition):
+    name = 'ecrit_par'
+    subject = 'Note'
+    object ='Personne'
+    cardinality = '?*'
+
+class ecrit_par_2(RelationDefinition):
+    name = 'ecrit_par'
+    subject = 'Note'
+    object ='CWUser'
+    cardinality='?*'
+
+
+class copain(RelationDefinition):
+    subject = object = 'CWUser'
+
+class tags(RelationDefinition):
+    subject = 'Tag'
+    object = ('CWUser', 'CWGroup', 'State', 'Note', 'Card', 'Affaire')
+
+class Folder(EntityType):
+    """folders are used to classify entities. They may be defined as a tree.
+    """
+    name = String(required=True, indexed=True, internationalizable=True,
+                  maxsize=64)
+    description = RichString(fulltextindexed=True)
+    filed_under = SubjectRelation('Folder', description=_('parent folder'))
+
+class filed_under(RelationDefinition):
+    subject = ('Note', 'Affaire')
+    object = 'Folder'
+
+class require_permission(RelationDefinition):
+    subject = ('Card', 'Note', 'Personne')
+    object = 'CWPermission'
+
+class require_state(RelationDefinition):
+    subject = 'CWPermission'
+    object = 'State'
+
+class personne_composite(RelationDefinition):
+    subject='Personne'
+    object='Personne'
+    composite='subject'
+
+class personne_inlined(RelationDefinition):
+    subject='Personne'
+    object='Personne'
+    cardinality='?*'
+    inlined=True
+
+
+class login_user(RelationDefinition):
+    subject = 'Personne'
+    object = 'CWUser'
+    cardinality = '??'
+
+class ambiguous_inlined(RelationDefinition):
+    subject = ('Affaire', 'Note')
+    object = 'CWUser'
+    inlined = True
+    cardinality = '?*'
+
+
+class user_login(ComputedRelation):
+    rule = 'O login_user S'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/data-schema2sql/schema/Company.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,67 @@
+# copyright 2004-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of yams.
+#
+# yams is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# yams is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with yams. If not, see <http://www.gnu.org/licenses/>.
+from yams.buildobjs import EntityType, RelationType, RelationDefinition, \
+     SubjectRelation, String
+
+class Company(EntityType):
+    name = String()
+
+class Subcompany(Company):
+    __specializes_schema__ = True
+    subcompany_of = SubjectRelation('Company')
+
+class Division(Company):
+    __specializes_schema__ = True
+    division_of = SubjectRelation('Company')
+
+class Subdivision(Division):
+    __specializes_schema__ = True
+    subdivision_of = SubjectRelation('Company')
+
+class Employee(EntityType):
+    works_for = SubjectRelation('Company')
+
+class require_permission(RelationType):
+    """link a permission to the entity. This permission should be used in the
+    security definition of the entity's type to be useful.
+    """
+    fulltext_container = 'subject'
+    __permissions__ = {
+        'read':   ('managers', 'users', 'guests'),
+        'add':    ('managers',),
+        'delete': ('managers',),
+        }
+
+
+class missing_require_permission(RelationDefinition):
+    name = 'require_permission'
+    subject = 'Company'
+    object = 'EPermission'
+
+class EPermission(EntityType):
+    """entity type that may be used to construct some advanced security configuration
+    """
+    __permissions__ = {
+        'read':   ('managers', 'users', 'guests',),
+        'add':    ('managers',),
+        'delete': ('managers',),
+        'update': ('managers', 'owners',),
+        }
+    name = String(required=True, indexed=True, internationalizable=True,
+                  fulltextindexed=True, maxsize=100,
+                  description=_('name or identifier of the permission'))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/data-schema2sql/schema/Dates.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,29 @@
+# copyright 2004-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of yams.
+#
+# yams is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# yams is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with yams. If not, see <http://www.gnu.org/licenses/>.
+from datetime import time, date
+from yams.buildobjs import EntityType, Datetime, Date, Time
+from yams.constraints import TODAY, BoundaryConstraint
+
+class Datetest(EntityType):
+    dt1 = Datetime(default=u'now')
+    dt2 = Datetime(default=u'today')
+    d1  = Date(default=u'today', constraints=[BoundaryConstraint('<=', TODAY())])
+    d2  = Date(default=date(2007, 12, 11))
+    t1  = Time(default=time(8, 40))
+    t2  = Time(default=time(9, 45))
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/data-schema2sql/schema/State.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,81 @@
+# copyright 2004-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of yams.
+#
+# yams is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# yams is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with yams. If not, see <http://www.gnu.org/licenses/>.
+from yams.buildobjs import (EntityType, RelationType, RelationDefinition,
+                            SubjectRelation, Int, String,  Boolean)
+from yams.constraints import SizeConstraint, UniqueConstraint
+
+from __init__ import RESTRICTED_RTYPE_PERMS
+
+class State(EntityType):
+    """used to associate simple states to an entity
+    type and/or to define workflows
+    """
+    __permissions__ = {
+        'read':   ('managers', 'users', 'guests',),
+        'add':    ('managers', 'users',),
+        'delete': ('managers', 'owners',),
+        'update': ('managers', 'owners',),
+        }
+
+    # attributes
+    eid = Int(required=True, uid=True)
+    name = String(required=True,
+                  indexed=True, internationalizable=True,
+                  constraints=[SizeConstraint(256)])
+    description = String(fulltextindexed=True)
+    # relations
+    state_of = SubjectRelation('Eetype', cardinality='+*')
+    next_state = SubjectRelation('State', cardinality='**')
+
+
+class state_of(RelationType):
+    """link a state to one or more entity type"""
+    __permissions__ = RESTRICTED_RTYPE_PERMS
+
+class next_state(RelationType):
+    """define a workflow by associating a state to possible following states
+    """
+    __permissions__ = RESTRICTED_RTYPE_PERMS
+
+class initial_state(RelationType):
+    """indicate which state should be used by default when an entity using states
+    is created
+    """
+    __permissions__ = {
+        'read':   ('managers', 'users', 'guests',),
+        'add':    ('managers', 'users',),
+        'delete': ('managers', 'users',),
+        }
+    subject = 'Eetype'
+    object = 'State'
+    cardinality = '?*'
+    inlined = True
+
+class Eetype(EntityType):
+    """define an entity type, used to build the application schema"""
+    __permissions__ = {
+        'read':   ('managers', 'users', 'guests',),
+        'add':    ('managers',),
+        'delete': ('managers',),
+        'update': ('managers', 'owners',),
+        }
+    name = String(required=True, indexed=True, internationalizable=True,
+                  constraints=[UniqueConstraint(), SizeConstraint(64)])
+    description = String(fulltextindexed=True)
+    meta = Boolean()
+    final = Boolean()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/data-schema2sql/schema/__init__.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,23 @@
+# copyright 2004-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of yams.
+#
+# yams is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# yams is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with yams. If not, see <http://www.gnu.org/licenses/>.
+"""test schema"""
+RESTRICTED_RTYPE_PERMS = {
+    'read':   ('managers', 'users', 'guests',),
+    'add':    ('managers',),
+    'delete': ('managers',),
+    }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/data-schema2sql/schema/schema.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,113 @@
+# copyright 2004-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of yams.
+#
+# yams is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# yams is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with yams. If not, see <http://www.gnu.org/licenses/>.
+from yams.buildobjs import (EntityType, RelationDefinition, RelationType,
+                            SubjectRelation, String, Int, Float, Date, Boolean)
+from yams.constraints import Attribute, BoundaryConstraint
+
+class Affaire(EntityType):
+    sujet = String(maxsize=128)
+    ref = String(maxsize=12)
+
+    concerne = SubjectRelation('Societe')
+    obj_wildcard = SubjectRelation('*')
+    sym_rel = SubjectRelation('Person', symmetric=True)
+    inline_rel = SubjectRelation('Person', inlined=True, cardinality='?*')
+
+class subj_wildcard(RelationDefinition):
+    subject = '*'
+    object = 'Affaire'
+
+
+class Person(EntityType):
+    __unique_together__ = [('nom', 'prenom')]
+    nom    = String(maxsize=64, fulltextindexed=True, required=True)
+    prenom = String(maxsize=64, fulltextindexed=True)
+    sexe   = String(maxsize=1, default='M')
+    promo  = String(vocabulary=('bon','pasbon'))
+    titre  = String(maxsize=128, fulltextindexed=True)
+    adel   = String(maxsize=128)
+    ass    = String(maxsize=128)
+    web    = String(maxsize=128)
+    tel    = Int(__permissions__={'read': (),
+                                  'add': ('managers',),
+                                  'update': ('managers',)})
+    fax    = Int()
+    datenaiss = Date()
+    test   = Boolean()
+    salary = Float()
+    travaille = SubjectRelation('Societe',
+                                __permissions__={'read': (),
+                                                 'add': (),
+                                                 'delete': ('managers',),
+                                                 })
+
+    evaluee = SubjectRelation('Note')
+
+class Salaried(Person):
+    __specializes_schema__ = True
+
+class Societe(EntityType):
+    nom  = String(maxsize=64, fulltextindexed=True)
+    web = String(maxsize=128)
+    tel  = Int()
+    fax  = Int(constraints=[BoundaryConstraint('<=', Attribute('tel'))])
+    rncs = String(maxsize=32)
+    ad1  = String(maxsize=128)
+    ad2  = String(maxsize=128)
+    ad3  = String(maxsize=128)
+    cp   = String(maxsize=12)
+    ville = String(maxsize=32)
+
+    evaluee = SubjectRelation('Note')
+
+
+class Note(EntityType):
+    date = String(maxsize=10)
+    type = String(maxsize=1)
+    para = String(maxsize=512)
+
+
+class pkginfo(EntityType):
+    modname = String(maxsize=30, required=True)
+    version = String(maxsize=10, required=True, default='0.1')
+    copyright = String(required=True)
+    license = String(vocabulary=('GPL', 'ZPL'))
+    short_desc = String(maxsize=80, required=True)
+    long_desc = String(required=True, fulltextindexed=True)
+    author = String(maxsize=100, required=True)
+    author_email = String(maxsize=100, required=True)
+    mailinglist = String(maxsize=100)
+    debian_handler = String(vocabulary=('machin', 'bidule'))
+
+
+class evaluee(RelationType):
+    __permissions__ = {
+        'read': ('managers',),
+        'add': ('managers',),
+        'delete': ('managers',),
+        }
+
+class concerne(RelationDefinition):
+    subject = 'Person'
+    object = 'Affaire'
+    __permissions__ = {
+        'read': ('managers',),
+        'add': ('managers',),
+        'delete': ('managers',),
+        }
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/data-schema2sql/schema/toignore	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,1 @@
+coucou
--- a/server/test/data/bootstrap_cubes	Mon May 09 17:24:03 2016 +0200
+++ b/server/test/data/bootstrap_cubes	Tue Jun 21 07:42:30 2016 +0200
@@ -1,1 +1,1 @@
-card,comment,folder,tag,basket,email,file,localperms
+card,comment,tag,basket,file,localperms
--- a/server/test/data/migratedapp/__init__.py	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of CubicWeb.
-#
-# CubicWeb is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
--- a/server/test/data/migratedapp/bootstrap_cubes	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-card,comment,folder,tag,basket,email,file
--- a/server/test/data/migratedapp/schema.py	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,203 +0,0 @@
-# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of CubicWeb.
-#
-# CubicWeb is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""cw.server.migraction test"""
-import datetime as dt
-from yams.buildobjs import (EntityType, RelationType, RelationDefinition,
-                            SubjectRelation, Bytes,
-                            RichString, String, Int, Boolean, Datetime, Date)
-from yams.constraints import SizeConstraint, UniqueConstraint
-from cubicweb.schema import (WorkflowableEntityType, RQLConstraint,
-                             RQLVocabularyConstraint,
-                             ERQLExpression, RRQLExpression)
-
-class Affaire(EntityType):
-    __permissions__ = {
-        'read':   ('managers', 'users', 'guests'),
-        'add':    ('managers', ERQLExpression('X concerne S, S owned_by U')),
-        '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,
-                 constraints=[SizeConstraint(256)])
-    concerne = SubjectRelation('Societe')
-    opt_attr = Bytes()
-
-class Societe(WorkflowableEntityType):
-    __permissions__ = {
-        'read': ('managers', 'users', 'guests'),
-        'update': ('managers', 'owners'),
-        'delete': ('managers', 'owners'),
-        'add': ('managers', 'users',)
-        }
-    nom  = String(maxsize=64, fulltextindexed=True)
-    web  = String(maxsize=128)
-    tel  = Int()
-    fax  = Int()
-    rncs = String(maxsize=128)
-    ad1  = String(maxsize=128)
-    ad2  = String(maxsize=128)
-    ad3  = String(maxsize=128)
-    cp   = String(maxsize=12)
-    ville= String(maxsize=32)
-
-# Division and SubDivision are gone
-
-# New
-class Para(EntityType):
-    para = String(maxsize=512)
-    newattr = String()
-    newinlined = SubjectRelation('Affaire', cardinality='?*', inlined=True)
-    newnotinlined = SubjectRelation('Affaire', cardinality='?*')
-
-class Note(Para):
-    __specializes_schema__ = True
-
-    __permissions__ = {'read':   ('managers', 'users', 'guests',),
-                   'update': ('managers', 'owners',),
-                   'delete': ('managers', ),
-                   'add':    ('managers',
-                              ERQLExpression('X ecrit_part PE, U in_group G, '
-                                             'PE require_permission P, P name "add_note", '
-                                             'P require_group G'),)}
-
-    whatever = Int(default=0)  # keep it before `date` for unittest_migraction.test_add_attribute_int
-    yesno = Boolean(default=False)
-    date = Datetime()
-    type = String(maxsize=1)
-    unique_id = String(maxsize=1, required=True, unique=True)
-    mydate = Date(default='TODAY')
-    oldstyledefaultdate = Date(default='2013/01/01')
-    newstyledefaultdate = Date(default=dt.date(2013, 1, 1))
-    shortpara = String(maxsize=64, default='hop')
-    ecrit_par = SubjectRelation('Personne', constraints=[RQLConstraint('S concerne A, O concerne A')])
-    attachment = SubjectRelation('File')
-
-
-class Frozable(EntityType):
-    __permissions__ = {
-        'read':   ('managers', 'users'),
-        'add':    ('managers', 'users'),
-        'update': ('managers', ERQLExpression('X frozen False'),),
-        'delete': ('managers', ERQLExpression('X frozen False'),)
-    }
-    name = String()
-    frozen = Boolean(default=False,
-                     __permissions__ = {
-                         'read':   ('managers', 'users'),
-                         'add':    ('managers', 'users'),
-                         'update': ('managers', 'owners')
-                         })
-
-
-class Personne(EntityType):
-    __permissions__ = {
-        'read':   ('managers', 'users'), # 'guests' was removed
-        'add':    ('managers', 'users'),
-        'update': ('managers', 'owners'),
-        'delete': ('managers', 'owners')
-    }
-    __unique_together__ = [('nom', 'prenom', 'datenaiss')]
-    nom    = String(fulltextindexed=True, required=True, maxsize=64)
-    prenom = String(fulltextindexed=True, maxsize=64)
-    civility   = String(maxsize=1, default='M', fulltextindexed=True)
-    promo  = String(vocabulary=('bon','pasbon'))
-    titre  = String(fulltextindexed=True, maxsize=128)
-    adel   = String(maxsize=128)
-    ass    = String(maxsize=128)
-    web    = String(maxsize=128)
-    tel    = Int()
-    fax    = Int()
-    datenaiss = Datetime()
-    test   = Boolean()
-
-    travaille = SubjectRelation('Societe')
-    concerne = SubjectRelation('Affaire')
-    concerne2 = SubjectRelation(('Affaire', 'Note'), cardinality='1*')
-    connait = SubjectRelation('Personne', symmetric=True)
-
-class concerne(RelationType):
-    __permissions__ = {
-        'read':   ('managers', 'users', 'guests'),
-        'add':    ('managers', RRQLExpression('U has_update_permission S')),
-        'delete': ('managers', RRQLExpression('O owned_by U')),
-        }
-
-# `Old` entity type is gonce
-# `comments` is gone
-# `fiche` is gone
-# `multisource_*` rdefs are gone
-# `see_also_*` rdefs are gone
-
-class evaluee(RelationDefinition):
-    subject = ('Personne', 'CWUser', 'Societe')
-    object = ('Note')
-    constraints = [RQLVocabularyConstraint('S owned_by U')]
-
-class ecrit_par(RelationType):
-    __permissions__ = {'read':   ('managers', 'users', 'guests',),
-                   'delete': ('managers', ),
-                   'add':    ('managers',
-                              RRQLExpression('O require_permission P, P name "add_note", '
-                                             'U in_group G, P require_group G'),)
-                   }
-    inlined = True
-    cardinality = '?*'
-
-# `copain` rdef is gone
-# `tags` rdef is gone
-# `filed_under` rdef is gone
-# `require_permission` rdef is gone
-# `require_state` rdef is gone
-# `personne_composite` rdef is gone
-# `personne_inlined` rdef is gone
-# `login_user` rdef is gone
-# `ambiguous_inlined` rdef is gone
-
-# New
-class Text(Para):
-    __specializes_schema__ = True
-    summary = String(maxsize=512)
-
-
-# New
-class Folder2(EntityType):
-    """folders are used to classify entities. They may be defined as a tree.
-    When you include the Folder entity, all application specific entities
-    may then be classified using the "filed_under" relation.
-    """
-    name = String(required=True, indexed=True, internationalizable=True,
-                  constraints=[UniqueConstraint(), SizeConstraint(64)])
-    description = RichString(fulltextindexed=True)
-
-# New
-class filed_under2(RelationDefinition):
-    subject ='*'
-    object = 'Folder2'
-
-
-# New
-class New(EntityType):
-    new_name = String()
-
-# New
-class same_as(RelationDefinition):
-    subject = ('Societe',)
-    object = 'ExternalUri'
--- a/server/test/data/schema.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/test/data/schema.py	Tue Jun 21 07:42:30 2016 +0200
@@ -89,7 +89,7 @@
 
 class Note(WorkflowableEntityType):
     date = String(maxsize=10)
-    type = String(maxsize=6)
+    type = String(vocabulary=[u'todo', u'a', u'b', u'T', u'lalala'])
     para = String(maxsize=512,
                   __permissions__ = {
                       'add': ('managers', ERQLExpression('X in_state S, S name "todo"')),
@@ -168,6 +168,22 @@
     })
 
 
+class Email(EntityType):
+    subject = String(fulltextindexed=True)
+    messageid = String(required=True, indexed=True, unique=True)
+    sender = SubjectRelation('EmailAddress', cardinality='?*')
+    recipients = SubjectRelation('EmailAddress')
+    attachment = SubjectRelation('File')
+
+
+class EmailPart(EntityType):
+    pass
+
+
+class EmailThread(EntityType):
+    see_also = SubjectRelation('EmailThread')
+
+
 class connait(RelationType):
     symmetric = True
 
@@ -246,6 +262,14 @@
     subject = 'Tag'
     object = ('CWUser', 'CWGroup', 'State', 'Note', 'Card', 'Affaire')
 
+class Folder(EntityType):
+    """folders are used to classify entities. They may be defined as a tree.
+    """
+    name = String(required=True, indexed=True, internationalizable=True,
+                  maxsize=64)
+    description = RichString(fulltextindexed=True)
+    filed_under = SubjectRelation('Folder', description=_('parent folder'))
+
 class filed_under(RelationDefinition):
     subject = ('Note', 'Affaire')
     object = 'Folder'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/requirements.txt	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,7 @@
+psycopg2
+cubicweb-basket
+cubicweb-card
+cubicweb-comment
+cubicweb-file
+cubicweb-localperms
+cubicweb-tag
--- a/server/test/unittest_ldapsource.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/test/unittest_ldapsource.py	Tue Jun 21 07:42:30 2016 +0200
@@ -25,8 +25,6 @@
 import subprocess
 import tempfile
 
-from logilab.common.testlib import TestCase, unittest_main, mock_object, Tags
-
 from cubicweb import AuthenticationError
 from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.devtools.repotest import RQLGeneratorTC
@@ -480,4 +478,5 @@
 
 
 if __name__ == '__main__':
+    from logilab.common.testlib import unittest_main
     unittest_main()
--- a/server/test/unittest_migractions.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/test/unittest_migractions.py	Tue Jun 21 07:42:30 2016 +0200
@@ -18,7 +18,7 @@
 """unit tests for module cubicweb.server.migractions"""
 
 from datetime import date
-import os.path as osp
+import os, os.path as osp
 from contextlib import contextmanager
 
 from logilab.common.testlib import unittest_main, Tags, tag
@@ -26,6 +26,7 @@
 from yams.constraints import UniqueConstraint
 
 from cubicweb import ConfigurationError, ValidationError, ExecutionError
+from cubicweb.devtools import startpgcluster, stoppgcluster
 from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.server.sqlutils import SQL_PREFIX
 from cubicweb.server.migractions import ServerMigrationHelper
@@ -35,6 +36,11 @@
 
 HERE = osp.dirname(osp.abspath(__file__))
 
+
+def setUpModule():
+    startpgcluster(__file__)
+
+
 migrschema = None
 def tearDownModule(*args):
     global migrschema
@@ -43,10 +49,19 @@
         del MigrationCommandsTC.origschema
     if hasattr(MigrationCommandsComputedTC, 'origschema'):
         del MigrationCommandsComputedTC.origschema
+    stoppgcluster(__file__)
+
+
+class MigrationConfig(cubicweb.devtools.TestServerConfiguration):
+    default_sources = cubicweb.devtools.DEFAULT_PSQL_SOURCES
+    CUBES_PATH = [osp.join(HERE, 'data-migractions', 'cubes')]
+
 
 class MigrationTC(CubicWebTC):
 
-    configcls = cubicweb.devtools.TestServerConfiguration
+    appid = 'data-migractions'
+
+    configcls = MigrationConfig
 
     tags = CubicWebTC.tags | Tags(('server', 'migration', 'migractions'))
 
@@ -64,31 +79,32 @@
         config._apphome = osp.join(HERE, self.appid)
 
     def setUp(self):
-        CubicWebTC.setUp(self)
+        self.configcls.cls_adjust_sys_path()
+        super(MigrationTC, self).setUp()
 
     def tearDown(self):
-        CubicWebTC.tearDown(self)
+        super(MigrationTC, self).tearDown()
         self.repo.vreg['etypes'].clear_caches()
 
     @contextmanager
     def mh(self):
-        with self.admin_access.client_cnx() as cnx:
+        with self.admin_access.repo_cnx() as cnx:
             yield cnx, ServerMigrationHelper(self.repo.config, migrschema,
                                              repo=self.repo, cnx=cnx,
                                              interactive=False)
 
     def table_sql(self, mh, tablename):
-        result = mh.sqlexec("SELECT sql FROM sqlite_master WHERE type='table' "
-                            "and name=%(table)s", {'table': tablename})
+        result = mh.sqlexec("SELECT table_name FROM information_schema.tables WHERE LOWER(table_name)=%(table)s",
+                            {'table': tablename.lower()})
         if result:
             return result[0][0]
         return None # no such table
 
     def table_schema(self, mh, tablename):
-        sql = self.table_sql(mh, tablename)
-        assert sql, 'no table %s' % tablename
-        return dict(x.split()[:2]
-                    for x in sql.split('(', 1)[1].rsplit(')', 1)[0].split(','))
+        result = mh.sqlexec("SELECT column_name, data_type, character_maximum_length FROM information_schema.columns "
+                            "WHERE LOWER(table_name) = %(table)s", {'table': tablename.lower()})
+        assert result, 'no table %s' % tablename
+        return dict((x[0], (x[1], x[2])) for x in result)
 
 
 class MigrationCommandsTC(MigrationTC):
@@ -160,7 +176,7 @@
             self.assertEqual(self.schema['shortpara'].objects(), ('String', ))
             # test created column is actually a varchar(64)
             fields = self.table_schema(mh, '%sNote' % SQL_PREFIX)
-            self.assertEqual(fields['%sshortpara' % SQL_PREFIX], 'varchar(64)')
+            self.assertEqual(fields['%sshortpara' % SQL_PREFIX], ('character varying', 64))
             # test default value set on existing entities
             self.assertEqual(cnx.execute('Note X').get_entity(0, 0).shortpara, 'hop')
             # test default value set for next entities
@@ -212,6 +228,7 @@
                                             droprequired=True):
                 mh.cmd_add_attribute('Note', 'unique_id')
                 mh.rqlexec('INSERT Note N')
+                mh.rqlexec('SET N unique_id "x"')
             # make sure the required=True was restored
             self.assertRaises(ValidationError, mh.rqlexec, 'INSERT Note N')
             mh.rollback()
@@ -259,8 +276,8 @@
                                'filed_under2', 'has_text',
                                'identity', 'in_basket', 'is', 'is_instance_of',
                                'modification_date', 'name', 'owned_by'])
-            self.assertEqual([str(rs) for rs in self.schema['Folder2'].object_relations()],
-                              ['filed_under2', 'identity'])
+            self.assertCountEqual([str(rs) for rs in self.schema['Folder2'].object_relations()],
+                                  ['filed_under2', 'identity'])
             # Old will be missing as it has been renamed into 'New' in the migrated
             # schema while New hasn't been added here.
             self.assertEqual(sorted(str(e) for e in self.schema['filed_under2'].subjects()),
@@ -567,15 +584,15 @@
                                      ('Bookmark', 'Bookmark'), ('Bookmark', 'Note'),
                                      ('Note', 'Note'), ('Note', 'Bookmark')]))
             try:
-                mh.cmd_drop_cube('email', removedeps=True)
+                mh.cmd_drop_cube('fakeemail', removedeps=True)
                 # file was there because it's an email dependancy, should have been removed
-                self.assertNotIn('email', self.config.cubes())
-                self.assertNotIn(self.config.cube_dir('email'), self.config.cubes_path())
+                self.assertNotIn('fakeemail', self.config.cubes())
+                self.assertNotIn(self.config.cube_dir('fakeemail'), self.config.cubes_path())
                 self.assertNotIn('file', self.config.cubes())
                 self.assertNotIn(self.config.cube_dir('file'), self.config.cubes_path())
                 for ertype in ('Email', 'EmailThread', 'EmailPart', 'File',
                                'sender', 'in_thread', 'reply_to', 'data_format'):
-                    self.assertFalse(ertype in schema, ertype)
+                    self.assertNotIn(ertype, schema)
                 self.assertEqual(sorted(schema['see_also'].rdefs.iterkeys()),
                                   sorted([('Folder', 'Folder'),
                                           ('Bookmark', 'Bookmark'),
@@ -584,17 +601,17 @@
                                           ('Note', 'Bookmark')]))
                 self.assertEqual(sorted(schema['see_also'].subjects()), ['Bookmark', 'Folder', 'Note'])
                 self.assertEqual(sorted(schema['see_also'].objects()), ['Bookmark', 'Folder', 'Note'])
-                self.assertEqual(cnx.execute('Any X WHERE X pkey "system.version.email"').rowcount, 0)
+                self.assertEqual(cnx.execute('Any X WHERE X pkey "system.version.fakeemail"').rowcount, 0)
                 self.assertEqual(cnx.execute('Any X WHERE X pkey "system.version.file"').rowcount, 0)
             finally:
-                mh.cmd_add_cube('email')
-                self.assertIn('email', self.config.cubes())
-                self.assertIn(self.config.cube_dir('email'), self.config.cubes_path())
+                mh.cmd_add_cube('fakeemail')
+                self.assertIn('fakeemail', self.config.cubes())
+                self.assertIn(self.config.cube_dir('fakeemail'), self.config.cubes_path())
                 self.assertIn('file', self.config.cubes())
                 self.assertIn(self.config.cube_dir('file'), self.config.cubes_path())
                 for ertype in ('Email', 'EmailThread', 'EmailPart', 'File',
                                'sender', 'in_thread', 'reply_to', 'data_format'):
-                    self.assertTrue(ertype in schema, ertype)
+                    self.assertIn(ertype, schema)
                 self.assertEqual(sorted(schema['see_also'].rdefs.iterkeys()),
                                   sorted([('EmailThread', 'EmailThread'), ('Folder', 'Folder'),
                                           ('Bookmark', 'Bookmark'),
@@ -603,9 +620,9 @@
                                           ('Note', 'Bookmark')]))
                 self.assertEqual(sorted(schema['see_also'].subjects()), ['Bookmark', 'EmailThread', 'Folder', 'Note'])
                 self.assertEqual(sorted(schema['see_also'].objects()), ['Bookmark', 'EmailThread', 'Folder', 'Note'])
-                from cubes.email.__pkginfo__ import version as email_version
+                from cubes.fakeemail.__pkginfo__ import version as email_version
                 from cubes.file.__pkginfo__ import version as file_version
-                self.assertEqual(cnx.execute('Any V WHERE X value V, X pkey "system.version.email"')[0][0],
+                self.assertEqual(cnx.execute('Any V WHERE X value V, X pkey "system.version.fakeemail"')[0][0],
                                   email_version)
                 self.assertEqual(cnx.execute('Any V WHERE X value V, X pkey "system.version.file"')[0][0],
                                   file_version)
@@ -623,16 +640,16 @@
             cubes = set(self.config.cubes())
             schema = self.repo.schema
             try:
-                mh.cmd_drop_cube('email')
-                cubes.remove('email')
-                self.assertNotIn('email', self.config.cubes())
+                mh.cmd_drop_cube('fakeemail')
+                cubes.remove('fakeemail')
+                self.assertNotIn('fakeemail', self.config.cubes())
                 self.assertIn('file', self.config.cubes())
                 for ertype in ('Email', 'EmailThread', 'EmailPart',
                                'sender', 'in_thread', 'reply_to'):
-                    self.assertFalse(ertype in schema, ertype)
+                    self.assertNotIn(ertype, schema)
             finally:
-                mh.cmd_add_cube('email')
-                self.assertIn('email', self.config.cubes())
+                mh.cmd_add_cube('fakeemail')
+                self.assertIn('fakeemail', self.config.cubes())
                 # trick: overwrite self.maxeid to avoid deletion of just reintroduced
                 #        types (and their associated tables!)
                 self.maxeid = cnx.execute('Any MAX(X)')[0][0] # XXXXXXX KILL KENNY
@@ -690,6 +707,18 @@
             mh.cmd_add_relation_type('same_as')
             self.assertTrue(self.table_sql(mh, 'same_as_relation'))
 
+    def test_change_attribute_type(self):
+        with self.mh() as (cnx, mh):
+            mh.cmd_create_entity('Societe', tel=1)
+            mh.commit()
+            mh.change_attribute_type('Societe', 'tel', 'Float')
+            self.assertNotIn(('Societe', 'Int'), self.schema['tel'].rdefs)
+            self.assertIn(('Societe', 'Float'), self.schema['tel'].rdefs)
+            self.assertEqual(self.schema['tel'].rdefs[('Societe', 'Float')].object, 'Float')
+            tel = mh.rqlexec('Any T WHERE X tel T')[0][0]
+            self.assertEqual(tel, 1.0)
+            self.assertIsInstance(tel, float)
+
 
 class MigrationCommandsComputedTC(MigrationTC):
     """ Unit tests for computed relations and attributes
@@ -787,7 +816,7 @@
         self.assertEqual(self.schema['score'].rdefs['Company', 'Float'].formula,
                          'Any AVG(NN) WHERE X employees E, N concerns E, N note NN')
         fields = self.table_schema(mh, '%sCompany' % SQL_PREFIX)
-        self.assertEqual(fields['%sscore' % SQL_PREFIX], 'float')
+        self.assertEqual(fields['%sscore' % SQL_PREFIX], ('double precision', None))
         self.assertEqual([[3.0]],
                          mh.rqlexec('Any CS WHERE C score CS, C is Company').rows)
 
@@ -811,10 +840,9 @@
 
     def assert_computed_attribute_dropped(self):
         self.assertNotIn('note20', self.schema)
-        # DROP COLUMN not supported by sqlite
-        #with self.mh() as (cnx, mh):
-        #    fields = self.table_schema(mh, '%sNote' % SQL_PREFIX)
-        #self.assertNotIn('%snote20' % SQL_PREFIX, fields)
+        with self.mh() as (cnx, mh):
+            fields = self.table_schema(mh, '%sNote' % SQL_PREFIX)
+        self.assertNotIn('%snote20' % SQL_PREFIX, fields)
 
     def test_computed_attribute_drop_type(self):
         self.assertIn('note20', self.schema)
--- a/server/test/unittest_postgres.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/test/unittest_postgres.py	Tue Jun 21 07:42:30 2016 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -21,17 +21,29 @@
 
 from logilab.common.testlib import SkipTest
 
-from cubicweb.devtools import PostgresApptestConfiguration
+from cubicweb import ValidationError
+from cubicweb.devtools import PostgresApptestConfiguration, startpgcluster, stoppgcluster
 from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.predicates import is_instance
 from cubicweb.entities.adapters import IFTIndexableAdapter
 
 from unittest_querier import FixedOffset
 
+
+def setUpModule():
+    startpgcluster(__file__)
+
+
+def tearDownModule():
+    stoppgcluster(__file__)
+
+
 class PostgresTimeoutConfiguration(PostgresApptestConfiguration):
-    default_sources = PostgresApptestConfiguration.default_sources.copy()
-    default_sources['system'] = PostgresApptestConfiguration.default_sources['system'].copy()
-    default_sources['system']['db-statement-timeout'] = 200
+    def __init__(self, *args, **kwargs):
+        self.default_sources = PostgresApptestConfiguration.default_sources.copy()
+        self.default_sources['system'] = PostgresApptestConfiguration.default_sources['system'].copy()
+        self.default_sources['system']['db-statement-timeout'] = 200
+        super(PostgresTimeoutConfiguration, self).__init__(*args, **kwargs)
 
 
 class PostgresFTITC(CubicWebTC):
@@ -119,6 +131,24 @@
             self.assertEqual(datenaiss.tzinfo, None)
             self.assertEqual(datenaiss.utctimetuple()[:5], (1977, 6, 7, 2, 0))
 
+    def test_constraint_validationerror(self):
+        with self.admin_access.repo_cnx() as cnx:
+            with cnx.allow_all_hooks_but('integrity'):
+                with self.assertRaises(ValidationError) as cm:
+                    cnx.execute("INSERT Note N: N type 'nogood'")
+                self.assertEqual(cm.exception.errors,
+                        {'type-subject': u'invalid value %(KEY-value)s, it must be one of %(KEY-choices)s'})
+                self.assertEqual(cm.exception.msgargs,
+                        {'type-subject-value': u'"nogood"',
+                         'type-subject-choices': u'"todo", "a", "b", "T", "lalala"'})
+
+    def test_statement_timeout(self):
+        with self.admin_access.repo_cnx() as cnx:
+            cnx.system_sql('select pg_sleep(0.1)')
+            with self.assertRaises(Exception):
+                cnx.system_sql('select pg_sleep(0.3)')
+
+
 class PostgresLimitSizeTC(CubicWebTC):
     configcls = PostgresApptestConfiguration
 
@@ -137,12 +167,6 @@
             yield self.assertEqual, sql("SELECT limit_size('<span>a>b</span>', 'text/html', 2)"), \
                 'a>...'
 
-    def test_statement_timeout(self):
-        with self.admin_access.repo_cnx() as cnx:
-            cnx.system_sql('select pg_sleep(0.1)')
-            with self.assertRaises(Exception):
-                cnx.system_sql('select pg_sleep(0.3)')
-
 
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main
--- a/server/test/unittest_querier.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/test/unittest_querier.py	Tue Jun 21 07:42:30 2016 +0200
@@ -69,7 +69,6 @@
 
 def tearDownClass(cls, *args):
     global repo, cnx
-    cnx.close()
     repo.shutdown()
     del repo, cnx
 
@@ -1173,11 +1172,10 @@
     def test_delete_3(self):
         s = self.user_groups_session('users')
         with s.new_cnx() as cnx:
-            with cnx.ensure_cnx_set:
-                peid, = self.o.execute(cnx, "INSERT Personne P: P nom 'toto'")[0]
-                seid, = self.o.execute(cnx, "INSERT Societe S: S nom 'logilab'")[0]
-                self.o.execute(cnx, "SET P travaille S")
-                cnx.commit()
+            peid, = self.o.execute(cnx, "INSERT Personne P: P nom 'toto'")[0]
+            seid, = self.o.execute(cnx, "INSERT Societe S: S nom 'logilab'")[0]
+            self.o.execute(cnx, "SET P travaille S")
+            cnx.commit()
         rset = self.qexecute('Personne P WHERE P travaille S')
         self.assertEqual(len(rset.rows), 1)
         self.qexecute("DELETE X travaille Y WHERE X eid %s, Y eid %s" % (peid, seid))
@@ -1212,12 +1210,11 @@
                               'X sender Y, X recipients Y WHERE Y is EmailAddress')[0]
         self.qexecute("DELETE Email X")
         with self.session.new_cnx() as cnx:
-            with cnx.ensure_cnx_set:
-                sqlc = cnx.cnxset.cu
-                sqlc.execute('SELECT * FROM recipients_relation')
-                self.assertEqual(len(sqlc.fetchall()), 0)
-                sqlc.execute('SELECT * FROM owned_by_relation WHERE eid_from=%s'%eeid)
-                self.assertEqual(len(sqlc.fetchall()), 0)
+            sqlc = cnx.cnxset.cu
+            sqlc.execute('SELECT * FROM recipients_relation')
+            self.assertEqual(len(sqlc.fetchall()), 0)
+            sqlc.execute('SELECT * FROM owned_by_relation WHERE eid_from=%s'%eeid)
+            self.assertEqual(len(sqlc.fetchall()), 0)
 
     def test_nonregr_delete_cache2(self):
         eid = self.qexecute("INSERT Folder T: T name 'toto'")[0][0]
@@ -1364,12 +1361,11 @@
         self.assertRaises(Unauthorized,
                           self.qexecute, "Any P WHERE X is CWUser, X login 'bob', X upassword P")
         with self.session.new_cnx() as cnx:
-            with cnx.ensure_cnx_set:
-                cursor = cnx.cnxset.cu
-                cursor.execute("SELECT %supassword from %sCWUser WHERE %slogin='bob'"
-                               % (SQL_PREFIX, SQL_PREFIX, SQL_PREFIX))
-                passwd = str(cursor.fetchone()[0])
-                self.assertEqual(passwd, crypt_password('toto', passwd))
+            cursor = cnx.cnxset.cu
+            cursor.execute("SELECT %supassword from %sCWUser WHERE %slogin='bob'"
+                           % (SQL_PREFIX, SQL_PREFIX, SQL_PREFIX))
+            passwd = str(cursor.fetchone()[0])
+            self.assertEqual(passwd, crypt_password('toto', passwd))
         rset = self.qexecute("Any X WHERE X is CWUser, X login 'bob', X upassword %(pwd)s",
                             {'pwd': Binary(passwd)})
         self.assertEqual(len(rset.rows), 1)
@@ -1377,21 +1373,20 @@
 
     def test_update_upassword(self):
         with self.session.new_cnx() as cnx:
-            with cnx.ensure_cnx_set:
-                rset = cnx.execute("INSERT CWUser X: X login 'bob', X upassword %(pwd)s",
-                                   {'pwd': 'toto'})
-                self.assertEqual(rset.description[0][0], 'CWUser')
-                rset = cnx.execute("SET X upassword %(pwd)s WHERE X is CWUser, X login 'bob'",
-                                   {'pwd': 'tutu'})
-                cursor = cnx.cnxset.cu
-                cursor.execute("SELECT %supassword from %sCWUser WHERE %slogin='bob'"
-                               % (SQL_PREFIX, SQL_PREFIX, SQL_PREFIX))
-                passwd = str(cursor.fetchone()[0])
-                self.assertEqual(passwd, crypt_password('tutu', passwd))
-                rset = cnx.execute("Any X WHERE X is CWUser, X login 'bob', X upassword %(pwd)s",
-                                   {'pwd': Binary(passwd)})
-                self.assertEqual(len(rset.rows), 1)
-                self.assertEqual(rset.description, [('CWUser',)])
+            rset = cnx.execute("INSERT CWUser X: X login 'bob', X upassword %(pwd)s",
+                               {'pwd': 'toto'})
+            self.assertEqual(rset.description[0][0], 'CWUser')
+            rset = cnx.execute("SET X upassword %(pwd)s WHERE X is CWUser, X login 'bob'",
+                               {'pwd': 'tutu'})
+            cursor = cnx.cnxset.cu
+            cursor.execute("SELECT %supassword from %sCWUser WHERE %slogin='bob'"
+                           % (SQL_PREFIX, SQL_PREFIX, SQL_PREFIX))
+            passwd = str(cursor.fetchone()[0])
+            self.assertEqual(passwd, crypt_password('tutu', passwd))
+            rset = cnx.execute("Any X WHERE X is CWUser, X login 'bob', X upassword %(pwd)s",
+                               {'pwd': Binary(passwd)})
+            self.assertEqual(len(rset.rows), 1)
+            self.assertEqual(rset.description, [('CWUser',)])
 
     # ZT datetime tests ########################################################
 
@@ -1402,6 +1397,13 @@
         self.assertEqual(datenaiss.tzinfo, None)
         self.assertEqual(datenaiss.utctimetuple()[:5], (1977, 6, 7, 1, 0))
 
+    def test_tz_datetime_cache_nonregr(self):
+        datenaiss = datetime(1977, 6, 7, 2, 0, tzinfo=FixedOffset(1))
+        self.qexecute("INSERT Personne X: X nom 'bob', X tzdatenaiss %(date)s",
+                     {'date': datenaiss})
+        self.assertTrue(self.qexecute("Any X WHERE X tzdatenaiss %(d)s", {'d': datenaiss}))
+        self.assertFalse(self.qexecute("Any X WHERE X tzdatenaiss %(d)s", {'d': datenaiss - timedelta(1)}))
+
     # non regression tests #####################################################
 
     def test_nonregr_1(self):
@@ -1512,9 +1514,9 @@
     def test_nonregr_has_text_cache(self):
         eid1 = self.qexecute("INSERT Personne X: X nom 'bidule'")[0][0]
         eid2 = self.qexecute("INSERT Personne X: X nom 'tag'")[0][0]
-        rset = self.qexecute("Any X WHERE X has_text %(text)s", {'text': 'bidule'})
+        rset = self.qexecute("Any X WHERE X has_text %(text)s", {'text': u'bidule'})
         self.assertEqual(rset.rows, [[eid1]])
-        rset = self.qexecute("Any X WHERE X has_text %(text)s", {'text': 'tag'})
+        rset = self.qexecute("Any X WHERE X has_text %(text)s", {'text': u'tag'})
         self.assertEqual(rset.rows, [[eid2]])
 
     def test_nonregr_sortterm_management(self):
@@ -1625,9 +1627,9 @@
             aff2 = cnx.create_entity('Societe', nom=u'aff2')
             cnx.commit()
         with self.new_access('user').repo_cnx() as cnx:
-            res = cnx.execute('Any X WHERE X has_text %(text)s', {'text': 'aff1'})
+            res = cnx.execute('Any X WHERE X has_text %(text)s', {'text': u'aff1'})
             self.assertEqual(res.rows, [[aff1.eid]])
-            res = cnx.execute('Any X WHERE X has_text %(text)s', {'text': 'aff2'})
+            res = cnx.execute('Any X WHERE X has_text %(text)s', {'text': u'aff2'})
             self.assertEqual(res.rows, [[aff2.eid]])
 
     def test_set_relations_eid(self):
--- a/server/test/unittest_repository.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/test/unittest_repository.py	Tue Jun 21 07:42:30 2016 +0200
@@ -31,10 +31,9 @@
                       UnknownEid, AuthenticationError, Unauthorized, QueryError)
 from cubicweb.predicates import is_instance
 from cubicweb.schema import RQLConstraint
-from cubicweb.dbapi import connect, multiple_connections_unfix
 from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.devtools.repotest import tuplify
-from cubicweb.server import repository, hook
+from cubicweb.server import hook
 from cubicweb.server.sqlutils import SQL_PREFIX
 from cubicweb.server.hook import Hook
 from cubicweb.server.sources import native
@@ -93,35 +92,17 @@
         self.assertRaises(AuthenticationError,
                           self.repo.connect, None)
 
-    def test_execute(self):
+    def test_login_upassword_accent(self):
+        with self.admin_access.repo_cnx() as cnx:
+            cnx.execute('INSERT CWUser X: X login %(login)s, X upassword %(passwd)s, '
+                        'X in_group G WHERE G name "users"',
+                        {'login': u"barnabé", 'passwd': u"héhéhé".encode('UTF8')})
+            cnx.commit()
         repo = self.repo
-        cnxid = repo.connect(self.admlogin, password=self.admpassword)
-        repo.execute(cnxid, 'Any X')
-        repo.execute(cnxid, 'Any X where X is Personne')
-        repo.execute(cnxid, 'Any X where X is Personne, X nom ~= "to"')
-        repo.execute(cnxid, 'Any X WHERE X has_text %(text)s', {'text': u'\xe7a'})
-        repo.close(cnxid)
-
-    def test_login_upassword_accent(self):
-        repo = self.repo
-        cnxid = repo.connect(self.admlogin, password=self.admpassword)
-        repo.execute(cnxid, 'INSERT CWUser X: X login %(login)s, X upassword %(passwd)s, X in_group G WHERE G name "users"',
-                     {'login': u"barnabé", 'passwd': u"héhéhé".encode('UTF8')})
-        repo.commit(cnxid)
-        repo.close(cnxid)
         cnxid = repo.connect(u"barnabé", password=u"héhéhé".encode('UTF8'))
         self.assert_(cnxid)
         repo.close(cnxid)
 
-    def test_rollback_on_commit_error(self):
-        cnxid = self.repo.connect(self.admlogin, password=self.admpassword)
-        self.repo.execute(cnxid,
-                          'INSERT CWUser X: X login %(login)s, X upassword %(passwd)s',
-                          {'login': u"tutetute", 'passwd': 'tutetute'})
-        self.assertRaises(ValidationError, self.repo.commit, cnxid)
-        self.assertFalse(self.repo.execute(cnxid, 'CWUser X WHERE X login "tutetute"'))
-        self.repo.close(cnxid)
-
     def test_rollback_on_execute_validation_error(self):
         class ValidationErrorAfterHook(Hook):
             __regid__ = 'valerror-after-hook'
@@ -166,31 +147,6 @@
         cnxid = repo.connect(self.admlogin, password=self.admpassword)
         self.assert_(cnxid)
         repo.close(cnxid)
-        self.assertRaises(BadConnectionId, repo.execute, cnxid, 'Any X')
-
-    def test_invalid_cnxid(self):
-        self.assertRaises(BadConnectionId, self.repo.execute, 0, 'Any X')
-        self.assertRaises(BadConnectionId, self.repo.close, None)
-
-    def test_shared_data(self):
-        repo = self.repo
-        cnxid = repo.connect(self.admlogin, password=self.admpassword)
-        repo.set_shared_data(cnxid, 'data', 4)
-        cnxid2 = repo.connect(self.admlogin, password=self.admpassword)
-        self.assertEqual(repo.get_shared_data(cnxid, 'data'), 4)
-        self.assertEqual(repo.get_shared_data(cnxid2, 'data'), None)
-        repo.set_shared_data(cnxid2, 'data', 5)
-        self.assertEqual(repo.get_shared_data(cnxid, 'data'), 4)
-        self.assertEqual(repo.get_shared_data(cnxid2, 'data'), 5)
-        repo.get_shared_data(cnxid2, 'data', pop=True)
-        self.assertEqual(repo.get_shared_data(cnxid, 'data'), 4)
-        self.assertEqual(repo.get_shared_data(cnxid2, 'data'), None)
-        repo.close(cnxid)
-        repo.close(cnxid2)
-        self.assertRaises(BadConnectionId, repo.get_shared_data, cnxid, 'data')
-        self.assertRaises(BadConnectionId, repo.get_shared_data, cnxid2, 'data')
-        self.assertRaises(BadConnectionId, repo.set_shared_data, cnxid, 'data', 1)
-        self.assertRaises(BadConnectionId, repo.set_shared_data, cnxid2, 'data', 1)
 
     def test_check_session(self):
         repo = self.repo
@@ -199,76 +155,6 @@
         repo.close(cnxid)
         self.assertRaises(BadConnectionId, repo.check_session, cnxid)
 
-    def test_transaction_base(self):
-        repo = self.repo
-        cnxid = repo.connect(self.admlogin, password=self.admpassword)
-        # check db state
-        result = repo.execute(cnxid, 'Personne X')
-        self.assertEqual(result.rowcount, 0)
-        # rollback entity insertion
-        repo.execute(cnxid, "INSERT Personne X: X nom 'bidule'")
-        result = repo.execute(cnxid, 'Personne X')
-        self.assertEqual(result.rowcount, 1)
-        repo.rollback(cnxid)
-        result = repo.execute(cnxid, 'Personne X')
-        self.assertEqual(result.rowcount, 0, result.rows)
-        # commit
-        repo.execute(cnxid, "INSERT Personne X: X nom 'bidule'")
-        repo.commit(cnxid)
-        result = repo.execute(cnxid, 'Personne X')
-        self.assertEqual(result.rowcount, 1)
-        repo.close(cnxid)
-
-    def test_transaction_base2(self):
-        repo = self.repo
-        cnxid = repo.connect(self.admlogin, password=self.admpassword)
-        # rollback relation insertion
-        repo.execute(cnxid, "SET U in_group G WHERE U login 'admin', G name 'guests'")
-        result = repo.execute(cnxid, "Any U WHERE U in_group G, U login 'admin', G name 'guests'")
-        self.assertEqual(result.rowcount, 1)
-        repo.rollback(cnxid)
-        result = repo.execute(cnxid, "Any U WHERE U in_group G, U login 'admin', G name 'guests'")
-        self.assertEqual(result.rowcount, 0, result.rows)
-        repo.close(cnxid)
-
-    def test_transaction_base3(self):
-        repo = self.repo
-        cnxid = repo.connect(self.admlogin, password=self.admpassword)
-        # rollback state change which trigger TrInfo insertion
-        session = repo._get_session(cnxid)
-        user = session.user
-        user.cw_adapt_to('IWorkflowable').fire_transition('deactivate')
-        rset = repo.execute(cnxid, 'TrInfo T WHERE T wf_info_for X, X eid %(x)s', {'x': user.eid})
-        self.assertEqual(len(rset), 1)
-        repo.rollback(cnxid)
-        rset = repo.execute(cnxid, 'TrInfo T WHERE T wf_info_for X, X eid %(x)s', {'x': user.eid})
-        self.assertEqual(len(rset), 0)
-        repo.close(cnxid)
-
-    def test_close_kill_processing_request(self):
-        repo = self.repo
-        cnxid = repo.connect(self.admlogin, password=self.admpassword)
-        repo.execute(cnxid, 'INSERT CWUser X: X login "toto", X upassword "tutu", X in_group G WHERE G name "users"')
-        repo.commit(cnxid)
-        lock = threading.Lock()
-        lock.acquire()
-        # close has to be in the thread due to sqlite limitations
-        def close_in_a_few_moment():
-            lock.acquire()
-            repo.close(cnxid)
-        t = threading.Thread(target=close_in_a_few_moment)
-        t.start()
-        def run_transaction():
-            lock.release()
-            repo.execute(cnxid, 'DELETE CWUser X WHERE X login "toto"')
-            repo.commit(cnxid)
-        try:
-            with self.assertRaises(SessionClosedError) as cm:
-                run_transaction()
-            self.assertEqual(str(cm.exception), 'try to access connections set on a closed session %s' % cnxid)
-        finally:
-            t.join()
-
     def test_initial_schema(self):
         schema = self.repo.schema
         # check order of attributes is respected
@@ -312,118 +198,14 @@
         ownedby = schema.rschema('owned_by')
         self.assertEqual(ownedby.objects('CWEType'), ('CWUser',))
 
-    def test_pyro(self):
-        import Pyro
-        Pyro.config.PYRO_MULTITHREADED = 0
-        done = []
-        self.repo.config.global_set_option('pyro-ns-host', 'NO_PYRONS')
-        daemon = self.repo.pyro_register()
-        try:
-            uri = self.repo.pyro_uri.replace('PYRO', 'pyroloc')
-            # the client part has to be in the thread due to sqlite limitations
-            t = threading.Thread(target=self._pyro_client, args=(uri, done))
-            t.start()
-            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)
-            from logilab.common import pyro_ext
-            pyro_ext._DAEMONS.clear()
-
-
-    def _pyro_client(self, uri, done):
-        cnx = connect(uri,
-                      u'admin', password='gingkow',
-                      initlog=False) # don't reset logging configuration
-        try:
-            cnx.load_appobjects(subpath=('entities',))
-            # check we can get the schema
-            schema = cnx.get_schema()
-            self.assertTrue(cnx.vreg)
-            self.assertTrue('etypes'in cnx.vreg)
-            cu = cnx.cursor()
-            rset = cu.execute('Any U,G WHERE U in_group G')
-            user = iter(rset.entities()).next()
-            self.assertTrue(user._cw)
-            self.assertTrue(user._cw.vreg)
-            from cubicweb.entities import authobjs
-            self.assertIsInstance(user._cw.user, authobjs.CWUser)
-            # make sure the tcp connection is closed properly; yes, it's disgusting.
-            adapter = cnx._repo.adapter
-            cnx.close()
-            adapter.release()
-            done.append(True)
-        finally:
-            # connect monkey patch some method by default, remove them
-            multiple_connections_unfix()
-
-
-    def test_zmq(self):
-        try:
-            import zmq
-        except ImportError:
-            self.skipTest("zmq in not available")
-        done = []
-        from cubicweb.devtools import TestServerConfiguration as ServerConfiguration
-        from cubicweb.server.cwzmq import ZMQRepositoryServer
-        # the client part has to be in a thread due to sqlite limitations
-        t = threading.Thread(target=self._zmq_client, args=(done,))
-        t.start()
-
-        zmq_server = ZMQRepositoryServer(self.repo)
-        zmq_server.connect('zmqpickle-tcp://127.0.0.1:41415')
-
-        t2 = threading.Thread(target=self._zmq_quit, args=(done, zmq_server,))
-        t2.start()
-
-        zmq_server.run()
-
-        t2.join(1)
-        t.join(1)
-
-        if t.isAlive():
-            self.fail('something went wrong, thread still alive')
-
-    def _zmq_quit(self, done, srv):
-        while not done:
-            time.sleep(0.1)
-        srv.quit()
-
-    def _zmq_client(self, done):
-        try:
-            cnx = connect('zmqpickle-tcp://127.0.0.1:41415', u'admin', password=u'gingkow',
-                          initlog=False) # don't reset logging configuration
-            try:
-                cnx.load_appobjects(subpath=('entities',))
-                # check we can get the schema
-                schema = cnx.get_schema()
-                self.assertTrue(cnx.vreg)
-                self.assertTrue('etypes'in cnx.vreg)
-                cu = cnx.cursor()
-                rset = cu.execute('Any U,G WHERE U in_group G')
-                user = iter(rset.entities()).next()
-                self.assertTrue(user._cw)
-                self.assertTrue(user._cw.vreg)
-                from cubicweb.entities import authobjs
-                self.assertIsInstance(user._cw.user, authobjs.CWUser)
-                cnx.close()
-                done.append(True)
-            finally:
-                # connect monkey patch some method by default, remove them
-                multiple_connections_unfix()
-        finally:
-            done.append(False)
-
     def test_internal_api(self):
         repo = self.repo
         cnxid = repo.connect(self.admlogin, password=self.admpassword)
-        session = repo._get_session(cnxid, setcnxset=True)
-        self.assertEqual(repo.type_and_source_from_eid(2, session),
-                         ('CWGroup', None, 'system'))
-        self.assertEqual(repo.type_from_eid(2, session), 'CWGroup')
+        session = repo._get_session(cnxid)
+        with session.new_cnx() as cnx:
+            self.assertEqual(repo.type_and_source_from_eid(2, cnx),
+                             ('CWGroup', None, 'system'))
+            self.assertEqual(repo.type_from_eid(2, cnx), 'CWGroup')
         repo.close(cnxid)
 
     def test_public_api(self):
@@ -435,45 +217,11 @@
         # .properties() return a result set
         self.assertEqual(self.repo.properties().rql, 'Any K,V WHERE P is CWProperty,P pkey K, P value V, NOT P for_user U')
 
-    def test_session_api(self):
-        repo = self.repo
-        cnxid = repo.connect(self.admlogin, password=self.admpassword)
-        self.assertEqual(repo.user_info(cnxid), (6, 'admin', set([u'managers']), {}))
-        self.assertEqual({'type': u'CWGroup', 'extid': None, 'source': 'system'},
-                         repo.entity_metas(cnxid, 2))
-        self.assertEqual(repo.describe(cnxid, 2), (u'CWGroup', 'system', None, 'system'))
-        repo.close(cnxid)
-        self.assertRaises(BadConnectionId, repo.user_info, cnxid)
-        self.assertRaises(BadConnectionId, repo.describe, cnxid, 1)
-
-    def test_shared_data_api(self):
-        repo = self.repo
-        cnxid = repo.connect(self.admlogin, password=self.admpassword)
-        self.assertEqual(repo.get_shared_data(cnxid, 'data'), None)
-        repo.set_shared_data(cnxid, 'data', 4)
-        self.assertEqual(repo.get_shared_data(cnxid, 'data'), 4)
-        repo.get_shared_data(cnxid, 'data', pop=True)
-        repo.get_shared_data(cnxid, 'whatever', pop=True)
-        self.assertEqual(repo.get_shared_data(cnxid, 'data'), None)
-        repo.close(cnxid)
-        self.assertRaises(BadConnectionId, repo.set_shared_data, cnxid, 'data', 0)
-        self.assertRaises(BadConnectionId, repo.get_shared_data, cnxid, 'data')
-
     def test_schema_is_relation(self):
         with self.admin_access.repo_cnx() as cnx:
             no_is_rset = cnx.execute('Any X WHERE NOT X is ET')
             self.assertFalse(no_is_rset, no_is_rset.description)
 
-#     def test_perfo(self):
-#         self.set_debug(True)
-#         from time import time, clock
-#         t, c = time(), clock()
-#         try:
-#             self.create_user('toto')
-#         finally:
-#             self.set_debug(False)
-#         print 'test time: %.3f (time) %.3f (cpu)' % ((time() - t), clock() - c)
-
     def test_delete_if_singlecard1(self):
         with self.admin_access.repo_cnx() as cnx:
             note = cnx.create_entity('Affaire')
@@ -626,38 +374,37 @@
             namecol = SQL_PREFIX + 'name'
             finalcol = SQL_PREFIX + 'final'
             with self.admin_access.repo_cnx() as cnx:
-                with cnx.ensure_cnx_set:
-                    cu = cnx.system_sql('SELECT %s FROM %s WHERE %s is NULL'
-                                        % (namecol, table, finalcol))
-                    self.assertEqual(cu.fetchall(), [])
-                    cu = cnx.system_sql('SELECT %s FROM %s '
-                                        'WHERE %s=%%(final)s ORDER BY %s'
-                                        % (namecol, table, finalcol, namecol),
-                                        {'final': True})
-                    self.assertEqual(cu.fetchall(),
-                                     [(u'BabarTestType',),
-                                      (u'BigInt',), (u'Boolean',), (u'Bytes',),
-                                      (u'Date',), (u'Datetime',),
-                                      (u'Decimal',),(u'Float',),
-                                      (u'Int',),
-                                      (u'Interval',), (u'Password',),
-                                      (u'String',),
-                                      (u'TZDatetime',), (u'TZTime',), (u'Time',)])
-                    sql = ("SELECT etype.cw_eid, etype.cw_name, cstr.cw_eid, rel.eid_to "
-                           "FROM cw_CWUniqueTogetherConstraint as cstr, "
-                           "     relations_relation as rel, "
-                           "     cw_CWEType as etype "
-                           "WHERE cstr.cw_eid = rel.eid_from "
-                           "  AND cstr.cw_constraint_of = etype.cw_eid "
-                           "  AND etype.cw_name = 'Personne' "
-                           ";")
-                    cu = cnx.system_sql(sql)
-                    rows = cu.fetchall()
-                    self.assertEqual(len(rows), 3)
-                    person = self.repo.schema.eschema('Personne')
-                    self.assertEqual(len(person._unique_together), 1)
-                    self.assertItemsEqual(person._unique_together[0],
-                                          ('nom', 'prenom', 'inline2'))
+                cu = cnx.system_sql('SELECT %s FROM %s WHERE %s is NULL'
+                                    % (namecol, table, finalcol))
+                self.assertEqual(cu.fetchall(), [])
+                cu = cnx.system_sql('SELECT %s FROM %s '
+                                    'WHERE %s=%%(final)s ORDER BY %s'
+                                    % (namecol, table, finalcol, namecol),
+                                    {'final': True})
+                self.assertEqual(cu.fetchall(),
+                                 [(u'BabarTestType',),
+                                  (u'BigInt',), (u'Boolean',), (u'Bytes',),
+                                  (u'Date',), (u'Datetime',),
+                                  (u'Decimal',),(u'Float',),
+                                  (u'Int',),
+                                  (u'Interval',), (u'Password',),
+                                  (u'String',),
+                                  (u'TZDatetime',), (u'TZTime',), (u'Time',)])
+                sql = ("SELECT etype.cw_eid, etype.cw_name, cstr.cw_eid, rel.eid_to "
+                       "FROM cw_CWUniqueTogetherConstraint as cstr, "
+                       "     relations_relation as rel, "
+                       "     cw_CWEType as etype "
+                       "WHERE cstr.cw_eid = rel.eid_from "
+                       "  AND cstr.cw_constraint_of = etype.cw_eid "
+                       "  AND etype.cw_name = 'Personne' "
+                       ";")
+                cu = cnx.system_sql(sql)
+                rows = cu.fetchall()
+                self.assertEqual(len(rows), 3)
+                person = self.repo.schema.eschema('Personne')
+                self.assertEqual(len(person._unique_together), 1)
+                self.assertItemsEqual(person._unique_together[0],
+                                      ('nom', 'prenom', 'inline2'))
 
         finally:
             self.repo.set_schema(origshema)
@@ -680,30 +427,26 @@
 
     def test_type_from_eid(self):
         with self.admin_access.repo_cnx() as cnx:
-            with cnx.ensure_cnx_set:
-                self.assertEqual(self.repo.type_from_eid(2, cnx), 'CWGroup')
+            self.assertEqual(self.repo.type_from_eid(2, cnx), 'CWGroup')
 
     def test_type_from_eid_raise(self):
         with self.admin_access.repo_cnx() as cnx:
-            with cnx.ensure_cnx_set:
-                self.assertRaises(UnknownEid, self.repo.type_from_eid, -2, cnx)
+            self.assertRaises(UnknownEid, self.repo.type_from_eid, -2, cnx)
 
     def test_add_delete_info(self):
         with self.admin_access.repo_cnx() as cnx:
-            with cnx.ensure_cnx_set:
-                cnx.mode = 'write'
-                entity = self.repo.vreg['etypes'].etype_class('Personne')(cnx)
-                entity.eid = -1
-                entity.complete = lambda x: None
-                self.repo.add_info(cnx, entity, self.repo.system_source)
-                cu = cnx.system_sql('SELECT * FROM entities WHERE eid = -1')
-                data = cu.fetchall()
-                self.assertEqual(tuplify(data), [(-1, 'Personne', 'system', None)])
-                self.repo.delete_info(cnx, entity, 'system')
-                #self.repo.commit()
-                cu = cnx.system_sql('SELECT * FROM entities WHERE eid = -1')
-                data = cu.fetchall()
-                self.assertEqual(data, [])
+            entity = self.repo.vreg['etypes'].etype_class('Personne')(cnx)
+            entity.eid = -1
+            entity.complete = lambda x: None
+            self.repo.add_info(cnx, entity, self.repo.system_source)
+            cu = cnx.system_sql('SELECT * FROM entities WHERE eid = -1')
+            data = cu.fetchall()
+            self.assertEqual(tuplify(data), [(-1, 'Personne', 'system', None)])
+            self.repo._delete_cascade_multi(cnx, [entity])
+            self.repo.system_source.delete_info_multi(cnx, [entity])
+            cu = cnx.system_sql('SELECT * FROM entities WHERE eid = -1')
+            data = cu.fetchall()
+            self.assertEqual(data, [])
 
 
 class FTITC(CubicWebTC):
@@ -757,9 +500,7 @@
                               u'system.version.card',
                               u'system.version.comment',
                               u'system.version.cubicweb',
-                              u'system.version.email',
                               u'system.version.file',
-                              u'system.version.folder',
                               u'system.version.localperms',
                               u'system.version.tag'])
 
--- a/server/test/unittest_rql2sql.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/test/unittest_rql2sql.py	Tue Jun 21 07:42:30 2016 +0200
@@ -396,13 +396,13 @@
 ORDER BY 1'''),
 
     # DISTINCT, can use relation under exists scope as principal
-    ('DISTINCT Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), EXISTS(X read_permission Y)',
+    ('DISTINCT Any X,Y WHERE X name "CWGroup", X is CWEType, Y eid IN(1, 2, 3), EXISTS(X read_permission Y)',
      '''SELECT DISTINCT _X.cw_eid, rel_read_permission0.eid_to
 FROM cw_CWEType AS _X, read_permission_relation AS rel_read_permission0
 WHERE _X.cw_name=CWGroup AND rel_read_permission0.eid_to IN(1, 2, 3) AND EXISTS(SELECT 1 WHERE rel_read_permission0.eid_from=_X.cw_eid)'''),
 
     # no distinct, Y can't be invariant
-    ('Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), EXISTS(X read_permission Y)',
+    ('Any X,Y WHERE X name "CWGroup", X is CWEType, Y eid IN(1, 2, 3), EXISTS(X read_permission Y)',
      '''SELECT _X.cw_eid, _Y.cw_eid
 FROM cw_CWEType AS _X, cw_CWGroup AS _Y
 WHERE _X.cw_name=CWGroup AND _Y.cw_eid IN(1, 2, 3) AND EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=_X.cw_eid AND rel_read_permission0.eid_to=_Y.cw_eid)
@@ -412,7 +412,7 @@
 WHERE _X.cw_name=CWGroup AND _Y.cw_eid IN(1, 2, 3) AND EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=_X.cw_eid AND rel_read_permission0.eid_to=_Y.cw_eid)'''),
 
     # DISTINCT but NEGED exists, can't be invariant
-    ('DISTINCT Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), NOT EXISTS(X read_permission Y)',
+    ('DISTINCT Any X,Y WHERE X name "CWGroup", X is CWEType, Y eid IN(1, 2, 3), NOT EXISTS(X read_permission Y)',
      '''SELECT DISTINCT _X.cw_eid, _Y.cw_eid
 FROM cw_CWEType AS _X, cw_CWGroup AS _Y
 WHERE _X.cw_name=CWGroup AND _Y.cw_eid IN(1, 2, 3) AND NOT (EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=_X.cw_eid AND rel_read_permission0.eid_to=_Y.cw_eid))
@@ -422,7 +422,7 @@
 WHERE _X.cw_name=CWGroup AND _Y.cw_eid IN(1, 2, 3) AND NOT (EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=_X.cw_eid AND rel_read_permission0.eid_to=_Y.cw_eid))'''),
 
     # should generate the same query as above
-    ('DISTINCT Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), NOT X read_permission Y',
+    ('DISTINCT Any X,Y WHERE X name "CWGroup", X is CWEType, Y eid IN(1, 2, 3), NOT X read_permission Y',
      '''SELECT DISTINCT _X.cw_eid, _Y.cw_eid
 FROM cw_CWEType AS _X, cw_CWGroup AS _Y
 WHERE _X.cw_name=CWGroup AND _Y.cw_eid IN(1, 2, 3) AND NOT (EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=_X.cw_eid AND rel_read_permission0.eid_to=_Y.cw_eid))
@@ -432,7 +432,7 @@
 WHERE _X.cw_name=CWGroup AND _Y.cw_eid IN(1, 2, 3) AND NOT (EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=_X.cw_eid AND rel_read_permission0.eid_to=_Y.cw_eid))'''),
 
     # neged relation, can't be inveriant
-    ('Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), NOT X read_permission Y',
+    ('Any X,Y WHERE X name "CWGroup", X is CWEType, Y eid IN(1, 2, 3), NOT X read_permission Y',
      '''SELECT _X.cw_eid, _Y.cw_eid
 FROM cw_CWEType AS _X, cw_CWGroup AS _Y
 WHERE _X.cw_name=CWGroup AND _Y.cw_eid IN(1, 2, 3) AND NOT (EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=_X.cw_eid AND rel_read_permission0.eid_to=_Y.cw_eid))
@@ -563,6 +563,17 @@
      '''SELECT _X.cw_eid
 FROM cw_Note AS _X
 WHERE _X.cw_eid IN(999998, 999999) AND NOT (EXISTS(SELECT 1 FROM cw_source_relation AS rel_cw_source0 WHERE rel_cw_source0.eid_from=_X.cw_eid))'''),
+
+    # Test for https://www.cubicweb.org/ticket/5503548
+    ('''Any X
+        WHERE X is CWSourceSchemaConfig,
+        EXISTS(X created_by U, U login L),
+        X cw_schema X_CW_SCHEMA,
+        X owned_by X_OWNED_BY?
+    ''', '''SELECT _X.cw_eid
+FROM cw_CWSourceSchemaConfig AS _X LEFT OUTER JOIN owned_by_relation AS rel_owned_by1 ON (rel_owned_by1.eid_from=_X.cw_eid)
+WHERE EXISTS(SELECT 1 FROM created_by_relation AS rel_created_by0, cw_CWUser AS _U WHERE rel_created_by0.eid_from=_X.cw_eid AND rel_created_by0.eid_to=_U.cw_eid) AND _X.cw_cw_schema IS NOT NULL
+''')
     ]
 
 ADVANCED_WITH_GROUP_CONCAT = [
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/unittest_schema2sql.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,285 @@
+# copyright 2004-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
+"""unit tests for module cubicweb.server.schema2sql
+"""
+
+import os.path as osp
+
+from logilab.common.testlib import TestCase, unittest_main
+from logilab.database import get_db_helper
+
+from yams.reader import SchemaLoader
+from cubicweb.server import schema2sql
+
+schema2sql.SET_DEFAULT = True
+
+DATADIR = osp.abspath(osp.join(osp.dirname(__file__), 'data-schema2sql'))
+
+schema = SchemaLoader().load([DATADIR])
+
+
+EXPECTED_DATA_NO_DROP = """
+CREATE TABLE Affaire(
+ sujet varchar(128),
+ ref varchar(12),
+ inline_rel integer REFERENCES entities (eid)
+);
+CREATE INDEX affaire_inline_rel_idx ON Affaire(inline_rel);
+
+CREATE TABLE Company(
+ name text
+);
+
+CREATE TABLE Datetest(
+ dt1 timestamp,
+ dt2 timestamp,
+ d1 date,
+ d2 date,
+ t1 time,
+ t2 time
+, CONSTRAINT cstredd407706bdfbd2285714dd689e8fcc0 CHECK(d1 <= CAST(clock_timestamp() AS DATE))
+);
+
+CREATE TABLE Division(
+ name text
+);
+
+CREATE TABLE EPermission(
+ name varchar(100) NOT NULL
+);
+CREATE INDEX epermission_name_idx ON EPermission(name);
+
+CREATE TABLE Eetype(
+ name varchar(64) UNIQUE NOT NULL,
+ description text,
+ meta boolean,
+ final boolean,
+ initial_state integer REFERENCES entities (eid)
+);
+CREATE INDEX eetype_name_idx ON Eetype(name);
+CREATE INDEX eetype_initial_state_idx ON Eetype(initial_state);
+
+CREATE TABLE Employee(
+);
+
+CREATE TABLE Note(
+ date varchar(10),
+ type varchar(1),
+ para varchar(512)
+);
+
+CREATE TABLE Person(
+ nom varchar(64) NOT NULL,
+ prenom varchar(64),
+ sexe varchar(1) DEFAULT 'M',
+ promo varchar(6),
+ titre varchar(128),
+ adel varchar(128),
+ ass varchar(128),
+ web varchar(128),
+ tel integer,
+ fax integer,
+ datenaiss date,
+ test boolean,
+ salary float
+, CONSTRAINT cstr41fe7db9ce1d5be95de2477e26590386 CHECK(promo IN ('bon', 'pasbon'))
+);
+CREATE UNIQUE INDEX unique_e6c2d219772dbf1715597f7d9a6b3892 ON Person(nom,prenom);
+
+CREATE TABLE Salaried(
+ nom varchar(64) NOT NULL,
+ prenom varchar(64),
+ sexe varchar(1) DEFAULT 'M',
+ promo varchar(6),
+ titre varchar(128),
+ adel varchar(128),
+ ass varchar(128),
+ web varchar(128),
+ tel integer,
+ fax integer,
+ datenaiss date,
+ test boolean,
+ salary float
+, CONSTRAINT cstrc8556fcc665865217761cdbcd220cae0 CHECK(promo IN ('bon', 'pasbon'))
+);
+CREATE UNIQUE INDEX unique_98da0f9de8588baa8966f0b1a6f850a3 ON Salaried(nom,prenom);
+
+CREATE TABLE Societe(
+ nom varchar(64),
+ web varchar(128),
+ tel integer,
+ fax integer,
+ rncs varchar(32),
+ ad1 varchar(128),
+ ad2 varchar(128),
+ ad3 varchar(128),
+ cp varchar(12),
+ ville varchar(32)
+, CONSTRAINT cstrc51dd462e9f6115506a0fe468d4c8114 CHECK(fax <= tel)
+);
+
+CREATE TABLE State(
+ eid integer PRIMARY KEY REFERENCES entities (eid),
+ name varchar(256) NOT NULL,
+ description text
+);
+CREATE INDEX state_name_idx ON State(name);
+
+CREATE TABLE Subcompany(
+ name text
+);
+
+CREATE TABLE Subdivision(
+ name text
+);
+
+CREATE TABLE pkginfo(
+ modname varchar(30) NOT NULL,
+ version varchar(10) DEFAULT '0.1' NOT NULL,
+ copyright text NOT NULL,
+ license varchar(3),
+ short_desc varchar(80) NOT NULL,
+ long_desc text NOT NULL,
+ author varchar(100) NOT NULL,
+ author_email varchar(100) NOT NULL,
+ mailinglist varchar(100),
+ debian_handler varchar(6)
+, CONSTRAINT cstr70f766f834557c715815d76f0a0db956 CHECK(license IN ('GPL', 'ZPL'))
+, CONSTRAINT cstr831a117424d0007ae0278cc15f344f5e CHECK(debian_handler IN ('machin', 'bidule'))
+);
+
+
+CREATE TABLE concerne_relation (
+  eid_from INTEGER NOT NULL REFERENCES entities (eid),
+  eid_to INTEGER NOT NULL REFERENCES entities (eid),
+  CONSTRAINT concerne_relation_p_key PRIMARY KEY(eid_from, eid_to)
+);
+
+CREATE INDEX concerne_relation_from_idx ON concerne_relation(eid_from);
+CREATE INDEX concerne_relation_to_idx ON concerne_relation(eid_to);
+
+CREATE TABLE division_of_relation (
+  eid_from INTEGER NOT NULL REFERENCES entities (eid),
+  eid_to INTEGER NOT NULL REFERENCES entities (eid),
+  CONSTRAINT division_of_relation_p_key PRIMARY KEY(eid_from, eid_to)
+);
+
+CREATE INDEX division_of_relation_from_idx ON division_of_relation(eid_from);
+CREATE INDEX division_of_relation_to_idx ON division_of_relation(eid_to);
+
+CREATE TABLE evaluee_relation (
+  eid_from INTEGER NOT NULL REFERENCES entities (eid),
+  eid_to INTEGER NOT NULL REFERENCES entities (eid),
+  CONSTRAINT evaluee_relation_p_key PRIMARY KEY(eid_from, eid_to)
+);
+
+CREATE INDEX evaluee_relation_from_idx ON evaluee_relation(eid_from);
+CREATE INDEX evaluee_relation_to_idx ON evaluee_relation(eid_to);
+
+CREATE TABLE next_state_relation (
+  eid_from INTEGER NOT NULL REFERENCES entities (eid),
+  eid_to INTEGER NOT NULL REFERENCES entities (eid),
+  CONSTRAINT next_state_relation_p_key PRIMARY KEY(eid_from, eid_to)
+);
+
+CREATE INDEX next_state_relation_from_idx ON next_state_relation(eid_from);
+CREATE INDEX next_state_relation_to_idx ON next_state_relation(eid_to);
+
+CREATE TABLE obj_wildcard_relation (
+  eid_from INTEGER NOT NULL REFERENCES entities (eid),
+  eid_to INTEGER NOT NULL REFERENCES entities (eid),
+  CONSTRAINT obj_wildcard_relation_p_key PRIMARY KEY(eid_from, eid_to)
+);
+
+CREATE INDEX obj_wildcard_relation_from_idx ON obj_wildcard_relation(eid_from);
+CREATE INDEX obj_wildcard_relation_to_idx ON obj_wildcard_relation(eid_to);
+
+CREATE TABLE require_permission_relation (
+  eid_from INTEGER NOT NULL REFERENCES entities (eid),
+  eid_to INTEGER NOT NULL REFERENCES entities (eid),
+  CONSTRAINT require_permission_relation_p_key PRIMARY KEY(eid_from, eid_to)
+);
+
+CREATE INDEX require_permission_relation_from_idx ON require_permission_relation(eid_from);
+CREATE INDEX require_permission_relation_to_idx ON require_permission_relation(eid_to);
+
+CREATE TABLE state_of_relation (
+  eid_from INTEGER NOT NULL REFERENCES entities (eid),
+  eid_to INTEGER NOT NULL REFERENCES entities (eid),
+  CONSTRAINT state_of_relation_p_key PRIMARY KEY(eid_from, eid_to)
+);
+
+CREATE INDEX state_of_relation_from_idx ON state_of_relation(eid_from);
+CREATE INDEX state_of_relation_to_idx ON state_of_relation(eid_to);
+
+CREATE TABLE subcompany_of_relation (
+  eid_from INTEGER NOT NULL REFERENCES entities (eid),
+  eid_to INTEGER NOT NULL REFERENCES entities (eid),
+  CONSTRAINT subcompany_of_relation_p_key PRIMARY KEY(eid_from, eid_to)
+);
+
+CREATE INDEX subcompany_of_relation_from_idx ON subcompany_of_relation(eid_from);
+CREATE INDEX subcompany_of_relation_to_idx ON subcompany_of_relation(eid_to);
+
+CREATE TABLE subdivision_of_relation (
+  eid_from INTEGER NOT NULL REFERENCES entities (eid),
+  eid_to INTEGER NOT NULL REFERENCES entities (eid),
+  CONSTRAINT subdivision_of_relation_p_key PRIMARY KEY(eid_from, eid_to)
+);
+
+CREATE INDEX subdivision_of_relation_from_idx ON subdivision_of_relation(eid_from);
+CREATE INDEX subdivision_of_relation_to_idx ON subdivision_of_relation(eid_to);
+
+CREATE TABLE subj_wildcard_relation (
+  eid_from INTEGER NOT NULL REFERENCES entities (eid),
+  eid_to INTEGER NOT NULL REFERENCES entities (eid),
+  CONSTRAINT subj_wildcard_relation_p_key PRIMARY KEY(eid_from, eid_to)
+);
+
+CREATE INDEX subj_wildcard_relation_from_idx ON subj_wildcard_relation(eid_from);
+CREATE INDEX subj_wildcard_relation_to_idx ON subj_wildcard_relation(eid_to);
+
+CREATE TABLE sym_rel_relation (
+  eid_from INTEGER NOT NULL REFERENCES entities (eid),
+  eid_to INTEGER NOT NULL REFERENCES entities (eid),
+  CONSTRAINT sym_rel_relation_p_key PRIMARY KEY(eid_from, eid_to)
+);
+
+CREATE INDEX sym_rel_relation_from_idx ON sym_rel_relation(eid_from);
+CREATE INDEX sym_rel_relation_to_idx ON sym_rel_relation(eid_to);
+
+CREATE TABLE travaille_relation (
+  eid_from INTEGER NOT NULL REFERENCES entities (eid),
+  eid_to INTEGER NOT NULL REFERENCES entities (eid),
+  CONSTRAINT travaille_relation_p_key PRIMARY KEY(eid_from, eid_to)
+);
+
+CREATE INDEX travaille_relation_from_idx ON travaille_relation(eid_from);
+CREATE INDEX travaille_relation_to_idx ON travaille_relation(eid_to);
+"""
+
+class SQLSchemaTC(TestCase):
+
+    def test_known_values(self):
+        dbhelper = get_db_helper('postgres')
+        output = schema2sql.schema2sql(dbhelper, schema, skip_relations=('works_for',))
+        self.assertMultiLineEqual(EXPECTED_DATA_NO_DROP.strip(), output.strip())
+
+
+if __name__ == '__main__':
+    unittest_main()
--- a/server/test/unittest_schemaserial.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/test/unittest_schemaserial.py	Tue Jun 21 07:42:30 2016 +0200
@@ -17,9 +17,6 @@
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
 """unit tests for schema rql (de)serialization"""
 
-import sys
-from cStringIO import StringIO
-
 from logilab.common.testlib import TestCase, unittest_main
 
 from cubicweb import Binary
@@ -437,6 +434,8 @@
         self.repo.set_schema(self.repo.deserialize_schema(), resetvreg=False)
         schema = self.repo.schema
         self.assertEqual([('Company', 'Person')], list(schema['has_employee'].rdefs))
+        self.assertEqual(schema['has_employee'].rdef('Company', 'Person').permissions['read'],
+                         (u'managers',))
         self.assertEqual('O works_for S',
                          schema['has_employee'].rule)
         self.assertEqual([('Company', 'Int')], list(schema['total_salary'].rdefs))
--- a/server/test/unittest_security.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/test/unittest_security.py	Tue Jun 21 07:42:30 2016 +0200
@@ -31,9 +31,9 @@
     def setup_database(self):
         super(BaseSecurityTC, self).setup_database()
         with self.admin_access.client_cnx() as cnx:
-            self.create_user(cnx, 'iaminusersgrouponly')
+            self.create_user(cnx, u'iaminusersgrouponly')
             hash = _CRYPTO_CTX.encrypt('oldpassword', scheme='des_crypt')
-            self.create_user(cnx, 'oldpassword', password=Binary(hash))
+            self.create_user(cnx, u'oldpassword', password=Binary(hash))
 
 class LowLevelSecurityFunctionTC(BaseSecurityTC):
 
@@ -45,7 +45,7 @@
             with self.admin_access.repo_cnx() as cnx:
                 self.repo.vreg.solutions(cnx, rqlst, None)
                 check_relations_read_access(cnx, rqlst, {})
-            with self.new_access('anon').repo_cnx() as cnx:
+            with self.new_access(u'anon').repo_cnx() as cnx:
                 self.assertRaises(Unauthorized,
                                   check_relations_read_access,
                                   cnx, rqlst, {})
@@ -60,7 +60,7 @@
                 solution = rqlst.solutions[0]
                 localchecks = get_local_checks(cnx, rqlst, solution)
                 self.assertEqual({}, localchecks)
-            with self.new_access('anon').repo_cnx() as cnx:
+            with self.new_access(u'anon').repo_cnx() as cnx:
                 self.assertRaises(Unauthorized,
                                   get_local_checks,
                                   cnx, rqlst, solution)
@@ -70,7 +70,7 @@
         with self.admin_access.repo_cnx() as cnx:
             self.assertRaises(Unauthorized,
                               cnx.execute, 'Any X,P WHERE X is CWUser, X upassword P')
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             self.assertRaises(Unauthorized,
                               cnx.execute, 'Any X,P WHERE X is CWUser, X upassword P')
 
@@ -104,7 +104,7 @@
         super(SecurityRewritingTC, self).tearDown()
 
     def test_not_relation_read_security(self):
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             self.hijack_source_execute()
             cnx.execute('Any U WHERE NOT A todo_by U, A is Affaire')
             self.assertEqual(self.query[0][1].as_string(),
@@ -126,13 +126,13 @@
             cnx.commit()
 
     def test_insert_security(self):
-        with self.new_access('anon').repo_cnx() as cnx:
+        with self.new_access(u'anon').repo_cnx() as cnx:
             cnx.execute("INSERT Personne X: X nom 'bidule'")
             self.assertRaises(Unauthorized, cnx.commit)
             self.assertEqual(cnx.execute('Personne X').rowcount, 1)
 
     def test_insert_security_2(self):
-        with self.new_access('anon').repo_cnx() as cnx:
+        with self.new_access(u'anon').repo_cnx() as cnx:
             cnx.execute("INSERT Affaire X")
             self.assertRaises(Unauthorized, cnx.commit)
             # anon has no read permission on Affaire entities, so
@@ -141,20 +141,20 @@
 
     def test_insert_rql_permission(self):
         # test user can only add une affaire related to a societe he owns
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             cnx.execute("INSERT Affaire X: X sujet 'cool'")
             self.assertRaises(Unauthorized, cnx.commit)
         # test nothing has actually been inserted
         with self.admin_access.repo_cnx() as cnx:
             self.assertEqual(cnx.execute('Affaire X').rowcount, 1)
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             cnx.execute("INSERT Affaire X: X sujet 'cool'")
             cnx.execute("INSERT Societe X: X nom 'chouette'")
             cnx.execute("SET A concerne S WHERE A sujet 'cool', S nom 'chouette'")
             cnx.commit()
 
     def test_update_security_1(self):
-        with self.new_access('anon').repo_cnx() as cnx:
+        with self.new_access(u'anon').repo_cnx() as cnx:
             # local security check
             cnx.execute( "SET X nom 'bidulechouette' WHERE X is Personne")
             self.assertRaises(Unauthorized, cnx.commit)
@@ -164,7 +164,7 @@
     def test_update_security_2(self):
         with self.temporary_permissions(Personne={'read': ('users', 'managers'),
                                                   'add': ('guests', 'users', 'managers')}):
-            with self.new_access('anon').repo_cnx() as cnx:
+            with self.new_access(u'anon').repo_cnx() as cnx:
                 self.assertRaises(Unauthorized, cnx.execute,
                                   "SET X nom 'bidulechouette' WHERE X is Personne")
         # test nothing has actually been inserted
@@ -172,7 +172,7 @@
             self.assertEqual(cnx.execute('Personne X WHERE X nom "bidulechouette"').rowcount, 0)
 
     def test_update_security_3(self):
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             cnx.execute("INSERT Personne X: X nom 'biduuule'")
             cnx.execute("INSERT Societe X: X nom 'looogilab'")
             cnx.execute("SET X travaille S WHERE X nom 'biduuule', S nom 'looogilab'")
@@ -191,7 +191,7 @@
             cnx.execute("SET A concerne S WHERE A is Affaire, S is Societe")
             cnx.commit()
         # test user can only update une affaire related to a societe he owns
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             cnx.execute("SET X sujet 'pascool' WHERE X is Affaire")
             # this won't actually do anything since the selection query won't return anything
             cnx.commit()
@@ -212,7 +212,7 @@
         #self.assertRaises(Unauthorized,
         #                  self.o.execute, user, "DELETE CWUser X WHERE X login 'bidule'")
         # check local security
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             self.assertRaises(Unauthorized, cnx.execute, "DELETE CWGroup Y WHERE Y name 'staff'")
 
     def test_delete_rql_permission(self):
@@ -220,7 +220,7 @@
             cnx.execute("SET A concerne S WHERE A is Affaire, S is Societe")
             cnx.commit()
         # test user can only dele une affaire related to a societe he owns
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             # this won't actually do anything since the selection query won't return anything
             cnx.execute("DELETE Affaire X")
             cnx.commit()
@@ -239,7 +239,7 @@
             cnx.commit()
 
     def test_insert_relation_rql_permission(self):
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             cnx.execute("SET A concerne S WHERE A is Affaire, S is Societe")
             # should raise Unauthorized since user don't own S though this won't
             # actually do anything since the selection query won't return
@@ -266,7 +266,7 @@
         with self.admin_access.repo_cnx() as cnx:
             cnx.execute("SET A concerne S WHERE A is Affaire, S is Societe")
             cnx.commit()
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             # this won't actually do anything since the selection query won't return anything
             cnx.execute("DELETE A concerne S")
             cnx.commit()
@@ -277,7 +277,7 @@
                          {'x': eid})
             cnx.execute("SET A concerne S WHERE A sujet 'pascool', S is Societe")
             cnx.commit()
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             self.assertRaises(Unauthorized, cnx.execute, "DELETE A concerne S")
             self.assertRaises(QueryError, cnx.commit) # can't commit anymore
             cnx.rollback()
@@ -290,8 +290,8 @@
 
     def test_user_can_change_its_upassword(self):
         with self.admin_access.repo_cnx() as cnx:
-            ueid = self.create_user(cnx, 'user').eid
-        with self.new_access('user').repo_cnx() as cnx:
+            ueid = self.create_user(cnx, u'user').eid
+        with self.new_access(u'user').repo_cnx() as cnx:
             cnx.execute('SET X upassword %(passwd)s WHERE X eid %(x)s',
                        {'x': ueid, 'passwd': 'newpwd'})
             cnx.commit()
@@ -299,8 +299,8 @@
 
     def test_user_cant_change_other_upassword(self):
         with self.admin_access.repo_cnx() as cnx:
-            ueid = self.create_user(cnx, 'otheruser').eid
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+            ueid = self.create_user(cnx, u'otheruser').eid
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             cnx.execute('SET X upassword %(passwd)s WHERE X eid %(x)s',
                        {'x': ueid, 'passwd': 'newpwd'})
             self.assertRaises(Unauthorized, cnx.commit)
@@ -309,7 +309,7 @@
 
     def test_read_base(self):
         with self.temporary_permissions(Personne={'read': ('users', 'managers')}):
-            with self.new_access('anon').repo_cnx() as cnx:
+            with self.new_access(u'anon').repo_cnx() as cnx:
                 self.assertRaises(Unauthorized,
                                   cnx.execute, 'Personne U where U nom "managers"')
 
@@ -317,7 +317,7 @@
         with self.admin_access.repo_cnx() as cnx:
             eid = cnx.execute("INSERT Affaire X: X sujet 'cool'")[0][0]
             cnx.commit()
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             rset = cnx.execute('Affaire X')
             self.assertEqual(rset.rows, [])
             self.assertRaises(Unauthorized, cnx.execute, 'Any X WHERE X eid %(x)s', {'x': eid})
@@ -342,7 +342,7 @@
     def test_entity_created_in_transaction(self):
         affschema = self.schema['Affaire']
         with self.temporary_permissions(Affaire={'read': affschema.permissions['add']}):
-            with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+            with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
                 aff2 = cnx.execute("INSERT Affaire X: X sujet 'cool'")[0][0]
                 # entity created in transaction are readable *by eid*
                 self.assertTrue(cnx.execute('Any X WHERE X eid %(x)s', {'x':aff2}))
@@ -358,7 +358,7 @@
             cnx.execute('SET X owned_by U WHERE X eid %(x)s, U login "iaminusersgrouponly"',
                         {'x': card1})
             cnx.commit()
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             aff2 = cnx.execute("INSERT Affaire X: X sujet 'cool'")[0][0]
             soc1 = cnx.execute("INSERT Societe X: X nom 'chouette'")[0][0]
             cnx.execute("SET A concerne S WHERE A eid %(a)s, S eid %(s)s", {'a': aff2, 's': soc1})
@@ -376,7 +376,7 @@
             cnx.execute("INSERT Societe X: X nom 'bidule'")
             cnx.commit()
         with self.temporary_permissions(Personne={'read': ('managers',)}):
-            with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+            with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
                 rset = cnx.execute('Any N WHERE N has_text "bidule"')
                 self.assertEqual(len(rset.rows), 1, rset.rows)
                 rset = cnx.execute('Any N WITH N BEING (Any N WHERE N has_text "bidule")')
@@ -388,7 +388,7 @@
             cnx.execute("INSERT Societe X: X nom 'bidule'")
             cnx.commit()
         with self.temporary_permissions(Personne={'read': ('managers',)}):
-            with self.new_access('anon').repo_cnx() as cnx:
+            with self.new_access(u'anon').repo_cnx() as cnx:
                 rset = cnx.execute('Any N,U WHERE N has_text "bidule", N owned_by U?')
                 self.assertEqual(len(rset.rows), 1, rset.rows)
 
@@ -396,7 +396,7 @@
         with self.admin_access.repo_cnx() as cnx:
             cnx.execute("INSERT Affaire X: X sujet 'cool'")[0][0]
             cnx.commit()
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             rset = cnx.execute('Any COUNT(X) WHERE X is Affaire')
             self.assertEqual(rset.rows, [[0]])
             aff2 = cnx.execute("INSERT Affaire X: X sujet 'cool'")[0][0]
@@ -424,7 +424,7 @@
                                "X web 'http://www.debian.org', X test TRUE")[0][0]
             cnx.execute('SET X test FALSE WHERE X eid %(x)s', {'x': eid})
             cnx.commit()
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             cnx.execute("INSERT Personne X: X nom 'bidule', "
                        "X web 'http://www.debian.org', X test TRUE")
             self.assertRaises(Unauthorized, cnx.commit)
@@ -440,7 +440,7 @@
             self.assertRaises(Unauthorized, cnx.commit)
             cnx.execute('SET X web "http://www.logilab.org" WHERE X eid %(x)s', {'x': eid})
             cnx.commit()
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             cnx.execute('INSERT Frozable F: F name "Foo"')
             cnx.commit()
             cnx.execute('SET F name "Bar" WHERE F is Frozable')
@@ -464,7 +464,7 @@
             note.cw_adapt_to('IWorkflowable').fire_transition('markasdone')
             cnx.execute('SET X para "truc" WHERE X eid %(x)s', {'x': note.eid})
             cnx.commit()
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             cnx.execute("SET X para 'chouette' WHERE X eid %(x)s", {'x': note.eid})
             self.assertRaises(Unauthorized, cnx.commit)
             note2 = cnx.execute("INSERT Note X: X para 'bidule'").get_entity(0, 0)
@@ -496,10 +496,11 @@
         login_rdef = self.repo.schema['CWUser'].rdef('login')
         with self.temporary_permissions((login_rdef, {'read': ('users', 'managers')}),
                                         CWUser={'read': ('guests', 'users', 'managers')}):
-            with self.new_access('anon').repo_cnx() as cnx:
+            with self.new_access(u'anon').repo_cnx() as cnx:
                 rset = cnx.execute('CWUser X')
                 self.assertTrue(rset)
                 x = rset.get_entity(0, 0)
+                x.complete()
                 self.assertEqual(x.login, None)
                 self.assertTrue(x.creation_date)
                 x = rset.get_entity(1, 0)
@@ -510,7 +511,7 @@
     def test_yams_inheritance_and_security_bug(self):
         with self.temporary_permissions(Division={'read': ('managers',
                                                            ERQLExpression('X owned_by U'))}):
-            with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+            with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
                 querier = cnx.repo.querier
                 rqlst = querier.parse('Any X WHERE X is_instance_of Societe')
                 querier.solutions(cnx, rqlst, {})
@@ -519,7 +520,7 @@
                 plan.preprocess(rqlst)
                 self.assertEqual(
                     rqlst.as_string(),
-                    '(Any X WHERE X is IN(SubDivision, Societe)) UNION '
+                    '(Any X WHERE X is IN(Societe, SubDivision)) UNION '
                     '(Any X WHERE X is Division, EXISTS(X owned_by %(B)s))')
 
 
@@ -528,7 +529,7 @@
 
     def test_user_can_delete_object_he_created(self):
         # even if some other user have changed object'state
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             # due to security test, affaire has to concerne a societe the user owns
             cnx.execute('INSERT Societe X: X nom "ARCTIA"')
             cnx.execute('INSERT Affaire X: X ref "ARCT01", X concerne S WHERE S nom "ARCTIA"')
@@ -542,7 +543,7 @@
             self.assertEqual(len(cnx.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
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             cnx.execute('DELETE Affaire X WHERE X ref "ARCT01"')
             cnx.commit()
             self.assertFalse(cnx.execute('Affaire X'))
@@ -550,7 +551,7 @@
     def test_users_and_groups_non_readable_by_guests(self):
         with self.repo.internal_cnx() as cnx:
             admineid = cnx.execute('CWUser U WHERE U login "admin"').rows[0][0]
-        with self.new_access('anon').repo_cnx() as cnx:
+        with self.new_access(u'anon').repo_cnx() as cnx:
             anon = cnx.user
             # anonymous user can only read itself
             rset = cnx.execute('Any L WHERE X owned_by U, U login L')
@@ -569,7 +570,7 @@
             self.assertRaises(Unauthorized, cnx.commit)
 
     def test_in_group_relation(self):
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             rql = u"DELETE U in_group G WHERE U login 'admin'"
             self.assertRaises(Unauthorized, cnx.execute, rql)
             rql = u"SET U in_group G WHERE U login 'admin', G name 'users'"
@@ -579,7 +580,7 @@
         with self.admin_access.repo_cnx() as cnx:
             cnx.execute("INSERT Personne X: X nom 'bidule'")
             cnx.commit()
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             rql = u"SET X owned_by U WHERE U login 'iaminusersgrouponly', X is Personne"
             self.assertRaises(Unauthorized, cnx.execute, rql)
 
@@ -589,7 +590,7 @@
             beid2 = cnx.execute('INSERT Bookmark B: B path "?vid=index", B title "index", '
                                 'B bookmarked_by U WHERE U login "anon"')[0][0]
             cnx.commit()
-        with self.new_access('anon').repo_cnx() as cnx:
+        with self.new_access(u'anon').repo_cnx() as cnx:
             anoneid = cnx.user.eid
             self.assertEqual(cnx.execute('Any T,P ORDERBY lower(T) WHERE B is Bookmark,B title T,B path P,'
                                          'B bookmarked_by U, U eid %s' % anoneid).rows,
@@ -606,7 +607,7 @@
                               {'x': anoneid, 'b': beid1})
 
     def test_ambigous_ordered(self):
-        with self.new_access('anon').repo_cnx() as cnx:
+        with self.new_access(u'anon').repo_cnx() as cnx:
             names = [t for t, in cnx.execute('Any N ORDERBY lower(N) WHERE X name N')]
             self.assertEqual(names, sorted(names, key=lambda x: x.lower()))
 
@@ -617,7 +618,7 @@
         with self.admin_access.repo_cnx() as cnx:
             eid = cnx.execute('INSERT Affaire X: X ref "ARCT01"')[0][0]
             cnx.commit()
-        with self.new_access('iaminusersgrouponly').repo_cnx() as cnx:
+        with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx:
             # needed to remove rql expr granting update perm to the user
             affschema = self.schema['Affaire']
             with self.temporary_permissions(Affaire={'update': affschema.get_groups('update'),
@@ -675,7 +676,7 @@
                          'U use_email X WHERE U login "anon"').get_entity(0, 0)
             cnx.commit()
             self.assertEqual(len(cnx.execute('Any X WHERE X is EmailAddress')), 2)
-        with self.new_access('anon').repo_cnx() as cnx:
+        with self.new_access(u'anon').repo_cnx() as cnx:
             self.assertEqual(len(cnx.execute('Any X WHERE X is EmailAddress')), 1)
 
 if __name__ == '__main__':
--- a/server/test/unittest_session.py	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of CubicWeb.
-#
-# CubicWeb is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-
-from cubicweb.devtools.testlib import CubicWebTC
-from cubicweb.server.session import HOOKS_ALLOW_ALL, HOOKS_DENY_ALL
-from cubicweb.server import hook
-from cubicweb.predicates import is_instance
-
-class InternalSessionTC(CubicWebTC):
-    def test_dbapi_query(self):
-        session = self.repo.internal_session()
-        self.assertFalse(session.running_dbapi_query)
-        session.close()
-
-    def test_integrity_hooks(self):
-        with self.repo.internal_session() as session:
-            self.assertEqual(HOOKS_ALLOW_ALL, session.hooks_mode)
-            self.assertEqual(set(('integrity', 'security')), session.disabled_hook_categories)
-            self.assertEqual(set(), session.enabled_hook_categories)
-            session.commit()
-            self.assertEqual(HOOKS_ALLOW_ALL, session.hooks_mode)
-            self.assertEqual(set(('integrity', 'security')), session.disabled_hook_categories)
-            self.assertEqual(set(), session.enabled_hook_categories)
-
-class SessionTC(CubicWebTC):
-
-    def test_hooks_control(self):
-        session = self.session
-        # this test check the "old" behavior of session with automatic connection management
-        # close the default cnx, we do nto want it to interfer with the test
-        self.cnx.close()
-        # open a dedicated one
-        session.set_cnx('Some-random-cnx-unrelated-to-the-default-one')
-        # go test go
-        self.assertEqual(HOOKS_ALLOW_ALL, session.hooks_mode)
-        self.assertEqual(set(), session.disabled_hook_categories)
-        self.assertEqual(set(), session.enabled_hook_categories)
-        self.assertEqual(1, len(session._cnxs))
-        with session.deny_all_hooks_but('metadata'):
-            self.assertEqual(HOOKS_DENY_ALL, session.hooks_mode)
-            self.assertEqual(set(), session.disabled_hook_categories)
-            self.assertEqual(set(('metadata',)), session.enabled_hook_categories)
-            session.commit()
-            self.assertEqual(HOOKS_DENY_ALL, session.hooks_mode)
-            self.assertEqual(set(), session.disabled_hook_categories)
-            self.assertEqual(set(('metadata',)), session.enabled_hook_categories)
-            session.rollback()
-            self.assertEqual(HOOKS_DENY_ALL, session.hooks_mode)
-            self.assertEqual(set(), session.disabled_hook_categories)
-            self.assertEqual(set(('metadata',)), session.enabled_hook_categories)
-            with session.allow_all_hooks_but('integrity'):
-                self.assertEqual(HOOKS_ALLOW_ALL, session.hooks_mode)
-                self.assertEqual(set(('integrity',)), session.disabled_hook_categories)
-                self.assertEqual(set(('metadata',)), session.enabled_hook_categories) # not changed in such case
-            self.assertEqual(HOOKS_DENY_ALL, session.hooks_mode)
-            self.assertEqual(set(), session.disabled_hook_categories)
-            self.assertEqual(set(('metadata',)), session.enabled_hook_categories)
-        # leaving context manager with no transaction running should reset the
-        # transaction local storage (and associated cnxset)
-        self.assertEqual({}, session._cnxs)
-        self.assertEqual(None, session.cnxset)
-        self.assertEqual(HOOKS_ALLOW_ALL, session.hooks_mode, session.HOOKS_ALLOW_ALL)
-        self.assertEqual(set(), session.disabled_hook_categories)
-        self.assertEqual(set(), session.enabled_hook_categories)
-
-    def test_explicit_connection(self):
-        with self.session.new_cnx() as cnx:
-            rset = cnx.execute('Any X LIMIT 1 WHERE X is CWUser')
-            self.assertEqual(1, len(rset))
-            user = rset.get_entity(0, 0)
-            user.cw_delete()
-            cnx.rollback()
-            new_user = cnx.entity_from_eid(user.eid)
-            self.assertIsNotNone(new_user.login)
-        self.assertFalse(cnx._open)
-
-    def test_internal_cnx(self):
-        with self.repo.internal_cnx() as cnx:
-            rset = cnx.execute('Any X LIMIT 1 WHERE X is CWUser')
-            self.assertEqual(1, len(rset))
-            user = rset.get_entity(0, 0)
-            user.cw_delete()
-            cnx.rollback()
-            new_user = cnx.entity_from_eid(user.eid)
-            self.assertIsNotNone(new_user.login)
-        self.assertFalse(cnx._open)
-
-    def test_connection_exit(self):
-        """exiting a connection should roll back the transaction, including any
-        pending operations"""
-        self.rollbacked = False
-        class RollbackOp(hook.Operation):
-            _test = self
-            def rollback_event(self):
-                self._test.rollbacked = True
-        class RollbackHook(hook.Hook):
-            __regid__ = 'rollback'
-            events = ('after_update_entity',)
-            __select__ = hook.Hook.__select__ & is_instance('CWGroup')
-            def __call__(self):
-                RollbackOp(self._cw)
-        with self.temporary_appobjects(RollbackHook):
-            with self.admin_access.client_cnx() as cnx:
-                cnx.execute('SET G name "foo" WHERE G is CWGroup, G name "managers"')
-            self.assertTrue(self.rollbacked)
-
-if __name__ == '__main__':
-    from logilab.common.testlib import unittest_main
-    unittest_main()
--- a/server/test/unittest_storage.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/test/unittest_storage.py	Tue Jun 21 07:42:30 2016 +0200
@@ -87,27 +87,31 @@
                              'managed attribute. Is FSPATH() argument BFSS managed?')
 
     def test_bfss_storage(self):
-        with self.admin_access.repo_cnx() as cnx:
-            f1 = self.create_file(cnx)
+        with self.admin_access.web_request() as req:
+            cnx = req.cnx
+            f1 = self.create_file(req)
             filepaths = glob(osp.join(self.tempdir, '%s_data_*' % f1.eid))
             self.assertEqual(len(filepaths), 1, filepaths)
             expected_filepath = filepaths[0]
             # file should be read only
             self.assertFalse(os.access(expected_filepath, os.W_OK))
-            self.assertEqual(file(expected_filepath).read(), 'the-data')
+            self.assertEqual(open(expected_filepath).read(), 'the-data')
             cnx.rollback()
             self.assertFalse(osp.isfile(expected_filepath))
             filepaths = glob(osp.join(self.tempdir, '%s_data_*' % f1.eid))
             self.assertEqual(len(filepaths), 0, filepaths)
-            f1 = self.create_file(cnx)
+            f1 = self.create_file(req)
             cnx.commit()
             filepaths = glob(osp.join(self.tempdir, '%s_data_*' % f1.eid))
             self.assertEqual(len(filepaths), 1, filepaths)
             expected_filepath = filepaths[0]
-            self.assertEqual(file(expected_filepath).read(), 'the-data')
+            self.assertEqual(open(expected_filepath).read(), 'the-data')
+
+            # add f1 back to the entity cache with req as _cw
+            f1 = req.entity_from_eid(f1.eid)
             f1.cw_set(data=Binary('the new data'))
             cnx.rollback()
-            self.assertEqual(file(expected_filepath).read(), 'the-data')
+            self.assertEqual(open(expected_filepath).read(), 'the-data')
             f1.cw_delete()
             self.assertTrue(osp.isfile(expected_filepath))
             cnx.rollback()
--- a/server/test/unittest_tools.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/test/unittest_tools.py	Tue Jun 21 07:42:30 2016 +0200
@@ -23,7 +23,6 @@
 class ImportTC(TestCase):
     def test(self):
         # the minimal test: module is importable...
-        import cubicweb.server.server
         import cubicweb.server.checkintegrity
         import cubicweb.server.serverctl
 
--- a/server/test/unittest_undo.py	Mon May 09 17:24:03 2016 +0200
+++ b/server/test/unittest_undo.py	Tue Jun 21 07:42:30 2016 +0200
@@ -48,7 +48,6 @@
 
     def tearDown(self):
         cubicweb.server.session.Connection = OldConnection
-        self.restore_connection()
         super(UndoableTransactionTC, self).tearDown()
 
     def check_transaction_deleted(self, cnx, txuuid):
@@ -210,13 +209,12 @@
                               ['CWUser'])
             # undoing shouldn't be visble in undoable transaction, and the undone
             # transaction should be removed
-            txs = self.cnx.undoable_transactions()
+            txs = cnx.undoable_transactions()
             self.assertEqual(len(txs), 2)
             self.assertRaises(NoSuchTransaction,
-                              self.cnx.transaction_info, txuuid)
+                              cnx.transaction_info, txuuid)
         with self.admin_access.repo_cnx() as cnx:
-            with cnx.ensure_cnx_set:
-                self.check_transaction_deleted(cnx, txuuid)
+            self.check_transaction_deleted(cnx, txuuid)
             # the final test: check we can login with the previously deleted user
         with self.new_access('toto').client_cnx():
             pass
@@ -238,6 +236,8 @@
             cnx.commit()
             p.cw_clear_all_caches()
             self.assertEqual(p.fiche[0].eid, c2.eid)
+            # we restored the card
+            self.assertTrue(cnx.entity_from_eid(c.eid))
 
     def test_undo_deletion_integrity_2(self):
         with self.admin_access.client_cnx() as cnx:
@@ -272,18 +272,17 @@
             self.assertFalse(cnx.execute('Any X WHERE X eid %(x)s', {'x': p.eid}))
             self.assertFalse(cnx.execute('Any X,Y WHERE X fiche Y'))
         with self.admin_access.repo_cnx() as cnx:
-            with cnx.ensure_cnx_set:
-                for eid in (p.eid, c.eid):
-                    self.assertFalse(cnx.system_sql(
-                        'SELECT * FROM entities WHERE eid=%s' % eid).fetchall())
-                    self.assertFalse(cnx.system_sql(
-                        'SELECT 1 FROM owned_by_relation WHERE eid_from=%s' % eid).fetchall())
-                    # added by sql in hooks (except when using dataimport)
-                    self.assertFalse(cnx.system_sql(
-                        'SELECT 1 FROM is_relation WHERE eid_from=%s' % eid).fetchall())
-                    self.assertFalse(cnx.system_sql(
-                        'SELECT 1 FROM is_instance_of_relation WHERE eid_from=%s' % eid).fetchall())
-                self.check_transaction_deleted(cnx, txuuid)
+            for eid in (p.eid, c.eid):
+                self.assertFalse(cnx.system_sql(
+                    'SELECT * FROM entities WHERE eid=%s' % eid).fetchall())
+                self.assertFalse(cnx.system_sql(
+                    'SELECT 1 FROM owned_by_relation WHERE eid_from=%s' % eid).fetchall())
+                # added by sql in hooks (except when using dataimport)
+                self.assertFalse(cnx.system_sql(
+                    'SELECT 1 FROM is_relation WHERE eid_from=%s' % eid).fetchall())
+                self.assertFalse(cnx.system_sql(
+                    'SELECT 1 FROM is_instance_of_relation WHERE eid_from=%s' % eid).fetchall())
+            self.check_transaction_deleted(cnx, txuuid)
 
     def test_undo_creation_integrity_1(self):
         with self.admin_access.client_cnx() as cnx:
@@ -356,9 +355,8 @@
             p.cw_clear_all_caches()
             self.assertFalse(p.fiche)
         with self.admin_access.repo_cnx() as cnx:
-            with cnx.ensure_cnx_set:
-                self.assertIsNone(cnx.system_sql(
-                    'SELECT cw_fiche FROM cw_Personne WHERE cw_eid=%s' % p.eid).fetchall()[0][0])
+            self.assertIsNone(cnx.system_sql(
+                'SELECT cw_fiche FROM cw_Personne WHERE cw_eid=%s' % p.eid).fetchall()[0][0])
 
     def test_undo_inline_rel_add_ok(self):
         """Undo add relation  Personne (?) fiche (?) Card
@@ -375,6 +373,17 @@
             p.cw_clear_all_caches()
             self.assertFalse(p.fiche)
 
+    def test_undo_inline_rel_delete_ko(self):
+        with self.admin_access.client_cnx() as cnx:
+            c = cnx.create_entity('Card', title=u'hop', content=u'hop')
+            txuuid = cnx.commit()
+            p = cnx.create_entity('Personne', nom=u'louis', fiche=c)
+            cnx.commit()
+            integrityerror = self.repo.sources_by_uri['system'].dbhelper.dbapi_module.IntegrityError
+            with self.assertRaises(integrityerror):
+                cnx.undo_transaction(txuuid)
+
+
     def test_undo_inline_rel_add_ko(self):
         """Undo add relation  Personne (?) fiche (?) Card
 
--- a/skeleton/__pkginfo__.py.tmpl	Mon May 09 17:24:03 2016 +0200
+++ b/skeleton/__pkginfo__.py.tmpl	Tue Jun 21 07:42:30 2016 +0200
@@ -13,7 +13,7 @@
 description = '%(shortdesc)s'
 web = 'http://www.cubicweb.org/project/%%s' %% distname
 
-__depends__ =  %(dependencies)s
+__depends__ = %(dependencies)s
 __recommends__ = {}
 
 classifiers = [
@@ -29,6 +29,7 @@
 
 THIS_CUBE_DIR = join('share', 'cubicweb', 'cubes', modname)
 
+
 def listdir(dirpath):
     return [join(dirpath, fname) for fname in _listdir(dirpath)
             if fname[0] != '.' and not fname.endswith('.pyc')
@@ -40,9 +41,9 @@
     [THIS_CUBE_DIR, [fname for fname in glob('*.py') if fname != 'setup.py']],
     ]
 # check for possible extended cube layout
-for dname in ('entities', 'views', 'sobjects', 'hooks', 'schema', 'data', 'wdoc', 'i18n', 'migration'):
+for dname in ('entities', 'views', 'sobjects', 'hooks', 'schema', 'data',
+              'wdoc', 'i18n', 'migration'):
     if isdir(dname):
         data_files.append([join(THIS_CUBE_DIR, dname), listdir(dname)])
 # Note: here, you'll need to add subdirectories if you want
 # them to be included in the debian package
-
--- a/skeleton/migration/postcreate.py.tmpl	Mon May 09 17:24:03 2016 +0200
+++ b/skeleton/migration/postcreate.py.tmpl	Tue Jun 21 07:42:30 2016 +0200
@@ -11,4 +11,3 @@
 
 # Example of site property change
 #set_property('ui.site-title', "<sitename>")
-
--- a/skeleton/setup.py	Mon May 09 17:24:03 2016 +0200
+++ b/skeleton/setup.py	Tue Jun 21 07:42:30 2016 +0200
@@ -16,8 +16,8 @@
 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
 # details.
 #
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+# You should have received a copy of the GNU Lesser General Public License
+# along with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
 """Generic Setup script, takes package info from __pkginfo__.py file
 """
 __docformat__ = "restructuredtext en"
@@ -25,11 +25,11 @@
 import os
 import sys
 import shutil
-from os.path import isdir, exists, join, walk
+from os.path import exists, join, walk
 
 try:
     if os.environ.get('NO_SETUPTOOLS'):
-        raise ImportError() # do as there is no setuptools
+        raise ImportError()  # do as there is no setuptools
     from setuptools import setup
     from setuptools.command import install_lib
     USE_SETUPTOOLS = True
@@ -41,7 +41,7 @@
 
 # import required features
 from __pkginfo__ import modname, version, license, description, web, \
-     author, author_email, classifiers
+    author, author_email, classifiers
 
 if exists('README'):
     long_description = file('README').read()
@@ -52,10 +52,10 @@
 import __pkginfo__
 if USE_SETUPTOOLS:
     requires = {}
-    for entry in ("__depends__",): # "__recommends__"):
+    for entry in ("__depends__",):  # "__recommends__"):
         requires.update(getattr(__pkginfo__, entry, {}))
     install_requires = [("%s %s" % (d, v and v or "")).strip()
-                       for d, v in requires.iteritems()]
+                        for d, v in requires.iteritems()]
 else:
     install_requires = []
 
@@ -82,6 +82,7 @@
         scripts_ = linux_scripts
     return scripts_
 
+
 def export(from_dir, to_dir,
            blacklist=BASE_BLACKLIST,
            ignore_ext=IGNORED_EXTENSIONS,
@@ -150,13 +151,15 @@
             old_install_data.run(self)
             self.install_dir = _old_install_dir
     try:
-        import setuptools.command.easy_install # only if easy_install available
+        # only if easy_install available
+        import setuptools.command.easy_install  # noqa
         # monkey patch: Crack SandboxViolation verification
         from setuptools.sandbox import DirectorySandbox as DS
         old_ok = DS._ok
+
         def _ok(self, path):
             """Return True if ``path`` can be written during installation."""
-            out = old_ok(self, path) # here for side effect from setuptools
+            out = old_ok(self, path)  # here for side effect from setuptools
             realpath = os.path.normcase(os.path.realpath(path))
             allowed_path = os.path.normcase(sys.prefix)
             if realpath.startswith(allowed_path):
@@ -166,6 +169,7 @@
     except ImportError:
         pass
 
+
 def install(**kwargs):
     """setup entry point"""
     if USE_SETUPTOOLS:
@@ -181,21 +185,22 @@
         kwargs['zip_safe'] = False
         cmdclass['install_data'] = MyInstallData
 
-    return setup(name = distname,
-                 version = version,
-                 license = license,
-                 description = description,
-                 long_description = long_description,
-                 author = author,
-                 author_email = author_email,
-                 url = web,
-                 scripts = ensure_scripts(scripts),
-                 data_files = data_files,
-                 ext_modules = ext_modules,
-                 cmdclass = cmdclass,
-                 classifiers = classifiers,
+    return setup(name=distname,
+                 version=version,
+                 license=license,
+                 description=description,
+                 long_description=long_description,
+                 author=author,
+                 author_email=author_email,
+                 url=web,
+                 scripts=ensure_scripts(scripts),
+                 data_files=data_files,
+                 ext_modules=ext_modules,
+                 cmdclass=cmdclass,
+                 classifiers=classifiers,
                  **kwargs
                  )
 
-if __name__ == '__main__' :
+
+if __name__ == '__main__':
     install()
--- a/skeleton/test/pytestconf.py	Mon May 09 17:24:03 2016 +0200
+++ b/skeleton/test/pytestconf.py	Tue Jun 21 07:42:30 2016 +0200
@@ -13,8 +13,8 @@
 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
 # details.
 #
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+# You should have received a copy of the GNU Lesser General Public License
+# along with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
 """
 
 """
@@ -23,6 +23,7 @@
 
 from logilab.common.pytest import PyTester
 
+
 def getlogin():
     """avoid usinng os.getlogin() because of strange tty / stdin problems
     (man 3 getlogin)
--- a/skeleton/test/realdb_test_CUBENAME.py	Mon May 09 17:24:03 2016 +0200
+++ b/skeleton/test/realdb_test_CUBENAME.py	Tue Jun 21 07:42:30 2016 +0200
@@ -13,14 +13,15 @@
 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
 # details.
 #
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+# You should have received a copy of the GNU Lesser General Public License
+# along with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
 """
 
 """
 from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.devtools.realdbtest import buildconfig, loadconfig
 
+
 def setUpModule(options):
     if options.source:
         configcls = loadconfig(options.source)
@@ -28,13 +29,13 @@
         raise Exception('either <sourcefile> or <dbname> options are required')
     else:
         configcls = buildconfig(options.dbuser, options.dbpassword,
-                                               options.dbname, options.euser,
-                                               options.epassword)
+                                options.dbname,
+                                options.euser, options.epassword)
     RealDatabaseTC.configcls = configcls
 
 
 class RealDatabaseTC(CubicWebTC):
-    configcls = None # set by setUpModule()
+    configcls = None  # set by setUpModule()
 
     def test_all_primaries(self):
         for rset in self.iter_individual_rsets(limit=50):
--- a/skeleton/test/test_CUBENAME.py.tmpl	Mon May 09 17:24:03 2016 +0200
+++ b/skeleton/test/test_CUBENAME.py.tmpl	Tue Jun 21 07:42:30 2016 +0200
@@ -27,6 +27,7 @@
 
 from cubicweb.devtools import testlib
 
+
 class DefaultTC(testlib.CubicWebTC):
     def test_something(self):
         self.skipTest('this cube has no test')
--- a/sobjects/cwxmlparser.py	Mon May 09 17:24:03 2016 +0200
+++ b/sobjects/cwxmlparser.py	Tue Jun 21 07:42:30 2016 +0200
@@ -32,7 +32,8 @@
 """
 
 from datetime import datetime, time
-from cgi import parse_qs # in urlparse with python >= 2.6
+import urlparse
+import urllib
 
 from logilab.common.date import todate, totime
 from logilab.common.textutils import splitstrip, text_to_dict
@@ -238,20 +239,17 @@
         attrs = extract_typed_attrs(entity.e_schema, sourceparams['item'])
         entity.cw_edited.update(attrs)
 
-
     def normalize_url(self, url):
-        """overriden to add vid=xml"""
+        """overridden to add vid=xml if vid is not set in the qs"""
         url = super(CWEntityXMLParser, self).normalize_url(url)
-        if url.startswith('http'):
-            try:
-                url, qs = url.split('?', 1)
-            except ValueError:
-                params = {}
-            else:
-                params = parse_qs(qs)
-            if not 'vid' in params:
+        purl = urlparse.urlparse(url)
+        if purl.scheme in ('http', 'https'):
+            params = urlparse.parse_qs(purl.query)
+            if 'vid' not in params:
                 params['vid'] = ['xml']
-            return url + '?' + self._cw.build_url_params(**params)
+                purl = list(purl)
+                purl[4] = urllib.urlencode(params, doseq=True)
+                return urlparse.urlunparse(purl)
         return url
 
     def complete_url(self, url, etype=None, known_relations=None):
@@ -265,29 +263,22 @@
         If `known_relations` is given, it should be a dictionary of already
         known relations, so they don't get queried again.
         """
-        try:
-            url, qs = url.split('?', 1)
-        except ValueError:
-            qs = ''
-        # XXX vid will be added by later call to normalize_url (in parent class)
-        params = parse_qs(qs)
+        purl = urlparse.urlparse(url)
+        params = urlparse.parse_qs(purl.query)
         if etype is None:
-            try:
-                etype = url.rsplit('/', 1)[1]
-            except ValueError:
-                return url + '?' + self._cw.build_url_params(**params)
-            try:
-                etype = self._cw.vreg.case_insensitive_etypes[etype.lower()]
-            except KeyError:
-                return url + '?' + self._cw.build_url_params(**params)
-        relations = params.setdefault('relation', [])
+            etype = purl.path.split('/')[-1]
+        try:
+            etype = self._cw.vreg.case_insensitive_etypes[etype.lower()]
+        except KeyError:
+            return url
+        relations = params['relation'] = set(params.get('relation', ()))
         for rtype, role, _ in self.source.mapping.get(etype, ()):
             if known_relations and rtype in known_relations.get('role', ()):
                 continue
-            reldef = '%s-%s' % (rtype, role)
-            if not reldef in relations:
-                relations.append(reldef)
-        return url + '?' + self._cw.build_url_params(**params)
+            relations.add('%s-%s' % (rtype, role))
+        purl = list(purl)
+        purl[4] = urllib.urlencode(params, doseq=True)
+        return urlparse.urlunparse(purl)
 
     def complete_item(self, item, rels):
         try:
--- a/sobjects/notification.py	Mon May 09 17:24:03 2016 +0200
+++ b/sobjects/notification.py	Tue Jun 21 07:42:30 2016 +0200
@@ -270,7 +270,7 @@
     """
     __abstract__ = True
     __regid__ = 'notif_entity_updated'
-    msgid_timestamp = False
+    msgid_timestamp = True
     message = _('updated')
     no_detailed_change_attrs = ()
     content = """
--- a/sobjects/services.py	Mon May 09 17:24:03 2016 +0200
+++ b/sobjects/services.py	Tue Jun 21 07:42:30 2016 +0200
@@ -43,7 +43,7 @@
             (len(source._cache), repo.config['rql-cache-size'],
             source.cache_hit, source.cache_miss, 'sql'),
             ):
-            results['%s_cache_size' % title] = '%s / %s' % (size, maxsize)
+            results['%s_cache_size' % title] = {'size': size, 'maxsize': maxsize}
             results['%s_cache_hit' % title] = hits
             results['%s_cache_miss' % title] = misses
             results['%s_cache_hit_percent' % title] = (hits * 100) / (hits + misses)
@@ -53,9 +53,9 @@
         results['nb_open_sessions'] = len(repo._sessions)
         results['nb_active_threads'] = threading.activeCount()
         looping_tasks = repo._tasks_manager._looping_tasks
-        results['looping_tasks'] = ', '.join(str(t) for t in looping_tasks)
+        results['looping_tasks'] = [(t.name, t.interval) for t in looping_tasks]
         results['available_cnxsets'] = repo._cnxsets_pool.qsize()
-        results['threads'] = ', '.join(sorted(str(t) for t in threading.enumerate()))
+        results['threads'] = [t.name for t in threading.enumerate()]
         return results
 
 class GcStatsService(Service):
@@ -79,13 +79,11 @@
         from cubicweb._gcdebug import gc_info
         from cubicweb.appobject import AppObject
         from cubicweb.rset import ResultSet
-        from cubicweb.dbapi import Connection, Cursor
         from cubicweb.web.request import CubicWebRequestBase
         from rql.stmts import Union
 
         lookupclasses = (AppObject,
                          Union, ResultSet,
-                         Connection, Cursor,
                          CubicWebRequestBase)
         try:
             from cubicweb.server.session import Session, InternalSession
@@ -100,7 +98,7 @@
         results['lookupclasses'] = values
         values = sorted(ocounters.iteritems(), key=lambda x: x[1], reverse=True)[:nmax]
         results['referenced'] = values
-        results['unreachable'] = len(garbage)
+        results['unreachable'] = garbage
         return results
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sobjects/test/requirements.txt	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,2 @@
+cubicweb-card
+cubicweb-comment
--- a/sobjects/test/unittest_cwxmlparser.py	Mon May 09 17:24:03 2016 +0200
+++ b/sobjects/test/unittest_cwxmlparser.py	Tue Jun 21 07:42:30 2016 +0200
@@ -17,6 +17,7 @@
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
 
 from datetime import datetime
+from urlparse import urlsplit, parse_qsl
 
 from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.sobjects.cwxmlparser import CWEntityXMLParser
@@ -133,6 +134,16 @@
     """
     test_db_id = 'xmlparser'
 
+    def assertURLEquiv(self, first, second):
+        # ignore ordering differences in query params
+        parsed_first = urlsplit(first)
+        parsed_second = urlsplit(second)
+        self.assertEqual(parsed_first.scheme, parsed_second.scheme)
+        self.assertEqual(parsed_first.netloc, parsed_second.netloc)
+        self.assertEqual(parsed_first.path, parsed_second.path)
+        self.assertEqual(parsed_first.fragment, parsed_second.fragment)
+        self.assertCountEqual(parse_qsl(parsed_first.query), parse_qsl(parsed_second.query))
+
     @classmethod
     def pre_setup_database(cls, cnx, config):
         myfeed = cnx.create_entity('CWSource', name=u'myfeed', type=u'datafeed',
@@ -161,16 +172,16 @@
         dfsource = self.repo.sources_by_uri['myfeed']
         with self.admin_access.repo_cnx() as cnx:
             parser = dfsource._get_parser(cnx)
-            self.assertEqual(parser.complete_url('http://www.cubicweb.org/CWUser'),
-                             'http://www.cubicweb.org/CWUser?relation=tags-object&relation=in_group-subject&relation=in_state-subject&relation=use_email-subject')
-            self.assertEqual(parser.complete_url('http://www.cubicweb.org/cwuser'),
-                             'http://www.cubicweb.org/cwuser?relation=tags-object&relation=in_group-subject&relation=in_state-subject&relation=use_email-subject')
-            self.assertEqual(parser.complete_url('http://www.cubicweb.org/cwuser?vid=rdf&relation=hop'),
-                             'http://www.cubicweb.org/cwuser?relation=hop&relation=tags-object&relation=in_group-subject&relation=in_state-subject&relation=use_email-subject&vid=rdf')
-            self.assertEqual(parser.complete_url('http://www.cubicweb.org/?rql=cwuser&vid=rdf&relation=hop'),
-                             'http://www.cubicweb.org/?rql=cwuser&relation=hop&vid=rdf')
-            self.assertEqual(parser.complete_url('http://www.cubicweb.org/?rql=cwuser&relation=hop'),
-                             'http://www.cubicweb.org/?rql=cwuser&relation=hop')
+            self.assertURLEquiv(parser.complete_url('http://www.cubicweb.org/CWUser'),
+                                'http://www.cubicweb.org/CWUser?relation=tags-object&relation=in_group-subject&relation=in_state-subject&relation=use_email-subject')
+            self.assertURLEquiv(parser.complete_url('http://www.cubicweb.org/cwuser'),
+                                'http://www.cubicweb.org/cwuser?relation=tags-object&relation=in_group-subject&relation=in_state-subject&relation=use_email-subject')
+            self.assertURLEquiv(parser.complete_url('http://www.cubicweb.org/cwuser?vid=rdf&relation=hop'),
+                                'http://www.cubicweb.org/cwuser?relation=hop&relation=tags-object&relation=in_group-subject&relation=in_state-subject&relation=use_email-subject&vid=rdf')
+            self.assertURLEquiv(parser.complete_url('http://www.cubicweb.org/?rql=cwuser&vid=rdf&relation=hop'),
+                                'http://www.cubicweb.org/?rql=cwuser&relation=hop&vid=rdf')
+            self.assertURLEquiv(parser.complete_url('http://www.cubicweb.org/?rql=cwuser&relation=hop'),
+                                'http://www.cubicweb.org/?rql=cwuser&relation=hop')
 
 
     def test_actions(self):
@@ -256,7 +267,11 @@
             self.assertEqual(e.cw_source[0].name, 'system')
             self.assertEqual(e.reverse_use_email[0].login, 'sthenault')
             # test everything is still fine after source synchronization
+            # clear caches to make sure we look at the moved_entities table
+            self.repo._type_source_cache.clear()
+            self.repo._extid_cache.clear()
             stats = dfsource.pull_data(cnx, force=True, raise_on_error=True)
+            self.assertEqual(stats['updated'], set((email.eid,)))
             rset = cnx.execute('EmailAddress X WHERE X address "syt@logilab.fr"')
             self.assertEqual(len(rset), 1)
             e = rset.get_entity(0, 0)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/statsd_logger.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,135 @@
+# copyright 2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+
+"""Simple statsd_ logger for cubicweb.
+
+This module is meant to be configured by setting a couple of global variables:
+
+- ``bucket`` global variable will be used as statsd bucket in every
+statsd_ UDP sent packet.
+
+`- `address`` is a pair (IP, port) specifying the address of the
+statsd_ server
+
+
+There are 3 kinds of statds_ message::
+
+- ``statsd_c(context, n)`` is a simple function to send statsd_
+  counter-type of messages like::
+
+    <bucket>.<context>:<n>|c\n
+
+- ``statsd_g(context, value)`` to send statsd_ gauge-type of messages
+  like::
+
+    <bucket>.<context>:<n>|g\n
+
+- ``statsd_t(context, ms)`` to send statsd_ time-type of messages
+  like::
+
+    <bucket>.<context>:<ms>|ms\n
+
+There is also a decorator (``statsd_timeit``) that may be used to
+measure and send to the statsd_ server the time passed in a function
+or a method and the number of calls. It will send a message like::
+   
+    <bucket>.<funcname>:<ms>|ms\n<bucket>.<funcname>:1|c\n
+
+
+.. _statsd: https://github.com/etsy/statsd
+
+"""
+
+__docformat__ = "restructuredtext en"
+
+import time
+import socket
+
+_bucket = 'cubicweb'
+_address = None
+_socket = None
+
+
+def setup(bucket, address):
+    """Configure the statsd endpoint
+
+    :param bucket: the name of the statsd bucket that will be used to
+                   build messages.
+
+    :param address: the UDP endpoint of the statsd server. Must a
+                    couple (ip, port).
+    """
+    global _bucket, _address, _socket
+    packed = None
+    for family in (socket.AF_INET6, socket.AF_INET):
+        try:
+            packed = socket.inet_pton(family, address[0])
+            break
+        except socket.error:
+            continue
+    if packed is None:
+        return
+    _bucket, _address = bucket, address
+    _socket = socket.socket(family, socket.SOCK_DGRAM)
+
+
+def statsd_c(context, n=1):
+    if _address is not None:
+        _socket.sendto('{0}.{1}:{2}|c\n'.format(_bucket, context, n), _address)
+
+
+def statsd_g(context, value):
+    if _address is not None:
+        _socket.sendto('{0}.{1}:{2}|g\n'.format(_bucket, context, value), _address)
+
+
+def statsd_t(context, value):
+    if _address is not None:
+        _socket.sendto('{0}.{1}:{2:.4f}|ms\n'.format(_bucket, context, value), _address)
+
+
+class statsd_timeit(object):
+    __slots__ = ('callable',)
+
+    def __init__(self, callableobj):
+        self.callable = callableobj
+
+    @property
+    def __doc__(self):
+        return self.callable.__doc__
+    @property
+    def __name__(self):
+        return self.callable.__name__
+    
+    def __call__(self, *args, **kw):
+        if _address is None:
+            return self.callable(*args, **kw)
+        t0 = time.time()
+        try:
+            return self.callable(*args, **kw)
+        finally:
+            dt = 1000*(time.time()-t0)
+            msg = '{0}.{1}:{2:.4f}|ms\n{0}.{1}:1|c\n'.format(_bucket, self.__name__, dt)
+            _socket.sendto(msg, _address)
+                
+    def __get__(self, obj, objtype):
+        """Support instance methods."""
+        if obj is None: # class method or some already wrapped method
+            return self
+        import functools
+        return functools.partial(self.__call__, obj)
--- a/test/data/bootstrap_cubes	Mon May 09 17:24:03 2016 +0200
+++ b/test/data/bootstrap_cubes	Tue Jun 21 07:42:30 2016 +0200
@@ -1,1 +1,1 @@
-card, file, tag, localperms
+card, tag, localperms
--- a/test/data/entities.py	Mon May 09 17:24:03 2016 +0200
+++ b/test/data/entities.py	Tue Jun 21 07:42:30 2016 +0200
@@ -16,7 +16,9 @@
 # You should have received a copy of the GNU Lesser General Public License along
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
 
-from cubicweb.entities import AnyEntity, fetch_config
+from cubicweb.entities import AnyEntity, fetch_config, adapters
+from cubicweb.predicates import is_instance
+
 
 class Societe(AnyEntity):
     __regid__ = 'Societe'
@@ -34,3 +36,7 @@
 
 class Note(AnyEntity):
     __regid__ = 'Note'
+
+
+class FakeFileIDownloadableAdapter(adapters.IDownloadableAdapter):
+    __select__ = is_instance('FakeFile')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/data/rqlexpr_on_computedrel.py	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,18 @@
+from yams.buildobjs import ComputedRelation, EntityType, RelationDefinition
+from cubicweb.schema import RRQLExpression
+
+class Subject(EntityType):
+    pass
+
+class Object(EntityType):
+    pass
+
+class relation(RelationDefinition):
+    subject = 'Subject'
+    object = 'Object'
+
+class computed(ComputedRelation):
+    rule = 'S relation O'
+    __permissions__ = {'read': (RRQLExpression('S is ET'),)}
+
+
--- a/test/data/schema.py	Mon May 09 17:24:03 2016 +0200
+++ b/test/data/schema.py	Tue Jun 21 07:42:30 2016 +0200
@@ -16,13 +16,16 @@
 # You should have received a copy of the GNU Lesser General Public License along
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
 
-from yams.buildobjs import (EntityType, String, SubjectRelation,
-                            RelationDefinition)
+from yams.buildobjs import (EntityType, String, RichString, Bytes,
+                            SubjectRelation, RelationDefinition)
 
 from cubicweb.schema import (WorkflowableEntityType,
                              RQLConstraint, RQLVocabularyConstraint)
 
 
+_ = unicode
+
+
 class Personne(EntityType):
     nom = String(required=True)
     prenom = String()
@@ -94,3 +97,17 @@
 class Reference(EntityType):
     nom = String(unique=True)
     ean = String(unique=True, required=True)
+
+
+class FakeFile(EntityType):
+    title = String(fulltextindexed=True, maxsize=256)
+    data = Bytes(required=True, fulltextindexed=True, description=_('file to upload'))
+    data_format = String(required=True, maxsize=128,
+                         description=_('MIME type of the file. Should be dynamically set at upload time.'))
+    data_encoding = String(maxsize=32,
+                           description=_('encoding of the file when it applies (e.g. text). '
+                                         'Should be dynamically set at upload time.'))
+    data_name = String(required=True, fulltextindexed=True,
+                       description=_('name of the file. Should be dynamically set at upload time.'))
+    description = RichString(fulltextindexed=True, internationalizable=True,
+                             default_format='text/rest')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/requirements.txt	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,6 @@
+Pygments
+#fyzz XXX pip install fails
+cubicweb-card
+cubicweb-file
+cubicweb-localperms
+cubicweb-tag
--- a/test/unittest_cwconfig.py	Mon May 09 17:24:03 2016 +0200
+++ b/test/unittest_cwconfig.py	Tue Jun 21 07:42:30 2016 +0200
@@ -104,11 +104,14 @@
     def test_appobjects_path(self):
         self.config.__class__.CUBES_PATH = [CUSTOM_CUBES_DIR]
         self.config.adjust_sys_path()
-        self.assertEqual([unabsolutize(p) for p in self.config.appobjects_path()],
-                          ['entities', 'web/views', 'sobjects', 'hooks',
-                           'file/entities', 'file/views.py', 'file/hooks',
-                           'email/entities.py', 'email/views', 'email/hooks.py',
-                           'test/data/entities.py', 'test/data/views.py'])
+        path = [unabsolutize(p) for p in self.config.appobjects_path()]
+        self.assertEqual(path[0], 'entities')
+        self.assertCountEqual(path[1:4], ['web/views', 'sobjects', 'hooks'])
+        self.assertEqual(path[4], 'file/entities')
+        self.assertCountEqual(path[5:7], ['file/views.py', 'file/hooks'])
+        self.assertEqual(path[7], 'email/entities.py')
+        self.assertCountEqual(path[8:10], ['email/views', 'email/hooks.py'])
+        self.assertEqual(path[10:], ['test/data/entities.py', 'test/data/views.py'])
 
     def test_cubes_path(self):
         # make sure we don't import the email cube, but the stdlib email package
--- a/test/unittest_cwctl.py	Mon May 09 17:24:03 2016 +0200
+++ b/test/unittest_cwctl.py	Tue Jun 21 07:42:30 2016 +0200
@@ -44,7 +44,7 @@
 
     def test_process_script_args_context(self):
         repo = self.repo
-        with self.admin_access.client_cnx() as cnx:
+        with self.admin_access.repo_cnx() as cnx:
             mih = ServerMigrationHelper(None, repo=repo, cnx=cnx,
                                         interactive=False,
                                         # hack so it don't try to load fs schema
--- a/test/unittest_dataimport.py	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,167 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import datetime as DT
-from StringIO import StringIO
-
-from logilab.common.testlib import TestCase, unittest_main
-
-from cubicweb import dataimport
-from cubicweb.devtools.testlib import CubicWebTC
-
-
-class RQLObjectStoreTC(CubicWebTC):
-
-    def test_all(self):
-        with self.admin_access.repo_cnx() as cnx:
-            store = dataimport.RQLObjectStore(cnx)
-            group_eid = store.create_entity('CWGroup', name=u'grp').eid
-            user_eid = store.create_entity('CWUser', login=u'lgn', upassword=u'pwd').eid
-            store.relate(user_eid, 'in_group', group_eid)
-            cnx.commit()
-
-        with self.admin_access.repo_cnx() as cnx:
-            users = cnx.execute('CWUser X WHERE X login "lgn"')
-            self.assertEqual(1, len(users))
-            self.assertEqual(user_eid, users.one().eid)
-            groups = cnx.execute('CWGroup X WHERE U in_group X, U login "lgn"')
-            self.assertEqual(1, len(users))
-            self.assertEqual(group_eid, groups.one().eid)
-
-
-class CreateCopyFromBufferTC(TestCase):
-
-    # test converters
-
-    def test_convert_none(self):
-        cnvt = dataimport._copyfrom_buffer_convert_None
-        self.assertEqual('NULL', cnvt(None))
-
-    def test_convert_number(self):
-        cnvt = dataimport._copyfrom_buffer_convert_number
-        self.assertEqual('42', cnvt(42))
-        self.assertEqual('42', cnvt(42L))
-        self.assertEqual('42.42', cnvt(42.42))
-
-    def test_convert_string(self):
-        cnvt = dataimport._copyfrom_buffer_convert_string
-        # simple
-        self.assertEqual('babar', cnvt('babar'))
-        # unicode
-        self.assertEqual('\xc3\xa9l\xc3\xa9phant', cnvt(u'éléphant'))
-        self.assertEqual('\xe9l\xe9phant', cnvt(u'éléphant', encoding='latin1'))
-        self.assertEqual('babar#', cnvt('babar\t', replace_sep='#'))
-        self.assertRaises(ValueError, cnvt, 'babar\t')
-
-    def test_convert_date(self):
-        cnvt = dataimport._copyfrom_buffer_convert_date
-        self.assertEqual('0666-01-13', cnvt(DT.date(666, 1, 13)))
-
-    def test_convert_time(self):
-        cnvt = dataimport._copyfrom_buffer_convert_time
-        self.assertEqual('06:06:06.000100', cnvt(DT.time(6, 6, 6, 100)))
-
-    def test_convert_datetime(self):
-        cnvt = dataimport._copyfrom_buffer_convert_datetime
-        self.assertEqual('0666-06-13 06:06:06.000000', cnvt(DT.datetime(666, 6, 13, 6, 6, 6)))
-
-    # test buffer
-    def test_create_copyfrom_buffer_tuple(self):
-        cnvt = dataimport._create_copyfrom_buffer
-        data = ((42, 42L, 42.42, u'éléphant', DT.date(666, 1, 13), DT.time(6, 6, 6), DT.datetime(666, 6, 13, 6, 6, 6)),
-                (6, 6L, 6.6, u'babar', DT.date(2014, 1, 14), DT.time(4, 2, 1), DT.datetime(2014, 1, 1, 0, 0, 0)))
-        results = dataimport._create_copyfrom_buffer(data)
-        # all columns
-        expected = '''42\t42\t42.42\téléphant\t0666-01-13\t06:06:06.000000\t0666-06-13 06:06:06.000000
-6\t6\t6.6\tbabar\t2014-01-14\t04:02:01.000000\t2014-01-01 00:00:00.000000'''
-        self.assertMultiLineEqual(expected, results.getvalue())
-        # selected columns
-        results = dataimport._create_copyfrom_buffer(data, columns=(1, 3, 6))
-        expected = '''42\téléphant\t0666-06-13 06:06:06.000000
-6\tbabar\t2014-01-01 00:00:00.000000'''
-        self.assertMultiLineEqual(expected, results.getvalue())
-
-    def test_create_copyfrom_buffer_dict(self):
-        cnvt = dataimport._create_copyfrom_buffer
-        data = (dict(integer=42, double=42.42, text=u'éléphant', date=DT.datetime(666, 6, 13, 6, 6, 6)),
-                dict(integer=6, double=6.6, text=u'babar', date=DT.datetime(2014, 1, 1, 0, 0, 0)))
-        results = dataimport._create_copyfrom_buffer(data, ('integer', 'text'))
-        expected = '''42\téléphant\n6\tbabar'''
-        self.assertMultiLineEqual(expected, results.getvalue())
-
-
-class UcsvreaderTC(TestCase):
-
-    def test_empty_lines_skipped(self):
-        stream = StringIO('''a,b,c,d,
-1,2,3,4,
-,,,,
-,,,,
-''')
-        self.assertEqual([[u'a', u'b', u'c', u'd', u''],
-                          [u'1', u'2', u'3', u'4', u''],
-                          ],
-                         list(dataimport.ucsvreader(stream)))
-        stream.seek(0)
-        self.assertEqual([[u'a', u'b', u'c', u'd', u''],
-                          [u'1', u'2', u'3', u'4', u''],
-                          [u'', u'', u'', u'', u''],
-                          [u'', u'', u'', u'', u'']
-                          ],
-                         list(dataimport.ucsvreader(stream, skip_empty=False)))
-
-    def test_skip_first(self):
-        stream = StringIO('a,b,c,d,\n'
-                          '1,2,3,4,\n')
-        reader = dataimport.ucsvreader(stream, skipfirst=True,
-                                       ignore_errors=True)
-        self.assertEqual(list(reader),
-                         [[u'1', u'2', u'3', u'4', u'']])
-
-        stream.seek(0)
-        reader = dataimport.ucsvreader(stream, skipfirst=True,
-                                       ignore_errors=False)
-        self.assertEqual(list(reader),
-                         [[u'1', u'2', u'3', u'4', u'']])
-
-        stream.seek(0)
-        reader = dataimport.ucsvreader(stream, skipfirst=False,
-                                       ignore_errors=True)
-        self.assertEqual(list(reader),
-                         [[u'a', u'b', u'c', u'd', u''],
-                          [u'1', u'2', u'3', u'4', u'']])
-
-        stream.seek(0)
-        reader = dataimport.ucsvreader(stream, skipfirst=False,
-                                       ignore_errors=False)
-        self.assertEqual(list(reader),
-                         [[u'a', u'b', u'c', u'd', u''],
-                          [u'1', u'2', u'3', u'4', u'']])
-
-
-class MetaGeneratorTC(CubicWebTC):
-
-    def test_dont_generate_relation_to_internal_manager(self):
-        with self.admin_access.repo_cnx() as cnx:
-            metagen = dataimport.MetaGenerator(cnx)
-            self.assertIn('created_by', metagen.etype_rels)
-            self.assertIn('owned_by', metagen.etype_rels)
-        with self.repo.internal_cnx() as cnx:
-            metagen = dataimport.MetaGenerator(cnx)
-            self.assertNotIn('created_by', metagen.etype_rels)
-            self.assertNotIn('owned_by', metagen.etype_rels)
-
-    def test_dont_generate_specified_values(self):
-        with self.admin_access.repo_cnx() as cnx:
-            metagen = dataimport.MetaGenerator(cnx)
-            # hijack gen_modification_date to ensure we don't go through it
-            metagen.gen_modification_date = None
-            md = DT.datetime.now() - DT.timedelta(days=1)
-            entity, rels = metagen.base_etype_dicts('CWUser')
-            entity.cw_edited.update(dict(modification_date=md))
-            with cnx.ensure_cnx_set:
-                metagen.init_entity(entity)
-            self.assertEqual(entity.cw_edited['modification_date'], md)
-
-
-if __name__ == '__main__':
-    unittest_main()
--- a/test/unittest_dbapi.py	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of CubicWeb.
-#
-# CubicWeb is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""unittest for cubicweb.dbapi"""
-
-from copy import copy
-
-from logilab.common import tempattr
-
-from cubicweb import ConnectionError, cwconfig, NoSelectableObject
-from cubicweb.dbapi import ProgrammingError, _repo_connect
-from cubicweb.devtools.testlib import CubicWebTC
-
-
-class DBAPITC(CubicWebTC):
-
-    def test_public_repo_api(self):
-        cnx = _repo_connect(self.repo, login='anon', password='anon')
-        self.assertEqual(cnx.get_schema(), self.repo.schema)
-        self.assertEqual(cnx.source_defs(), {'system': {'type': 'native', 'uri': 'system',
-                                                        'use-cwuri-as-url': False}})
-        cnx.close()
-        self.assertRaises(ProgrammingError, cnx.get_schema)
-        self.assertRaises(ProgrammingError, cnx.source_defs)
-
-    def test_db_api(self):
-        cnx = _repo_connect(self.repo, login='anon', password='anon')
-        self.assertEqual(cnx.rollback(), None)
-        self.assertEqual(cnx.commit(), None)
-        cnx.close()
-        self.assertRaises(ProgrammingError, cnx.rollback)
-        self.assertRaises(ProgrammingError, cnx.commit)
-        self.assertRaises(ProgrammingError, cnx.close)
-
-    def test_api(self):
-        cnx = _repo_connect(self.repo, login='anon', password='anon')
-        self.assertEqual(cnx.user(None).login, 'anon')
-        self.assertEqual({'type': u'CWSource', 'source': u'system', 'extid': None},
-                         cnx.entity_metas(1))
-        self.assertEqual(cnx.describe(1), (u'CWSource', u'system', None))
-        cnx.close()
-        self.assertRaises(ProgrammingError, cnx.user, None)
-        self.assertRaises(ProgrammingError, cnx.entity_metas, 1)
-        self.assertRaises(ProgrammingError, cnx.describe, 1)
-
-    def test_shared_data_api(self):
-        cnx = _repo_connect(self.repo, login='anon', password='anon')
-        self.assertEqual(cnx.get_shared_data('data'), None)
-        cnx.set_shared_data('data', 4)
-        self.assertEqual(cnx.get_shared_data('data'), 4)
-        cnx.get_shared_data('data', pop=True)
-        cnx.get_shared_data('whatever', pop=True)
-        self.assertEqual(cnx.get_shared_data('data'), None)
-        cnx.set_shared_data('data', 4)
-        self.assertEqual(cnx.get_shared_data('data'), 4)
-        cnx.close()
-        self.assertRaises(ProgrammingError, cnx.check)
-        self.assertRaises(ProgrammingError, cnx.set_shared_data, 'data', 0)
-        self.assertRaises(ProgrammingError, cnx.get_shared_data, 'data')
-
-    def test_web_compatible_request(self):
-        config = cwconfig.CubicWebNoAppConfiguration()
-        cnx = _repo_connect(self.repo, login='admin', password='gingkow')
-        with tempattr(cnx.vreg, 'config', config):
-            cnx.use_web_compatible_requests('http://perdu.com')
-            req = cnx.request()
-            self.assertEqual(req.base_url(), 'http://perdu.com/')
-            self.assertEqual(req.from_controller(), 'view')
-            self.assertEqual(req.relative_path(), '')
-            req.ajax_replace_url('domid') # don't crash
-            req.user.cw_adapt_to('IBreadCrumbs') # don't crash
-
-    def test_call_service(self):
-        ServiceClass = self.vreg['services']['test_service'][0]
-        for _cw in (self.request(), self.session):
-            ret_value = _cw.call_service('test_service', msg='coucou')
-            self.assertEqual('coucou', ServiceClass.passed_here.pop())
-            self.assertEqual('babar', ret_value)
-        with self.login('anon') as ctm:
-            for _cw in (self.request(), self.session):
-                with self.assertRaises(NoSelectableObject):
-                    _cw.call_service('test_service', msg='toto')
-                self.rollback()
-                self.assertEqual([], ServiceClass.passed_here)
-
-
-if __name__ == '__main__':
-    from logilab.common.testlib import unittest_main
-    unittest_main()
--- a/test/unittest_entity.py	Mon May 09 17:24:03 2016 +0200
+++ b/test/unittest_entity.py	Tue Jun 21 07:42:30 2016 +0200
@@ -140,13 +140,24 @@
         with self.admin_access.web_request() as req:
             user = req.execute('Any X WHERE X eid %(x)s', {'x':req.user.eid}).get_entity(0, 0)
             adeleid = req.execute('INSERT EmailAddress X: X address "toto@logilab.org", U use_email X WHERE U login "admin"')[0][0]
+            self.assertEqual({}, user._cw_related_cache)
             req.cnx.commit()
-            self.assertEqual(user._cw_related_cache, {})
+            self.assertEqual(['primary_email_subject', 'use_email_subject', 'wf_info_for_object'],
+                             sorted(user._cw_related_cache))
             email = user.primary_email[0]
-            self.assertEqual(sorted(user._cw_related_cache), ['primary_email_subject'])
-            self.assertEqual(list(email._cw_related_cache), ['primary_email_object'])
+            self.assertEqual(u'toto@logilab.org', email.address)
+            self.assertEqual(['created_by_subject',
+                              'cw_source_subject',
+                              'is_instance_of_subject',
+                              'is_subject',
+                              'owned_by_subject',
+                              'prefered_form_object',
+                              'prefered_form_subject',
+                              'primary_email_object',
+                              'use_email_object'],
+                             sorted(email._cw_related_cache))
+            self.assertEqual('admin', email._cw_related_cache['primary_email_object'][1][0].login)
             groups = user.in_group
-            self.assertEqual(sorted(user._cw_related_cache), ['in_group_subject', 'primary_email_subject'])
             for group in groups:
                 self.assertNotIn('in_group_subject', group._cw_related_cache)
             user.cw_clear_all_caches()
@@ -223,8 +234,8 @@
                 user = req.user
                 # testing basic fetch_attrs attribute
                 self.assertEqual(Personne.fetch_rql(user),
-                                 'Any X,AA,AB,AC ORDERBY AA '
-                                 'WHERE X is_instance_of Personne, X nom AA, X prenom AB, X modification_date AC')
+                                 'Any X,AA,AB,AC ORDERBY AB '
+                                 'WHERE X is_instance_of Personne, X modification_date AA, X nom AB, X prenom AC')
                 # testing unknown attributes
                 Personne.fetch_attrs = ('bloug', 'beep')
                 self.assertEqual(Personne.fetch_rql(user), 'Any X WHERE X is_instance_of Personne')
@@ -236,21 +247,20 @@
                 # testing two non final relations
                 Personne.fetch_attrs = ('nom', 'prenom', 'travaille', 'evaluee')
                 self.assertEqual(Personne.fetch_rql(user),
-                                 'Any X,AA,AB,AC,AD,AE ORDERBY AA '
-                                 'WHERE X is_instance_of Personne, X nom AA, X prenom AB, X travaille AC?, AC nom AD, '
-                                 'X evaluee AE?')
+                                 'Any X,AA,AB,AC,AD,AE ORDERBY AB '
+                                 'WHERE X is_instance_of Personne, X evaluee AA?, X nom AB, X prenom AC, X travaille AD?, '
+                                 'AD nom AE')
                 # testing one non final relation with recursion
                 Personne.fetch_attrs = ('nom', 'prenom', 'travaille')
                 Societe.fetch_attrs = ('nom', 'evaluee')
                 self.assertEqual(Personne.fetch_rql(user),
-                                 'Any X,AA,AB,AC,AD,AE,AF ORDERBY AA,AF DESC '
-                                 'WHERE X is_instance_of Personne, X nom AA, X prenom AB, X travaille AC?, AC nom AD, '
-                                 'AC evaluee AE?, AE modification_date AF'
-                                  )
+                                 'Any X,AA,AB,AC,AD,AE,AF ORDERBY AA,AE DESC '
+                                 'WHERE X is_instance_of Personne, X nom AA, X prenom AB, X travaille AC?, '
+                                 'AC evaluee AD?, AD modification_date AE, AC nom AF')
                 # testing symmetric relation
                 Personne.fetch_attrs = ('nom', 'connait')
-                self.assertEqual(Personne.fetch_rql(user), 'Any X,AA,AB ORDERBY AA '
-                                 'WHERE X is_instance_of Personne, X nom AA, X connait AB?')
+                self.assertEqual(Personne.fetch_rql(user), 'Any X,AA,AB ORDERBY AB '
+                                 'WHERE X is_instance_of Personne, X connait AA?, X nom AB')
                 # testing optional relation
                 peschema.subjrels['travaille'].rdef(peschema, seschema).cardinality = '?*'
                 Personne.fetch_attrs = ('nom', 'prenom', 'travaille')
@@ -278,8 +288,8 @@
         with self.admin_access.web_request() as req:
             p = req.create_entity('Personne', nom=u'pouet')
             self.assertEqual(p.cw_related_rql('evaluee'),
-                             'Any X,AA,AB ORDERBY AA WHERE E eid %(x)s, E evaluee X, '
-                             'X type AA, X modification_date AB')
+                             'Any X,AA,AB ORDERBY AB WHERE E eid %(x)s, E evaluee X, '
+                             'X modification_date AA, X type AB')
             n = req.create_entity('Note')
             self.assertEqual(n.cw_related_rql('evaluee', role='object',
                                               targettypes=('Societe', 'Personne')),
@@ -297,9 +307,9 @@
                               'Any X,AA ORDERBY AA DESC '
                               'WHERE E eid %(x)s, E tags X, X modification_date AA')
             self.assertEqual(tag.cw_related_rql('tags', 'subject', ('Personne',)),
-                              'Any X,AA,AB ORDERBY AA '
-                              'WHERE E eid %(x)s, E tags X, X is Personne, X nom AA, '
-                              'X modification_date AB')
+                              'Any X,AA,AB ORDERBY AB '
+                              'WHERE E eid %(x)s, E tags X, X is Personne, X modification_date AA, '
+                              'X nom AB')
 
     def test_related_rql_ambiguous_cant_use_fetch_order(self):
         with self.admin_access.web_request() as req:
@@ -363,9 +373,9 @@
         with self.admin_access.web_request() as req:
             email = req.execute('INSERT EmailAddress X: X address "hop"').get_entity(0, 0)
             rql = email.cw_unrelated_rql('use_email', 'CWUser', 'object')[0]
-            self.assertEqual(rql, 'Any S,AA,AB,AC,AD ORDERBY AA '
+            self.assertEqual(rql, 'Any S,AA,AB,AC,AD ORDERBY AB '
                              'WHERE NOT S use_email O, O eid %(x)s, S is_instance_of CWUser, '
-                             'S login AA, S firstname AB, S surname AC, S modification_date AD')
+                             'S firstname AA, S login AB, S modification_date AC, S surname AD')
             req.cnx.commit()
         rperms = self.schema['EmailAddress'].permissions['read']
         clear_cache(self.schema['EmailAddress'], 'get_groups')
@@ -375,9 +385,9 @@
             with self.new_access('anon').web_request() as req:
                 email = req.execute('Any X WHERE X eid %(x)s', {'x': email.eid}).get_entity(0, 0)
                 rql = email.cw_unrelated_rql('use_email', 'CWUser', 'object')[0]
-                self.assertEqual(rql, 'Any S,AA,AB,AC,AD ORDERBY AA '
+                self.assertEqual(rql, 'Any S,AA,AB,AC,AD ORDERBY AB '
                              'WHERE NOT S use_email O, O eid %(x)s, S is CWUser, '
-                             'S login AA, S firstname AB, S surname AC, S modification_date AD, '
+                             'S firstname AA, S login AB, S modification_date AC, S surname AD, '
                              'AE eid %(AF)s, EXISTS(S identity AE, NOT AE in_group AG, AG name "guests", AG is CWGroup)')
         finally:
             clear_cache(self.schema['EmailAddress'], 'get_groups')
@@ -388,17 +398,17 @@
         with self.admin_access.web_request() as req:
             email = req.execute('INSERT EmailAddress X: X address "hop"').get_entity(0, 0)
             rql = email.cw_linkable_rql('use_email', 'CWUser', 'object')[0]
-            self.assertEqual(rql, 'Any S,AA,AB,AC,AD ORDERBY AA '
+            self.assertEqual(rql, 'Any S,AA,AB,AC,AD ORDERBY AB '
                              'WHERE O eid %(x)s, S is_instance_of CWUser, '
-                             'S login AA, S firstname AB, S surname AC, S modification_date AD')
+                             'S firstname AA, S login AB, S modification_date AC, S surname AD')
 
     def test_unrelated_rql_security_nonexistant(self):
         with self.new_access('anon').web_request() as req:
             email = self.vreg['etypes'].etype_class('EmailAddress')(req)
             rql = email.cw_unrelated_rql('use_email', 'CWUser', 'object')[0]
-            self.assertEqual(rql, 'Any S,AA,AB,AC,AD ORDERBY AA '
+            self.assertEqual(rql, 'Any S,AA,AB,AC,AD ORDERBY AB '
                          'WHERE S is CWUser, '
-                         'S login AA, S firstname AB, S surname AC, S modification_date AD, '
+                         'S firstname AA, S login AB, S modification_date AC, S surname AD, '
                          'AE eid %(AF)s, EXISTS(S identity AE, NOT AE in_group AG, AG name "guests", AG is CWGroup)')
 
     def test_unrelated_rql_constraints_creation_subject(self):
@@ -406,16 +416,16 @@
             person = self.vreg['etypes'].etype_class('Personne')(req)
             rql = person.cw_unrelated_rql('connait', 'Personne', 'subject')[0]
             self.assertEqual(
-            rql, 'Any O,AA,AB,AC ORDERBY AC DESC WHERE '
-            'O is_instance_of Personne, O nom AA, O prenom AB, O modification_date AC')
+            rql, 'Any O,AA,AB,AC ORDERBY AA DESC WHERE '
+            'O is_instance_of Personne, O modification_date AA, O nom AB, O prenom AC')
 
     def test_unrelated_rql_constraints_creation_object(self):
         with self.admin_access.web_request() as req:
             person = self.vreg['etypes'].etype_class('Personne')(req)
             rql = person.cw_unrelated_rql('connait', 'Personne', 'object')[0]
             self.assertEqual(
-            rql, 'Any S,AA,AB,AC ORDERBY AC DESC WHERE '
-            'S is Personne, S nom AA, S prenom AB, S modification_date AC, '
+            rql, 'Any S,AA,AB,AC ORDERBY AA DESC WHERE '
+            'S is Personne, S modification_date AA, S nom AB, S prenom AC, '
             'NOT (S connait AD, AD nom "toto"), AD is Personne, '
             'EXISTS(S travaille AE, AE nom "tutu")')
 
@@ -428,18 +438,18 @@
             with self.admin_access.web_request() as req:
                 person = self.vreg['etypes'].etype_class('Personne')(req)
                 rql = person.cw_unrelated_rql('connait', 'Personne', 'subject')[0]
-                self.assertEqual(rql, 'Any O,AA,AB,AC ORDERBY AC DESC WHERE '
-                         'O is_instance_of Personne, O nom AA, O prenom AB, '
-                         'O modification_date AC')
+                self.assertEqual(rql, 'Any O,AA,AB,AC ORDERBY AA DESC WHERE '
+                         'O is_instance_of Personne, O modification_date AA, O nom AB, '
+                         'O prenom AC')
 
     def test_unrelated_rql_constraints_edition_subject(self):
         with self.admin_access.web_request() as req:
             person = req.create_entity('Personne', nom=u'sylvain')
             rql = person.cw_unrelated_rql('connait', 'Personne', 'subject')[0]
             self.assertEqual(
-                rql, 'Any O,AA,AB,AC ORDERBY AC DESC WHERE '
+                rql, 'Any O,AA,AB,AC ORDERBY AA DESC WHERE '
             'NOT S connait O, S eid %(x)s, O is Personne, '
-            'O nom AA, O prenom AB, O modification_date AC, '
+            'O modification_date AA, O nom AB, O prenom AC, '
             'NOT S identity O')
 
     def test_unrelated_rql_constraints_edition_object(self):
@@ -447,9 +457,9 @@
             person = req.create_entity('Personne', nom=u'sylvain')
             rql = person.cw_unrelated_rql('connait', 'Personne', 'object')[0]
             self.assertEqual(
-            rql, 'Any S,AA,AB,AC ORDERBY AC DESC WHERE '
+            rql, 'Any S,AA,AB,AC ORDERBY AA DESC WHERE '
             'NOT S connait O, O eid %(x)s, S is Personne, '
-            'S nom AA, S prenom AB, S modification_date AC, '
+            'S modification_date AA, S nom AB, S prenom AC, '
             'NOT S identity O, NOT (S connait AD, AD nom "toto"), '
             'EXISTS(S travaille AE, AE nom "tutu")')
 
@@ -634,7 +644,7 @@
 
     def test_printable_value_bytes(self):
         with self.admin_access.web_request() as req:
-            e = req.create_entity('File', data=Binary('lambda x: 1'), data_format=u'text/x-python',
+            e = req.create_entity('FakeFile', data=Binary('lambda x: 1'), data_format=u'text/x-python',
                                   data_encoding=u'ascii', data_name=u'toto.py')
             from cubicweb import mttransforms
             if mttransforms.HAS_PYGMENTS_TRANSFORMS:
@@ -653,7 +663,7 @@
     <span style="color: #C00000;">lambda</span> <span style="color: #000000;">x</span><span style="color: #0000C0;">:</span> <span style="color: #0080C0;">1</span>
 </pre>''')
 
-            e = req.create_entity('File', data=Binary('*héhéhé*'), data_format=u'text/rest',
+            e = req.create_entity('FakeFile', data=Binary('*héhéhé*'), data_format=u'text/rest',
                                 data_encoding=u'utf-8', data_name=u'toto.txt')
             self.assertEqual(e.printable_value('data'),
                               u'<p><em>héhéhé</em></p>')
@@ -704,7 +714,7 @@
 
     def test_fulltextindex(self):
         with self.admin_access.web_request() as req:
-            e = self.vreg['etypes'].etype_class('File')(req)
+            e = self.vreg['etypes'].etype_class('FakeFile')(req)
             e.cw_attr_cache['description'] = 'du <em>html</em>'
             e.cw_attr_cache['description_format'] = 'text/html'
             e.cw_attr_cache['data'] = Binary('some <em>data</em>')
--- a/test/unittest_predicates.py	Mon May 09 17:24:03 2016 +0200
+++ b/test/unittest_predicates.py	Tue Jun 21 07:42:30 2016 +0200
@@ -27,7 +27,7 @@
 from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.predicates import (is_instance, adaptable, match_kwargs, match_user_groups,
                                 multi_lines_rset, score_entity, is_in_state,
-                                rql_condition, relation_possible)
+                                rql_condition, relation_possible, match_form_params)
 from cubicweb.selectors import on_transition # XXX on_transition is deprecated
 from cubicweb.view import EntityAdapter
 from cubicweb.web import action
@@ -37,12 +37,13 @@
 class ImplementsTC(CubicWebTC):
     def test_etype_priority(self):
         with self.admin_access.web_request() as req:
-            f = req.create_entity('File', data_name=u'hop.txt', data=Binary('hop'))
+            f = req.create_entity('FakeFile', data_name=u'hop.txt', data=Binary('hop'),
+                                  data_format=u'text/plain')
             rset = f.as_rset()
             anyscore = is_instance('Any')(f.__class__, req, rset=rset)
             idownscore = adaptable('IDownloadable')(f.__class__, req, rset=rset)
             self.assertTrue(idownscore > anyscore, (idownscore, anyscore))
-            filescore = is_instance('File')(f.__class__, req, rset=rset)
+            filescore = is_instance('FakeFile')(f.__class__, req, rset=rset)
             self.assertTrue(filescore > idownscore, (filescore, idownscore))
 
     def test_etype_inheritance_no_yams_inheritance(self):
@@ -391,6 +392,102 @@
                 rset = req.execute('Any X WHERE X is IN(CWGroup, CWUser)')
                 self.assertTrue(selector(None, req, rset=rset))
 
+
+class MatchFormParamsTC(CubicWebTC):
+    """tests for match_form_params predicate"""
+
+    def test_keyonly_match(self):
+        """test standard usage: ``match_form_params('param1', 'param2')``
+
+        ``param1`` and ``param2`` must be specified in request's form.
+        """
+        web_request = self.admin_access.web_request
+        vid_selector = match_form_params('vid')
+        vid_subvid_selector = match_form_params('vid', 'subvid')
+        # no parameter => KO,KO
+        with web_request() as req:
+            self.assertEqual(vid_selector(None, req), 0)
+            self.assertEqual(vid_subvid_selector(None, req), 0)
+        # one expected parameter found => OK,KO
+        with web_request(vid='foo') as req:
+            self.assertEqual(vid_selector(None, req), 1)
+            self.assertEqual(vid_subvid_selector(None, req), 0)
+        # all expected parameters found => OK,OK
+        with web_request(vid='foo', subvid='bar') as req:
+            self.assertEqual(vid_selector(None, req), 1)
+            self.assertEqual(vid_subvid_selector(None, req), 2)
+
+    def test_keyvalue_match_one_parameter(self):
+        """test dict usage: ``match_form_params(param1=value1)``
+
+        ``param1`` must be specified in the request's form and its value
+        must be ``value1``.
+        """
+        web_request = self.admin_access.web_request
+        # test both positional and named parameters
+        vid_selector = match_form_params(vid='foo')
+        # no parameter => should fail
+        with web_request() as req:
+            self.assertEqual(vid_selector(None, req), 0)
+        # expected parameter found with expected value => OK
+        with web_request(vid='foo', subvid='bar') as req:
+            self.assertEqual(vid_selector(None, req), 1)
+        # expected parameter found but value is incorrect => KO
+        with web_request(vid='bar') as req:
+            self.assertEqual(vid_selector(None, req), 0)
+
+    def test_keyvalue_match_two_parameters(self):
+        """test dict usage: ``match_form_params(param1=value1, param2=value2)``
+
+        ``param1`` and ``param2`` must be specified in the request's form and
+        their respective value must be ``value1`` and ``value2``.
+        """
+        web_request = self.admin_access.web_request
+        vid_subvid_selector = match_form_params(vid='list', subvid='tsearch')
+        # missing one expected parameter => KO
+        with web_request(vid='list') as req:
+            self.assertEqual(vid_subvid_selector(None, req), 0)
+        # expected parameters found but values are incorrect => KO
+        with web_request(vid='list', subvid='foo') as req:
+            self.assertEqual(vid_subvid_selector(None, req), 0)
+        # expected parameters found and values are correct => OK
+        with web_request(vid='list', subvid='tsearch') as req:
+            self.assertEqual(vid_subvid_selector(None, req), 2)
+
+    def test_keyvalue_multiple_match(self):
+        """test dict usage with multiple values
+
+        i.e. as in ``match_form_params(param1=('value1', 'value2'))``
+
+        ``param1`` must be specified in the request's form and its value
+        must be either ``value1`` or ``value2``.
+        """
+        web_request = self.admin_access.web_request
+        vid_subvid_selector = match_form_params(vid='list', subvid=('tsearch', 'listitem'))
+        # expected parameters found and values correct => OK
+        with web_request(vid='list', subvid='tsearch') as req:
+            self.assertEqual(vid_subvid_selector(None, req), 2)
+        with web_request(vid='list', subvid='listitem') as req:
+            self.assertEqual(vid_subvid_selector(None, req), 2)
+        # expected parameters found but values are incorrect => OK
+        with web_request(vid='list', subvid='foo') as req:
+            self.assertEqual(vid_subvid_selector(None, req), 0)
+
+    def test_invalid_calls(self):
+        """checks invalid calls raise a ValueError"""
+        # mixing named and positional arguments should fail
+        with self.assertRaises(ValueError) as cm:
+            match_form_params('list', x='1', y='2')
+        self.assertEqual(str(cm.exception),
+                         "match_form_params() can't be called with both "
+                         "positional and named arguments")
+        # using a dict as first and unique argument should fail
+        with self.assertRaises(ValueError) as cm:
+            match_form_params({'x': 1})
+        self.assertEqual(str(cm.exception),
+                         "match_form_params() positional arguments must be strings")
+
+
 if __name__ == '__main__':
     unittest_main()
 
--- a/test/unittest_repoapi.py	Mon May 09 17:24:03 2016 +0200
+++ b/test/unittest_repoapi.py	Tue Jun 21 07:42:30 2016 +0200
@@ -15,18 +15,18 @@
 #
 # You should have received a copy of the GNU Lesser General Public License along
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""unittest for cubicweb.dbapi"""
+"""unittest for cubicweb.repoapi"""
 
 
 from cubicweb.devtools.testlib import CubicWebTC
 
 from cubicweb import ProgrammingError
-from cubicweb.repoapi import ClientConnection, connect, anonymous_cnx
+from cubicweb.repoapi import Connection, connect, anonymous_cnx
 
 
 class REPOAPITC(CubicWebTC):
 
-    def test_clt_cnx_basic_usage(self):
+    def test_cnx_basic_usage(self):
         """Test that a client connection can be used to access the database"""
         with self.admin_access.client_cnx() as cltcnx:
             # (1) some RQL request
@@ -52,11 +52,11 @@
                                   ''')
             self.assertTrue(rset)
 
-    def test_clt_cnx_life_cycle(self):
+    def test_cnx_life_cycle(self):
         """Check that ClientConnection requires explicit open and close
         """
         access = self.admin_access
-        cltcnx = ClientConnection(access._session)
+        cltcnx = Connection(access._session)
         # connection not open yet
         with self.assertRaises(ProgrammingError):
             cltcnx.execute('Any X WHERE X is CWUser')
@@ -69,18 +69,18 @@
 
     def test_connect(self):
         """check that repoapi.connect works and returns a usable connection"""
-        clt_cnx = connect(self.repo, login='admin', password='gingkow')
-        self.assertEqual('admin', clt_cnx.user.login)
-        with clt_cnx:
-            rset = clt_cnx.execute('Any X WHERE X is CWUser')
+        cnx = connect(self.repo, login='admin', password='gingkow')
+        self.assertEqual('admin', cnx.user.login)
+        with cnx:
+            rset = cnx.execute('Any X WHERE X is CWUser')
             self.assertTrue(rset)
 
     def test_anonymous_connect(self):
         """check that you can get anonymous connection when the data exist"""
-        clt_cnx = anonymous_cnx(self.repo)
-        self.assertEqual('anon', clt_cnx.user.login)
-        with clt_cnx:
-            rset = clt_cnx.execute('Any X WHERE X is CWUser')
+        cnx = anonymous_cnx(self.repo)
+        self.assertEqual('anon', cnx.user.login)
+        with cnx:
+            rset = cnx.execute('Any X WHERE X is CWUser')
             self.assertTrue(rset)
 
 
--- a/test/unittest_schema.py	Mon May 09 17:24:03 2016 +0200
+++ b/test/unittest_schema.py	Tue Jun 21 07:42:30 2016 +0200
@@ -169,7 +169,7 @@
                              'CWRelation', 'CWPermission', 'CWProperty', 'CWRType',
                              'CWSource', 'CWSourceHostConfig', 'CWSourceSchemaConfig',
                              'CWUniqueTogetherConstraint', 'CWUser',
-                             'ExternalUri', 'File', 'Float', 'Int', 'Interval', 'Note',
+                             'ExternalUri', 'FakeFile', 'Float', 'Int', 'Interval', 'Note',
                              'Password', 'Personne', 'Produit',
                              'RQLExpression', 'Reference',
                              'Service', 'Societe', 'State', 'StateFull', 'String', 'SubNote', 'SubWorkflowExitPoint',
@@ -221,8 +221,6 @@
                               'value',
 
                               'wf_info_for', 'wikiid', 'workflow_of', 'tr_count']
-        if config.cube_version('file') >= (1, 14, 0):
-            expected_relations.append('data_sha1hex')
 
         self.assertListEqual(sorted(expected_relations), relations)
 
@@ -360,8 +358,8 @@
                          schema['produces_and_buys'].rdefs.keys())
         self.assertEqual([('Person','Service')],
                          schema['produces_and_buys2'].rdefs.keys())
-        self.assertEqual([('Company', 'Service'), ('Person', 'Service')],
-                         schema['reproduce'].rdefs.keys())
+        self.assertCountEqual([('Company', 'Service'), ('Person', 'Service')],
+                              schema['reproduce'].rdefs.keys())
         # check relation definitions are marked infered
         rdef = schema['produces_and_buys'].rdefs[('Person','Service')]
         self.assertTrue(rdef.infered)
@@ -419,6 +417,10 @@
         self._test('rrqlexpr_on_attr.py',
                    "can't use RRQLExpression on attribute ToTo.attr[String], use an ERQLExpression")
 
+    def test_rqlexpr_on_computedrel(self):
+        self._test('rqlexpr_on_computedrel.py',
+                   "can't use rql expression for read permission of relation Subject computed Object")
+
 
 class NormalizeExpressionTC(TestCase):
 
@@ -484,6 +486,7 @@
                        ('cw_schema', 'CWSourceSchemaConfig', 'CWRelation', 'object'),
                        ('delete_permission', 'CWRelation', 'RQLExpression', 'subject'),
                        ('read_permission', 'CWRelation', 'RQLExpression', 'subject')],
+        'CWComputedRType': [('read_permission', 'CWComputedRType', 'RQLExpression', 'subject')],
         'CWSource': [('cw_for_source', 'CWSourceSchemaConfig', 'CWSource', 'object'),
                      ('cw_host_config_of', 'CWSourceHostConfig', 'CWSource', 'object'),
                      ('cw_import_of', 'CWDataImport', 'CWSource', 'object'),
@@ -510,7 +513,7 @@
                      ('cw_source', 'Card', 'CWSource', 'object'),
                      ('cw_source', 'EmailAddress', 'CWSource', 'object'),
                      ('cw_source', 'ExternalUri', 'CWSource', 'object'),
-                     ('cw_source', 'File', 'CWSource', 'object'),
+                     ('cw_source', 'FakeFile', 'CWSource', 'object'),
                      ('cw_source', 'Note', 'CWSource', 'object'),
                      ('cw_source', 'Personne', 'CWSource', 'object'),
                      ('cw_source', 'Produit', 'CWSource', 'object'),
--- a/test/unittest_utils.py	Mon May 09 17:24:03 2016 +0200
+++ b/test/unittest_utils.py	Tue Jun 21 07:42:30 2016 +0200
@@ -58,12 +58,6 @@
                          parse_repo_uri('myapp'))
         self.assertEqual(('inmemory', None, 'myapp'),
                          parse_repo_uri('inmemory://myapp'))
-        self.assertEqual(('pyro', 'pyro-ns-host:pyro-ns-port', '/myapp'),
-                         parse_repo_uri('pyro://pyro-ns-host:pyro-ns-port/myapp'))
-        self.assertEqual(('pyroloc', 'host:port', '/appkey'),
-                         parse_repo_uri('pyroloc://host:port/appkey'))
-        self.assertEqual(('zmqpickle-tcp', '127.0.0.1:666', ''),
-                         parse_repo_uri('zmqpickle-tcp://127.0.0.1:666'))
         with self.assertRaises(NotImplementedError):
             parse_repo_uri('foo://bar')
 
--- a/toolsutils.py	Mon May 09 17:24:03 2016 +0200
+++ b/toolsutils.py	Tue Jun 21 07:42:30 2016 +0200
@@ -257,18 +257,6 @@
       }),
     )
 
-def config_connect(appid, optconfig):
-    from cubicweb.dbapi import connect
-    from getpass import getpass
-    user = optconfig.user
-    if not user:
-        user = raw_input('login: ')
-    password = optconfig.password
-    if not password:
-        password = getpass('password: ')
-    return connect(login=user, password=password, host=optconfig.host, database=appid)
-
-
 ## cwshell helpers #############################################################
 
 class AbstractMatcher(object):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tox.ini	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,55 @@
+[tox]
+env = py27
+
+[testenv]
+sitepackages = True
+commands = pytest -t {envname}/test {posargs}
+
+[testenv:cubicweb]
+deps =
+  -r{toxinidir}/test/requirements.txt
+commands = pytest -t test {posargs}
+
+[testenv:dataimport]
+
+[testenv:devtools]
+deps =
+  -r{toxinidir}/devtools/test/requirements.txt
+
+[testenv:entities]
+deps =
+  -r{toxinidir}/entities/test/requirements.txt
+
+[testenv:etwist]
+deps =
+  -r{toxinidir}/etwist/test/requirements.txt
+
+[testenv:ext]
+deps =
+  -r{toxinidir}/ext/test/requirements.txt
+
+[testenv:hooks]
+
+[testenv:server]
+deps =
+  -r{toxinidir}/server/test/requirements.txt
+
+[testenv:sobjects]
+deps =
+  -r{toxinidir}/sobjects/test/requirements.txt
+
+[testenv:web]
+deps =
+  -r{toxinidir}/web/test/requirements.txt
+
+[testenv:wsgi]
+deps =
+  -r{toxinidir}/wsgi/test/requirements.txt
+
+[testenv:doc]
+changedir = doc
+whitelist_externals =
+  sphinx-build
+deps =
+  sphinx
+commands = sphinx-build -b html -d {envtmpdir}/doctrees .  {envtmpdir}/html
--- a/transaction.py	Mon May 09 17:24:03 2016 +0200
+++ b/transaction.py	Tue Jun 21 07:42:30 2016 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -15,13 +15,7 @@
 #
 # You should have received a copy of the GNU Lesser General Public License along
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""undoable transaction objects.
-
-
-This module is in the cubicweb package and not in cubicweb.server because those
-objects should be accessible to client through pyro, where the cubicweb.server
-package may not be installed.
-"""
+""" undoable transaction objects. """
 __docformat__ = "restructuredtext en"
 _ = unicode
 
@@ -42,27 +36,21 @@
     msg = _("there is no transaction #%s")
 
     def __init__(self, txuuid):
-        super(RepositoryError, self).__init__(txuuid)
+        super(NoSuchTransaction, self).__init__(txuuid)
         self.txuuid = txuuid
 
 class Transaction(object):
     """an undoable transaction"""
 
-    def __init__(self, uuid, time, ueid):
+    def __init__(self, cnx, uuid, time, ueid):
+        self.cnx = cnx
         self.uuid = uuid
         self.datetime = time
         self.user_eid = ueid
-        # should be set by the dbapi connection
-        self.req = None  # old style
-        self.cnx = None  # new style
 
     def _execute(self, *args, **kwargs):
         """execute a query using either the req or the cnx"""
-        if self.req is None:
-            execute = self.cnx.execute
-        else:
-            execute = self.req
-        return execute(*args, **kwargs)
+        return self.cnx.execute(*args, **kwargs)
 
 
     def __repr__(self):
@@ -73,8 +61,7 @@
         """return the user entity which has done the transaction,
         none if not found.
         """
-        return self._execute('Any X WHERE X eid %(x)s',
-                             {'x': self.user_eid}).get_entity(0, 0)
+        return self.cnx.find('CWUser', eid=self.user_eid).one()
 
     def actions_list(self, public=True):
         """return an ordered list of action effectued during that transaction
@@ -82,14 +69,11 @@
         if public is true, return only 'public' action, eg not ones triggered
         under the cover by hooks.
         """
-        if self.req is not None:
-            cnx = self.req.cnx
-        else:
-            cnx = self.cnx
-        return cnx.transaction_actions(self.uuid, public)
+        return self.cnx.transaction_actions(self.uuid, public)
 
 
 class AbstractAction(object):
+
     def __init__(self, action, public, order):
         self.action = action
         self.public = public
@@ -106,8 +90,9 @@
 
 
 class EntityAction(AbstractAction):
+
     def __init__(self, action, public, order, etype, eid, changes):
-        AbstractAction.__init__(self, action, public, order)
+        super(EntityAction, self).__init__(action, public, order)
         self.etype = etype
         self.eid = eid
         self.changes = changes
@@ -124,8 +109,9 @@
 
 
 class RelationAction(AbstractAction):
+
     def __init__(self, action, public, order, rtype, eidfrom, eidto):
-        AbstractAction.__init__(self, action, public, order)
+        super(RelationAction, self).__init__(action, public, order)
         self.rtype = rtype
         self.eid_from = eidfrom
         self.eid_to = eidto
--- a/utils.py	Mon May 09 17:24:03 2016 +0200
+++ b/utils.py	Tue Jun 21 07:42:30 2016 +0200
@@ -21,7 +21,6 @@
 
 __docformat__ = "restructuredtext en"
 
-import sys
 import decimal
 import datetime
 import random
@@ -553,8 +552,12 @@
     """
 
 def _dict2js(d, predictable=False):
+    if predictable:
+        it = sorted(d.iteritems())
+    else:
+        it = d.iteritems()
     res = [key + ': ' + js_dumps(val, predictable)
-           for key, val in d.iteritems()]
+           for key, val in it]
     return '{%s}' % ', '.join(res)
 
 def _list2js(l, predictable=False):
@@ -578,7 +581,7 @@
         return _list2js(something, predictable)
     if isinstance(something, JSString):
         return something
-    return json_dumps(something)
+    return json_dumps(something, sort_keys=predictable)
 
 PERCENT_IN_URLQUOTE_RE = re.compile(r'%(?=[0-9a-fA-F]{2})')
 def js_href(javascript_code):
@@ -608,8 +611,6 @@
     """ transform a command line uri into a (protocol, hostport, appid), e.g:
     <myapp>                      -> 'inmemory', None, '<myapp>'
     inmemory://<myapp>           -> 'inmemory', None, '<myapp>'
-    pyro://[host][:port]         -> 'pyro', 'host:port', None
-    zmqpickle://[host][:port]    -> 'zmqpickle', 'host:port', None
     """
     parseduri = urlparse(uri)
     scheme = parseduri.scheme
@@ -617,8 +618,6 @@
         return ('inmemory', None, parseduri.path)
     if scheme == 'inmemory':
         return (scheme, None, parseduri.netloc)
-    if scheme in ('pyro', 'pyroloc') or scheme.startswith('zmqpickle-'):
-        return (scheme, parseduri.netloc, parseduri.path)
     raise NotImplementedError('URI protocol not implemented for `%s`' % uri)
 
 
@@ -647,7 +646,7 @@
 
     Occasional elements can be buggy requests (server-side) or
     end-user (web-ui provided) requests. These have to be cleaned up
-    when they fill the cache, without evicting the usefull, frequently
+    when they fill the cache, without evicting the useful, frequently
     used entries.
     """
     # quite arbitrary, but we want to never
--- a/view.py	Mon May 09 17:24:03 2016 +0200
+++ b/view.py	Tue Jun 21 07:42:30 2016 +0200
@@ -20,7 +20,7 @@
 __docformat__ = "restructuredtext en"
 _ = unicode
 
-from cStringIO import StringIO
+from io import BytesIO
 from warnings import warn
 from functools import partial
 
@@ -101,7 +101,7 @@
             return
         if w is None:
             if self.binary:
-                self._stream = stream = StringIO()
+                self._stream = stream = BytesIO()
             else:
                 self._stream = stream = UStringIO()
             w = stream.write
@@ -471,7 +471,7 @@
             return
         if w is None:
             if self.binary:
-                self._stream = stream = StringIO()
+                self._stream = stream = BytesIO()
             else:
                 self._stream = stream = HTMLStream(self._cw)
             w = stream.write
--- a/web/application.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/application.py	Tue Jun 21 07:42:30 2016 +0200
@@ -52,103 +52,14 @@
 @contextmanager
 def anonymized_request(req):
     orig_cnx = req.cnx
-    anon_clt_cnx = anonymous_cnx(orig_cnx._session.repo)
-    req.set_cnx(anon_clt_cnx)
+    anon_cnx = anonymous_cnx(orig_cnx.session.repo)
+    req.set_cnx(anon_cnx)
     try:
-        with anon_clt_cnx:
+        with anon_cnx:
             yield req
     finally:
         req.set_cnx(orig_cnx)
 
-class AbstractSessionManager(component.Component):
-    """manage session data associated to a session identifier"""
-    __regid__ = 'sessionmanager'
-
-    def __init__(self, repo):
-        vreg = repo.vreg
-        self.session_time = vreg.config['http-session-time'] or None
-        self.authmanager = vreg['components'].select('authmanager', repo=repo)
-        interval = (self.session_time or 0) / 2.
-        if vreg.config.anonymous_user()[0] is not None:
-            self.cleanup_anon_session_time = vreg.config['cleanup-anonymous-session-time'] or 5 * 60
-            assert self.cleanup_anon_session_time > 0
-            if self.session_time is not None:
-                self.cleanup_anon_session_time = min(self.session_time,
-                                                     self.cleanup_anon_session_time)
-            interval = self.cleanup_anon_session_time / 2.
-        # we don't want to check session more than once every 5 minutes
-        self.clean_sessions_interval = max(5 * 60, interval)
-
-    def clean_sessions(self):
-        """cleanup sessions which has not been unused since a given amount of
-        time. Return the number of sessions which have been closed.
-        """
-        self.debug('cleaning http sessions')
-        session_time = self.session_time
-        closed, total = 0, 0
-        for session in self.current_sessions():
-            total += 1
-            last_usage_time = session.mtime
-            no_use_time = (time() - last_usage_time)
-            if session.anonymous_session:
-                if no_use_time >= self.cleanup_anon_session_time:
-                    self.close_session(session)
-                    closed += 1
-            elif session_time is not None and no_use_time >= session_time:
-                self.close_session(session)
-                closed += 1
-        return closed, total - closed
-
-    def current_sessions(self):
-        """return currently open sessions"""
-        raise NotImplementedError()
-
-    def get_session(self, req, sessionid):
-        """return existing session for the given session identifier"""
-        raise NotImplementedError()
-
-    def open_session(self, req):
-        """open and return a new session for the given request.
-
-        raise :exc:`cubicweb.AuthenticationError` if authentication failed
-        (no authentication info found or wrong user/password)
-        """
-        raise NotImplementedError()
-
-    def close_session(self, session):
-        """close session on logout or on invalid session detected (expired out,
-        corrupted...)
-        """
-        raise NotImplementedError()
-
-
-class AbstractAuthenticationManager(component.Component):
-    """authenticate user associated to a request and check session validity"""
-    __regid__ = 'authmanager'
-
-    def __init__(self, repo):
-        self.vreg = repo.vreg
-
-    def validate_session(self, req, session):
-        """check session validity, reconnecting it to the repository if the
-        associated connection expired in the repository side (hence the
-        necessity for this method).
-
-        raise :exc:`InvalidSession` if session is corrupted for a reason or
-        another and should be closed
-        """
-        raise NotImplementedError()
-
-    def authenticate(self, req):
-        """authenticate user using connection information found in the request,
-        and return corresponding a :class:`~cubicweb.dbapi.Connection` instance,
-        as well as login and authentication information dictionary used to open
-        the connection.
-
-        raise :exc:`cubicweb.AuthenticationError` if authentication failed
-        (no authentication info found or wrong user/password)
-        """
-        raise NotImplementedError()
 
 
 class CookieSessionHandler(object):
@@ -350,7 +261,7 @@
             try:
                 session = self.get_session(req)
                 from  cubicweb import repoapi
-                cnx = repoapi.ClientConnection(session)
+                cnx = repoapi.Connection(session)
                 req.set_cnx(cnx)
             except AuthenticationError:
                 # Keep the dummy session set at initialisation.
@@ -365,12 +276,6 @@
                 # several cubes like registration or forgotten password rely on
                 # this principle.
 
-            # DENY https acces for anonymous_user
-            if (req.https
-                and req.session.anonymous_session
-                and self.vreg.config['https-deny-anonymous']):
-                # don't allow anonymous on https connection
-                raise AuthenticationError()
             # nested try to allow LogOut to delegate logic to AuthenticationError
             # handler
             try:
--- a/web/component.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/component.py	Tue Jun 21 07:42:30 2016 +0200
@@ -353,7 +353,7 @@
         has some content to display. If not, you can still raise
         :exc:`EmptyComponent` to inform it should be skipped.
 
-        Also, :exc:`Unauthorized` will be catched, logged, then the component
+        Also, :exc:`Unauthorized` will be caught, logged, then the component
         will be skipped.
         """
         self.items = []
--- a/web/data/cubicweb.ajax.js	Mon May 09 17:24:03 2016 +0200
+++ b/web/data/cubicweb.ajax.js	Tue Jun 21 07:42:30 2016 +0200
@@ -39,7 +39,7 @@
     },
 
     addCallback: function(callback) {
-        if ((this._req.readyState == 4) && this._result) {
+        if (this._req && (this._req.readyState == 4) && this._result) {
             var args = [this._result, this._req];
             jQuery.merge(args, cw.utils.sliceList(arguments, 1));
             callback.apply(null, args);
@@ -51,7 +51,7 @@
     },
 
     addErrback: function(callback) {
-        if (this._req.readyState == 4 && this._error) {
+        if (this._req && this._req.readyState == 4 && this._error) {
             callback.apply(null, [this._error, this._req]);
         }
         else {
@@ -275,9 +275,6 @@
     if (typeof roundedCorners != 'undefined') {
         roundedCorners(node);
     }
-    if (typeof setFormsTarget != 'undefined') {
-        setFormsTarget(node);
-    }
     _loadDynamicFragments(node);
     jQuery(cw).trigger('server-response', [true, node]);
     jQuery(node).trigger('server-response', [true, node]);
@@ -503,7 +500,7 @@
 function unloadPageData() {
     // NOTE: do not make async calls on unload if you want to avoid
     //       strange bugs
-    loadRemote(AJAX_BASE_URL, ajaxFuncArgs('unload_page_data'), 'GET', true);
+    loadRemote(AJAX_BASE_URL, ajaxFuncArgs('unload_page_data'), 'POST', true);
 }
 
 function removeBookmark(beid) {
@@ -518,59 +515,6 @@
     });
 }
 
-userCallback = cw.utils.deprecatedFunction(
-    '[3.19] use a plain ajaxfunc instead of user callbacks',
-    function userCallback(cbname) {
-    setProgressCursor();
-    var d = loadRemote(AJAX_BASE_URL, ajaxFuncArgs('user_callback', null, cbname));
-    d.addCallback(resetCursor);
-    d.addErrback(resetCursor);
-    d.addErrback(remoteCallFailed);
-    return d;
-});
-
-userCallbackThenUpdateUI = cw.utils.deprecatedFunction(
-    '[3.19] use a plain ajaxfunc instead of user callbacks',
-    function userCallbackThenUpdateUI(cbname, compid, rql, msg, registry, nodeid) {
-    var d = userCallback(cbname);
-    d.addCallback(function() {
-        $('#' + nodeid).loadxhtml(AJAX_BASE_URL, ajaxFuncArgs('render', {'rql': rql},
-                                                       registry, compid), null, 'swap');
-        if (msg) {
-            updateMessage(msg);
-        }
-    });
-});
-
-userCallbackThenReloadPage = cw.utils.deprecatedFunction(
-    '[3.19] use a plain ajaxfunc instead of user callbacks',
-    function userCallbackThenReloadPage(cbname, msg) {
-    var d = userCallback(cbname);
-    d.addCallback(function() {
-        window.location.reload();
-        if (msg) {
-            updateMessage(msg);
-        }
-    });
-});
-
-/**
- * .. function:: unregisterUserCallback(cbname)
- *
- * unregisters the python function registered on the server's side
- * while the page was generated.
- */
-unregisterUserCallback = cw.utils.deprecatedFunction(
-    '[3.19] use a plain ajaxfunc instead of user callbacks',
-    function unregisterUserCallback(cbname) {
-    setProgressCursor();
-    var d = loadRemote(AJAX_BASE_URL, ajaxFuncArgs('unregister_user_callback',
-                                            null, cbname));
-    d.addCallback(resetCursor);
-    d.addErrback(resetCursor);
-    d.addErrback(remoteCallFailed);
-});
-
 
 //============= XXX move those functions? ====================================//
 function openHash() {
--- a/web/data/cubicweb.edition.js	Mon May 09 17:24:03 2016 +0200
+++ b/web/data/cubicweb.edition.js	Tue Jun 21 07:42:30 2016 +0200
@@ -171,7 +171,7 @@
     var entityForm = jQuery('#entityForm');
     var oid = optionNode.id.substring(2); // option id is prefixed by "id"
     loadRemote(AJAX_BASE_URL, ajaxFuncArgs('add_pending_inserts', null,
-                                           [oid.split(':')]), 'GET', true);
+                                           [oid.split(':')]), 'POST', true);
     var selectNode = optionNode.parentNode;
     // remove option node
     selectNode.removeChild(optionNode);
@@ -537,53 +537,6 @@
 }
 
 /**
- * .. function:: setFormsTarget(node)
- *
- * called on load to set target and iframeso object.
- *
- * .. note::
- *
- *    This was a hack to make form loop handling XHTML compliant.
- *    Since we do not care about xhtml any longer, this may go away.
- *
- * .. note::
- *
- *   `object` nodes might be a potential replacement for iframes
- *
- * .. note::
- *
- *    The form's `target` attribute should probably become a simple data-target
- *    immediately generated server-side.
- *    Since we don't do xhtml any longer, the iframe should probably be either
- *    reconsidered or at least emitted server-side.
- */
-function setFormsTarget(node) {
-    var $node = jQuery(node || document.body);
-    $node.find('form').each(function() {
-        var form = jQuery(this);
-        var target = form.attr('cubicweb:target');
-        if (target) {
-            form.attr('target', target);
-            /* do not use display: none because some browsers ignore iframe
-             * with no display */
-            form.append(IFRAME({
-                name: target,
-                id: target,
-                src: 'javascript: void(0)',
-                width: '0px',
-                height: '0px'
-            }));
-            form.removeAttr('cubicweb:target'); // useles from now on, pop it
-                                                // to make IE9 happy
-        }
-    });
-}
-
-jQuery(document).ready(function() {
-    setFormsTarget();
-});
-
-/**
  * .. function:: validateForm(formid, action, onsuccess, onfailure)
  *
  * called on traditionnal form submission : the idea is to try
--- a/web/data/cubicweb.htmlhelpers.js	Mon May 09 17:24:03 2016 +0200
+++ b/web/data/cubicweb.htmlhelpers.js	Tue Jun 21 07:42:30 2016 +0200
@@ -39,7 +39,7 @@
  */
 function resetCursor(result) {
     var body = document.getElementsByTagName('body')[0];
-    body.style.cursor = 'default';
+    body.style.cursor = '';
     // pass result to next callback in the callback chain
     return result;
 }
--- a/web/data/cubicweb.js	Mon May 09 17:24:03 2016 +0200
+++ b/web/data/cubicweb.js	Tue Jun 21 07:42:30 2016 +0200
@@ -16,7 +16,7 @@
     detachEvent: function() {},
 
     log: function log() {
-        if (typeof(window) != "undefined" && window.console && window.console.log) {
+        if (typeof window !== "undefined" && window.console && window.console.log) {
             // NOTE console.log requires "console" to be the console to be "this"
             window.console.log.apply(console, arguments);
         }
@@ -33,7 +33,7 @@
      * safe version of jQuery('#nodeid') because we use ':' in nodeids
      * which messes with jQuery selection mechanism
      */
-        if (typeof(node) == 'string') {
+        if (typeof node === 'string') {
             node = document.getElementById(node);
         }
         if (node) {
@@ -44,7 +44,7 @@
 
     // escapes string selectors (e.g. "foo.[subject]:42" -> "foo\.\[subject\]\:42"
     escape: function(selector) {
-        if (typeof(selector) == 'string') {
+        if (typeof selector === 'string') {
             return  selector.replace( /(:|\.|\[|\])/g, "\\$1" );
         }
         // cw.log('non string selector', selector);
@@ -52,7 +52,7 @@
     },
 
     getNode: function (node) {
-        if (typeof(node) == 'string') {
+        if (typeof node === 'string') {
             return document.getElementById(node);
         }
         return node;
@@ -69,7 +69,7 @@
     },
 
     urlEncode: function (str) {
-        if (typeof(encodeURIComponent) != "undefined") {
+        if (typeof encodeURIComponent !== "undefined") {
             return encodeURIComponent(str).replace(/\'/g, '%27');
         } else {
             return escape(str).replace(/\+/g, '%2B').replace(/\"/g, '%22').
@@ -93,7 +93,7 @@
         var $node = $(node);
         var sortvalue = $node.attr('cubicweb:sortvalue');
         // No metadata found, use cell content as sort key
-        if (sortvalue === undefined) {
+        if (typeof sortvalue === 'undefined') {
             return $node.text();
         }
         return cw.evalJSON(sortvalue);
@@ -117,9 +117,9 @@
             var node = document.createElement(tag);
             for (key in params) {
                 var value = params[key];
-                if (key.substring(0, 2) == 'on') {
+                if (key.substring(0, 2) === 'on') {
                     // this is an event handler definition
-                    if (typeof value == 'string') {
+                    if (typeof value === 'string') {
                         // litteral definition
                         value = new Function(value);
                     }
@@ -142,7 +142,7 @@
                 }
                 for (var i = 0; i < children.length; i++) {
                     var child = children[i];
-                    if (typeof child == "string" || typeof child == "number") {
+                    if (typeof child === "string" || typeof child === "number") {
                         child = document.createTextNode(child);
                     }
                     node.appendChild(child);
@@ -158,7 +158,7 @@
      *
      */
     toISOTimestamp: function (date) {
-        if (typeof(date) == "undefined" || date === null) {
+        if (date == null) {
             return null;
         }
 
@@ -188,11 +188,11 @@
     },
 
     isArray: function (it) { // taken from dojo
-        return it && (it instanceof Array || typeof it == "array");
+        return it && (it instanceof Array || typeof it === "array");
     },
 
     isString: function (it) { // taken from dojo
-        return !!arguments.length && it != null && (typeof it == "string" || it instanceof String);
+        return !!arguments.length && it != null && (typeof it === "string" || it instanceof String);
     },
 
     isArrayLike: function (it) { // taken from dojo
@@ -405,11 +405,11 @@
         var node = document.createElement('iframe');
     }
     for (key in params) {
-        if (key != 'name') {
+        if (key !== 'name') {
             var value = params[key];
-            if (key.substring(0, 2) == 'on') {
+            if (key.substring(0, 2) === 'on') {
                 // this is an event handler definition
-                if (typeof value == 'string') {
+                if (typeof value === 'string') {
                     // litteral definition
                     value = new Function(value);
                 }
--- a/web/data/cubicweb.preferences.js	Mon May 09 17:24:03 2016 +0200
+++ b/web/data/cubicweb.preferences.js	Tue Jun 21 07:42:30 2016 +0200
@@ -45,7 +45,7 @@
 function validatePrefsForm(formid) {
     clearPreviousMessages();
     _clearPreviousErrors(formid);
-    return validateForm(formid, null, submitSucces, submitFailure);
+    return validateForm(formid, null, submitSuccess, submitFailure);
 }
 
 function submitFailure(result, formid, cbargs) {
@@ -59,13 +59,13 @@
     return false; // so handleFormValidationResponse doesn't try to display error
 }
 
-function submitSucces(result, formid, cbargs) {
+function submitSuccess(result, formid, cbargs) {
     var $form = jQuery('#' + formid);
     setCurrentValues($form);
     var dom = DIV({'class': 'msg'}, _("changes applied"));
     $form.find('div.formsg').empty().append(dom);
     $form.find('input').removeClass('changed');
-    checkValues(form, true);
+    checkValues($form, true);
     return;
 }
 
--- a/web/data/cubicweb.timeline-bundle.js	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,10129 +0,0 @@
-/**
- *  This file contains timeline utilities
- *  :organization: Logilab
- */
-
-var SimileAjax_urlPrefix = BASE_URL + 'data/';
-var Timeline_urlPrefix = BASE_URL + 'data/';
-
-/*
- *  Simile Ajax API
- *
- *  Include this file in your HTML file as follows::
- *
- *    <script src="http://simile.mit.edu/ajax/api/simile-ajax-api.js" type="text/javascript"></script>
- *
- *
- */
-
-if (typeof SimileAjax == "undefined") {
-    var SimileAjax = {
-        loaded:                 false,
-        loadingScriptsCount:    0,
-        error:                  null,
-        params:                 { bundle:"true" }
-    };
-
-    SimileAjax.Platform = new Object();
-        /*
-            HACK: We need these 2 things here because we cannot simply append
-            a <script> element containing code that accesses SimileAjax.Platform
-            to initialize it because IE executes that <script> code first
-            before it loads ajax.js and platform.js.
-        */
-
-    var getHead = function(doc) {
-        return doc.getElementsByTagName("head")[0];
-    };
-
-    SimileAjax.findScript = function(doc, substring) {
-        var heads = doc.documentElement.getElementsByTagName("head");
-        for (var h = 0; h < heads.length; h++) {
-            var node = heads[h].firstChild;
-            while (node != null) {
-                if (node.nodeType == 1 && node.tagName.toLowerCase() == "script") {
-                    var url = node.src;
-                    var i = url.indexOf(substring);
-                    if (i >= 0) {
-                        return url;
-                    }
-                }
-                node = node.nextSibling;
-            }
-        }
-        return null;
-    };
-    SimileAjax.includeJavascriptFile = function(doc, url, onerror, charset) {
-        onerror = onerror || "";
-        if (doc.body == null) {
-            try {
-                var q = "'" + onerror.replace( /'/g, '&apos' ) + "'"; // "
-                doc.write("<script src='" + url + "' onerror="+ q +
-                          (charset ? " charset='"+ charset +"'" : "") +
-                          " type='text/javascript'>"+ onerror + "</script>");
-                return;
-            } catch (e) {
-                // fall through
-            }
-        }
-
-        var script = doc.createElement("script");
-        if (onerror) {
-            try { script.innerHTML = onerror; } catch(e) {}
-            script.setAttribute("onerror", onerror);
-        }
-        if (charset) {
-            script.setAttribute("charset", charset);
-        }
-        script.type = "text/javascript";
-        script.language = "JavaScript";
-        script.src = url;
-        return getHead(doc).appendChild(script);
-    };
-    SimileAjax.includeJavascriptFiles = function(doc, urlPrefix, filenames) {
-        for (var i = 0; i < filenames.length; i++) {
-            SimileAjax.includeJavascriptFile(doc, urlPrefix + filenames[i]);
-        }
-        SimileAjax.loadingScriptsCount += filenames.length;
-        // XXX adim SimileAjax.includeJavascriptFile(doc, SimileAjax.urlPrefix + "scripts/signal.js?" + filenames.length);
-    };
-    SimileAjax.includeCssFile = function(doc, url) {
-        if (doc.body == null) {
-            try {
-                doc.write("<link rel='stylesheet' href='" + url + "' type='text/css'/>");
-                return;
-            } catch (e) {
-                // fall through
-            }
-        }
-
-        var link = doc.createElement("link");
-        link.setAttribute("rel", "stylesheet");
-        link.setAttribute("type", "text/css");
-        link.setAttribute("href", url);
-        getHead(doc).appendChild(link);
-    };
-    SimileAjax.includeCssFiles = function(doc, urlPrefix, filenames) {
-        for (var i = 0; i < filenames.length; i++) {
-            SimileAjax.includeCssFile(doc, urlPrefix + filenames[i]);
-        }
-    };
-
-    /**
-     * Append into urls each string in suffixes after prefixing it with urlPrefix.
-     * @param {Array} urls
-     * @param {String} urlPrefix
-     * @param {Array} suffixes
-     */
-    SimileAjax.prefixURLs = function(urls, urlPrefix, suffixes) {
-        for (var i = 0; i < suffixes.length; i++) {
-            urls.push(urlPrefix + suffixes[i]);
-        }
-    };
-
-    /**
-     * Parse out the query parameters from a URL
-     * @param {String} url    the url to parse, or location.href if undefined
-     * @param {Object} to     optional object to extend with the parameters
-     * @param {Object} types  optional object mapping keys to value types
-     *        (String, Number, Boolean or Array, String by default)
-     * @return a key/value Object whose keys are the query parameter names
-     * @type Object
-     */
-    SimileAjax.parseURLParameters = function(url, to, types) {
-        to = to || {};
-        types = types || {};
-
-        if (typeof url == "undefined") {
-            url = location.href;
-        }
-        var q = url.indexOf("?");
-        if (q < 0) {
-            return to;
-        }
-        url = (url+"#").slice(q+1, url.indexOf("#")); // toss the URL fragment
-
-        var params = url.split("&"), param, parsed = {};
-        var decode = window.decodeURIComponent || unescape;
-        for (var i = 0; param = params[i]; i++) {
-            var eq = param.indexOf("=");
-            var name = decode(param.slice(0,eq));
-            var old = parsed[name];
-            if (typeof old == "undefined") {
-                old = [];
-            } else if (!(old instanceof Array)) {
-                old = [old];
-            }
-            parsed[name] = old.concat(decode(param.slice(eq+1)));
-        }
-        for (var i in parsed) {
-            if (!parsed.hasOwnProperty(i)) continue;
-            var type = types[i] || String;
-            var data = parsed[i];
-            if (!(data instanceof Array)) {
-                data = [data];
-            }
-            if (type === Boolean && data[0] == "false") {
-                to[i] = false; // because Boolean("false") === true
-            } else {
-                to[i] = type.apply(this, data);
-            }
-        }
-        return to;
-    };
-
-    (function() {
-        var javascriptFiles = [
-            "jquery-1.2.6.js",
-            "platform.js",
-            "debug.js",
-            "xmlhttp.js",
-            "json.js",
-            "dom.js",
-            "graphics.js",
-            "date-time.js",
-            "string.js",
-            "html.js",
-            "data-structure.js",
-            "units.js",
-
-            "ajax.js",
-            "history.js",
-            "window-manager.js"
-        ];
-        var cssFiles = [
-            "graphics.css"
-        ];
-
-        if (typeof SimileAjax_urlPrefix == "string") {
-            SimileAjax.urlPrefix = SimileAjax_urlPrefix;
-        } else {
-            var url = SimileAjax.findScript(document, "simile-ajax-api.js");
-            if (url == null) {
-                SimileAjax.error = new Error("Failed to derive URL prefix for Simile Ajax API code files");
-                return;
-            }
-
-            SimileAjax.urlPrefix = url.substr(0, url.indexOf("simile-ajax-api.js"));
-        }
-
-        SimileAjax.parseURLParameters(url, SimileAjax.params, {bundle:Boolean});
-//         if (SimileAjax.params.bundle) {
-//             SimileAjax.includeJavascriptFiles(document, SimileAjax.urlPrefix, [ "simile-ajax-bundle.js" ]);
-//         } else {
-//             SimileAjax.includeJavascriptFiles(document, SimileAjax.urlPrefix + "scripts/", javascriptFiles);
-//         }
-//         SimileAjax.includeCssFiles(document, SimileAjax.urlPrefix + "styles/", cssFiles);
-
-        SimileAjax.loaded = true;
-    })();
-}
-/*
- *  Platform Utility Functions and Constants
- *
- */
-
-/*  This must be called after our jQuery has been loaded
-    but before control returns to user-code.
-*/
-SimileAjax.jQuery = jQuery;
-// SimileAjax.jQuery = jQuery.noConflict(true);
-if (typeof window["$"] == "undefined") {
-    window.$ = SimileAjax.jQuery;
-}
-
-SimileAjax.Platform.os = {
-    isMac:   false,
-    isWin:   false,
-    isWin32: false,
-    isUnix:  false
-};
-SimileAjax.Platform.browser = {
-    isIE:           false,
-    isNetscape:     false,
-    isMozilla:      false,
-    isFirefox:      false,
-    isOpera:        false,
-    isSafari:       false,
-
-    majorVersion:   0,
-    minorVersion:   0
-};
-
-(function() {
-    var an = navigator.appName.toLowerCase();
-	var ua = navigator.userAgent.toLowerCase();
-
-    /*
-     *  Operating system
-     */
-	SimileAjax.Platform.os.isMac = (ua.indexOf('mac') != -1);
-	SimileAjax.Platform.os.isWin = (ua.indexOf('win') != -1);
-	SimileAjax.Platform.os.isWin32 = SimileAjax.Platform.isWin && (
-        ua.indexOf('95') != -1 ||
-        ua.indexOf('98') != -1 ||
-        ua.indexOf('nt') != -1 ||
-        ua.indexOf('win32') != -1 ||
-        ua.indexOf('32bit') != -1
-    );
-	SimileAjax.Platform.os.isUnix = (ua.indexOf('x11') != -1);
-
-    /*
-     *  Browser
-     */
-    SimileAjax.Platform.browser.isIE = (an.indexOf("microsoft") != -1);
-    SimileAjax.Platform.browser.isNetscape = (an.indexOf("netscape") != -1);
-    SimileAjax.Platform.browser.isMozilla = (ua.indexOf("mozilla") != -1);
-    SimileAjax.Platform.browser.isFirefox = (ua.indexOf("firefox") != -1);
-    SimileAjax.Platform.browser.isOpera = (an.indexOf("opera") != -1);
-    SimileAjax.Platform.browser.isSafari = (an.indexOf("safari") != -1);
-
-    var parseVersionString = function(s) {
-        var a = s.split(".");
-        SimileAjax.Platform.browser.majorVersion = parseInt(a[0]);
-        SimileAjax.Platform.browser.minorVersion = parseInt(a[1]);
-    };
-    var indexOf = function(s, sub, start) {
-        var i = s.indexOf(sub, start);
-        return i >= 0 ? i : s.length;
-    };
-
-    if (SimileAjax.Platform.browser.isMozilla) {
-        var offset = ua.indexOf("mozilla/");
-        if (offset >= 0) {
-            parseVersionString(ua.substring(offset + 8, indexOf(ua, " ", offset)));
-        }
-    }
-    if (SimileAjax.Platform.browser.isIE) {
-        var offset = ua.indexOf("msie ");
-        if (offset >= 0) {
-            parseVersionString(ua.substring(offset + 5, indexOf(ua, ";", offset)));
-        }
-    }
-    if (SimileAjax.Platform.browser.isNetscape) {
-        var offset = ua.indexOf("rv:");
-        if (offset >= 0) {
-            parseVersionString(ua.substring(offset + 3, indexOf(ua, ")", offset)));
-        }
-    }
-    if (SimileAjax.Platform.browser.isFirefox) {
-        var offset = ua.indexOf("firefox/");
-        if (offset >= 0) {
-            parseVersionString(ua.substring(offset + 8, indexOf(ua, " ", offset)));
-        }
-    }
-
-    if (!("localeCompare" in String.prototype)) {
-        String.prototype.localeCompare = function (s) {
-            if (this < s) return -1;
-            else if (this > s) return 1;
-            else return 0;
-        };
-    }
-})();
-
-SimileAjax.Platform.getDefaultLocale = function() {
-    return SimileAjax.Platform.clientLocale;
-};
-/*
- *  Debug Utility Functions
- *
- */
-
-SimileAjax.Debug = {
-    silent: false
-};
-
-SimileAjax.Debug.log = function(msg) {
-    var f;
-    if ("console" in window && "log" in window.console) { // FireBug installed
-        f = function(msg2) {
-            console.log(msg2);
-        }
-    } else {
-        f = function(msg2) {
-            if (!SimileAjax.Debug.silent) {
-                alert(msg2);
-            }
-        }
-    }
-    SimileAjax.Debug.log = f;
-    f(msg);
-};
-
-SimileAjax.Debug.warn = function(msg) {
-    var f;
-    if ("console" in window && "warn" in window.console) { // FireBug installed
-        f = function(msg2) {
-            console.warn(msg2);
-        }
-    } else {
-        f = function(msg2) {
-            if (!SimileAjax.Debug.silent) {
-                alert(msg2);
-            }
-        }
-    }
-    SimileAjax.Debug.warn = f;
-    f(msg);
-};
-
-SimileAjax.Debug.exception = function(e, msg) {
-    var f, params = SimileAjax.parseURLParameters();
-    if (params.errors == "throw" || SimileAjax.params.errors == "throw") {
-        f = function(e2, msg2) {
-            throw(e2); // do not hide from browser's native debugging features
-        };
-    } else if ("console" in window && "error" in window.console) { // FireBug installed
-        f = function(e2, msg2) {
-            if (msg2 != null) {
-                console.error(msg2 + " %o", e2);
-            } else {
-                console.error(e2);
-            }
-            throw(e2); // do not hide from browser's native debugging features
-        };
-    } else {
-        f = function(e2, msg2) {
-            if (!SimileAjax.Debug.silent) {
-                alert("Caught exception: " + msg2 + "\n\nDetails: " + ("description" in e2 ? e2.description : e2));
-            }
-            throw(e2); // do not hide from browser's native debugging features
-        };
-    }
-    SimileAjax.Debug.exception = f;
-    f(e, msg);
-};
-
-SimileAjax.Debug.objectToString = function(o) {
-    return SimileAjax.Debug._objectToString(o, "");
-};
-
-SimileAjax.Debug._objectToString = function(o, indent) {
-    var indent2 = indent + " ";
-    if (typeof o == "object") {
-        var s = "{";
-        for (n in o) {
-            s += indent2 + n + ": " + SimileAjax.Debug._objectToString(o[n], indent2) + "\n";
-        }
-        s += indent + "}";
-        return s;
-    } else if (typeof o == "array") {
-        var s = "[";
-        for (var n = 0; n < o.length; n++) {
-            s += SimileAjax.Debug._objectToString(o[n], indent2) + "\n";
-        }
-        s += indent + "]";
-        return s;
-    } else {
-        return o;
-    }
-};
-/**
- * @fileOverview XmlHttp utility functions
- * @name SimileAjax.XmlHttp
- */
-
-SimileAjax.XmlHttp = new Object();
-
-/**
- *  Callback for XMLHttp onRequestStateChange.
- */
-SimileAjax.XmlHttp._onReadyStateChange = function(xmlhttp, fError, fDone) {
-    switch (xmlhttp.readyState) {
-    // 1: Request not yet made
-    // 2: Contact established with server but nothing downloaded yet
-    // 3: Called multiple while downloading in progress
-
-    // Download complete
-    case 4:
-        try {
-            if (xmlhttp.status == 0     // file:// urls, works on Firefox
-             || xmlhttp.status == 200   // http:// urls
-            ) {
-                if (fDone) {
-                    fDone(xmlhttp);
-                }
-            } else {
-                if (fError) {
-                    fError(
-                        xmlhttp.statusText,
-                        xmlhttp.status,
-                        xmlhttp
-                    );
-                }
-            }
-        } catch (e) {
-            SimileAjax.Debug.exception("XmlHttp: Error handling onReadyStateChange", e);
-        }
-        break;
-    }
-};
-
-/**
- *  Creates an XMLHttpRequest object. On the first run, this
- *  function creates a platform-specific function for
- *  instantiating an XMLHttpRequest object and then replaces
- *  itself with that function.
- */
-SimileAjax.XmlHttp._createRequest = function() {
-    if (SimileAjax.Platform.browser.isIE) {
-        var programIDs = [
-        "Msxml2.XMLHTTP",
-        "Microsoft.XMLHTTP",
-        "Msxml2.XMLHTTP.4.0"
-        ];
-        for (var i = 0; i < programIDs.length; i++) {
-            try {
-                var programID = programIDs[i];
-                var f = function() {
-                    return new ActiveXObject(programID);
-                };
-                var o = f();
-
-                // We are replacing the SimileAjax._createXmlHttpRequest
-                // function with this inner function as we've
-                // found out that it works. This is so that we
-                // don't have to do all the testing over again
-                // on subsequent calls.
-                SimileAjax.XmlHttp._createRequest = f;
-
-                return o;
-            } catch (e) {
-                // silent
-            }
-        }
-        // fall through to try new XMLHttpRequest();
-    }
-
-    try {
-        var f = function() {
-            return new XMLHttpRequest();
-        };
-        var o = f();
-
-        // We are replacing the SimileAjax._createXmlHttpRequest
-        // function with this inner function as we've
-        // found out that it works. This is so that we
-        // don't have to do all the testing over again
-        // on subsequent calls.
-        SimileAjax.XmlHttp._createRequest = f;
-
-        return o;
-    } catch (e) {
-        throw new Error("Failed to create an XMLHttpRequest object");
-    }
-};
-
-/**
- * Performs an asynchronous HTTP GET.
- *
- * @param {Function} fError a function of the form
-     function(statusText, statusCode, xmlhttp)
- * @param {Function} fDone a function of the form function(xmlhttp)
- */
-SimileAjax.XmlHttp.get = function(url, fError, fDone) {
-    var xmlhttp = SimileAjax.XmlHttp._createRequest();
-
-    xmlhttp.open("GET", url, true);
-    xmlhttp.onreadystatechange = function() {
-        SimileAjax.XmlHttp._onReadyStateChange(xmlhttp, fError, fDone);
-    };
-    xmlhttp.send(null);
-};
-
-/**
- * Performs an asynchronous HTTP POST.
- *
- * @param {Function} fError a function of the form
-     function(statusText, statusCode, xmlhttp)
- * @param {Function} fDone a function of the form function(xmlhttp)
- */
-SimileAjax.XmlHttp.post = function(url, body, fError, fDone) {
-    var xmlhttp = SimileAjax.XmlHttp._createRequest();
-
-    xmlhttp.open("POST", url, true);
-    xmlhttp.onreadystatechange = function() {
-        SimileAjax.XmlHttp._onReadyStateChange(xmlhttp, fError, fDone);
-    };
-    xmlhttp.send(body);
-};
-
-SimileAjax.XmlHttp._forceXML = function(xmlhttp) {
-    try {
-        xmlhttp.overrideMimeType("text/xml");
-    } catch (e) {
-        xmlhttp.setrequestheader("Content-Type", "text/xml");
-    }
-};/*
- *  Copied directly from http://www.json.org/json.js.
- */
-
-/*
-    json.js
-    2006-04-28
-
-    This file adds these methods to JavaScript:
-
-        object.toJSONString()
-
-            This method produces a JSON text from an object. The
-            object must not contain any cyclical references.
-
-        array.toJSONString()
-
-            This method produces a JSON text from an array. The
-            array must not contain any cyclical references.
-
-        string.parseJSON()
-
-            This method parses a JSON text to produce an object or
-            array. It will return false if there is an error.
-*/
-
-SimileAjax.JSON = new Object();
-
-(function () {
-    var m = {
-        '\b': '\\b',
-        '\t': '\\t',
-        '\n': '\\n',
-        '\f': '\\f',
-        '\r': '\\r',
-        '"' : '\\"',
-        '\\': '\\\\'
-    };
-    var s = {
-        array: function (x) {
-            var a = ['['], b, f, i, l = x.length, v;
-            for (i = 0; i < l; i += 1) {
-                v = x[i];
-                f = s[typeof v];
-                if (f) {
-                    v = f(v);
-                    if (typeof v == 'string') {
-                        if (b) {
-                            a[a.length] = ',';
-                        }
-                        a[a.length] = v;
-                        b = true;
-                    }
-                }
-            }
-            a[a.length] = ']';
-            return a.join('');
-        },
-        'boolean': function (x) {
-            return String(x);
-        },
-        'null': function (x) {
-            return "null";
-        },
-        number: function (x) {
-            return isFinite(x) ? String(x) : 'null';
-        },
-        object: function (x) {
-            if (x) {
-                if (x instanceof Array) {
-                    return s.array(x);
-                }
-                var a = ['{'], b, f, i, v;
-                for (i in x) {
-                    v = x[i];
-                    f = s[typeof v];
-                    if (f) {
-                        v = f(v);
-                        if (typeof v == 'string') {
-                            if (b) {
-                                a[a.length] = ',';
-                            }
-                            a.push(s.string(i), ':', v);
-                            b = true;
-                        }
-                    }
-                }
-                a[a.length] = '}';
-                return a.join('');
-            }
-            return 'null';
-        },
-        string: function (x) {
-            if (/["\\\x00-\x1f]/.test(x)) {
-                x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
-                    var c = m[b];
-                    if (c) {
-                        return c;
-                    }
-                    c = b.charCodeAt();
-                    return '\\u00' +
-                        Math.floor(c / 16).toString(16) +
-                        (c % 16).toString(16);
-                });
-            }
-            return '"' + x + '"';
-        }
-    };
-
-    SimileAjax.JSON.toJSONString = function(o) {
-        if (o instanceof Object) {
-            return s.object(o);
-        } else if (o instanceof Array) {
-            return s.array(o);
-        } else {
-            return o.toString();
-        }
-    };
-
-    SimileAjax.JSON.parseJSON = function () {
-        try {
-            return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
-                    this.replace(/"(\\.|[^"\\])*"/g, ''))) &&
-                eval('(' + this + ')');
-        } catch (e) {
-            return false;
-        }
-    };
-})();
-/*
- *  DOM Utility Functions
- *
- */
-
-SimileAjax.DOM = new Object();
-
-SimileAjax.DOM.registerEventWithObject = function(elmt, eventName, obj, handlerName) {
-    SimileAjax.DOM.registerEvent(elmt, eventName, function(elmt2, evt, target) {
-        return obj[handlerName].call(obj, elmt2, evt, target);
-    });
-};
-
-SimileAjax.DOM.registerEvent = function(elmt, eventName, handler) {
-    var handler2 = function(evt) {
-        evt = (evt) ? evt : ((event) ? event : null);
-        if (evt) {
-            var target = (evt.target) ?
-                evt.target : ((evt.srcElement) ? evt.srcElement : null);
-            if (target) {
-                target = (target.nodeType == 1 || target.nodeType == 9) ?
-                    target : target.parentNode;
-            }
-
-            return handler(elmt, evt, target);
-        }
-        return true;
-    }
-
-    if (SimileAjax.Platform.browser.isIE) {
-        elmt.attachEvent("on" + eventName, handler2);
-    } else {
-        elmt.addEventListener(eventName, handler2, false);
-    }
-};
-
-SimileAjax.DOM.getPageCoordinates = function(elmt) {
-    var left = 0;
-    var top = 0;
-
-    if (elmt.nodeType != 1) {
-        elmt = elmt.parentNode;
-    }
-
-    var elmt2 = elmt;
-    while (elmt2 != null) {
-        left += elmt2.offsetLeft;
-        top += elmt2.offsetTop;
-        elmt2 = elmt2.offsetParent;
-    }
-
-    var body = document.body;
-    while (elmt != null && elmt != body) {
-        if ("scrollLeft" in elmt) {
-            left -= elmt.scrollLeft;
-            top -= elmt.scrollTop;
-        }
-        elmt = elmt.parentNode;
-    }
-
-    return { left: left, top: top };
-};
-
-SimileAjax.DOM.getSize = function(elmt) {
-	var w = this.getStyle(elmt,"width");
-	var h = this.getStyle(elmt,"height");
-	if (w.indexOf("px") > -1) w = w.replace("px","");
-	if (h.indexOf("px") > -1) h = h.replace("px","");
-	return {
-		w: w,
-		h: h
-	}
-}
-
-SimileAjax.DOM.getStyle = function(elmt, styleProp) {
-    if (elmt.currentStyle) { // IE
-        var style = elmt.currentStyle[styleProp];
-    } else if (window.getComputedStyle) { // standard DOM
-        var style = document.defaultView.getComputedStyle(elmt, null).getPropertyValue(styleProp);
-    } else {
-    	var style = "";
-    }
-    return style;
-}
-
-SimileAjax.DOM.getEventRelativeCoordinates = function(evt, elmt) {
-    if (SimileAjax.Platform.browser.isIE) {
-      if (evt.type == "mousewheel") {
-        var coords = SimileAjax.DOM.getPageCoordinates(elmt);
-        return {
-          x: evt.clientX - coords.left,
-          y: evt.clientY - coords.top
-        };
-      } else {
-        return {
-          x: evt.offsetX,
-          y: evt.offsetY
-        };
-      }
-    } else {
-        var coords = SimileAjax.DOM.getPageCoordinates(elmt);
-
-        if ((evt.type == "DOMMouseScroll") &&
-          SimileAjax.Platform.browser.isFirefox &&
-          (SimileAjax.Platform.browser.majorVersion == 2)) {
-          // Due to: https://bugzilla.mozilla.org/show_bug.cgi?id=352179
-
-          return {
-            x: evt.screenX - coords.left,
-            y: evt.screenY - coords.top
-          };
-        } else {
-          return {
-              x: evt.pageX - coords.left,
-              y: evt.pageY - coords.top
-          };
-        }
-    }
-};
-
-SimileAjax.DOM.getEventPageCoordinates = function(evt) {
-    if (SimileAjax.Platform.browser.isIE) {
-        return {
-            x: evt.clientX + document.body.scrollLeft,
-            y: evt.clientY + document.body.scrollTop
-        };
-    } else {
-        return {
-            x: evt.pageX,
-            y: evt.pageY
-        };
-    }
-};
-
-SimileAjax.DOM.hittest = function(x, y, except) {
-    return SimileAjax.DOM._hittest(document.body, x, y, except);
-};
-
-SimileAjax.DOM._hittest = function(elmt, x, y, except) {
-    var childNodes = elmt.childNodes;
-    outer: for (var i = 0; i < childNodes.length; i++) {
-        var childNode = childNodes[i];
-        for (var j = 0; j < except.length; j++) {
-            if (childNode == except[j]) {
-                continue outer;
-            }
-        }
-
-        if (childNode.offsetWidth == 0 && childNode.offsetHeight == 0) {
-            /*
-             *  Sometimes SPAN elements have zero width and height but
-             *  they have children like DIVs that cover non-zero areas.
-             */
-            var hitNode = SimileAjax.DOM._hittest(childNode, x, y, except);
-            if (hitNode != childNode) {
-                return hitNode;
-            }
-        } else {
-            var top = 0;
-            var left = 0;
-
-            var node = childNode;
-            while (node) {
-                top += node.offsetTop;
-                left += node.offsetLeft;
-                node = node.offsetParent;
-            }
-
-            if (left <= x && top <= y && (x - left) < childNode.offsetWidth && (y - top) < childNode.offsetHeight) {
-                return SimileAjax.DOM._hittest(childNode, x, y, except);
-            } else if (childNode.nodeType == 1 && childNode.tagName == "TR") {
-                /*
-                 *  Table row might have cells that span several rows.
-                 */
-                var childNode2 = SimileAjax.DOM._hittest(childNode, x, y, except);
-                if (childNode2 != childNode) {
-                    return childNode2;
-                }
-            }
-        }
-    }
-    return elmt;
-};
-
-SimileAjax.DOM.cancelEvent = function(evt) {
-    evt.returnValue = false;
-    evt.cancelBubble = true;
-    if ("preventDefault" in evt) {
-        evt.preventDefault();
-    }
-};
-
-SimileAjax.DOM.appendClassName = function(elmt, className) {
-    var classes = elmt.className.split(" ");
-    for (var i = 0; i < classes.length; i++) {
-        if (classes[i] == className) {
-            return;
-        }
-    }
-    classes.push(className);
-    elmt.className = classes.join(" ");
-};
-
-SimileAjax.DOM.createInputElement = function(type) {
-    var div = document.createElement("div");
-    div.innerHTML = "<input type='" + type + "' />";
-
-    return div.firstChild;
-};
-
-SimileAjax.DOM.createDOMFromTemplate = function(template) {
-    var result = {};
-    result.elmt = SimileAjax.DOM._createDOMFromTemplate(template, result, null);
-
-    return result;
-};
-
-SimileAjax.DOM._createDOMFromTemplate = function(templateNode, result, parentElmt) {
-    if (templateNode == null) {
-        /*
-        var node = doc.createTextNode("--null--");
-        if (parentElmt != null) {
-            parentElmt.appendChild(node);
-        }
-        return node;
-        */
-        return null;
-    } else if (typeof templateNode != "object") {
-        var node = document.createTextNode(templateNode);
-        if (parentElmt != null) {
-            parentElmt.appendChild(node);
-        }
-        return node;
-    } else {
-        var elmt = null;
-        if ("tag" in templateNode) {
-            var tag = templateNode.tag;
-            if (parentElmt != null) {
-                if (tag == "tr") {
-                    elmt = parentElmt.insertRow(parentElmt.rows.length);
-                } else if (tag == "td") {
-                    elmt = parentElmt.insertCell(parentElmt.cells.length);
-                }
-            }
-            if (elmt == null) {
-                elmt = tag == "input" ?
-                    SimileAjax.DOM.createInputElement(templateNode.type) :
-                    document.createElement(tag);
-
-                if (parentElmt != null) {
-                    parentElmt.appendChild(elmt);
-                }
-            }
-        } else {
-            elmt = templateNode.elmt;
-            if (parentElmt != null) {
-                parentElmt.appendChild(elmt);
-            }
-        }
-
-        for (var attribute in templateNode) {
-            var value = templateNode[attribute];
-
-            if (attribute == "field") {
-                result[value] = elmt;
-
-            } else if (attribute == "className") {
-                elmt.className = value;
-            } else if (attribute == "id") {
-                elmt.id = value;
-            } else if (attribute == "title") {
-                elmt.title = value;
-            } else if (attribute == "type" && elmt.tagName == "input") {
-                // do nothing
-            } else if (attribute == "style") {
-                for (n in value) {
-                    var v = value[n];
-                    if (n == "float") {
-                        n = SimileAjax.Platform.browser.isIE ? "styleFloat" : "cssFloat";
-                    }
-                    elmt.style[n] = v;
-                }
-            } else if (attribute == "children") {
-                for (var i = 0; i < value.length; i++) {
-                    SimileAjax.DOM._createDOMFromTemplate(value[i], result, elmt);
-                }
-            } else if (attribute != "tag" && attribute != "elmt") {
-                elmt.setAttribute(attribute, value);
-            }
-        }
-        return elmt;
-    }
-}
-
-SimileAjax.DOM._cachedParent = null;
-SimileAjax.DOM.createElementFromString = function(s) {
-    if (SimileAjax.DOM._cachedParent == null) {
-        SimileAjax.DOM._cachedParent = document.createElement("div");
-    }
-    SimileAjax.DOM._cachedParent.innerHTML = s;
-    return SimileAjax.DOM._cachedParent.firstChild;
-};
-
-SimileAjax.DOM.createDOMFromString = function(root, s, fieldElmts) {
-    var elmt = typeof root == "string" ? document.createElement(root) : root;
-    elmt.innerHTML = s;
-
-    var dom = { elmt: elmt };
-    SimileAjax.DOM._processDOMChildrenConstructedFromString(dom, elmt, fieldElmts != null ? fieldElmts : {} );
-
-    return dom;
-};
-
-SimileAjax.DOM._processDOMConstructedFromString = function(dom, elmt, fieldElmts) {
-    var id = elmt.id;
-    if (id != null && id.length > 0) {
-        elmt.removeAttribute("id");
-        if (id in fieldElmts) {
-            var parentElmt = elmt.parentNode;
-            parentElmt.insertBefore(fieldElmts[id], elmt);
-            parentElmt.removeChild(elmt);
-
-            dom[id] = fieldElmts[id];
-            return;
-        } else {
-            dom[id] = elmt;
-        }
-    }
-
-    if (elmt.hasChildNodes()) {
-        SimileAjax.DOM._processDOMChildrenConstructedFromString(dom, elmt, fieldElmts);
-    }
-};
-
-SimileAjax.DOM._processDOMChildrenConstructedFromString = function(dom, elmt, fieldElmts) {
-    var node = elmt.firstChild;
-    while (node != null) {
-        var node2 = node.nextSibling;
-        if (node.nodeType == 1) {
-            SimileAjax.DOM._processDOMConstructedFromString(dom, node, fieldElmts);
-        }
-        node = node2;
-    }
-};
-/**
- * @fileOverview Graphics utility functions and constants
- * @name SimileAjax.Graphics
- */
-
-SimileAjax.Graphics = new Object();
-
-/**
- * A boolean value indicating whether PNG translucency is supported on the
- * user's browser or not.
- *
- * @type Boolean
- */
-SimileAjax.Graphics.pngIsTranslucent = (!SimileAjax.Platform.browser.isIE) || (SimileAjax.Platform.browser.majorVersion > 6);
-if (!SimileAjax.Graphics.pngIsTranslucent) {
-    SimileAjax.includeCssFile(document, SimileAjax.urlPrefix + "styles/graphics-ie6.css");
-}
-
-/*
- *  Opacity, translucency
- *
- */
-SimileAjax.Graphics._createTranslucentImage1 = function(url, verticalAlign) {
-    var elmt = document.createElement("img");
-    elmt.setAttribute("src", url);
-    if (verticalAlign != null) {
-        elmt.style.verticalAlign = verticalAlign;
-    }
-    return elmt;
-};
-SimileAjax.Graphics._createTranslucentImage2 = function(url, verticalAlign) {
-    var elmt = document.createElement("img");
-    elmt.style.width = "1px";  // just so that IE will calculate the size property
-    elmt.style.height = "1px";
-    elmt.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + url +"', sizingMethod='image')";
-    elmt.style.verticalAlign = (verticalAlign != null) ? verticalAlign : "middle";
-    return elmt;
-};
-
-/**
- * Creates a DOM element for an <code>img</code> tag using the URL given. This
- * is a convenience method that automatically includes the necessary CSS to
- * allow for translucency, even on IE.
- *
- * @function
- * @param {String} url the URL to the image
- * @param {String} verticalAlign the CSS value for the image's vertical-align
- * @return {Element} a DOM element containing the <code>img</code> tag
- */
-SimileAjax.Graphics.createTranslucentImage = SimileAjax.Graphics.pngIsTranslucent ?
-    SimileAjax.Graphics._createTranslucentImage1 :
-    SimileAjax.Graphics._createTranslucentImage2;
-
-SimileAjax.Graphics._createTranslucentImageHTML1 = function(url, verticalAlign) {
-    return "<img src=\"" + url + "\"" +
-        (verticalAlign != null ? " style=\"vertical-align: " + verticalAlign + ";\"" : "") +
-        " />";
-};
-SimileAjax.Graphics._createTranslucentImageHTML2 = function(url, verticalAlign) {
-    var style =
-        "width: 1px; height: 1px; " +
-        "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + url +"', sizingMethod='image');" +
-        (verticalAlign != null ? " vertical-align: " + verticalAlign + ";" : "");
-
-    return "<img src='" + url + "' style=\"" + style + "\" />";
-};
-
-/**
- * Creates an HTML string for an <code>img</code> tag using the URL given.
- * This is a convenience method that automatically includes the necessary CSS
- * to allow for translucency, even on IE.
- *
- * @function
- * @param {String} url the URL to the image
- * @param {String} verticalAlign the CSS value for the image's vertical-align
- * @return {String} a string containing the <code>img</code> tag
- */
-SimileAjax.Graphics.createTranslucentImageHTML = SimileAjax.Graphics.pngIsTranslucent ?
-    SimileAjax.Graphics._createTranslucentImageHTML1 :
-    SimileAjax.Graphics._createTranslucentImageHTML2;
-
-/**
- * Sets the opacity on the given DOM element.
- *
- * @param {Element} elmt the DOM element to set the opacity on
- * @param {Number} opacity an integer from 0 to 100 specifying the opacity
- */
-SimileAjax.Graphics.setOpacity = function(elmt, opacity) {
-    if (SimileAjax.Platform.browser.isIE) {
-        elmt.style.filter = "progid:DXImageTransform.Microsoft.Alpha(Style=0,Opacity=" + opacity + ")";
-    } else {
-        var o = (opacity / 100).toString();
-        elmt.style.opacity = o;
-        elmt.style.MozOpacity = o;
-    }
-};
-
-/*
- *  Bubble
- *
- */
-
-SimileAjax.Graphics.bubbleConfig = {
-    containerCSSClass:              "simileAjax-bubble-container",
-    innerContainerCSSClass:         "simileAjax-bubble-innerContainer",
-    contentContainerCSSClass:       "simileAjax-bubble-contentContainer",
-
-    borderGraphicSize:              50,
-    borderGraphicCSSClassPrefix:    "simileAjax-bubble-border-",
-
-    arrowGraphicTargetOffset:       33,  // from tip of arrow to the side of the graphic that touches the content of the bubble
-    arrowGraphicLength:             100, // dimension of arrow graphic along the direction that the arrow points
-    arrowGraphicWidth:              49,  // dimension of arrow graphic perpendicular to the direction that the arrow points
-    arrowGraphicCSSClassPrefix:     "simileAjax-bubble-arrow-",
-
-    closeGraphicCSSClass:           "simileAjax-bubble-close",
-
-    extraPadding:                   20
-};
-
-/**
- * Creates a nice, rounded bubble popup with the given content in a div,
- * page coordinates and a suggested width. The bubble will point to the
- * location on the page as described by pageX and pageY.  All measurements
- * should be given in pixels.
- *
- * @param {Element} the content div
- * @param {Number} pageX the x coordinate of the point to point to
- * @param {Number} pageY the y coordinate of the point to point to
- * @param {Number} contentWidth a suggested width of the content
- * @param {String} orientation a string ("top", "bottom", "left", or "right")
- *   that describes the orientation of the arrow on the bubble
- * @param {Number} maxHeight. Add a scrollbar div if bubble would be too tall.
- *   Default of 0 or null means no maximum
- */
-SimileAjax.Graphics.createBubbleForContentAndPoint = function(
-       div, pageX, pageY, contentWidth, orientation, maxHeight) {
-    if (typeof contentWidth != "number") {
-        contentWidth = 300;
-    }
-    if (typeof maxHeight != "number") {
-        maxHeight = 0;
-    }
-
-    div.style.position = "absolute";
-    div.style.left = "-5000px";
-    div.style.top = "0px";
-    div.style.width = contentWidth + "px";
-    document.body.appendChild(div);
-
-    window.setTimeout(function() {
-        var width = div.scrollWidth + 10;
-        var height = div.scrollHeight + 10;
-        var scrollDivW = 0; // width of the possible inner container when we want vertical scrolling
-        if (maxHeight > 0 && height > maxHeight) {
-          height = maxHeight;
-          scrollDivW = width - 25;
-        }
-
-        var bubble = SimileAjax.Graphics.createBubbleForPoint(pageX, pageY, width, height, orientation);
-
-        document.body.removeChild(div);
-        div.style.position = "static";
-        div.style.left = "";
-        div.style.top = "";
-
-        // create a scroll div if needed
-        if (scrollDivW > 0) {
-          var scrollDiv = document.createElement("div");
-          div.style.width = "";
-          scrollDiv.style.width = scrollDivW + "px";
-          scrollDiv.appendChild(div);
-          bubble.content.appendChild(scrollDiv);
-        } else {
-          div.style.width = width + "px";
-          bubble.content.appendChild(div);
-        }
-    }, 200);
-};
-
-/**
- * Creates a nice, rounded bubble popup with the given page coordinates and
- * content dimensions.  The bubble will point to the location on the page
- * as described by pageX and pageY.  All measurements should be given in
- * pixels.
- *
- * @param {Number} pageX the x coordinate of the point to point to
- * @param {Number} pageY the y coordinate of the point to point to
- * @param {Number} contentWidth the width of the content box in the bubble
- * @param {Number} contentHeight the height of the content box in the bubble
- * @param {String} orientation a string ("top", "bottom", "left", or "right")
- *   that describes the orientation of the arrow on the bubble
- * @return {Element} a DOM element for the newly created bubble
- */
-SimileAjax.Graphics.createBubbleForPoint = function(pageX, pageY, contentWidth, contentHeight, orientation) {
-    contentWidth = parseInt(contentWidth, 10); // harden against bad input bugs
-    contentHeight = parseInt(contentHeight, 10); // getting numbers-as-strings
-
-    var bubbleConfig = SimileAjax.Graphics.bubbleConfig;
-    var pngTransparencyClassSuffix =
-        SimileAjax.Graphics.pngIsTranslucent ? "pngTranslucent" : "pngNotTranslucent";
-
-    var bubbleWidth = contentWidth + 2 * bubbleConfig.borderGraphicSize;
-    var bubbleHeight = contentHeight + 2 * bubbleConfig.borderGraphicSize;
-
-    var generatePngSensitiveClass = function(className) {
-        return className + " " + className + "-" + pngTransparencyClassSuffix;
-    };
-
-    /*
-     *  Render container divs
-     */
-    var div = document.createElement("div");
-    div.className = generatePngSensitiveClass(bubbleConfig.containerCSSClass);
-    div.style.width = contentWidth + "px";
-    div.style.height = contentHeight + "px";
-
-    var divInnerContainer = document.createElement("div");
-    divInnerContainer.className = generatePngSensitiveClass(bubbleConfig.innerContainerCSSClass);
-    div.appendChild(divInnerContainer);
-
-    /*
-     *  Create layer for bubble
-     */
-    var close = function() {
-        if (!bubble._closed) {
-            document.body.removeChild(bubble._div);
-            bubble._doc = null;
-            bubble._div = null;
-            bubble._content = null;
-            bubble._closed = true;
-        }
-    }
-    var bubble = { _closed: false };
-    var layer = SimileAjax.WindowManager.pushLayer(close, true, div);
-    bubble._div = div;
-    bubble.close = function() { SimileAjax.WindowManager.popLayer(layer); }
-
-    /*
-     *  Render border graphics
-     */
-    var createBorder = function(classNameSuffix) {
-        var divBorderGraphic = document.createElement("div");
-        divBorderGraphic.className = generatePngSensitiveClass(bubbleConfig.borderGraphicCSSClassPrefix + classNameSuffix);
-        divInnerContainer.appendChild(divBorderGraphic);
-    };
-    createBorder("top-left");
-    createBorder("top-right");
-    createBorder("bottom-left");
-    createBorder("bottom-right");
-    createBorder("left");
-    createBorder("right");
-    createBorder("top");
-    createBorder("bottom");
-
-    /*
-     *  Render content
-     */
-    var divContentContainer = document.createElement("div");
-    divContentContainer.className = generatePngSensitiveClass(bubbleConfig.contentContainerCSSClass);
-    divInnerContainer.appendChild(divContentContainer);
-    bubble.content = divContentContainer;
-
-    /*
-     *  Render close button
-     */
-    var divClose = document.createElement("div");
-    divClose.className = generatePngSensitiveClass(bubbleConfig.closeGraphicCSSClass);
-    divInnerContainer.appendChild(divClose);
-    SimileAjax.WindowManager.registerEventWithObject(divClose, "click", bubble, "close");
-
-    (function() {
-        var dims = SimileAjax.Graphics.getWindowDimensions();
-        var docWidth = dims.w;
-        var docHeight = dims.h;
-
-        var halfArrowGraphicWidth = Math.ceil(bubbleConfig.arrowGraphicWidth / 2);
-
-        var createArrow = function(classNameSuffix) {
-            var divArrowGraphic = document.createElement("div");
-            divArrowGraphic.className = generatePngSensitiveClass(bubbleConfig.arrowGraphicCSSClassPrefix + "point-" + classNameSuffix);
-            divInnerContainer.appendChild(divArrowGraphic);
-            return divArrowGraphic;
-        };
-
-        if (pageX - halfArrowGraphicWidth - bubbleConfig.borderGraphicSize - bubbleConfig.extraPadding > 0 &&
-            pageX + halfArrowGraphicWidth + bubbleConfig.borderGraphicSize + bubbleConfig.extraPadding < docWidth) {
-
-            /*
-             *  Bubble can be positioned above or below the target point.
-             */
-
-            var left = pageX - Math.round(contentWidth / 2);
-            left = pageX < (docWidth / 2) ?
-                Math.max(left, bubbleConfig.extraPadding + bubbleConfig.borderGraphicSize) :
-                Math.min(left, docWidth - bubbleConfig.extraPadding - bubbleConfig.borderGraphicSize - contentWidth);
-
-            if ((orientation && orientation == "top") ||
-                (!orientation &&
-                    (pageY
-                        - bubbleConfig.arrowGraphicTargetOffset
-                        - contentHeight
-                        - bubbleConfig.borderGraphicSize
-                        - bubbleConfig.extraPadding > 0))) {
-
-                /*
-                 *  Position bubble above the target point.
-                 */
-
-                var divArrow = createArrow("down");
-                divArrow.style.left = (pageX - halfArrowGraphicWidth - left) + "px";
-
-                div.style.left = left + "px";
-                div.style.top = (pageY - bubbleConfig.arrowGraphicTargetOffset - contentHeight) + "px";
-
-                return;
-            } else if ((orientation && orientation == "bottom") ||
-                (!orientation &&
-                    (pageY
-                        + bubbleConfig.arrowGraphicTargetOffset
-                        + contentHeight
-                        + bubbleConfig.borderGraphicSize
-                        + bubbleConfig.extraPadding < docHeight))) {
-
-                /*
-                 *  Position bubble below the target point.
-                 */
-
-                var divArrow = createArrow("up");
-                divArrow.style.left = (pageX - halfArrowGraphicWidth - left) + "px";
-
-                div.style.left = left + "px";
-                div.style.top = (pageY + bubbleConfig.arrowGraphicTargetOffset) + "px";
-
-                return;
-            }
-        }
-
-        var top = pageY - Math.round(contentHeight / 2);
-        top = pageY < (docHeight / 2) ?
-            Math.max(top, bubbleConfig.extraPadding + bubbleConfig.borderGraphicSize) :
-            Math.min(top, docHeight - bubbleConfig.extraPadding - bubbleConfig.borderGraphicSize - contentHeight);
-
-        if ((orientation && orientation == "left") ||
-            (!orientation &&
-                (pageX
-                    - bubbleConfig.arrowGraphicTargetOffset
-                    - contentWidth
-                    - bubbleConfig.borderGraphicSize
-                    - bubbleConfig.extraPadding > 0))) {
-
-            /*
-             *  Position bubble left of the target point.
-             */
-
-            var divArrow = createArrow("right");
-            divArrow.style.top = (pageY - halfArrowGraphicWidth - top) + "px";
-
-            div.style.top = top + "px";
-            div.style.left = (pageX - bubbleConfig.arrowGraphicTargetOffset - contentWidth) + "px";
-        } else {
-
-            /*
-             *  Position bubble right of the target point, as the last resort.
-             */
-
-            var divArrow = createArrow("left");
-            divArrow.style.top = (pageY - halfArrowGraphicWidth - top) + "px";
-
-            div.style.top = top + "px";
-            div.style.left = (pageX + bubbleConfig.arrowGraphicTargetOffset) + "px";
-        }
-    })();
-
-    document.body.appendChild(div);
-
-    return bubble;
-};
-
-SimileAjax.Graphics.getWindowDimensions = function() {
-    if (typeof window.innerHeight == 'number') {
-        return { w:window.innerWidth, h:window.innerHeight }; // Non-IE
-    } else if (document.documentElement && document.documentElement.clientHeight) {
-        return { // IE6+, in "standards compliant mode"
-            w:document.documentElement.clientWidth,
-            h:document.documentElement.clientHeight
-        };
-    } else if (document.body && document.body.clientHeight) {
-        return { // IE 4 compatible
-            w:document.body.clientWidth,
-            h:document.body.clientHeight
-        };
-    }
-};
-
-
-/**
- * Creates a floating, rounded message bubble in the center of the window for
- * displaying modal information, e.g. "Loading..."
- *
- * @param {Document} doc the root document for the page to render on
- * @param {Object} an object with two properties, contentDiv and containerDiv,
- *   consisting of the newly created DOM elements
- */
-SimileAjax.Graphics.createMessageBubble = function(doc) {
-    var containerDiv = doc.createElement("div");
-    if (SimileAjax.Graphics.pngIsTranslucent) {
-        var topDiv = doc.createElement("div");
-        topDiv.style.height = "33px";
-        topDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-top-left.png) top left no-repeat";
-        topDiv.style.paddingLeft = "44px";
-        containerDiv.appendChild(topDiv);
-
-        var topRightDiv = doc.createElement("div");
-        topRightDiv.style.height = "33px";
-        topRightDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-top-right.png) top right no-repeat";
-        topDiv.appendChild(topRightDiv);
-
-        var middleDiv = doc.createElement("div");
-        middleDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-left.png) top left repeat-y";
-        middleDiv.style.paddingLeft = "44px";
-        containerDiv.appendChild(middleDiv);
-
-        var middleRightDiv = doc.createElement("div");
-        middleRightDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-right.png) top right repeat-y";
-        middleRightDiv.style.paddingRight = "44px";
-        middleDiv.appendChild(middleRightDiv);
-
-        var contentDiv = doc.createElement("div");
-        middleRightDiv.appendChild(contentDiv);
-
-        var bottomDiv = doc.createElement("div");
-        bottomDiv.style.height = "55px";
-        bottomDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-bottom-left.png) bottom left no-repeat";
-        bottomDiv.style.paddingLeft = "44px";
-        containerDiv.appendChild(bottomDiv);
-
-        var bottomRightDiv = doc.createElement("div");
-        bottomRightDiv.style.height = "55px";
-        bottomRightDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-bottom-right.png) bottom right no-repeat";
-        bottomDiv.appendChild(bottomRightDiv);
-    } else {
-        containerDiv.style.border = "2px solid #7777AA";
-        containerDiv.style.padding = "20px";
-        containerDiv.style.background = "white";
-        SimileAjax.Graphics.setOpacity(containerDiv, 90);
-
-        var contentDiv = doc.createElement("div");
-        containerDiv.appendChild(contentDiv);
-    }
-
-    return {
-        containerDiv:   containerDiv,
-        contentDiv:     contentDiv
-    };
-};
-
-/*
- *  Animation
- *
- */
-
-/**
- * Creates an animation for a function, and an interval of values.  The word
- * "animation" here is used in the sense of repeatedly calling a function with
- * a current value from within an interval, and a delta value.
- *
- * @param {Function} f a function to be called every 50 milliseconds throughout
- *   the animation duration, of the form f(current, delta), where current is
- *   the current value within the range and delta is the current change.
- * @param {Number} from a starting value
- * @param {Number} to an ending value
- * @param {Number} duration the duration of the animation in milliseconds
- * @param {Function} [cont] an optional function that is called at the end of
- *   the animation, i.e. a continuation.
- * @return {SimileAjax.Graphics._Animation} a new animation object
- */
-SimileAjax.Graphics.createAnimation = function(f, from, to, duration, cont) {
-    return new SimileAjax.Graphics._Animation(f, from, to, duration, cont);
-};
-
-SimileAjax.Graphics._Animation = function(f, from, to, duration, cont) {
-    this.f = f;
-    this.cont = (typeof cont == "function") ? cont : function() {};
-
-    this.from = from;
-    this.to = to;
-    this.current = from;
-
-    this.duration = duration;
-    this.start = new Date().getTime();
-    this.timePassed = 0;
-};
-
-/**
- * Runs this animation.
- */
-SimileAjax.Graphics._Animation.prototype.run = function() {
-    var a = this;
-    window.setTimeout(function() { a.step(); }, 50);
-};
-
-/**
- * Increments this animation by one step, and then continues the animation with
- * <code>run()</code>.
- */
-SimileAjax.Graphics._Animation.prototype.step = function() {
-    this.timePassed += 50;
-
-    var timePassedFraction = this.timePassed / this.duration;
-    var parameterFraction = -Math.cos(timePassedFraction * Math.PI) / 2 + 0.5;
-    var current = parameterFraction * (this.to - this.from) + this.from;
-
-    try {
-        this.f(current, current - this.current);
-    } catch (e) {
-    }
-    this.current = current;
-
-    if (this.timePassed < this.duration) {
-        this.run();
-    } else {
-        this.f(this.to, 0);
-        this["cont"]();
-    }
-};
-
-/*
- *  CopyPasteButton
- *
- *  Adapted from http://spaces.live.com/editorial/rayozzie/demo/liveclip/liveclipsample/techPreview.html.
- *
- */
-
-/**
- * Creates a button and textarea for displaying structured data and copying it
- * to the clipboard.  The data is dynamically generated by the given
- * createDataFunction parameter.
- *
- * @param {String} image an image URL to use as the background for the
- *   generated box
- * @param {Number} width the width in pixels of the generated box
- * @param {Number} height the height in pixels of the generated box
- * @param {Function} createDataFunction a function that is called with no
- *   arguments to generate the structured data
- * @return a new DOM element
- */
-SimileAjax.Graphics.createStructuredDataCopyButton = function(image, width, height, createDataFunction) {
-    var div = document.createElement("div");
-    div.style.position = "relative";
-    div.style.display = "inline";
-    div.style.width = width + "px";
-    div.style.height = height + "px";
-    div.style.overflow = "hidden";
-    div.style.margin = "2px";
-
-    if (SimileAjax.Graphics.pngIsTranslucent) {
-        div.style.background = "url(" + image + ") no-repeat";
-    } else {
-        div.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + image +"', sizingMethod='image')";
-    }
-
-    var style;
-    if (SimileAjax.Platform.browser.isIE) {
-        style = "filter:alpha(opacity=0)";
-    } else {
-        style = "opacity: 0";
-    }
-    div.innerHTML = "<textarea rows='1' autocomplete='off' value='none' style='" + style + "' />";
-
-    var textarea = div.firstChild;
-    textarea.style.width = width + "px";
-    textarea.style.height = height + "px";
-    textarea.onmousedown = function(evt) {
-        evt = (evt) ? evt : ((event) ? event : null);
-        if (evt.button == 2) {
-            textarea.value = createDataFunction();
-            textarea.select();
-        }
-    };
-
-    return div;
-};
-
-/*
- *  getWidthHeight
- *
- */
-SimileAjax.Graphics.getWidthHeight = function(el) {
-    // RETURNS hash {width:  w, height: h} in pixels
-
-    var w, h;
-    // offsetWidth rounds on FF, so doesn't work for us.
-    // See https://bugzilla.mozilla.org/show_bug.cgi?id=458617
-    if (el.getBoundingClientRect == null) {
-    	// use offsetWidth
-      w = el.offsetWidth;
-      h = el.offsetHeight;
-    } else {
-    	// use getBoundingClientRect
-      var rect = el.getBoundingClientRect();
-      w = Math.ceil(rect.right - rect.left);
-    	h = Math.ceil(rect.bottom - rect.top);
-    }
-    return {
-        width:  w,
-        height: h
-    };
-};
-
-
-/*
- *  FontRenderingContext
- *
- */
-SimileAjax.Graphics.getFontRenderingContext = function(elmt, width) {
-    return new SimileAjax.Graphics._FontRenderingContext(elmt, width);
-};
-
-SimileAjax.Graphics._FontRenderingContext = function(elmt, width) {
-    this._elmt = elmt;
-    this._elmt.style.visibility = "hidden";
-    if (typeof width == "string") {
-        this._elmt.style.width = width;
-    } else if (typeof width == "number") {
-        this._elmt.style.width = width + "px";
-    }
-};
-
-SimileAjax.Graphics._FontRenderingContext.prototype.dispose = function() {
-    this._elmt = null;
-};
-
-SimileAjax.Graphics._FontRenderingContext.prototype.update = function() {
-    this._elmt.innerHTML = "A";
-    this._lineHeight = this._elmt.offsetHeight;
-};
-
-SimileAjax.Graphics._FontRenderingContext.prototype.computeSize = function(text, className) {
-    // className arg is optional
-    var el = this._elmt;
-    el.innerHTML = text;
-    el.className = className === undefined ? '' : className;
-    var wh = SimileAjax.Graphics.getWidthHeight(el);
-    el.className = ''; // reset for the next guy
-
-    return wh;
-};
-
-SimileAjax.Graphics._FontRenderingContext.prototype.getLineHeight = function() {
-    return this._lineHeight;
-};
-
-/**
- * @fileOverview A collection of date/time utility functions
- * @name SimileAjax.DateTime
- */
-
-SimileAjax.DateTime = new Object();
-
-SimileAjax.DateTime.MILLISECOND    = 0;
-SimileAjax.DateTime.SECOND         = 1;
-SimileAjax.DateTime.MINUTE         = 2;
-SimileAjax.DateTime.HOUR           = 3;
-SimileAjax.DateTime.DAY            = 4;
-SimileAjax.DateTime.WEEK           = 5;
-SimileAjax.DateTime.MONTH          = 6;
-SimileAjax.DateTime.YEAR           = 7;
-SimileAjax.DateTime.DECADE         = 8;
-SimileAjax.DateTime.CENTURY        = 9;
-SimileAjax.DateTime.MILLENNIUM     = 10;
-
-SimileAjax.DateTime.EPOCH          = -1;
-SimileAjax.DateTime.ERA            = -2;
-
-/**
- * An array of unit lengths, expressed in milliseconds, of various lengths of
- * time.  The array indices are predefined and stored as properties of the
- * SimileAjax.DateTime object, e.g. SimileAjax.DateTime.YEAR.
- * @type Array
- */
-SimileAjax.DateTime.gregorianUnitLengths = [];
-    (function() {
-        var d = SimileAjax.DateTime;
-        var a = d.gregorianUnitLengths;
-
-        a[d.MILLISECOND] = 1;
-        a[d.SECOND]      = 1000;
-        a[d.MINUTE]      = a[d.SECOND] * 60;
-        a[d.HOUR]        = a[d.MINUTE] * 60;
-        a[d.DAY]         = a[d.HOUR] * 24;
-        a[d.WEEK]        = a[d.DAY] * 7;
-        a[d.MONTH]       = a[d.DAY] * 31;
-        a[d.YEAR]        = a[d.DAY] * 365;
-        a[d.DECADE]      = a[d.YEAR] * 10;
-        a[d.CENTURY]     = a[d.YEAR] * 100;
-        a[d.MILLENNIUM]  = a[d.YEAR] * 1000;
-    })();
-
-SimileAjax.DateTime._dateRegexp = new RegExp(
-    "^(-?)([0-9]{4})(" + [
-        "(-?([0-9]{2})(-?([0-9]{2}))?)", // -month-dayOfMonth
-        "(-?([0-9]{3}))",                // -dayOfYear
-        "(-?W([0-9]{2})(-?([1-7]))?)"    // -Wweek-dayOfWeek
-    ].join("|") + ")?$"
-);
-SimileAjax.DateTime._timezoneRegexp = new RegExp(
-    "Z|(([-+])([0-9]{2})(:?([0-9]{2}))?)$"
-);
-SimileAjax.DateTime._timeRegexp = new RegExp(
-    "^([0-9]{2})(:?([0-9]{2})(:?([0-9]{2})(\.([0-9]+))?)?)?$"
-);
-
-/**
- * Takes a date object and a string containing an ISO 8601 date and sets the
- * the date using information parsed from the string.  Note that this method
- * does not parse any time information.
- *
- * @param {Date} dateObject the date object to modify
- * @param {String} string an ISO 8601 string to parse
- * @return {Date} the modified date object
- */
-SimileAjax.DateTime.setIso8601Date = function(dateObject, string) {
-    /*
-     *  This function has been adapted from dojo.date, v.0.3.0
-     *  http://dojotoolkit.org/.
-     */
-
-    var d = string.match(SimileAjax.DateTime._dateRegexp);
-    if(!d) {
-        throw new Error("Invalid date string: " + string);
-    }
-
-    var sign = (d[1] == "-") ? -1 : 1; // BC or AD
-    var year = sign * d[2];
-    var month = d[5];
-    var date = d[7];
-    var dayofyear = d[9];
-    var week = d[11];
-    var dayofweek = (d[13]) ? d[13] : 1;
-
-    dateObject.setUTCFullYear(year);
-    if (dayofyear) {
-        dateObject.setUTCMonth(0);
-        dateObject.setUTCDate(Number(dayofyear));
-    } else if (week) {
-        dateObject.setUTCMonth(0);
-        dateObject.setUTCDate(1);
-        var gd = dateObject.getUTCDay();
-        var day =  (gd) ? gd : 7;
-        var offset = Number(dayofweek) + (7 * Number(week));
-
-        if (day <= 4) {
-            dateObject.setUTCDate(offset + 1 - day);
-        } else {
-            dateObject.setUTCDate(offset + 8 - day);
-        }
-    } else {
-        if (month) {
-            dateObject.setUTCDate(1);
-            dateObject.setUTCMonth(month - 1);
-        }
-        if (date) {
-            dateObject.setUTCDate(date);
-        }
-    }
-
-    return dateObject;
-};
-
-/**
- * Takes a date object and a string containing an ISO 8601 time and sets the
- * the time using information parsed from the string.  Note that this method
- * does not parse any date information.
- *
- * @param {Date} dateObject the date object to modify
- * @param {String} string an ISO 8601 string to parse
- * @return {Date} the modified date object
- */
-SimileAjax.DateTime.setIso8601Time = function (dateObject, string) {
-    /*
-     *  This function has been adapted from dojo.date, v.0.3.0
-     *  http://dojotoolkit.org/.
-     */
-
-    var d = string.match(SimileAjax.DateTime._timeRegexp);
-    if(!d) {
-        SimileAjax.Debug.warn("Invalid time string: " + string);
-        return false;
-    }
-    var hours = d[1];
-    var mins = Number((d[3]) ? d[3] : 0);
-    var secs = (d[5]) ? d[5] : 0;
-    var ms = d[7] ? (Number("0." + d[7]) * 1000) : 0;
-
-    dateObject.setUTCHours(hours);
-    dateObject.setUTCMinutes(mins);
-    dateObject.setUTCSeconds(secs);
-    dateObject.setUTCMilliseconds(ms);
-
-    return dateObject;
-};
-
-/**
- * The timezone offset in minutes in the user's browser.
- * @type Number
- */
-SimileAjax.DateTime.timezoneOffset = new Date().getTimezoneOffset();
-
-/**
- * Takes a date object and a string containing an ISO 8601 date and time and
- * sets the date object using information parsed from the string.
- *
- * @param {Date} dateObject the date object to modify
- * @param {String} string an ISO 8601 string to parse
- * @return {Date} the modified date object
- */
-SimileAjax.DateTime.setIso8601 = function (dateObject, string){
-    /*
-     *  This function has been adapted from dojo.date, v.0.3.0
-     *  http://dojotoolkit.org/.
-     */
-
-    var offset = null;
-    var comps = (string.indexOf("T") == -1) ? string.split(" ") : string.split("T");
-
-    SimileAjax.DateTime.setIso8601Date(dateObject, comps[0]);
-    if (comps.length == 2) {
-        // first strip timezone info from the end
-        var d = comps[1].match(SimileAjax.DateTime._timezoneRegexp);
-        if (d) {
-            if (d[0] == 'Z') {
-                offset = 0;
-            } else {
-                offset = (Number(d[3]) * 60) + Number(d[5]);
-                offset *= ((d[2] == '-') ? 1 : -1);
-            }
-            comps[1] = comps[1].substr(0, comps[1].length - d[0].length);
-        }
-
-        SimileAjax.DateTime.setIso8601Time(dateObject, comps[1]);
-    }
-    if (offset == null) {
-        offset = dateObject.getTimezoneOffset(); // local time zone if no tz info
-    }
-    dateObject.setTime(dateObject.getTime() + offset * 60000);
-
-    return dateObject;
-};
-
-/**
- * Takes a string containing an ISO 8601 date and returns a newly instantiated
- * date object with the parsed date and time information from the string.
- *
- * @param {String} string an ISO 8601 string to parse
- * @return {Date} a new date object created from the string
- */
-SimileAjax.DateTime.parseIso8601DateTime = function (string) {
-    try {
-        return SimileAjax.DateTime.setIso8601(new Date(0), string);
-    } catch (e) {
-        return null;
-    }
-};
-
-/**
- * Takes a string containing a Gregorian date and time and returns a newly
- * instantiated date object with the parsed date and time information from the
- * string.  If the param is actually an instance of Date instead of a string,
- * simply returns the given date instead.
- *
- * @param {Object} o an object, to either return or parse as a string
- * @return {Date} the date object
- */
-SimileAjax.DateTime.parseGregorianDateTime = function(o) {
-    if (o == null) {
-        return null;
-    } else if (o instanceof Date) {
-        return o;
-    }
-
-    var s = o.toString();
-    if (s.length > 0 && s.length < 8) {
-        var space = s.indexOf(" ");
-        if (space > 0) {
-            var year = parseInt(s.substr(0, space));
-            var suffix = s.substr(space + 1);
-            if (suffix.toLowerCase() == "bc") {
-                year = 1 - year;
-            }
-        } else {
-            var year = parseInt(s);
-        }
-
-        var d = new Date(0);
-        d.setUTCFullYear(year);
-
-        return d;
-    }
-
-    try {
-        return new Date(Date.parse(s));
-    } catch (e) {
-        return null;
-    }
-};
-
-/**
- * Rounds date objects down to the nearest interval or multiple of an interval.
- * This method modifies the given date object, converting it to the given
- * timezone if specified.
- *
- * @param {Date} date the date object to round
- * @param {Number} intervalUnit a constant, integer index specifying an
- *   interval, e.g. SimileAjax.DateTime.HOUR
- * @param {Number} timeZone a timezone shift, given in hours
- * @param {Number} multiple a multiple of the interval to round by
- * @param {Number} firstDayOfWeek an integer specifying the first day of the
- *   week, 0 corresponds to Sunday, 1 to Monday, etc.
- */
-SimileAjax.DateTime.roundDownToInterval = function(date, intervalUnit, timeZone, multiple, firstDayOfWeek) {
-    var timeShift = timeZone *
-        SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR];
-
-    var date2 = new Date(date.getTime() + timeShift);
-    var clearInDay = function(d) {
-        d.setUTCMilliseconds(0);
-        d.setUTCSeconds(0);
-        d.setUTCMinutes(0);
-        d.setUTCHours(0);
-    };
-    var clearInYear = function(d) {
-        clearInDay(d);
-        d.setUTCDate(1);
-        d.setUTCMonth(0);
-    };
-
-    switch(intervalUnit) {
-    case SimileAjax.DateTime.MILLISECOND:
-        var x = date2.getUTCMilliseconds();
-        date2.setUTCMilliseconds(x - (x % multiple));
-        break;
-    case SimileAjax.DateTime.SECOND:
-        date2.setUTCMilliseconds(0);
-
-        var x = date2.getUTCSeconds();
-        date2.setUTCSeconds(x - (x % multiple));
-        break;
-    case SimileAjax.DateTime.MINUTE:
-        date2.setUTCMilliseconds(0);
-        date2.setUTCSeconds(0);
-
-        var x = date2.getUTCMinutes();
-        date2.setTime(date2.getTime() -
-            (x % multiple) * SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.MINUTE]);
-        break;
-    case SimileAjax.DateTime.HOUR:
-        date2.setUTCMilliseconds(0);
-        date2.setUTCSeconds(0);
-        date2.setUTCMinutes(0);
-
-        var x = date2.getUTCHours();
-        date2.setUTCHours(x - (x % multiple));
-        break;
-    case SimileAjax.DateTime.DAY:
-        clearInDay(date2);
-        break;
-    case SimileAjax.DateTime.WEEK:
-        clearInDay(date2);
-        var d = (date2.getUTCDay() + 7 - firstDayOfWeek) % 7;
-        date2.setTime(date2.getTime() -
-            d * SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.DAY]);
-        break;
-    case SimileAjax.DateTime.MONTH:
-        clearInDay(date2);
-        date2.setUTCDate(1);
-
-        var x = date2.getUTCMonth();
-        date2.setUTCMonth(x - (x % multiple));
-        break;
-    case SimileAjax.DateTime.YEAR:
-        clearInYear(date2);
-
-        var x = date2.getUTCFullYear();
-        date2.setUTCFullYear(x - (x % multiple));
-        break;
-    case SimileAjax.DateTime.DECADE:
-        clearInYear(date2);
-        date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 10) * 10);
-        break;
-    case SimileAjax.DateTime.CENTURY:
-        clearInYear(date2);
-        date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 100) * 100);
-        break;
-    case SimileAjax.DateTime.MILLENNIUM:
-        clearInYear(date2);
-        date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 1000) * 1000);
-        break;
-    }
-
-    date.setTime(date2.getTime() - timeShift);
-};
-
-/**
- * Rounds date objects up to the nearest interval or multiple of an interval.
- * This method modifies the given date object, converting it to the given
- * timezone if specified.
- *
- * @param {Date} date the date object to round
- * @param {Number} intervalUnit a constant, integer index specifying an
- *   interval, e.g. SimileAjax.DateTime.HOUR
- * @param {Number} timeZone a timezone shift, given in hours
- * @param {Number} multiple a multiple of the interval to round by
- * @param {Number} firstDayOfWeek an integer specifying the first day of the
- *   week, 0 corresponds to Sunday, 1 to Monday, etc.
- * @see SimileAjax.DateTime.roundDownToInterval
- */
-SimileAjax.DateTime.roundUpToInterval = function(date, intervalUnit, timeZone, multiple, firstDayOfWeek) {
-    var originalTime = date.getTime();
-    SimileAjax.DateTime.roundDownToInterval(date, intervalUnit, timeZone, multiple, firstDayOfWeek);
-    if (date.getTime() < originalTime) {
-        date.setTime(date.getTime() +
-            SimileAjax.DateTime.gregorianUnitLengths[intervalUnit] * multiple);
-    }
-};
-
-/**
- * Increments a date object by a specified interval, taking into
- * consideration the timezone.
- *
- * @param {Date} date the date object to increment
- * @param {Number} intervalUnit a constant, integer index specifying an
- *   interval, e.g. SimileAjax.DateTime.HOUR
- * @param {Number} timeZone the timezone offset in hours
- */
-SimileAjax.DateTime.incrementByInterval = function(date, intervalUnit, timeZone) {
-    timeZone = (typeof timeZone == 'undefined') ? 0 : timeZone;
-
-    var timeShift = timeZone *
-        SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR];
-
-    var date2 = new Date(date.getTime() + timeShift);
-
-    switch(intervalUnit) {
-    case SimileAjax.DateTime.MILLISECOND:
-        date2.setTime(date2.getTime() + 1)
-        break;
-    case SimileAjax.DateTime.SECOND:
-        date2.setTime(date2.getTime() + 1000);
-        break;
-    case SimileAjax.DateTime.MINUTE:
-        date2.setTime(date2.getTime() +
-            SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.MINUTE]);
-        break;
-    case SimileAjax.DateTime.HOUR:
-        date2.setTime(date2.getTime() +
-            SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR]);
-        break;
-    case SimileAjax.DateTime.DAY:
-        date2.setUTCDate(date2.getUTCDate() + 1);
-        break;
-    case SimileAjax.DateTime.WEEK:
-        date2.setUTCDate(date2.getUTCDate() + 7);
-        break;
-    case SimileAjax.DateTime.MONTH:
-        date2.setUTCMonth(date2.getUTCMonth() + 1);
-        break;
-    case SimileAjax.DateTime.YEAR:
-        date2.setUTCFullYear(date2.getUTCFullYear() + 1);
-        break;
-    case SimileAjax.DateTime.DECADE:
-        date2.setUTCFullYear(date2.getUTCFullYear() + 10);
-        break;
-    case SimileAjax.DateTime.CENTURY:
-        date2.setUTCFullYear(date2.getUTCFullYear() + 100);
-        break;
-    case SimileAjax.DateTime.MILLENNIUM:
-        date2.setUTCFullYear(date2.getUTCFullYear() + 1000);
-        break;
-    }
-
-    date.setTime(date2.getTime() - timeShift);
-};
-
-/**
- * Returns a new date object with the given time offset removed.
- *
- * @param {Date} date the starting date
- * @param {Number} timeZone a timezone specified in an hour offset to remove
- * @return {Date} a new date object with the offset removed
- */
-SimileAjax.DateTime.removeTimeZoneOffset = function(date, timeZone) {
-    return new Date(date.getTime() +
-        timeZone * SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR]);
-};
-
-/**
- * Returns the timezone of the user's browser.
- *
- * @return {Number} the timezone in the user's locale in hours
- */
-SimileAjax.DateTime.getTimezone = function() {
-    var d = new Date().getTimezoneOffset();
-    return d / -60;
-};
-/*
- *  String Utility Functions and Constants
- *
- */
-
-String.prototype.trim = function() {
-    return this.replace(/^\s+|\s+$/g, '');
-};
-
-String.prototype.startsWith = function(prefix) {
-    return this.length >= prefix.length && this.substr(0, prefix.length) == prefix;
-};
-
-String.prototype.endsWith = function(suffix) {
-    return this.length >= suffix.length && this.substr(this.length - suffix.length) == suffix;
-};
-
-String.substitute = function(s, objects) {
-    var result = "";
-    var start = 0;
-    while (start < s.length - 1) {
-        var percent = s.indexOf("%", start);
-        if (percent < 0 || percent == s.length - 1) {
-            break;
-        } else if (percent > start && s.charAt(percent - 1) == "\\") {
-            result += s.substring(start, percent - 1) + "%";
-            start = percent + 1;
-        } else {
-            var n = parseInt(s.charAt(percent + 1));
-            if (isNaN(n) || n >= objects.length) {
-                result += s.substring(start, percent + 2);
-            } else {
-                result += s.substring(start, percent) + objects[n].toString();
-            }
-            start = percent + 2;
-        }
-    }
-
-    if (start < s.length) {
-        result += s.substring(start);
-    }
-    return result;
-};
-/*
- *  HTML Utility Functions
- *
- */
-
-SimileAjax.HTML = new Object();
-
-SimileAjax.HTML._e2uHash = {};
-(function() {
-    var e2uHash = SimileAjax.HTML._e2uHash;
-    e2uHash['nbsp']= '\u00A0[space]';
-    e2uHash['iexcl']= '\u00A1';
-    e2uHash['cent']= '\u00A2';
-    e2uHash['pound']= '\u00A3';
-    e2uHash['curren']= '\u00A4';
-    e2uHash['yen']= '\u00A5';
-    e2uHash['brvbar']= '\u00A6';
-    e2uHash['sect']= '\u00A7';
-    e2uHash['uml']= '\u00A8';
-    e2uHash['copy']= '\u00A9';
-    e2uHash['ordf']= '\u00AA';
-    e2uHash['laquo']= '\u00AB';
-    e2uHash['not']= '\u00AC';
-    e2uHash['shy']= '\u00AD';
-    e2uHash['reg']= '\u00AE';
-    e2uHash['macr']= '\u00AF';
-    e2uHash['deg']= '\u00B0';
-    e2uHash['plusmn']= '\u00B1';
-    e2uHash['sup2']= '\u00B2';
-    e2uHash['sup3']= '\u00B3';
-    e2uHash['acute']= '\u00B4';
-    e2uHash['micro']= '\u00B5';
-    e2uHash['para']= '\u00B6';
-    e2uHash['middot']= '\u00B7';
-    e2uHash['cedil']= '\u00B8';
-    e2uHash['sup1']= '\u00B9';
-    e2uHash['ordm']= '\u00BA';
-    e2uHash['raquo']= '\u00BB';
-    e2uHash['frac14']= '\u00BC';
-    e2uHash['frac12']= '\u00BD';
-    e2uHash['frac34']= '\u00BE';
-    e2uHash['iquest']= '\u00BF';
-    e2uHash['Agrave']= '\u00C0';
-    e2uHash['Aacute']= '\u00C1';
-    e2uHash['Acirc']= '\u00C2';
-    e2uHash['Atilde']= '\u00C3';
-    e2uHash['Auml']= '\u00C4';
-    e2uHash['Aring']= '\u00C5';
-    e2uHash['AElig']= '\u00C6';
-    e2uHash['Ccedil']= '\u00C7';
-    e2uHash['Egrave']= '\u00C8';
-    e2uHash['Eacute']= '\u00C9';
-    e2uHash['Ecirc']= '\u00CA';
-    e2uHash['Euml']= '\u00CB';
-    e2uHash['Igrave']= '\u00CC';
-    e2uHash['Iacute']= '\u00CD';
-    e2uHash['Icirc']= '\u00CE';
-    e2uHash['Iuml']= '\u00CF';
-    e2uHash['ETH']= '\u00D0';
-    e2uHash['Ntilde']= '\u00D1';
-    e2uHash['Ograve']= '\u00D2';
-    e2uHash['Oacute']= '\u00D3';
-    e2uHash['Ocirc']= '\u00D4';
-    e2uHash['Otilde']= '\u00D5';
-    e2uHash['Ouml']= '\u00D6';
-    e2uHash['times']= '\u00D7';
-    e2uHash['Oslash']= '\u00D8';
-    e2uHash['Ugrave']= '\u00D9';
-    e2uHash['Uacute']= '\u00DA';
-    e2uHash['Ucirc']= '\u00DB';
-    e2uHash['Uuml']= '\u00DC';
-    e2uHash['Yacute']= '\u00DD';
-    e2uHash['THORN']= '\u00DE';
-    e2uHash['szlig']= '\u00DF';
-    e2uHash['agrave']= '\u00E0';
-    e2uHash['aacute']= '\u00E1';
-    e2uHash['acirc']= '\u00E2';
-    e2uHash['atilde']= '\u00E3';
-    e2uHash['auml']= '\u00E4';
-    e2uHash['aring']= '\u00E5';
-    e2uHash['aelig']= '\u00E6';
-    e2uHash['ccedil']= '\u00E7';
-    e2uHash['egrave']= '\u00E8';
-    e2uHash['eacute']= '\u00E9';
-    e2uHash['ecirc']= '\u00EA';
-    e2uHash['euml']= '\u00EB';
-    e2uHash['igrave']= '\u00EC';
-    e2uHash['iacute']= '\u00ED';
-    e2uHash['icirc']= '\u00EE';
-    e2uHash['iuml']= '\u00EF';
-    e2uHash['eth']= '\u00F0';
-    e2uHash['ntilde']= '\u00F1';
-    e2uHash['ograve']= '\u00F2';
-    e2uHash['oacute']= '\u00F3';
-    e2uHash['ocirc']= '\u00F4';
-    e2uHash['otilde']= '\u00F5';
-    e2uHash['ouml']= '\u00F6';
-    e2uHash['divide']= '\u00F7';
-    e2uHash['oslash']= '\u00F8';
-    e2uHash['ugrave']= '\u00F9';
-    e2uHash['uacute']= '\u00FA';
-    e2uHash['ucirc']= '\u00FB';
-    e2uHash['uuml']= '\u00FC';
-    e2uHash['yacute']= '\u00FD';
-    e2uHash['thorn']= '\u00FE';
-    e2uHash['yuml']= '\u00FF';
-    e2uHash['quot']= '\u0022';
-    e2uHash['amp']= '\u0026';
-    e2uHash['lt']= '\u003C';
-    e2uHash['gt']= '\u003E';
-    e2uHash['OElig']= '';
-    e2uHash['oelig']= '\u0153';
-    e2uHash['Scaron']= '\u0160';
-    e2uHash['scaron']= '\u0161';
-    e2uHash['Yuml']= '\u0178';
-    e2uHash['circ']= '\u02C6';
-    e2uHash['tilde']= '\u02DC';
-    e2uHash['ensp']= '\u2002';
-    e2uHash['emsp']= '\u2003';
-    e2uHash['thinsp']= '\u2010';
-    e2uHash['zwnj']= '\u200C';
-    e2uHash['zwj']= '\u200D';
-    e2uHash['lrm']= '\u200E';
-    e2uHash['rlm']= '\u200F';
-    e2uHash['ndash']= '\u2013';
-    e2uHash['mdash']= '\u2014';
-    e2uHash['lsquo']= '\u2018';
-    e2uHash['rsquo']= '\u2019';
-    e2uHash['sbquo']= '\u201A';
-    e2uHash['ldquo']= '\u201C';
-    e2uHash['rdquo']= '\u201D';
-    e2uHash['bdquo']= '\u201E';
-    e2uHash['dagger']= '\u2020';
-    e2uHash['Dagger']= '\u2021';
-    e2uHash['permil']= '\u2030';
-    e2uHash['lsaquo']= '\u2039';
-    e2uHash['rsaquo']= '\u203A';
-    e2uHash['euro']= '\u20AC';
-    e2uHash['fnof']= '\u0192';
-    e2uHash['Alpha']= '\u0391';
-    e2uHash['Beta']= '\u0392';
-    e2uHash['Gamma']= '\u0393';
-    e2uHash['Delta']= '\u0394';
-    e2uHash['Epsilon']= '\u0395';
-    e2uHash['Zeta']= '\u0396';
-    e2uHash['Eta']= '\u0397';
-    e2uHash['Theta']= '\u0398';
-    e2uHash['Iota']= '\u0399';
-    e2uHash['Kappa']= '\u039A';
-    e2uHash['Lambda']= '\u039B';
-    e2uHash['Mu']= '\u039C';
-    e2uHash['Nu']= '\u039D';
-    e2uHash['Xi']= '\u039E';
-    e2uHash['Omicron']= '\u039F';
-    e2uHash['Pi']= '\u03A0';
-    e2uHash['Rho']= '\u03A1';
-    e2uHash['Sigma']= '\u03A3';
-    e2uHash['Tau']= '\u03A4';
-    e2uHash['Upsilon']= '\u03A5';
-    e2uHash['Phi']= '\u03A6';
-    e2uHash['Chi']= '\u03A7';
-    e2uHash['Psi']= '\u03A8';
-    e2uHash['Omega']= '\u03A9';
-    e2uHash['alpha']= '\u03B1';
-    e2uHash['beta']= '\u03B2';
-    e2uHash['gamma']= '\u03B3';
-    e2uHash['delta']= '\u03B4';
-    e2uHash['epsilon']= '\u03B5';
-    e2uHash['zeta']= '\u03B6';
-    e2uHash['eta']= '\u03B7';
-    e2uHash['theta']= '\u03B8';
-    e2uHash['iota']= '\u03B9';
-    e2uHash['kappa']= '\u03BA';
-    e2uHash['lambda']= '\u03BB';
-    e2uHash['mu']= '\u03BC';
-    e2uHash['nu']= '\u03BD';
-    e2uHash['xi']= '\u03BE';
-    e2uHash['omicron']= '\u03BF';
-    e2uHash['pi']= '\u03C0';
-    e2uHash['rho']= '\u03C1';
-    e2uHash['sigmaf']= '\u03C2';
-    e2uHash['sigma']= '\u03C3';
-    e2uHash['tau']= '\u03C4';
-    e2uHash['upsilon']= '\u03C5';
-    e2uHash['phi']= '\u03C6';
-    e2uHash['chi']= '\u03C7';
-    e2uHash['psi']= '\u03C8';
-    e2uHash['omega']= '\u03C9';
-    e2uHash['thetasym']= '\u03D1';
-    e2uHash['upsih']= '\u03D2';
-    e2uHash['piv']= '\u03D6';
-    e2uHash['bull']= '\u2022';
-    e2uHash['hellip']= '\u2026';
-    e2uHash['prime']= '\u2032';
-    e2uHash['Prime']= '\u2033';
-    e2uHash['oline']= '\u203E';
-    e2uHash['frasl']= '\u2044';
-    e2uHash['weierp']= '\u2118';
-    e2uHash['image']= '\u2111';
-    e2uHash['real']= '\u211C';
-    e2uHash['trade']= '\u2122';
-    e2uHash['alefsym']= '\u2135';
-    e2uHash['larr']= '\u2190';
-    e2uHash['uarr']= '\u2191';
-    e2uHash['rarr']= '\u2192';
-    e2uHash['darr']= '\u2193';
-    e2uHash['harr']= '\u2194';
-    e2uHash['crarr']= '\u21B5';
-    e2uHash['lArr']= '\u21D0';
-    e2uHash['uArr']= '\u21D1';
-    e2uHash['rArr']= '\u21D2';
-    e2uHash['dArr']= '\u21D3';
-    e2uHash['hArr']= '\u21D4';
-    e2uHash['forall']= '\u2200';
-    e2uHash['part']= '\u2202';
-    e2uHash['exist']= '\u2203';
-    e2uHash['empty']= '\u2205';
-    e2uHash['nabla']= '\u2207';
-    e2uHash['isin']= '\u2208';
-    e2uHash['notin']= '\u2209';
-    e2uHash['ni']= '\u220B';
-    e2uHash['prod']= '\u220F';
-    e2uHash['sum']= '\u2211';
-    e2uHash['minus']= '\u2212';
-    e2uHash['lowast']= '\u2217';
-    e2uHash['radic']= '\u221A';
-    e2uHash['prop']= '\u221D';
-    e2uHash['infin']= '\u221E';
-    e2uHash['ang']= '\u2220';
-    e2uHash['and']= '\u2227';
-    e2uHash['or']= '\u2228';
-    e2uHash['cap']= '\u2229';
-    e2uHash['cup']= '\u222A';
-    e2uHash['int']= '\u222B';
-    e2uHash['there4']= '\u2234';
-    e2uHash['sim']= '\u223C';
-    e2uHash['cong']= '\u2245';
-    e2uHash['asymp']= '\u2248';
-    e2uHash['ne']= '\u2260';
-    e2uHash['equiv']= '\u2261';
-    e2uHash['le']= '\u2264';
-    e2uHash['ge']= '\u2265';
-    e2uHash['sub']= '\u2282';
-    e2uHash['sup']= '\u2283';
-    e2uHash['nsub']= '\u2284';
-    e2uHash['sube']= '\u2286';
-    e2uHash['supe']= '\u2287';
-    e2uHash['oplus']= '\u2295';
-    e2uHash['otimes']= '\u2297';
-    e2uHash['perp']= '\u22A5';
-    e2uHash['sdot']= '\u22C5';
-    e2uHash['lceil']= '\u2308';
-    e2uHash['rceil']= '\u2309';
-    e2uHash['lfloor']= '\u230A';
-    e2uHash['rfloor']= '\u230B';
-    e2uHash['lang']= '\u2329';
-    e2uHash['rang']= '\u232A';
-    e2uHash['loz']= '\u25CA';
-    e2uHash['spades']= '\u2660';
-    e2uHash['clubs']= '\u2663';
-    e2uHash['hearts']= '\u2665';
-    e2uHash['diams']= '\u2666';
-})();
-
-SimileAjax.HTML.deEntify = function(s) {
-    var e2uHash = SimileAjax.HTML._e2uHash;
-
-    var re = /&(\w+?);/;
-    while (re.test(s)) {
-        var m = s.match(re);
-        s = s.replace(re, e2uHash[m[1]]);
-    }
-    return s;
-};/**
- * A basic set (in the mathematical sense) data structure
- *
- * @constructor
- * @param {Array or SimileAjax.Set} [a] an initial collection
- */
-SimileAjax.Set = function(a) {
-    this._hash = {};
-    this._count = 0;
-
-    if (a instanceof Array) {
-        for (var i = 0; i < a.length; i++) {
-            this.add(a[i]);
-        }
-    } else if (a instanceof SimileAjax.Set) {
-        this.addSet(a);
-    }
-}
-
-/**
- * Adds the given object to this set, assuming there it does not already exist
- *
- * @param {Object} o the object to add
- * @return {Boolean} true if the object was added, false if not
- */
-SimileAjax.Set.prototype.add = function(o) {
-    if (!(o in this._hash)) {
-        this._hash[o] = true;
-        this._count++;
-        return true;
-    }
-    return false;
-}
-
-/**
- * Adds each element in the given set to this set
- *
- * @param {SimileAjax.Set} set the set of elements to add
- */
-SimileAjax.Set.prototype.addSet = function(set) {
-    for (var o in set._hash) {
-        this.add(o);
-    }
-}
-
-/**
- * Removes the given element from this set
- *
- * @param {Object} o the object to remove
- * @return {Boolean} true if the object was successfully removed,
- *   false otherwise
- */
-SimileAjax.Set.prototype.remove = function(o) {
-    if (o in this._hash) {
-        delete this._hash[o];
-        this._count--;
-        return true;
-    }
-    return false;
-}
-
-/**
- * Removes the elements in this set that correspond to the elements in the
- * given set
- *
- * @param {SimileAjax.Set} set the set of elements to remove
- */
-SimileAjax.Set.prototype.removeSet = function(set) {
-    for (var o in set._hash) {
-        this.remove(o);
-    }
-}
-
-/**
- * Removes all elements in this set that are not present in the given set, i.e.
- * modifies this set to the intersection of the two sets
- *
- * @param {SimileAjax.Set} set the set to intersect
- */
-SimileAjax.Set.prototype.retainSet = function(set) {
-    for (var o in this._hash) {
-        if (!set.contains(o)) {
-            delete this._hash[o];
-            this._count--;
-        }
-    }
-}
-
-/**
- * Returns whether or not the given element exists in this set
- *
- * @param {SimileAjax.Set} o the object to test for
- * @return {Boolean} true if the object is present, false otherwise
- */
-SimileAjax.Set.prototype.contains = function(o) {
-    return (o in this._hash);
-}
-
-/**
- * Returns the number of elements in this set
- *
- * @return {Number} the number of elements in this set
- */
-SimileAjax.Set.prototype.size = function() {
-    return this._count;
-}
-
-/**
- * Returns the elements of this set as an array
- *
- * @return {Array} a new array containing the elements of this set
- */
-SimileAjax.Set.prototype.toArray = function() {
-    var a = [];
-    for (var o in this._hash) {
-        a.push(o);
-    }
-    return a;
-}
-
-/**
- * Iterates through the elements of this set, order unspecified, executing the
- * given function on each element until the function returns true
- *
- * @param {Function} f a function of form f(element)
- */
-SimileAjax.Set.prototype.visit = function(f) {
-    for (var o in this._hash) {
-        if (f(o) == true) {
-            break;
-        }
-    }
-}
-
-/**
- * A sorted array data structure
- *
- * @constructor
- */
-SimileAjax.SortedArray = function(compare, initialArray) {
-    this._a = (initialArray instanceof Array) ? initialArray : [];
-    this._compare = compare;
-};
-
-SimileAjax.SortedArray.prototype.add = function(elmt) {
-    var sa = this;
-    var index = this.find(function(elmt2) {
-        return sa._compare(elmt2, elmt);
-    });
-
-    if (index < this._a.length) {
-        this._a.splice(index, 0, elmt);
-    } else {
-        this._a.push(elmt);
-    }
-};
-
-SimileAjax.SortedArray.prototype.remove = function(elmt) {
-    var sa = this;
-    var index = this.find(function(elmt2) {
-        return sa._compare(elmt2, elmt);
-    });
-
-    while (index < this._a.length && this._compare(this._a[index], elmt) == 0) {
-        if (this._a[index] == elmt) {
-            this._a.splice(index, 1);
-            return true;
-        } else {
-            index++;
-        }
-    }
-    return false;
-};
-
-SimileAjax.SortedArray.prototype.removeAll = function() {
-    this._a = [];
-};
-
-SimileAjax.SortedArray.prototype.elementAt = function(index) {
-    return this._a[index];
-};
-
-SimileAjax.SortedArray.prototype.length = function() {
-    return this._a.length;
-};
-
-SimileAjax.SortedArray.prototype.find = function(compare) {
-    var a = 0;
-    var b = this._a.length;
-
-    while (a < b) {
-        var mid = Math.floor((a + b) / 2);
-        var c = compare(this._a[mid]);
-        if (mid == a) {
-            return c < 0 ? a+1 : a;
-        } else if (c < 0) {
-            a = mid;
-        } else {
-            b = mid;
-        }
-    }
-    return a;
-};
-
-SimileAjax.SortedArray.prototype.getFirst = function() {
-    return (this._a.length > 0) ? this._a[0] : null;
-};
-
-SimileAjax.SortedArray.prototype.getLast = function() {
-    return (this._a.length > 0) ? this._a[this._a.length - 1] : null;
-};
-
-/*
- *  Event Index
- *
- */
-
-SimileAjax.EventIndex = function(unit) {
-    var eventIndex = this;
-
-    this._unit = (unit != null) ? unit : SimileAjax.NativeDateUnit;
-    this._events = new SimileAjax.SortedArray(
-        function(event1, event2) {
-            return eventIndex._unit.compare(event1.getStart(), event2.getStart());
-        }
-    );
-    this._idToEvent = {};
-    this._indexed = true;
-};
-
-SimileAjax.EventIndex.prototype.getUnit = function() {
-    return this._unit;
-};
-
-SimileAjax.EventIndex.prototype.getEvent = function(id) {
-    return this._idToEvent[id];
-};
-
-SimileAjax.EventIndex.prototype.add = function(evt) {
-    this._events.add(evt);
-    this._idToEvent[evt.getID()] = evt;
-    this._indexed = false;
-};
-
-SimileAjax.EventIndex.prototype.removeAll = function() {
-    this._events.removeAll();
-    this._idToEvent = {};
-    this._indexed = false;
-};
-
-SimileAjax.EventIndex.prototype.getCount = function() {
-    return this._events.length();
-};
-
-SimileAjax.EventIndex.prototype.getIterator = function(startDate, endDate) {
-    if (!this._indexed) {
-        this._index();
-    }
-    return new SimileAjax.EventIndex._Iterator(this._events, startDate, endDate, this._unit);
-};
-
-SimileAjax.EventIndex.prototype.getReverseIterator = function(startDate, endDate) {
-    if (!this._indexed) {
-        this._index();
-    }
-    return new SimileAjax.EventIndex._ReverseIterator(this._events, startDate, endDate, this._unit);
-};
-
-SimileAjax.EventIndex.prototype.getAllIterator = function() {
-    return new SimileAjax.EventIndex._AllIterator(this._events);
-};
-
-SimileAjax.EventIndex.prototype.getEarliestDate = function() {
-    var evt = this._events.getFirst();
-    return (evt == null) ? null : evt.getStart();
-};
-
-SimileAjax.EventIndex.prototype.getLatestDate = function() {
-    var evt = this._events.getLast();
-    if (evt == null) {
-        return null;
-    }
-
-    if (!this._indexed) {
-        this._index();
-    }
-
-    var index = evt._earliestOverlapIndex;
-    var date = this._events.elementAt(index).getEnd();
-    for (var i = index + 1; i < this._events.length(); i++) {
-        date = this._unit.later(date, this._events.elementAt(i).getEnd());
-    }
-
-    return date;
-};
-
-SimileAjax.EventIndex.prototype._index = function() {
-    /*
-     *  For each event, we want to find the earliest preceding
-     *  event that overlaps with it, if any.
-     */
-
-    var l = this._events.length();
-    for (var i = 0; i < l; i++) {
-        var evt = this._events.elementAt(i);
-        evt._earliestOverlapIndex = i;
-    }
-
-    var toIndex = 1;
-    for (var i = 0; i < l; i++) {
-        var evt = this._events.elementAt(i);
-        var end = evt.getEnd();
-
-        toIndex = Math.max(toIndex, i + 1);
-        while (toIndex < l) {
-            var evt2 = this._events.elementAt(toIndex);
-            var start2 = evt2.getStart();
-
-            if (this._unit.compare(start2, end) < 0) {
-                evt2._earliestOverlapIndex = i;
-                toIndex++;
-            } else {
-                break;
-            }
-        }
-    }
-    this._indexed = true;
-};
-
-SimileAjax.EventIndex._Iterator = function(events, startDate, endDate, unit) {
-    this._events = events;
-    this._startDate = startDate;
-    this._endDate = endDate;
-    this._unit = unit;
-
-    this._currentIndex = events.find(function(evt) {
-        return unit.compare(evt.getStart(), startDate);
-    });
-    if (this._currentIndex - 1 >= 0) {
-        this._currentIndex = this._events.elementAt(this._currentIndex - 1)._earliestOverlapIndex;
-    }
-    this._currentIndex--;
-
-    this._maxIndex = events.find(function(evt) {
-        return unit.compare(evt.getStart(), endDate);
-    });
-
-    this._hasNext = false;
-    this._next = null;
-    this._findNext();
-};
-
-SimileAjax.EventIndex._Iterator.prototype = {
-    hasNext: function() { return this._hasNext; },
-    next: function() {
-        if (this._hasNext) {
-            var next = this._next;
-            this._findNext();
-
-            return next;
-        } else {
-            return null;
-        }
-    },
-    _findNext: function() {
-        var unit = this._unit;
-        while ((++this._currentIndex) < this._maxIndex) {
-            var evt = this._events.elementAt(this._currentIndex);
-            if (unit.compare(evt.getStart(), this._endDate) < 0 &&
-                unit.compare(evt.getEnd(), this._startDate) > 0) {
-
-                this._next = evt;
-                this._hasNext = true;
-                return;
-            }
-        }
-        this._next = null;
-        this._hasNext = false;
-    }
-};
-
-SimileAjax.EventIndex._ReverseIterator = function(events, startDate, endDate, unit) {
-    this._events = events;
-    this._startDate = startDate;
-    this._endDate = endDate;
-    this._unit = unit;
-
-    this._minIndex = events.find(function(evt) {
-        return unit.compare(evt.getStart(), startDate);
-    });
-    if (this._minIndex - 1 >= 0) {
-        this._minIndex = this._events.elementAt(this._minIndex - 1)._earliestOverlapIndex;
-    }
-
-    this._maxIndex = events.find(function(evt) {
-        return unit.compare(evt.getStart(), endDate);
-    });
-
-    this._currentIndex = this._maxIndex;
-    this._hasNext = false;
-    this._next = null;
-    this._findNext();
-};
-
-SimileAjax.EventIndex._ReverseIterator.prototype = {
-    hasNext: function() { return this._hasNext; },
-    next: function() {
-        if (this._hasNext) {
-            var next = this._next;
-            this._findNext();
-
-            return next;
-        } else {
-            return null;
-        }
-    },
-    _findNext: function() {
-        var unit = this._unit;
-        while ((--this._currentIndex) >= this._minIndex) {
-            var evt = this._events.elementAt(this._currentIndex);
-            if (unit.compare(evt.getStart(), this._endDate) < 0 &&
-                unit.compare(evt.getEnd(), this._startDate) > 0) {
-
-                this._next = evt;
-                this._hasNext = true;
-                return;
-            }
-        }
-        this._next = null;
-        this._hasNext = false;
-    }
-};
-
-SimileAjax.EventIndex._AllIterator = function(events) {
-    this._events = events;
-    this._index = 0;
-};
-
-SimileAjax.EventIndex._AllIterator.prototype = {
-    hasNext: function() {
-        return this._index < this._events.length();
-    },
-    next: function() {
-        return this._index < this._events.length() ?
-            this._events.elementAt(this._index++) : null;
-    }
-};/*
- *  Default Unit
- *
- */
-
-SimileAjax.NativeDateUnit = new Object();
-
-SimileAjax.NativeDateUnit.makeDefaultValue = function() {
-    return new Date();
-};
-
-SimileAjax.NativeDateUnit.cloneValue = function(v) {
-    return new Date(v.getTime());
-};
-
-SimileAjax.NativeDateUnit.getParser = function(format) {
-    if (typeof format == "string") {
-        format = format.toLowerCase();
-    }
-    return (format == "iso8601" || format == "iso 8601") ?
-        SimileAjax.DateTime.parseIso8601DateTime : 
-        SimileAjax.DateTime.parseGregorianDateTime;
-};
-
-SimileAjax.NativeDateUnit.parseFromObject = function(o) {
-    return SimileAjax.DateTime.parseGregorianDateTime(o);
-};
-
-SimileAjax.NativeDateUnit.toNumber = function(v) {
-    return v.getTime();
-};
-
-SimileAjax.NativeDateUnit.fromNumber = function(n) {
-    return new Date(n);
-};
-
-SimileAjax.NativeDateUnit.compare = function(v1, v2) {
-    var n1, n2;
-    if (typeof v1 == "object") {
-        n1 = v1.getTime();
-    } else {
-        n1 = Number(v1);
-    }
-    if (typeof v2 == "object") {
-        n2 = v2.getTime();
-    } else {
-        n2 = Number(v2);
-    }
-    
-    return n1 - n2;
-};
-
-SimileAjax.NativeDateUnit.earlier = function(v1, v2) {
-    return SimileAjax.NativeDateUnit.compare(v1, v2) < 0 ? v1 : v2;
-};
-
-SimileAjax.NativeDateUnit.later = function(v1, v2) {
-    return SimileAjax.NativeDateUnit.compare(v1, v2) > 0 ? v1 : v2;
-};
-
-SimileAjax.NativeDateUnit.change = function(v, n) {
-    return new Date(v.getTime() + n);
-};
-
-/*
- *  General, miscellaneous SimileAjax stuff
- *
- */
-
-SimileAjax.ListenerQueue = function(wildcardHandlerName) {
-    this._listeners = [];
-    this._wildcardHandlerName = wildcardHandlerName;
-};
-
-SimileAjax.ListenerQueue.prototype.add = function(listener) {
-    this._listeners.push(listener);
-};
-
-SimileAjax.ListenerQueue.prototype.remove = function(listener) {
-    var listeners = this._listeners;
-    for (var i = 0; i < listeners.length; i++) {
-        if (listeners[i] == listener) {
-            listeners.splice(i, 1);
-            break;
-        }
-    }
-};
-
-SimileAjax.ListenerQueue.prototype.fire = function(handlerName, args) {
-    var listeners = [].concat(this._listeners);
-    for (var i = 0; i < listeners.length; i++) {
-        var listener = listeners[i];
-        if (handlerName in listener) {
-            try {
-                listener[handlerName].apply(listener, args);
-            } catch (e) {
-                SimileAjax.Debug.exception("Error firing event of name " + handlerName, e);
-            }
-        } else if (this._wildcardHandlerName != null &&
-            this._wildcardHandlerName in listener) {
-            try {
-                listener[this._wildcardHandlerName].apply(listener, [ handlerName ]);
-            } catch (e) {
-                SimileAjax.Debug.exception("Error firing event of name " + handlerName + " to wildcard handler", e);
-            }
-        }
-    }
-};
-
-/*
- *  History
- *
- *  This is a singleton that keeps track of undoable user actions and
- *  performs undos and redos in response to the browser's Back and
- *  Forward buttons.
- *
- *  Call addAction(action) to register an undoable user action. action
- *  must have 4 fields:
- *
- *      perform: an argument-less function that carries out the action
- *      undo:    an argument-less function that undos the action
- *      label:   a short, user-friendly string describing the action
- *      uiLayer: the UI layer on which the action takes place
- *
- *  By default, the history keeps track of upto 10 actions. You can
- *  configure this behavior by setting
- *      SimileAjax.History.maxHistoryLength
- *  to a different number.
- *
- *  An iframe is inserted into the document's body element to track
- *  onload events.
- *
- */
-
-SimileAjax.History = {
-    maxHistoryLength:       10,
-    historyFile:            "__history__.html",
-    enabled:               true,
-
-    _initialized:           false,
-    _listeners:             new SimileAjax.ListenerQueue(),
-
-    _actions:               [],
-    _baseIndex:             0,
-    _currentIndex:          0,
-
-    _plainDocumentTitle:    document.title
-};
-
-SimileAjax.History.formatHistoryEntryTitle = function(actionLabel) {
-    return SimileAjax.History._plainDocumentTitle + " {" + actionLabel + "}";
-};
-
-SimileAjax.History.initialize = function() {
-    if (SimileAjax.History._initialized) {
-        return;
-    }
-
-    if (SimileAjax.History.enabled) {
-        var iframe = document.createElement("iframe");
-        iframe.id = "simile-ajax-history";
-        iframe.style.position = "absolute";
-        iframe.style.width = "10px";
-        iframe.style.height = "10px";
-        iframe.style.top = "0px";
-        iframe.style.left = "0px";
-        iframe.style.visibility = "hidden";
-        iframe.src = SimileAjax.History.historyFile + "?0";
-
-        document.body.appendChild(iframe);
-        SimileAjax.DOM.registerEvent(iframe, "load", SimileAjax.History._handleIFrameOnLoad);
-
-        SimileAjax.History._iframe = iframe;
-    }
-    SimileAjax.History._initialized = true;
-};
-
-SimileAjax.History.addListener = function(listener) {
-    SimileAjax.History.initialize();
-
-    SimileAjax.History._listeners.add(listener);
-};
-
-SimileAjax.History.removeListener = function(listener) {
-    SimileAjax.History.initialize();
-
-    SimileAjax.History._listeners.remove(listener);
-};
-
-SimileAjax.History.addAction = function(action) {
-    SimileAjax.History.initialize();
-
-    SimileAjax.History._listeners.fire("onBeforePerform", [ action ]);
-    window.setTimeout(function() {
-        try {
-            action.perform();
-            SimileAjax.History._listeners.fire("onAfterPerform", [ action ]);
-
-            if (SimileAjax.History.enabled) {
-                SimileAjax.History._actions = SimileAjax.History._actions.slice(
-                    0, SimileAjax.History._currentIndex - SimileAjax.History._baseIndex);
-
-                SimileAjax.History._actions.push(action);
-                SimileAjax.History._currentIndex++;
-
-                var diff = SimileAjax.History._actions.length - SimileAjax.History.maxHistoryLength;
-                if (diff > 0) {
-                    SimileAjax.History._actions = SimileAjax.History._actions.slice(diff);
-                    SimileAjax.History._baseIndex += diff;
-                }
-
-                try {
-                    SimileAjax.History._iframe.contentWindow.location.search =
-                        "?" + SimileAjax.History._currentIndex;
-                } catch (e) {
-                    /*
-                     *  We can't modify location.search most probably because it's a file:// url.
-                     *  We'll just going to modify the document's title.
-                     */
-                    var title = SimileAjax.History.formatHistoryEntryTitle(action.label);
-                    document.title = title;
-                }
-            }
-        } catch (e) {
-            SimileAjax.Debug.exception(e, "Error adding action {" + action.label + "} to history");
-        }
-    }, 0);
-};
-
-SimileAjax.History.addLengthyAction = function(perform, undo, label) {
-    SimileAjax.History.addAction({
-        perform:    perform,
-        undo:       undo,
-        label:      label,
-        uiLayer:    SimileAjax.WindowManager.getBaseLayer(),
-        lengthy:    true
-    });
-};
-
-SimileAjax.History._handleIFrameOnLoad = function() {
-    /*
-     *  This function is invoked when the user herself
-     *  navigates backward or forward. We need to adjust
-     *  the application's state accordingly.
-     */
-
-    try {
-        var q = SimileAjax.History._iframe.contentWindow.location.search;
-        var c = (q.length == 0) ? 0 : Math.max(0, parseInt(q.substr(1)));
-
-        var finishUp = function() {
-            var diff = c - SimileAjax.History._currentIndex;
-            SimileAjax.History._currentIndex += diff;
-            SimileAjax.History._baseIndex += diff;
-
-            SimileAjax.History._iframe.contentWindow.location.search = "?" + c;
-        };
-
-        if (c < SimileAjax.History._currentIndex) { // need to undo
-            SimileAjax.History._listeners.fire("onBeforeUndoSeveral", []);
-            window.setTimeout(function() {
-                while (SimileAjax.History._currentIndex > c &&
-                       SimileAjax.History._currentIndex > SimileAjax.History._baseIndex) {
-
-                    SimileAjax.History._currentIndex--;
-
-                    var action = SimileAjax.History._actions[SimileAjax.History._currentIndex - SimileAjax.History._baseIndex];
-
-                    try {
-                        action.undo();
-                    } catch (e) {
-                        SimileAjax.Debug.exception(e, "History: Failed to undo action {" + action.label + "}");
-                    }
-                }
-
-                SimileAjax.History._listeners.fire("onAfterUndoSeveral", []);
-                finishUp();
-            }, 0);
-        } else if (c > SimileAjax.History._currentIndex) { // need to redo
-            SimileAjax.History._listeners.fire("onBeforeRedoSeveral", []);
-            window.setTimeout(function() {
-                while (SimileAjax.History._currentIndex < c &&
-                       SimileAjax.History._currentIndex - SimileAjax.History._baseIndex < SimileAjax.History._actions.length) {
-
-                    var action = SimileAjax.History._actions[SimileAjax.History._currentIndex - SimileAjax.History._baseIndex];
-
-                    try {
-                        action.perform();
-                    } catch (e) {
-                        SimileAjax.Debug.exception(e, "History: Failed to redo action {" + action.label + "}");
-                    }
-
-                    SimileAjax.History._currentIndex++;
-                }
-
-                SimileAjax.History._listeners.fire("onAfterRedoSeveral", []);
-                finishUp();
-            }, 0);
-        } else {
-            var index = SimileAjax.History._currentIndex - SimileAjax.History._baseIndex - 1;
-            var title = (index >= 0 && index < SimileAjax.History._actions.length) ?
-                SimileAjax.History.formatHistoryEntryTitle(SimileAjax.History._actions[index].label) :
-                SimileAjax.History._plainDocumentTitle;
-
-            SimileAjax.History._iframe.contentWindow.document.title = title;
-            document.title = title;
-        }
-    } catch (e) {
-        // silent
-    }
-};
-
-SimileAjax.History.getNextUndoAction = function() {
-    try {
-        var index = SimileAjax.History._currentIndex - SimileAjax.History._baseIndex - 1;
-        return SimileAjax.History._actions[index];
-    } catch (e) {
-        return null;
-    }
-};
-
-SimileAjax.History.getNextRedoAction = function() {
-    try {
-        var index = SimileAjax.History._currentIndex - SimileAjax.History._baseIndex;
-        return SimileAjax.History._actions[index];
-    } catch (e) {
-        return null;
-    }
-};
-/**
- * @fileOverview UI layers and window-wide dragging
- * @name SimileAjax.WindowManager
- */
-
-/**
- *  This is a singleton that keeps track of UI layers (modal and
- *  modeless) and enables/disables UI elements based on which layers
- *  they belong to. It also provides window-wide dragging
- *  implementation.
- */
-SimileAjax.WindowManager = {
-    _initialized:       false,
-    _listeners:         [],
-
-    _draggedElement:                null,
-    _draggedElementCallback:        null,
-    _dropTargetHighlightElement:    null,
-    _lastCoords:                    null,
-    _ghostCoords:                   null,
-    _draggingMode:                  "",
-    _dragging:                      false,
-
-    _layers:            []
-};
-
-SimileAjax.WindowManager.initialize = function() {
-    if (SimileAjax.WindowManager._initialized) {
-        return;
-    }
-
-    SimileAjax.DOM.registerEvent(document.body, "mousedown", SimileAjax.WindowManager._onBodyMouseDown);
-    SimileAjax.DOM.registerEvent(document.body, "mousemove", SimileAjax.WindowManager._onBodyMouseMove);
-    SimileAjax.DOM.registerEvent(document.body, "mouseup",   SimileAjax.WindowManager._onBodyMouseUp);
-    SimileAjax.DOM.registerEvent(document, "keydown",       SimileAjax.WindowManager._onBodyKeyDown);
-    SimileAjax.DOM.registerEvent(document, "keyup",         SimileAjax.WindowManager._onBodyKeyUp);
-
-    SimileAjax.WindowManager._layers.push({index: 0});
-
-    SimileAjax.WindowManager._historyListener = {
-        onBeforeUndoSeveral:    function() {},
-        onAfterUndoSeveral:     function() {},
-        onBeforeUndo:           function() {},
-        onAfterUndo:            function() {},
-
-        onBeforeRedoSeveral:    function() {},
-        onAfterRedoSeveral:     function() {},
-        onBeforeRedo:           function() {},
-        onAfterRedo:            function() {}
-    };
-    SimileAjax.History.addListener(SimileAjax.WindowManager._historyListener);
-
-    SimileAjax.WindowManager._initialized = true;
-};
-
-SimileAjax.WindowManager.getBaseLayer = function() {
-    SimileAjax.WindowManager.initialize();
-    return SimileAjax.WindowManager._layers[0];
-};
-
-SimileAjax.WindowManager.getHighestLayer = function() {
-    SimileAjax.WindowManager.initialize();
-    return SimileAjax.WindowManager._layers[SimileAjax.WindowManager._layers.length - 1];
-};
-
-SimileAjax.WindowManager.registerEventWithObject = function(elmt, eventName, obj, handlerName, layer) {
-    SimileAjax.WindowManager.registerEvent(
-        elmt,
-        eventName,
-        function(elmt2, evt, target) {
-            return obj[handlerName].call(obj, elmt2, evt, target);
-        },
-        layer
-    );
-};
-
-SimileAjax.WindowManager.registerEvent = function(elmt, eventName, handler, layer) {
-    if (layer == null) {
-        layer = SimileAjax.WindowManager.getHighestLayer();
-    }
-
-    var handler2 = function(elmt, evt, target) {
-        if (SimileAjax.WindowManager._canProcessEventAtLayer(layer)) {
-            SimileAjax.WindowManager._popToLayer(layer.index);
-            try {
-                handler(elmt, evt, target);
-            } catch (e) {
-                SimileAjax.Debug.exception(e);
-            }
-        }
-        SimileAjax.DOM.cancelEvent(evt);
-        return false;
-    }
-
-    SimileAjax.DOM.registerEvent(elmt, eventName, handler2);
-};
-
-SimileAjax.WindowManager.pushLayer = function(f, ephemeral, elmt) {
-    var layer = { onPop: f, index: SimileAjax.WindowManager._layers.length, ephemeral: (ephemeral), elmt: elmt };
-    SimileAjax.WindowManager._layers.push(layer);
-
-    return layer;
-};
-
-SimileAjax.WindowManager.popLayer = function(layer) {
-    for (var i = 1; i < SimileAjax.WindowManager._layers.length; i++) {
-        if (SimileAjax.WindowManager._layers[i] == layer) {
-            SimileAjax.WindowManager._popToLayer(i - 1);
-            break;
-        }
-    }
-};
-
-SimileAjax.WindowManager.popAllLayers = function() {
-    SimileAjax.WindowManager._popToLayer(0);
-};
-
-SimileAjax.WindowManager.registerForDragging = function(elmt, callback, layer) {
-    SimileAjax.WindowManager.registerEvent(
-        elmt,
-        "mousedown",
-        function(elmt, evt, target) {
-            SimileAjax.WindowManager._handleMouseDown(elmt, evt, callback);
-        },
-        layer
-    );
-};
-
-SimileAjax.WindowManager._popToLayer = function(level) {
-    while (level+1 < SimileAjax.WindowManager._layers.length) {
-        try {
-            var layer = SimileAjax.WindowManager._layers.pop();
-            if (layer.onPop != null) {
-                layer.onPop();
-            }
-        } catch (e) {
-        }
-    }
-};
-
-SimileAjax.WindowManager._canProcessEventAtLayer = function(layer) {
-    if (layer.index == (SimileAjax.WindowManager._layers.length - 1)) {
-        return true;
-    }
-    for (var i = layer.index + 1; i < SimileAjax.WindowManager._layers.length; i++) {
-        if (!SimileAjax.WindowManager._layers[i].ephemeral) {
-            return false;
-        }
-    }
-    return true;
-};
-
-SimileAjax.WindowManager.cancelPopups = function(evt) {
-    var evtCoords = (evt) ? SimileAjax.DOM.getEventPageCoordinates(evt) : { x: -1, y: -1 };
-
-    var i = SimileAjax.WindowManager._layers.length - 1;
-    while (i > 0 && SimileAjax.WindowManager._layers[i].ephemeral) {
-        var layer = SimileAjax.WindowManager._layers[i];
-        if (layer.elmt != null) { // if event falls within main element of layer then don't cancel
-            var elmt = layer.elmt;
-            var elmtCoords = SimileAjax.DOM.getPageCoordinates(elmt);
-            if (evtCoords.x >= elmtCoords.left && evtCoords.x < (elmtCoords.left + elmt.offsetWidth) &&
-                evtCoords.y >= elmtCoords.top && evtCoords.y < (elmtCoords.top + elmt.offsetHeight)) {
-                break;
-            }
-        }
-        i--;
-    }
-    SimileAjax.WindowManager._popToLayer(i);
-};
-
-SimileAjax.WindowManager._onBodyMouseDown = function(elmt, evt, target) {
-    if (!("eventPhase" in evt) || evt.eventPhase == evt.BUBBLING_PHASE) {
-        SimileAjax.WindowManager.cancelPopups(evt);
-    }
-};
-
-SimileAjax.WindowManager._handleMouseDown = function(elmt, evt, callback) {
-    SimileAjax.WindowManager._draggedElement = elmt;
-    SimileAjax.WindowManager._draggedElementCallback = callback;
-    SimileAjax.WindowManager._lastCoords = { x: evt.clientX, y: evt.clientY };
-
-    SimileAjax.DOM.cancelEvent(evt);
-    return false;
-};
-
-SimileAjax.WindowManager._onBodyKeyDown = function(elmt, evt, target) {
-    if (SimileAjax.WindowManager._dragging) {
-        if (evt.keyCode == 27) { // esc
-            SimileAjax.WindowManager._cancelDragging();
-        } else if ((evt.keyCode == 17 || evt.keyCode == 16) && SimileAjax.WindowManager._draggingMode != "copy") {
-            SimileAjax.WindowManager._draggingMode = "copy";
-
-            var img = SimileAjax.Graphics.createTranslucentImage(SimileAjax.urlPrefix + "images/copy.png");
-            img.style.position = "absolute";
-            img.style.left = (SimileAjax.WindowManager._ghostCoords.left - 16) + "px";
-            img.style.top = (SimileAjax.WindowManager._ghostCoords.top) + "px";
-            document.body.appendChild(img);
-
-            SimileAjax.WindowManager._draggingModeIndicatorElmt = img;
-        }
-    }
-};
-
-SimileAjax.WindowManager._onBodyKeyUp = function(elmt, evt, target) {
-    if (SimileAjax.WindowManager._dragging) {
-        if (evt.keyCode == 17 || evt.keyCode == 16) {
-            SimileAjax.WindowManager._draggingMode = "";
-            if (SimileAjax.WindowManager._draggingModeIndicatorElmt != null) {
-                document.body.removeChild(SimileAjax.WindowManager._draggingModeIndicatorElmt);
-                SimileAjax.WindowManager._draggingModeIndicatorElmt = null;
-            }
-        }
-    }
-};
-
-SimileAjax.WindowManager._onBodyMouseMove = function(elmt, evt, target) {
-    if (SimileAjax.WindowManager._draggedElement != null) {
-        var callback = SimileAjax.WindowManager._draggedElementCallback;
-
-        var lastCoords = SimileAjax.WindowManager._lastCoords;
-        var diffX = evt.clientX - lastCoords.x;
-        var diffY = evt.clientY - lastCoords.y;
-
-        if (!SimileAjax.WindowManager._dragging) {
-            if (Math.abs(diffX) > 5 || Math.abs(diffY) > 5) {
-                try {
-                    if ("onDragStart" in callback) {
-                        callback.onDragStart();
-                    }
-
-                    if ("ghost" in callback && callback.ghost) {
-                        var draggedElmt = SimileAjax.WindowManager._draggedElement;
-
-                        SimileAjax.WindowManager._ghostCoords = SimileAjax.DOM.getPageCoordinates(draggedElmt);
-                        SimileAjax.WindowManager._ghostCoords.left += diffX;
-                        SimileAjax.WindowManager._ghostCoords.top += diffY;
-
-                        var ghostElmt = draggedElmt.cloneNode(true);
-                        ghostElmt.style.position = "absolute";
-                        ghostElmt.style.left = SimileAjax.WindowManager._ghostCoords.left + "px";
-                        ghostElmt.style.top = SimileAjax.WindowManager._ghostCoords.top + "px";
-                        ghostElmt.style.zIndex = 1000;
-                        SimileAjax.Graphics.setOpacity(ghostElmt, 50);
-
-                        document.body.appendChild(ghostElmt);
-                        callback._ghostElmt = ghostElmt;
-                    }
-
-                    SimileAjax.WindowManager._dragging = true;
-                    SimileAjax.WindowManager._lastCoords = { x: evt.clientX, y: evt.clientY };
-
-                    document.body.focus();
-                } catch (e) {
-                    SimileAjax.Debug.exception("WindowManager: Error handling mouse down", e);
-                    SimileAjax.WindowManager._cancelDragging();
-                }
-            }
-        } else {
-            try {
-                SimileAjax.WindowManager._lastCoords = { x: evt.clientX, y: evt.clientY };
-
-                if ("onDragBy" in callback) {
-                    callback.onDragBy(diffX, diffY);
-                }
-
-                if ("_ghostElmt" in callback) {
-                    var ghostElmt = callback._ghostElmt;
-
-                    SimileAjax.WindowManager._ghostCoords.left += diffX;
-                    SimileAjax.WindowManager._ghostCoords.top += diffY;
-
-                    ghostElmt.style.left = SimileAjax.WindowManager._ghostCoords.left + "px";
-                    ghostElmt.style.top = SimileAjax.WindowManager._ghostCoords.top + "px";
-                    if (SimileAjax.WindowManager._draggingModeIndicatorElmt != null) {
-                        var indicatorElmt = SimileAjax.WindowManager._draggingModeIndicatorElmt;
-
-                        indicatorElmt.style.left = (SimileAjax.WindowManager._ghostCoords.left - 16) + "px";
-                        indicatorElmt.style.top = SimileAjax.WindowManager._ghostCoords.top + "px";
-                    }
-
-                    if ("droppable" in callback && callback.droppable) {
-                        var coords = SimileAjax.DOM.getEventPageCoordinates(evt);
-                        var target = SimileAjax.DOM.hittest(
-                            coords.x, coords.y,
-                            [   SimileAjax.WindowManager._ghostElmt,
-                                SimileAjax.WindowManager._dropTargetHighlightElement
-                            ]
-                        );
-                        target = SimileAjax.WindowManager._findDropTarget(target);
-
-                        if (target != SimileAjax.WindowManager._potentialDropTarget) {
-                            if (SimileAjax.WindowManager._dropTargetHighlightElement != null) {
-                                document.body.removeChild(SimileAjax.WindowManager._dropTargetHighlightElement);
-
-                                SimileAjax.WindowManager._dropTargetHighlightElement = null;
-                                SimileAjax.WindowManager._potentialDropTarget = null;
-                            }
-
-                            var droppable = false;
-                            if (target != null) {
-                                if ((!("canDropOn" in callback) || callback.canDropOn(target)) &&
-                                    (!("canDrop" in target) || target.canDrop(SimileAjax.WindowManager._draggedElement))) {
-
-                                    droppable = true;
-                                }
-                            }
-
-                            if (droppable) {
-                                var border = 4;
-                                var targetCoords = SimileAjax.DOM.getPageCoordinates(target);
-                                var highlight = document.createElement("div");
-                                highlight.style.border = border + "px solid yellow";
-                                highlight.style.backgroundColor = "yellow";
-                                highlight.style.position = "absolute";
-                                highlight.style.left = targetCoords.left + "px";
-                                highlight.style.top = targetCoords.top + "px";
-                                highlight.style.width = (target.offsetWidth - border * 2) + "px";
-                                highlight.style.height = (target.offsetHeight - border * 2) + "px";
-                                SimileAjax.Graphics.setOpacity(highlight, 30);
-                                document.body.appendChild(highlight);
-
-                                SimileAjax.WindowManager._potentialDropTarget = target;
-                                SimileAjax.WindowManager._dropTargetHighlightElement = highlight;
-                            }
-                        }
-                    }
-                }
-            } catch (e) {
-                SimileAjax.Debug.exception("WindowManager: Error handling mouse move", e);
-                SimileAjax.WindowManager._cancelDragging();
-            }
-        }
-
-        SimileAjax.DOM.cancelEvent(evt);
-        return false;
-    }
-};
-
-SimileAjax.WindowManager._onBodyMouseUp = function(elmt, evt, target) {
-    if (SimileAjax.WindowManager._draggedElement != null) {
-        try {
-            if (SimileAjax.WindowManager._dragging) {
-                var callback = SimileAjax.WindowManager._draggedElementCallback;
-                if ("onDragEnd" in callback) {
-                    callback.onDragEnd();
-                }
-                if ("droppable" in callback && callback.droppable) {
-                    var dropped = false;
-
-                    var target = SimileAjax.WindowManager._potentialDropTarget;
-                    if (target != null) {
-                        if ((!("canDropOn" in callback) || callback.canDropOn(target)) &&
-                            (!("canDrop" in target) || target.canDrop(SimileAjax.WindowManager._draggedElement))) {
-
-                            if ("onDropOn" in callback) {
-                                callback.onDropOn(target);
-                            }
-                            target.ondrop(SimileAjax.WindowManager._draggedElement, SimileAjax.WindowManager._draggingMode);
-
-                            dropped = true;
-                        }
-                    }
-
-                    if (!dropped) {
-                        // TODO: do holywood explosion here
-                    }
-                }
-            }
-        } finally {
-            SimileAjax.WindowManager._cancelDragging();
-        }
-
-        SimileAjax.DOM.cancelEvent(evt);
-        return false;
-    }
-};
-
-SimileAjax.WindowManager._cancelDragging = function() {
-    var callback = SimileAjax.WindowManager._draggedElementCallback;
-    if ("_ghostElmt" in callback) {
-        var ghostElmt = callback._ghostElmt;
-        document.body.removeChild(ghostElmt);
-
-        delete callback._ghostElmt;
-    }
-    if (SimileAjax.WindowManager._dropTargetHighlightElement != null) {
-        document.body.removeChild(SimileAjax.WindowManager._dropTargetHighlightElement);
-        SimileAjax.WindowManager._dropTargetHighlightElement = null;
-    }
-    if (SimileAjax.WindowManager._draggingModeIndicatorElmt != null) {
-        document.body.removeChild(SimileAjax.WindowManager._draggingModeIndicatorElmt);
-        SimileAjax.WindowManager._draggingModeIndicatorElmt = null;
-    }
-
-    SimileAjax.WindowManager._draggedElement = null;
-    SimileAjax.WindowManager._draggedElementCallback = null;
-    SimileAjax.WindowManager._potentialDropTarget = null;
-    SimileAjax.WindowManager._dropTargetHighlightElement = null;
-    SimileAjax.WindowManager._lastCoords = null;
-    SimileAjax.WindowManager._ghostCoords = null;
-    SimileAjax.WindowManager._draggingMode = "";
-    SimileAjax.WindowManager._dragging = false;
-};
-
-SimileAjax.WindowManager._findDropTarget = function(elmt) {
-    while (elmt != null) {
-        if ("ondrop" in elmt && (typeof elmt.ondrop) == "function") {
-            break;
-        }
-        elmt = elmt.parentNode;
-    }
-    return elmt;
-};
-/*
- *  Timeline API
- *
- *  This file will load all the Javascript files
- *  necessary to make the standard timeline work.
- *  It also detects the default locale.
- *
- *  To run from the MIT copy of Timeline:
- *  Include this file in your HTML file as follows:
- *
- *    <script src="http://api.simile-widgets.org/timeline/2.3.1/timeline-api.js"
- *     type="text/javascript"></script>
- *
- *
- * To host the Timeline files on your own server:
- *   1) Install the Timeline and Simile-Ajax files onto your webserver using
- *      timeline_libraries.zip or timeline_source.zip
- *
- *   2) Set global js variables used to send parameters to this script:
- *        var Timeline_ajax_url -- url for simile-ajax-api.js
- *        var Timeline_urlPrefix -- url for the *directory* that contains timeline-api.js
- *            Include trailing slash
- *        var Timeline_parameters='bundle=true'; // you must set bundle to true if you are using
- *                                               // timeline_libraries.zip since only the
- *                                               // bundled libraries are included
- *
- * eg your html page would include
- *
- *   <script>
- *     var Timeline_ajax_url="http://YOUR_SERVER/javascripts/timeline/timeline_ajax/simile-ajax-api.js";
- *     var Timeline_urlPrefix='http://YOUR_SERVER/javascripts/timeline/timeline_js/';
- *     var Timeline_parameters='bundle=true';
- *   </script>
- *   <script src="http://YOUR_SERVER/javascripts/timeline/timeline_js/timeline-api.js"
- *     type="text/javascript">
- *   </script>
- *
- * SCRIPT PARAMETERS
- * This script auto-magically figures out locale and has defaults for other parameters
- * To set parameters explicity, set js global variable Timeline_parameters or include as
- * parameters on the url using GET style. Eg the two next lines pass the same parameters:
- *     Timeline_parameters='bundle=true';                    // pass parameter via js variable
- *     <script src="http://....timeline-api.js?bundle=true"  // pass parameter via url
- *
- * Parameters
- *   timeline-use-local-resources --
- *   bundle -- true: use the single js bundle file; false: load individual files (for debugging)
- *   locales --
- *   defaultLocale --
- *   forceLocale -- force locale to be a particular value--used for debugging. Normally locale is determined
- *                  by browser's and server's locale settings.
- *
- * DEBUGGING
- * If you have a problem with Timeline, the first step is to use the unbundled Javascript files. To do so:
- * To use the unbundled Timeline and Ajax libraries
- * Change
- *   <script src="http://api.simile-widgets.org/timeline/2.3.1/api/timeline-api.js?bundle=true" type="text/javascript"></script>
- * To
- *   <script>var Timeline_ajax_url = "http://api.simile-widgets.org/ajax/2.2.1/simile-ajax-api.js?bundle=false"</script>
- *   <script src="http://api.simile-widgets.org/timeline/2.3.1/api/timeline-api.js?bundle=false" type="text/javascript"></script>
- *
- * Note that the Ajax version is usually NOT the same as the Timeline version.
- * See variable simile_ajax_ver below for the current version
- *
- *
- */
-
-(function() {
-
-    var simile_ajax_ver = "2.2.1"; // ===========>>>  current Simile-Ajax version
-
-    var useLocalResources = false;
-    if (document.location.search.length > 0) {
-        var params = document.location.search.substr(1).split("&");
-        for (var i = 0; i < params.length; i++) {
-            if (params[i] == "timeline-use-local-resources") {
-                useLocalResources = true;
-            }
-        }
-    };
-
-    var loadMe = function() {
-        if ("Timeline" in window) {
-            return;
-        }
-
-        window.Timeline = new Object();
-        window.Timeline.DateTime = window.SimileAjax.DateTime; // for backward compatibility
-
-        var bundle = false;
-        var javascriptFiles = [
-            "timeline.js",
-            "band.js",
-            "themes.js",
-            "ethers.js",
-            "ether-painters.js",
-            "event-utils.js",
-            "labellers.js",
-            "sources.js",
-            "original-painter.js",
-            "detailed-painter.js",
-            "overview-painter.js",
-            "compact-painter.js",
-            "decorators.js",
-            "units.js"
-        ];
-        var cssFiles = [
-            "timeline.css",
-            "ethers.css",
-            "events.css"
-        ];
-
-        var localizedJavascriptFiles = [
-            "timeline.js",
-            "labellers.js"
-        ];
-        var localizedCssFiles = [
-        ];
-
-        // ISO-639 language codes, ISO-3166 country codes (2 characters)
-        var supportedLocales = [
-            "cs",       // Czech
-            "de",       // German
-            "en",       // English
-            "es",       // Spanish
-            "fr",       // French
-            "it",       // Italian
-            "nl",       // Dutch (The Netherlands)
-            "ru",       // Russian
-            "se",       // Swedish
-            "tr",       // Turkish
-            "vi",       // Vietnamese
-            "zh"        // Chinese
-        ];
-
-        try {
-            var desiredLocales = [ "en" ],
-                defaultServerLocale = "en",
-                forceLocale = null;
-
-            var parseURLParameters = function(parameters) {
-                var params = parameters.split("&");
-                for (var p = 0; p < params.length; p++) {
-                    var pair = params[p].split("=");
-                    if (pair[0] == "locales") {
-                        desiredLocales = desiredLocales.concat(pair[1].split(","));
-                    } else if (pair[0] == "defaultLocale") {
-                        defaultServerLocale = pair[1];
-                    } else if (pair[0] == "forceLocale") {
-                        forceLocale = pair[1];
-                        desiredLocales = desiredLocales.concat(pair[1].split(","));
-                    } else if (pair[0] == "bundle") {
-                        bundle = pair[1] != "false";
-                    }
-                }
-            };
-
-            (function() {
-                if (typeof Timeline_urlPrefix == "string") {
-                    Timeline.urlPrefix = Timeline_urlPrefix;
-                    if (typeof Timeline_parameters == "string") {
-                        parseURLParameters(Timeline_parameters);
-                    }
-                } else {
-                    var heads = document.documentElement.getElementsByTagName("head");
-                    for (var h = 0; h < heads.length; h++) {
-                        var scripts = heads[h].getElementsByTagName("script");
-                        for (var s = 0; s < scripts.length; s++) {
-                            var url = scripts[s].src;
-                            var i = url.indexOf("timeline-api.js");
-                            if (i >= 0) {
-                                Timeline.urlPrefix = url.substr(0, i);
-                                var q = url.indexOf("?");
-                                if (q > 0) {
-                                    parseURLParameters(url.substr(q + 1));
-                                }
-                                return;
-                            }
-                        }
-                    }
-                    throw new Error("Failed to derive URL prefix for Timeline API code files");
-                }
-            })();
-
-            var includeJavascriptFiles = function(urlPrefix, filenames) {
-                SimileAjax.includeJavascriptFiles(document, urlPrefix, filenames);
-            }
-            var includeCssFiles = function(urlPrefix, filenames) {
-                SimileAjax.includeCssFiles(document, urlPrefix, filenames);
-            }
-
-            /*
-             *  Include non-localized files
-             */
-            if (bundle) {
-                includeJavascriptFiles(Timeline.urlPrefix, [ "timeline-bundle.js" ]);
-                includeCssFiles(Timeline.urlPrefix, [ "timeline-bundle.css" ]);
-            } else {
-                // XXX adim includeJavascriptFiles(Timeline.urlPrefix + "scripts/", javascriptFiles);
-                // XXX adim includeCssFiles(Timeline.urlPrefix + "styles/", cssFiles);
-            }
-
-            /*
-             *  Include localized files
-             */
-            var loadLocale = [];
-            loadLocale[defaultServerLocale] = true;
-
-            var tryExactLocale = function(locale) {
-                for (var l = 0; l < supportedLocales.length; l++) {
-                    if (locale == supportedLocales[l]) {
-                        loadLocale[locale] = true;
-                        return true;
-                    }
-                }
-                return false;
-            }
-            var tryLocale = function(locale) {
-                if (tryExactLocale(locale)) {
-                    return locale;
-                }
-
-                var dash = locale.indexOf("-");
-                if (dash > 0 && tryExactLocale(locale.substr(0, dash))) {
-                    return locale.substr(0, dash);
-                }
-
-                return null;
-            }
-
-            for (var l = 0; l < desiredLocales.length; l++) {
-                tryLocale(desiredLocales[l]);
-            }
-
-            var defaultClientLocale = defaultServerLocale;
-            var defaultClientLocales = ("language" in navigator ? navigator.language : navigator.browserLanguage).split(";");
-            for (var l = 0; l < defaultClientLocales.length; l++) {
-                var locale = tryLocale(defaultClientLocales[l]);
-                if (locale != null) {
-                    defaultClientLocale = locale;
-                    break;
-                }
-            }
-
-            for (var l = 0; l < supportedLocales.length; l++) {
-                var locale = supportedLocales[l];
-                if (loadLocale[locale]) {
-                    // XXX adim includeJavascriptFiles(Timeline.urlPrefix + "scripts/l10n/" + locale + "/", localizedJavascriptFiles);
-                    // XXX adim includeCssFiles(Timeline.urlPrefix + "styles/l10n/" + locale + "/", localizedCssFiles);
-                }
-            }
-
-            if (forceLocale == null) {
-              Timeline.serverLocale = defaultServerLocale;
-              Timeline.clientLocale = defaultClientLocale;
-            } else {
-              Timeline.serverLocale = forceLocale;
-              Timeline.clientLocale = forceLocale;
-            }
-        } catch (e) {
-            alert(e);
-        }
-    };
-
-    /*
-     *  Load SimileAjax if it's not already loaded
-     */
-    if (typeof SimileAjax == "undefined") {
-        window.SimileAjax_onLoad = loadMe;
-
-        var url = useLocalResources ?
-            "http://127.0.0.1:9999/ajax/api/simile-ajax-api.js?bundle=false" :
-            "http://api.simile-widgets.org/ajax/" + simile_ajax_ver + "/simile-ajax-api.js";
-        if (typeof Timeline_ajax_url == "string") {
-           url = Timeline_ajax_url;
-        }
-        var createScriptElement = function() {
-            var script = document.createElement("script");
-            script.type = "text/javascript";
-            script.language = "JavaScript";
-            script.src = url;
-            document.getElementsByTagName("head")[0].appendChild(script);
-        }
-        if (document.body == null) {
-            try {
-                document.write("<script src='" + url + "' type='text/javascript'></script>");
-            } catch (e) {
-                createScriptElement();
-            }
-        } else {
-            createScriptElement();
-        }
-    } else {
-        loadMe();
-    }
-})();
-/*
- *
- * Coding standards:
- *
- * We aim towards Douglas Crockford's Javascript conventions.
- * See:  http://javascript.crockford.com/code.html
- * See also: http://www.crockford.com/javascript/javascript.html
- *
- * That said, this JS code was written before some recent JS
- * support libraries became widely used or available.
- * In particular, the _ character is used to indicate a class function or
- * variable that should be considered private to the class.
- *
- * The code mostly uses accessor methods for getting/setting the private
- * class variables.
- *
- * Over time, we'd like to formalize the convention by using support libraries
- * which enforce privacy in objects.
- *
- * We also want to use jslint:  http://www.jslint.com/
- *
- *
- *
- */
-
-
-
-/*
- *  Timeline VERSION
- *
- */
-// Note: version is also stored in the build.xml file
-Timeline.version = 'pre 2.4.0';  // use format 'pre 1.2.3' for trunk versions
-Timeline.ajax_lib_version = SimileAjax.version;
-Timeline.display_version = Timeline.version + ' (with Ajax lib ' + Timeline.ajax_lib_version + ')';
- // cf method Timeline.writeVersion
-
-/*
- *  Timeline
- *
- */
-Timeline.strings = {}; // localization string tables
-Timeline.HORIZONTAL = 0;
-Timeline.VERTICAL = 1;
-Timeline._defaultTheme = null;
-
-Timeline.getDefaultLocale = function() {
-    return Timeline.clientLocale;
-};
-
-Timeline.create = function(elmt, bandInfos, orientation, unit) {
-    if (Timeline.timelines == null) {
-        Timeline.timelines = [];
-        // Timeline.timelines array can have null members--Timelines that
-        // once existed on the page, but were later disposed of.
-    }
-
-    var timelineID = Timeline.timelines.length;
-    Timeline.timelines[timelineID] = null; // placeholder until we have the object
-    var new_tl = new Timeline._Impl(elmt, bandInfos, orientation, unit,
-      timelineID);
-    Timeline.timelines[timelineID] = new_tl;
-    return new_tl;
-};
-
-Timeline.createBandInfo = function(params) {
-    var theme = ("theme" in params) ? params.theme : Timeline.getDefaultTheme();
-
-    var eventSource = ("eventSource" in params) ? params.eventSource : null;
-
-    var etherParams = {
-        interval:           SimileAjax.DateTime.gregorianUnitLengths[params.intervalUnit],
-        pixelsPerInterval: params.intervalPixels,
-	theme: theme
-    };
-    if ('startsOn' in params || 'endsOn' in params) {
-	if ('startsOn' in params) {
-	    etherParams.startsOn = params.startsOn;
-	}
-	if ('endsOn' in params) {
-	    etherParams.endsOn = params.endsOn;
-	}
-    } else {
-	etherParams.centersOn = ("date" in params) ? params.date : new Date();
-    }
-    var ether = new Timeline.LinearEther(etherParams);
-
-    var etherPainter = new Timeline.GregorianEtherPainter({
-        unit:       params.intervalUnit,
-        multiple:   ("multiple" in params) ? params.multiple : 1,
-        theme:      theme,
-        align:      ("align" in params) ? params.align : undefined
-    });
-
-    var eventPainterParams = {
-        showText:   ("showEventText" in params) ? params.showEventText : true,
-        theme:      theme
-    };
-    // pass in custom parameters for the event painter
-    if ("eventPainterParams" in params) {
-        for (var prop in params.eventPainterParams) {
-            eventPainterParams[prop] = params.eventPainterParams[prop];
-        }
-    }
-
-    if ("trackHeight" in params) {
-        eventPainterParams.trackHeight = params.trackHeight;
-    }
-    if ("trackGap" in params) {
-        eventPainterParams.trackGap = params.trackGap;
-    }
-
-    var layout = ("overview" in params && params.overview) ? "overview" : ("layout" in params ? params.layout : "original");
-    var eventPainter;
-    if ("eventPainter" in params) {
-        eventPainter = new params.eventPainter(eventPainterParams);
-    } else {
-        switch (layout) {
-            case "overview" :
-                eventPainter = new Timeline.OverviewEventPainter(eventPainterParams);
-                break;
-            case "detailed" :
-                eventPainter = new Timeline.DetailedEventPainter(eventPainterParams);
-                break;
-            default:
-                eventPainter = new Timeline.OriginalEventPainter(eventPainterParams);
-        }
-    }
-
-    return {
-        width:          params.width,
-        eventSource:    eventSource,
-        timeZone:       ("timeZone" in params) ? params.timeZone : 0,
-        ether:          ether,
-        etherPainter:   etherPainter,
-        eventPainter:   eventPainter,
-        theme:          theme,
-        zoomIndex:      ("zoomIndex" in params) ? params.zoomIndex : 0,
-        zoomSteps:      ("zoomSteps" in params) ? params.zoomSteps : null
-    };
-};
-
-Timeline.createHotZoneBandInfo = function(params) {
-    var theme = ("theme" in params) ? params.theme : Timeline.getDefaultTheme();
-
-    var eventSource = ("eventSource" in params) ? params.eventSource : null;
-
-    var ether = new Timeline.HotZoneEther({
-        centersOn:          ("date" in params) ? params.date : new Date(),
-        interval:           SimileAjax.DateTime.gregorianUnitLengths[params.intervalUnit],
-        pixelsPerInterval:  params.intervalPixels,
-        zones:              params.zones,
-        theme:              theme
-    });
-
-    var etherPainter = new Timeline.HotZoneGregorianEtherPainter({
-        unit:       params.intervalUnit,
-        zones:      params.zones,
-        theme:      theme,
-        align:      ("align" in params) ? params.align : undefined
-    });
-
-    var eventPainterParams = {
-        showText:   ("showEventText" in params) ? params.showEventText : true,
-        theme:      theme
-    };
-    // pass in custom parameters for the event painter
-    if ("eventPainterParams" in params) {
-        for (var prop in params.eventPainterParams) {
-            eventPainterParams[prop] = params.eventPainterParams[prop];
-        }
-    }
-    if ("trackHeight" in params) {
-        eventPainterParams.trackHeight = params.trackHeight;
-    }
-    if ("trackGap" in params) {
-        eventPainterParams.trackGap = params.trackGap;
-    }
-
-    var layout = ("overview" in params && params.overview) ? "overview" : ("layout" in params ? params.layout : "original");
-    var eventPainter;
-    if ("eventPainter" in params) {
-        eventPainter = new params.eventPainter(eventPainterParams);
-    } else {
-        switch (layout) {
-            case "overview" :
-                eventPainter = new Timeline.OverviewEventPainter(eventPainterParams);
-                break;
-            case "detailed" :
-                eventPainter = new Timeline.DetailedEventPainter(eventPainterParams);
-                break;
-            default:
-                eventPainter = new Timeline.OriginalEventPainter(eventPainterParams);
-        }
-    }
-    return {
-        width:          params.width,
-        eventSource:    eventSource,
-        timeZone:       ("timeZone" in params) ? params.timeZone : 0,
-        ether:          ether,
-        etherPainter:   etherPainter,
-        eventPainter:   eventPainter,
-        theme:          theme,
-        zoomIndex:      ("zoomIndex" in params) ? params.zoomIndex : 0,
-        zoomSteps:      ("zoomSteps" in params) ? params.zoomSteps : null
-    };
-};
-
-Timeline.getDefaultTheme = function() {
-    if (Timeline._defaultTheme == null) {
-        Timeline._defaultTheme = Timeline.ClassicTheme.create(Timeline.getDefaultLocale());
-    }
-    return Timeline._defaultTheme;
-};
-
-Timeline.setDefaultTheme = function(theme) {
-    Timeline._defaultTheme = theme;
-};
-
-Timeline.loadXML = function(url, f) {
-    var fError = function(statusText, status, xmlhttp) {
-        alert("Failed to load data xml from " + url + "\n" + statusText);
-    };
-    var fDone = function(xmlhttp) {
-        var xml = xmlhttp.responseXML;
-        if (!xml.documentElement && xmlhttp.responseStream) {
-            xml.load(xmlhttp.responseStream);
-        }
-        f(xml, url);
-    };
-    SimileAjax.XmlHttp.get(url, fError, fDone);
-};
-
-
-Timeline.loadJSON = function(url, f) {
-    var fError = function(statusText, status, xmlhttp) {
-        alert("Failed to load json data from " + url + "\n" + statusText);
-    };
-    var fDone = function(xmlhttp) {
-        f(eval('(' + xmlhttp.responseText + ')'), url);
-    };
-    SimileAjax.XmlHttp.get(url, fError, fDone);
-};
-
-Timeline.getTimelineFromID = function(timelineID) {
-    return Timeline.timelines[timelineID];
-};
-
-// Write the current Timeline version as the contents of element with id el_id
-Timeline.writeVersion = function(el_id) {
-  document.getElementById(el_id).innerHTML = this.display_version;
-};
-
-
-
-/*
- *  Timeline Implementation object
- *
- */
-Timeline._Impl = function(elmt, bandInfos, orientation, unit, timelineID) {
-    SimileAjax.WindowManager.initialize();
-
-    this._containerDiv = elmt;
-
-    this._bandInfos = bandInfos;
-    this._orientation = orientation == null ? Timeline.HORIZONTAL : orientation;
-    this._unit = (unit != null) ? unit : SimileAjax.NativeDateUnit;
-    this._starting = true; // is the Timeline being created? Used by autoWidth
-                           // functions
-    this._autoResizing = false;
-
-    // autoWidth is a "public" property of the Timeline object
-    this.autoWidth = bandInfos && bandInfos[0] && bandInfos[0].theme &&
-                     bandInfos[0].theme.autoWidth;
-    this.autoWidthAnimationTime = bandInfos && bandInfos[0] && bandInfos[0].theme &&
-                     bandInfos[0].theme.autoWidthAnimationTime;
-    this.timelineID = timelineID; // also public attribute
-    this.timeline_start = bandInfos && bandInfos[0] && bandInfos[0].theme &&
-                     bandInfos[0].theme.timeline_start;
-    this.timeline_stop  = bandInfos && bandInfos[0] && bandInfos[0].theme &&
-                     bandInfos[0].theme.timeline_stop;
-    this.timeline_at_start = false; // already at start or stop? Then won't
-    this.timeline_at_stop = false;  // try to move further in the wrong direction
-
-    this._initialize();
-};
-
-//
-// Public functions used by client sw
-//
-Timeline._Impl.prototype.dispose = function() {
-    for (var i = 0; i < this._bands.length; i++) {
-        this._bands[i].dispose();
-    }
-    this._bands = null;
-    this._bandInfos = null;
-    this._containerDiv.innerHTML = "";
-    // remove from array of Timelines
-    Timeline.timelines[this.timelineID] = null;
-};
-
-Timeline._Impl.prototype.getBandCount = function() {
-    return this._bands.length;
-};
-
-Timeline._Impl.prototype.getBand = function(index) {
-    return this._bands[index];
-};
-
-Timeline._Impl.prototype.finishedEventLoading = function() {
-    // Called by client after events have been loaded into Timeline
-    // Only used if the client has set autoWidth
-    // Sets width to Timeline's requested amount and will shrink down the div if
-    // need be.
-    this._autoWidthCheck(true);
-    this._starting = false;
-};
-
-Timeline._Impl.prototype.layout = function() {
-    // called by client when browser is resized
-    this._autoWidthCheck(true);
-    this._distributeWidths();
-};
-
-Timeline._Impl.prototype.paint = function() {
-    for (var i = 0; i < this._bands.length; i++) {
-        this._bands[i].paint();
-    }
-};
-
-Timeline._Impl.prototype.getDocument = function() {
-    return this._containerDiv.ownerDocument;
-};
-
-Timeline._Impl.prototype.addDiv = function(div) {
-    this._containerDiv.appendChild(div);
-};
-
-Timeline._Impl.prototype.removeDiv = function(div) {
-    this._containerDiv.removeChild(div);
-};
-
-Timeline._Impl.prototype.isHorizontal = function() {
-    return this._orientation == Timeline.HORIZONTAL;
-};
-
-Timeline._Impl.prototype.isVertical = function() {
-    return this._orientation == Timeline.VERTICAL;
-};
-
-Timeline._Impl.prototype.getPixelLength = function() {
-    return this._orientation == Timeline.HORIZONTAL ?
-        this._containerDiv.offsetWidth : this._containerDiv.offsetHeight;
-};
-
-Timeline._Impl.prototype.getPixelWidth = function() {
-    return this._orientation == Timeline.VERTICAL ?
-        this._containerDiv.offsetWidth : this._containerDiv.offsetHeight;
-};
-
-Timeline._Impl.prototype.getUnit = function() {
-    return this._unit;
-};
-
-Timeline._Impl.prototype.getWidthStyle = function() {
-    // which element.style attribute should be changed to affect Timeline's "width"
-    return this._orientation == Timeline.HORIZONTAL ? 'height' : 'width';
-};
-
-Timeline._Impl.prototype.loadXML = function(url, f) {
-    var tl = this;
-
-
-    var fError = function(statusText, status, xmlhttp) {
-        alert("Failed to load data xml from " + url + "\n" + statusText);
-        tl.hideLoadingMessage();
-    };
-    var fDone = function(xmlhttp) {
-        try {
-            var xml = xmlhttp.responseXML;
-            if (!xml.documentElement && xmlhttp.responseStream) {
-                xml.load(xmlhttp.responseStream);
-            }
-            f(xml, url);
-        } finally {
-            tl.hideLoadingMessage();
-        }
-    };
-
-    this.showLoadingMessage();
-    window.setTimeout(function() { SimileAjax.XmlHttp.get(url, fError, fDone); }, 0);
-};
-
-Timeline._Impl.prototype.loadJSON = function(url, f) {
-    var tl = this;
-
-    var fError = function(statusText, status, xmlhttp) {
-        alert("Failed to load json data from " + url + "\n" + statusText);
-        tl.hideLoadingMessage();
-    };
-    var fDone = function(xmlhttp) {
-        try {
-            f(eval('(' + xmlhttp.responseText + ')'), url);
-        } finally {
-            tl.hideLoadingMessage();
-        }
-    };
-
-    this.showLoadingMessage();
-    window.setTimeout(function() { SimileAjax.XmlHttp.get(url, fError, fDone); }, 0);
-};
-
-
-//
-// Private functions used by Timeline object functions
-//
-
-Timeline._Impl.prototype._autoWidthScrollListener = function(band) {
-    band.getTimeline()._autoWidthCheck(false);
-};
-
-// called to re-calculate auto width and adjust the overall Timeline div if needed
-Timeline._Impl.prototype._autoWidthCheck = function(okToShrink) {
-    var timeline = this; // this Timeline
-    var immediateChange = timeline._starting;
-    var newWidth = 0;
-
-    function changeTimelineWidth() {
-        var widthStyle = timeline.getWidthStyle();
-        if (immediateChange) {
-            timeline._containerDiv.style[widthStyle] = newWidth + 'px';
-        } else {
-        	  // animate change
-        	  timeline._autoResizing = true;
-        	  var animateParam ={};
-        	  animateParam[widthStyle] = newWidth + 'px';
-
-        	  SimileAjax.jQuery(timeline._containerDiv).animate(
-        	      animateParam, timeline.autoWidthAnimationTime,
-        	      'linear', function(){timeline._autoResizing = false;});
-        }
-    }
-
-    function checkTimelineWidth() {
-        var targetWidth = 0; // the new desired width
-        var currentWidth = timeline.getPixelWidth();
-
-        if (timeline._autoResizing) {
-        	return; // early return
-        }
-
-        // compute targetWidth
-        for (var i = 0; i < timeline._bands.length; i++) {
-            timeline._bands[i].checkAutoWidth();
-            targetWidth += timeline._bandInfos[i].width;
-        }
-
-        if (targetWidth > currentWidth || okToShrink) {
-            // yes, let's change the size
-            newWidth = targetWidth;
-            changeTimelineWidth();
-            timeline._distributeWidths();
-        }
-    }
-
-    // function's mainline
-    if (!timeline.autoWidth) {
-        return; // early return
-    }
-
-    checkTimelineWidth();
-};
-
-Timeline._Impl.prototype._initialize = function() {
-    var containerDiv = this._containerDiv;
-    var doc = containerDiv.ownerDocument;
-
-    containerDiv.className =
-        containerDiv.className.split(" ").concat("timeline-container").join(" ");
-
-	/*
-	 * Set css-class on container div that will define orientation
-	 */
-	var orientation = (this.isHorizontal()) ? 'horizontal' : 'vertical'
-	containerDiv.className +=' timeline-'+orientation;
-
-
-    while (containerDiv.firstChild) {
-        containerDiv.removeChild(containerDiv.firstChild);
-    }
-
-    /*
-     *  inserting copyright and link to simile
-     */
-    var elmtCopyright = SimileAjax.Graphics.createTranslucentImage(Timeline.urlPrefix + (this.isHorizontal() ? "images/copyright-vertical.png" : "images/copyright.png"));
-    elmtCopyright.className = "timeline-copyright";
-    elmtCopyright.title = "Timeline copyright SIMILE - www.code.google.com/p/simile-widgets/";
-    SimileAjax.DOM.registerEvent(elmtCopyright, "click", function() { window.location = "http://www.simile-widgets.org/"; });
-    containerDiv.appendChild(elmtCopyright);
-
-    /*
-     *  creating bands
-     */
-    this._bands = [];
-    for (var i = 0; i < this._bandInfos.length; i++) {
-        var bandInfo = this._bandInfos[i];
-        var bandClass = bandInfo.bandClass || Timeline._Band;
-        var band = new bandClass(this, this._bandInfos[i], i);
-        this._bands.push(band);
-    }
-    this._distributeWidths();
-
-    /*
-     *  sync'ing bands
-     */
-    for (var i = 0; i < this._bandInfos.length; i++) {
-        var bandInfo = this._bandInfos[i];
-        if ("syncWith" in bandInfo) {
-            this._bands[i].setSyncWithBand(
-                this._bands[bandInfo.syncWith],
-                ("highlight" in bandInfo) ? bandInfo.highlight : false
-            );
-        }
-    }
-
-
-    if (this.autoWidth) {
-        for (var i = 0; i < this._bands.length; i++) {
-            this._bands[i].addOnScrollListener(this._autoWidthScrollListener);
-        }
-    }
-
-
-    /*
-     *  creating loading UI
-     */
-    var message = SimileAjax.Graphics.createMessageBubble(doc);
-    message.containerDiv.className = "timeline-message-container";
-    containerDiv.appendChild(message.containerDiv);
-
-    message.contentDiv.className = "timeline-message";
-    message.contentDiv.innerHTML = "<img src='" + Timeline.urlPrefix + "images/progress-running.gif' /> Loading...";
-
-    this.showLoadingMessage = function() { message.containerDiv.style.display = "block"; };
-    this.hideLoadingMessage = function() { message.containerDiv.style.display = "none"; };
-};
-
-Timeline._Impl.prototype._distributeWidths = function() {
-    var length = this.getPixelLength();
-    var width = this.getPixelWidth();
-    var cumulativeWidth = 0;
-
-    for (var i = 0; i < this._bands.length; i++) {
-        var band = this._bands[i];
-        var bandInfos = this._bandInfos[i];
-        var widthString = bandInfos.width;
-        var bandWidth;
-
-        if (typeof widthString == 'string') {
-          var x =  widthString.indexOf("%");
-          if (x > 0) {
-              var percent = parseInt(widthString.substr(0, x));
-              bandWidth = Math.round(percent * width / 100);
-          } else {
-              bandWidth = parseInt(widthString);
-          }
-        } else {
-        	// was given an integer
-        	bandWidth = widthString;
-        }
-
-        band.setBandShiftAndWidth(cumulativeWidth, bandWidth);
-        band.setViewLength(length);
-
-        cumulativeWidth += bandWidth;
-    }
-};
-
-Timeline._Impl.prototype.shiftOK = function(index, shift) {
-    // Returns true if the proposed shift is ok
-    //
-    // Positive shift means going back in time
-    var going_back = shift > 0,
-        going_forward = shift < 0;
-
-    // Is there an edge?
-    if ((going_back    && this.timeline_start == null) ||
-        (going_forward && this.timeline_stop  == null) ||
-        (shift == 0)) {
-        return (true);  // early return
-    }
-
-    // If any of the bands has noted that it is changing the others,
-    // then this shift is a secondary shift in reaction to the real shift,
-    // which already happened. In such cases, ignore it. (The issue is
-    // that a positive original shift can cause a negative secondary shift,
-    // as the bands adjust.)
-    var secondary_shift = false;
-    for (var i = 0; i < this._bands.length && !secondary_shift; i++) {
-       secondary_shift = this._bands[i].busy();
-    }
-    if (secondary_shift) {
-        return(true); // early return
-    }
-
-    // If we are already at an edge, then don't even think about going any further
-    if ((going_back    && this.timeline_at_start) ||
-        (going_forward && this.timeline_at_stop)) {
-        return (false);  // early return
-    }
-
-    // Need to check all the bands
-    var ok = false; // return value
-    // If any of the bands will be or are showing an ok date, then let the shift proceed.
-    for (var i = 0; i < this._bands.length && !ok; i++) {
-       var band = this._bands[i];
-       if (going_back) {
-           ok = (i == index ? band.getMinVisibleDateAfterDelta(shift) : band.getMinVisibleDate())
-                >= this.timeline_start;
-       } else {
-           ok = (i == index ? band.getMaxVisibleDateAfterDelta(shift) : band.getMaxVisibleDate())
-                <= this.timeline_stop;
-       }
-    }
-
-    // process results
-    if (going_back) {
-       this.timeline_at_start = !ok;
-       this.timeline_at_stop = false;
-    } else {
-       this.timeline_at_stop = !ok;
-       this.timeline_at_start = false;
-    }
-    // This is where you could have an effect once per hitting an
-    // edge of the Timeline. Eg jitter the Timeline
-    //if (!ok) {
-        //alert(going_back ? "At beginning" : "At end");
-    //}
-    return (ok);
-};
-
-Timeline._Impl.prototype.zoom = function (zoomIn, x, y, target) {
-  var matcher = new RegExp("^timeline-band-([0-9]+)$");
-  var bandIndex = null;
-
-  var result = matcher.exec(target.id);
-  if (result) {
-    bandIndex = parseInt(result[1]);
-  }
-
-  if (bandIndex != null) {
-    this._bands[bandIndex].zoom(zoomIn, x, y, target);
-  }
-
-  this.paint();
-};
-
-/*
- *
- * Coding standards:
- *
- * We aim towards Douglas Crockford's Javascript conventions.
- * See:  http://javascript.crockford.com/code.html
- * See also: http://www.crockford.com/javascript/javascript.html
- *
- * That said, this JS code was written before some recent JS
- * support libraries became widely used or available.
- * In particular, the _ character is used to indicate a class function or
- * variable that should be considered private to the class.
- *
- * The code mostly uses accessor methods for getting/setting the private
- * class variables.
- *
- * Over time, we'd like to formalize the convention by using support libraries
- * which enforce privacy in objects.
- *
- * We also want to use jslint:  http://www.jslint.com/
- *
- *
- *
- */
-
-
-
-/*
- *  Band
- *
- */
-Timeline._Band = function(timeline, bandInfo, index) {
-    // hack for easier subclassing
-    if (timeline !== undefined) {
-        this.initialize(timeline, bandInfo, index);
-    }
-};
-
-Timeline._Band.prototype.initialize = function(timeline, bandInfo, index) {
-    // Set up the band's object
-
-    // Munge params: If autoWidth is on for the Timeline, then ensure that
-    // bandInfo.width is an integer
-    if (timeline.autoWidth && typeof bandInfo.width == 'string') {
-        bandInfo.width = bandInfo.width.indexOf("%") > -1 ? 0 : parseInt(bandInfo.width);
-    }
-
-    this._timeline = timeline;
-    this._bandInfo = bandInfo;
-
-    this._index = index;
-
-    this._locale = ("locale" in bandInfo) ? bandInfo.locale : Timeline.getDefaultLocale();
-    this._timeZone = ("timeZone" in bandInfo) ? bandInfo.timeZone : 0;
-    this._labeller = ("labeller" in bandInfo) ? bandInfo.labeller :
-        (("createLabeller" in timeline.getUnit()) ?
-            timeline.getUnit().createLabeller(this._locale, this._timeZone) :
-            new Timeline.GregorianDateLabeller(this._locale, this._timeZone));
-    this._theme = bandInfo.theme;
-    this._zoomIndex = ("zoomIndex" in bandInfo) ? bandInfo.zoomIndex : 0;
-    this._zoomSteps = ("zoomSteps" in bandInfo) ? bandInfo.zoomSteps : null;
-
-    this._dragging = false;
-    this._changing = false;
-    this._originalScrollSpeed = 5; // pixels
-    this._scrollSpeed = this._originalScrollSpeed;
-    this._viewOrthogonalOffset= 0; // vertical offset if the timeline is horizontal, and vice versa
-    this._onScrollListeners = [];
-
-    var b = this;
-    this._syncWithBand = null;
-    this._syncWithBandHandler = function(band) {
-        b._onHighlightBandScroll();
-    };
-    this._selectorListener = function(band) {
-        b._onHighlightBandScroll();
-    };
-
-    /*
-     *  Install a textbox to capture keyboard events
-     */
-    var inputDiv = this._timeline.getDocument().createElement("div");
-    inputDiv.className = "timeline-band-input";
-    this._timeline.addDiv(inputDiv);
-
-    this._keyboardInput = document.createElement("input");
-    this._keyboardInput.type = "text";
-    inputDiv.appendChild(this._keyboardInput);
-    SimileAjax.DOM.registerEventWithObject(this._keyboardInput, "keydown", this, "_onKeyDown");
-    SimileAjax.DOM.registerEventWithObject(this._keyboardInput, "keyup", this, "_onKeyUp");
-
-    /*
-     *  The band's outer most div that slides with respect to the timeline's div
-     */
-    this._div = this._timeline.getDocument().createElement("div");
-    this._div.id = "timeline-band-" + index;
-    this._div.className = "timeline-band timeline-band-" + index;
-    this._timeline.addDiv(this._div);
-
-    SimileAjax.DOM.registerEventWithObject(this._div, "mousedown", this, "_onMouseDown");
-    SimileAjax.DOM.registerEventWithObject(this._div, "mousemove", this, "_onMouseMove");
-    SimileAjax.DOM.registerEventWithObject(this._div, "mouseup", this, "_onMouseUp");
-    SimileAjax.DOM.registerEventWithObject(this._div, "mouseout", this, "_onMouseOut");
-    SimileAjax.DOM.registerEventWithObject(this._div, "dblclick", this, "_onDblClick");
-
-    var mouseWheel = this._theme!= null ? this._theme.mouseWheel : 'scroll'; // theme is not always defined
-    if (mouseWheel === 'zoom' || mouseWheel === 'scroll' || this._zoomSteps) {
-        // capture mouse scroll
-        if (SimileAjax.Platform.browser.isFirefox) {
-            SimileAjax.DOM.registerEventWithObject(this._div, "DOMMouseScroll", this, "_onMouseScroll");
-        } else {
-            SimileAjax.DOM.registerEventWithObject(this._div, "mousewheel", this, "_onMouseScroll");
-        }
-    }
-
-    /*
-     *  The inner div that contains layers
-     */
-    this._innerDiv = this._timeline.getDocument().createElement("div");
-    this._innerDiv.className = "timeline-band-inner";
-    this._div.appendChild(this._innerDiv);
-
-    /*
-     *  Initialize parts of the band
-     */
-    this._ether = bandInfo.ether;
-    bandInfo.ether.initialize(this, timeline);
-
-    this._etherPainter = bandInfo.etherPainter;
-    bandInfo.etherPainter.initialize(this, timeline);
-
-    this._eventSource = bandInfo.eventSource;
-    if (this._eventSource) {
-        this._eventListener = {
-            onAddMany: function() { b._onAddMany(); },
-            onClear:   function() { b._onClear(); }
-        }
-        this._eventSource.addListener(this._eventListener);
-    }
-
-    this._eventPainter = bandInfo.eventPainter;
-    this._eventTracksNeeded = 0;   // set by painter via updateEventTrackInfo
-    this._eventTrackIncrement = 0;
-    bandInfo.eventPainter.initialize(this, timeline);
-
-    this._decorators = ("decorators" in bandInfo) ? bandInfo.decorators : [];
-    for (var i = 0; i < this._decorators.length; i++) {
-        this._decorators[i].initialize(this, timeline);
-    }
-};
-
-Timeline._Band.SCROLL_MULTIPLES = 5;
-
-Timeline._Band.prototype.dispose = function() {
-    this.closeBubble();
-
-    if (this._eventSource) {
-        this._eventSource.removeListener(this._eventListener);
-        this._eventListener = null;
-        this._eventSource = null;
-    }
-
-    this._timeline = null;
-    this._bandInfo = null;
-
-    this._labeller = null;
-    this._ether = null;
-    this._etherPainter = null;
-    this._eventPainter = null;
-    this._decorators = null;
-
-    this._onScrollListeners = null;
-    this._syncWithBandHandler = null;
-    this._selectorListener = null;
-
-    this._div = null;
-    this._innerDiv = null;
-    this._keyboardInput = null;
-};
-
-Timeline._Band.prototype.addOnScrollListener = function(listener) {
-    this._onScrollListeners.push(listener);
-};
-
-Timeline._Band.prototype.removeOnScrollListener = function(listener) {
-    for (var i = 0; i < this._onScrollListeners.length; i++) {
-        if (this._onScrollListeners[i] == listener) {
-            this._onScrollListeners.splice(i, 1);
-            break;
-        }
-    }
-};
-
-Timeline._Band.prototype.setSyncWithBand = function(band, highlight) {
-    if (this._syncWithBand) {
-        this._syncWithBand.removeOnScrollListener(this._syncWithBandHandler);
-    }
-
-    this._syncWithBand = band;
-    this._syncWithBand.addOnScrollListener(this._syncWithBandHandler);
-    this._highlight = highlight;
-    this._positionHighlight();
-};
-
-Timeline._Band.prototype.getLocale = function() {
-    return this._locale;
-};
-
-Timeline._Band.prototype.getTimeZone = function() {
-    return this._timeZone;
-};
-
-Timeline._Band.prototype.getLabeller = function() {
-    return this._labeller;
-};
-
-Timeline._Band.prototype.getIndex = function() {
-    return this._index;
-};
-
-Timeline._Band.prototype.getEther = function() {
-    return this._ether;
-};
-
-Timeline._Band.prototype.getEtherPainter = function() {
-    return this._etherPainter;
-};
-
-Timeline._Band.prototype.getEventSource = function() {
-    return this._eventSource;
-};
-
-Timeline._Band.prototype.getEventPainter = function() {
-    return this._eventPainter;
-};
-
-Timeline._Band.prototype.getTimeline = function() {
-    return this._timeline;
-};
-
-// Autowidth support
-Timeline._Band.prototype.updateEventTrackInfo = function(tracks, increment) {
-    this._eventTrackIncrement = increment; // doesn't vary for a specific band
-
-    if (tracks > this._eventTracksNeeded) {
-        this._eventTracksNeeded = tracks;
-    }
-};
-
-// Autowidth support
-Timeline._Band.prototype.checkAutoWidth = function() {
-    // if a new (larger) width is needed by the band
-    // then: a) updates the band's bandInfo.width
-    //
-    // desiredWidth for the band is
-    //   (number of tracks + margin) * track increment
-    if (! this._timeline.autoWidth) {
-      return; // early return
-    }
-
-    var overviewBand = this._eventPainter.getType() == 'overview';
-    var margin = overviewBand ?
-       this._theme.event.overviewTrack.autoWidthMargin :
-       this._theme.event.track.autoWidthMargin;
-    var desiredWidth = Math.ceil((this._eventTracksNeeded + margin) *
-                       this._eventTrackIncrement);
-    // add offset amount (additional margin)
-    desiredWidth += overviewBand ? this._theme.event.overviewTrack.offset :
-                                   this._theme.event.track.offset;
-    var bandInfo = this._bandInfo;
-
-    if (desiredWidth != bandInfo.width) {
-        bandInfo.width = desiredWidth;
-    }
-};
-
-Timeline._Band.prototype.layout = function() {
-    this.paint();
-};
-
-Timeline._Band.prototype.paint = function() {
-    this._etherPainter.paint();
-    this._paintDecorators();
-    this._paintEvents();
-};
-
-Timeline._Band.prototype.softLayout = function() {
-    this.softPaint();
-};
-
-Timeline._Band.prototype.softPaint = function() {
-    this._etherPainter.softPaint();
-    this._softPaintDecorators();
-    this._softPaintEvents();
-};
-
-Timeline._Band.prototype.setBandShiftAndWidth = function(shift, width) {
-    var inputDiv = this._keyboardInput.parentNode;
-    var middle = shift + Math.floor(width / 2);
-    if (this._timeline.isHorizontal()) {
-        this._div.style.top = shift + "px";
-        this._div.style.height = width + "px";
-
-        inputDiv.style.top = middle + "px";
-        inputDiv.style.left = "-1em";
-    } else {
-        this._div.style.left = shift + "px";
-        this._div.style.width = width + "px";
-
-        inputDiv.style.left = middle + "px";
-        inputDiv.style.top = "-1em";
-    }
-};
-
-Timeline._Band.prototype.getViewWidth = function() {
-    if (this._timeline.isHorizontal()) {
-        return this._div.offsetHeight;
-    } else {
-        return this._div.offsetWidth;
-    }
-};
-
-Timeline._Band.prototype.setViewLength = function(length) {
-    this._viewLength = length;
-    this._recenterDiv();
-    this._onChanging();
-};
-
-Timeline._Band.prototype.getViewLength = function() {
-    return this._viewLength;
-};
-
-Timeline._Band.prototype.getTotalViewLength = function() {
-    return Timeline._Band.SCROLL_MULTIPLES * this._viewLength;
-};
-
-Timeline._Band.prototype.getViewOffset = function() {
-    return this._viewOffset;
-};
-
-Timeline._Band.prototype.getMinDate = function() {
-    return this._ether.pixelOffsetToDate(this._viewOffset);
-};
-
-Timeline._Band.prototype.getMaxDate = function() {
-    return this._ether.pixelOffsetToDate(this._viewOffset + Timeline._Band.SCROLL_MULTIPLES * this._viewLength);
-};
-
-Timeline._Band.prototype.getMinVisibleDate = function() {
-    return this._ether.pixelOffsetToDate(0);
-};
-
-Timeline._Band.prototype.getMinVisibleDateAfterDelta = function(delta) {
-    return this._ether.pixelOffsetToDate(delta);
-};
-
-Timeline._Band.prototype.getMaxVisibleDate = function() {
-    // Max date currently visible on band
-    return this._ether.pixelOffsetToDate(this._viewLength);
-};
-
-Timeline._Band.prototype.getMaxVisibleDateAfterDelta = function(delta) {
-    // Max date visible on band after delta px view change is applied
-    return this._ether.pixelOffsetToDate(this._viewLength + delta);
-};
-
-Timeline._Band.prototype.getCenterVisibleDate = function() {
-    return this._ether.pixelOffsetToDate(this._viewLength / 2);
-};
-
-Timeline._Band.prototype.setMinVisibleDate = function(date) {
-    if (!this._changing) {
-        this._moveEther(Math.round(-this._ether.dateToPixelOffset(date)));
-    }
-};
-
-Timeline._Band.prototype.setMaxVisibleDate = function(date) {
-    if (!this._changing) {
-        this._moveEther(Math.round(this._viewLength - this._ether.dateToPixelOffset(date)));
-    }
-};
-
-Timeline._Band.prototype.setCenterVisibleDate = function(date) {
-    if (!this._changing) {
-        this._moveEther(Math.round(this._viewLength / 2 - this._ether.dateToPixelOffset(date)));
-    }
-};
-
-Timeline._Band.prototype.dateToPixelOffset = function(date) {
-    return this._ether.dateToPixelOffset(date) - this._viewOffset;
-};
-
-Timeline._Band.prototype.pixelOffsetToDate = function(pixels) {
-    return this._ether.pixelOffsetToDate(pixels + this._viewOffset);
-};
-
-Timeline._Band.prototype.getViewOrthogonalOffset = function() {
-    return this._viewOrthogonalOffset;
-};
-
-Timeline._Band.prototype.setViewOrthogonalOffset = function(offset) {
-    this._viewOrthogonalOffset = Math.max(0, offset);
-};
-
-Timeline._Band.prototype.createLayerDiv = function(zIndex, className) {
-    var div = this._timeline.getDocument().createElement("div");
-    div.className = "timeline-band-layer" + (typeof className == "string" ? (" " + className) : "");
-    div.style.zIndex = zIndex;
-    this._innerDiv.appendChild(div);
-
-    var innerDiv = this._timeline.getDocument().createElement("div");
-    innerDiv.className = "timeline-band-layer-inner";
-    if (SimileAjax.Platform.browser.isIE) {
-        innerDiv.style.cursor = "move";
-    } else {
-        innerDiv.style.cursor = "-moz-grab";
-    }
-    div.appendChild(innerDiv);
-
-    return innerDiv;
-};
-
-Timeline._Band.prototype.removeLayerDiv = function(div) {
-    this._innerDiv.removeChild(div.parentNode);
-};
-
-Timeline._Band.prototype.scrollToCenter = function(date, f) {
-    var pixelOffset = this._ether.dateToPixelOffset(date);
-    if (pixelOffset < -this._viewLength / 2) {
-        this.setCenterVisibleDate(this.pixelOffsetToDate(pixelOffset + this._viewLength));
-    } else if (pixelOffset > 3 * this._viewLength / 2) {
-        this.setCenterVisibleDate(this.pixelOffsetToDate(pixelOffset - this._viewLength));
-    }
-    this._autoScroll(Math.round(this._viewLength / 2 - this._ether.dateToPixelOffset(date)), f);
-};
-
-Timeline._Band.prototype.showBubbleForEvent = function(eventID) {
-    var evt = this.getEventSource().getEvent(eventID);
-    if (evt) {
-        var self = this;
-        this.scrollToCenter(evt.getStart(), function() {
-            self._eventPainter.showBubble(evt);
-        });
-    }
-};
-
-Timeline._Band.prototype.zoom = function(zoomIn, x, y, target) {
-  if (!this._zoomSteps) {
-    // zoom disabled
-    return;
-  }
-
-  // shift the x value by our offset
-  x += this._viewOffset;
-
-  var zoomDate = this._ether.pixelOffsetToDate(x);
-  var netIntervalChange = this._ether.zoom(zoomIn);
-  this._etherPainter.zoom(netIntervalChange);
-
-  // shift our zoom date to the far left
-  this._moveEther(Math.round(-this._ether.dateToPixelOffset(zoomDate)));
-  // then shift it back to where the mouse was
-  this._moveEther(x);
-};
-
-Timeline._Band.prototype._onMouseDown = function(innerFrame, evt, target) {
-    this.closeBubble();
-
-    this._dragging = true;
-    this._dragX = evt.clientX;
-    this._dragY = evt.clientY;
-};
-
-Timeline._Band.prototype._onMouseMove = function(innerFrame, evt, target) {
-    if (this._dragging) {
-        var diffX = evt.clientX - this._dragX;
-        var diffY = evt.clientY - this._dragY;
-
-        this._dragX = evt.clientX;
-        this._dragY = evt.clientY;
-
-        if (this._timeline.isHorizontal()) {
-            this._moveEther(diffX, diffY);
-        } else {
-            this._moveEther(diffY, diffX);
-        }
-        this._positionHighlight();
-    }
-};
-
-Timeline._Band.prototype._onMouseUp = function(innerFrame, evt, target) {
-    this._dragging = false;
-    this._keyboardInput.focus();
-};
-
-Timeline._Band.prototype._onMouseOut = function(innerFrame, evt, target) {
-    var coords = SimileAjax.DOM.getEventRelativeCoordinates(evt, innerFrame);
-    coords.x += this._viewOffset;
-    if (coords.x < 0 || coords.x > innerFrame.offsetWidth ||
-        coords.y < 0 || coords.y > innerFrame.offsetHeight) {
-        this._dragging = false;
-    }
-};
-
-Timeline._Band.prototype._onMouseScroll = function(innerFrame, evt, target) {
-  var now = new Date();
-  now = now.getTime();
-
-  if (!this._lastScrollTime || ((now - this._lastScrollTime) > 50)) {
-    // limit 1 scroll per 200ms due to FF3 sending multiple events back to back
-    this._lastScrollTime = now;
-
-    var delta = 0;
-    if (evt.wheelDelta) {
-      delta = evt.wheelDelta/120;
-    } else if (evt.detail) {
-      delta = -evt.detail/3;
-    }
-
-    // either scroll or zoom
-    var mouseWheel = this._theme.mouseWheel;
-
-    if (this._zoomSteps || mouseWheel === 'zoom') {
-      var loc = SimileAjax.DOM.getEventRelativeCoordinates(evt, innerFrame);
-      if (delta != 0) {
-        var zoomIn;
-        if (delta > 0)
-          zoomIn = true;
-        if (delta < 0)
-          zoomIn = false;
-        // call zoom on the timeline so we could zoom multiple bands if desired
-        this._timeline.zoom(zoomIn, loc.x, loc.y, innerFrame);
-      }
-    }
-    else if (mouseWheel === 'scroll') {
-    	var move_amt = 50 * (delta < 0 ? -1 : 1);
-      this._moveEther(move_amt);
-    }
-  }
-
-  // prevent bubble
-  if (evt.stopPropagation) {
-    evt.stopPropagation();
-  }
-  evt.cancelBubble = true;
-
-  // prevent the default action
-  if (evt.preventDefault) {
-    evt.preventDefault();
-  }
-  evt.returnValue = false;
-};
-
-Timeline._Band.prototype._onDblClick = function(innerFrame, evt, target) {
-    var coords = SimileAjax.DOM.getEventRelativeCoordinates(evt, innerFrame);
-    var distance = coords.x - (this._viewLength / 2 - this._viewOffset);
-
-    this._autoScroll(-distance);
-};
-
-Timeline._Band.prototype._onKeyDown = function(keyboardInput, evt, target) {
-    if (!this._dragging) {
-        switch (evt.keyCode) {
-        case 27: // ESC
-            break;
-        case 37: // left arrow
-        case 38: // up arrow
-            this._scrollSpeed = Math.min(50, Math.abs(this._scrollSpeed * 1.05));
-            this._moveEther(this._scrollSpeed);
-            break;
-        case 39: // right arrow
-        case 40: // down arrow
-            this._scrollSpeed = -Math.min(50, Math.abs(this._scrollSpeed * 1.05));
-            this._moveEther(this._scrollSpeed);
-            break;
-        default:
-            return true;
-        }
-        this.closeBubble();
-
-        SimileAjax.DOM.cancelEvent(evt);
-        return false;
-    }
-    return true;
-};
-
-Timeline._Band.prototype._onKeyUp = function(keyboardInput, evt, target) {
-    if (!this._dragging) {
-        this._scrollSpeed = this._originalScrollSpeed;
-
-        switch (evt.keyCode) {
-        case 35: // end
-            this.setCenterVisibleDate(this._eventSource.getLatestDate());
-            break;
-        case 36: // home
-            this.setCenterVisibleDate(this._eventSource.getEarliestDate());
-            break;
-        case 33: // page up
-            this._autoScroll(this._timeline.getPixelLength());
-            break;
-        case 34: // page down
-            this._autoScroll(-this._timeline.getPixelLength());
-            break;
-        default:
-            return true;
-        }
-
-        this.closeBubble();
-
-        SimileAjax.DOM.cancelEvent(evt);
-        return false;
-    }
-    return true;
-};
-
-Timeline._Band.prototype._autoScroll = function(distance, f) {
-    var b = this;
-    var a = SimileAjax.Graphics.createAnimation(
-        function(abs, diff) {
-            b._moveEther(diff);
-        },
-        0,
-        distance,
-        1000,
-        f
-    );
-    a.run();
-};
-
-Timeline._Band.prototype._moveEther = function(shift, orthogonalShift) {
-    if (orthogonalShift === undefined) {
-        orthogonalShift = 0;
-    }
-
-    this.closeBubble();
-
-    // A positive shift means back in time
-    // Check that we're not moving beyond Timeline's limits
-    if (!this._timeline.shiftOK(this._index, shift)) {
-        return; // early return
-    }
-
-    this._viewOffset += shift;
-    this._viewOrthogonalOffset = Math.min(0, this._viewOrthogonalOffset + orthogonalShift);
-
-    this._ether.shiftPixels(-shift);
-    if (this._timeline.isHorizontal()) {
-        this._div.style.left = this._viewOffset + "px";
-    } else {
-        this._div.style.top = this._viewOffset + "px";
-    }
-
-    if (this._viewOffset > -this._viewLength * 0.5 ||
-        this._viewOffset < -this._viewLength * (Timeline._Band.SCROLL_MULTIPLES - 1.5)) {
-
-        this._recenterDiv();
-    } else {
-        this.softLayout();
-    }
-
-    this._onChanging();
-}
-
-Timeline._Band.prototype._onChanging = function() {
-    this._changing = true;
-
-    this._fireOnScroll();
-    this._setSyncWithBandDate();
-
-    this._changing = false;
-};
-
-Timeline._Band.prototype.busy = function() {
-    // Is this band busy changing other bands?
-    return(this._changing);
-};
-
-Timeline._Band.prototype._fireOnScroll = function() {
-    for (var i = 0; i < this._onScrollListeners.length; i++) {
-        this._onScrollListeners[i](this);
-    }
-};
-
-Timeline._Band.prototype._setSyncWithBandDate = function() {
-    if (this._syncWithBand) {
-        var centerDate = this._ether.pixelOffsetToDate(this.getViewLength() / 2);
-        this._syncWithBand.setCenterVisibleDate(centerDate);
-    }
-};
-
-Timeline._Band.prototype._onHighlightBandScroll = function() {
-    if (this._syncWithBand) {
-        var centerDate = this._syncWithBand.getCenterVisibleDate();
-        var centerPixelOffset = this._ether.dateToPixelOffset(centerDate);
-
-        this._moveEther(Math.round(this._viewLength / 2 - centerPixelOffset));
-
-        if (this._highlight) {
-            this._etherPainter.setHighlight(
-                this._syncWithBand.getMinVisibleDate(),
-                this._syncWithBand.getMaxVisibleDate());
-        }
-    }
-};
-
-Timeline._Band.prototype._onAddMany = function() {
-    this._paintEvents();
-};
-
-Timeline._Band.prototype._onClear = function() {
-    this._paintEvents();
-};
-
-Timeline._Band.prototype._positionHighlight = function() {
-    if (this._syncWithBand) {
-        var startDate = this._syncWithBand.getMinVisibleDate();
-        var endDate = this._syncWithBand.getMaxVisibleDate();
-
-        if (this._highlight) {
-            this._etherPainter.setHighlight(startDate, endDate);
-        }
-    }
-};
-
-Timeline._Band.prototype._recenterDiv = function() {
-    this._viewOffset = -this._viewLength * (Timeline._Band.SCROLL_MULTIPLES - 1) / 2;
-    if (this._timeline.isHorizontal()) {
-        this._div.style.left = this._viewOffset + "px";
-        this._div.style.width = (Timeline._Band.SCROLL_MULTIPLES * this._viewLength) + "px";
-    } else {
-        this._div.style.top = this._viewOffset + "px";
-        this._div.style.height = (Timeline._Band.SCROLL_MULTIPLES * this._viewLength) + "px";
-    }
-    this.layout();
-};
-
-Timeline._Band.prototype._paintEvents = function() {
-    this._eventPainter.paint();
-};
-
-Timeline._Band.prototype._softPaintEvents = function() {
-    this._eventPainter.softPaint();
-};
-
-Timeline._Band.prototype._paintDecorators = function() {
-    for (var i = 0; i < this._decorators.length; i++) {
-        this._decorators[i].paint();
-    }
-};
-
-Timeline._Band.prototype._softPaintDecorators = function() {
-    for (var i = 0; i < this._decorators.length; i++) {
-        this._decorators[i].softPaint();
-    }
-};
-
-Timeline._Band.prototype.closeBubble = function() {
-    SimileAjax.WindowManager.cancelPopups();
-};
-/*
- *  Classic Theme
- *
- */
-
-
-
-Timeline.ClassicTheme = new Object();
-
-Timeline.ClassicTheme.implementations = [];
-
-Timeline.ClassicTheme.create = function(locale) {
-    if (locale == null) {
-        locale = Timeline.getDefaultLocale();
-    }
-
-    var f = Timeline.ClassicTheme.implementations[locale];
-    if (f == null) {
-        f = Timeline.ClassicTheme._Impl;
-    }
-    return new f();
-};
-
-Timeline.ClassicTheme._Impl = function() {
-    this.firstDayOfWeek = 0; // Sunday
-
-    // Note: Many styles previously set here are now set using CSS
-    //       The comments indicate settings controlled by CSS, not
-    //       lines to be un-commented.
-    //
-    //
-    // Attributes autoWidth, autoWidthAnimationTime, timeline_start
-    // and timeline_stop must be set on the first band's theme.
-    // The other attributes can be set differently for each
-    // band by using different themes for the bands.
-    this.autoWidth = false; // Should the Timeline automatically grow itself, as
-                            // needed when too many events for the available width
-                            // are painted on the visible part of the Timeline?
-    this.autoWidthAnimationTime = 500; // mSec
-    this.timeline_start = null; // Setting a date, eg new Date(Date.UTC(2010,0,17,20,00,00,0)) will prevent the
-                                // Timeline from being moved to anytime before the date.
-    this.timeline_stop = null;  // Use for setting a maximum date. The Timeline will not be able
-                                // to be moved to anytime after this date.
-    this.ether = {
-        backgroundColors: [
-        //    "#EEE",
-        //    "#DDD",
-        //    "#CCC",
-        //    "#AAA"
-        ],
-     //   highlightColor:     "white",
-        highlightOpacity:   50,
-        interval: {
-            line: {
-                show:       true,
-                opacity:    25
-               // color:      "#aaa",
-            },
-            weekend: {
-                opacity:    30
-              //  color:      "#FFFFE0",
-            },
-            marker: {
-                hAlign:     "Bottom",
-                vAlign:     "Right"
-                                        /*
-                hBottomStyler: function(elmt) {
-                    elmt.className = "timeline-ether-marker-bottom";
-                },
-                hBottomEmphasizedStyler: function(elmt) {
-                    elmt.className = "timeline-ether-marker-bottom-emphasized";
-                },
-                hTopStyler: function(elmt) {
-                    elmt.className = "timeline-ether-marker-top";
-                },
-                hTopEmphasizedStyler: function(elmt) {
-                    elmt.className = "timeline-ether-marker-top-emphasized";
-                },
-                */
-
-
-               /*
-                                  vRightStyler: function(elmt) {
-                    elmt.className = "timeline-ether-marker-right";
-                },
-                vRightEmphasizedStyler: function(elmt) {
-                    elmt.className = "timeline-ether-marker-right-emphasized";
-                },
-                vLeftStyler: function(elmt) {
-                    elmt.className = "timeline-ether-marker-left";
-                },
-                vLeftEmphasizedStyler:function(elmt) {
-                    elmt.className = "timeline-ether-marker-left-emphasized";
-                }
-                */
-            }
-        }
-    };
-
-    this.event = {
-        track: {
-                   height: 10, // px. You will need to change the track
-                               //     height if you change the tape height.
-                      gap:  2, // px. Gap between tracks
-                   offset:  2, // px. top margin above tapes
-          autoWidthMargin:  1.5
-          /* autoWidthMargin is only used if autoWidth (see above) is true.
-             The autoWidthMargin setting is used to set how close the bottom of the
-             lowest track is to the edge of the band's div. The units are total track
-             width (tape + label + gap). A min of 0.5 is suggested. Use this setting to
-             move the bottom track's tapes above the axis markers, if needed for your
-             Timeline.
-          */
-        },
-        overviewTrack: {
-                  offset: 20, // px -- top margin above tapes
-              tickHeight:  6, // px
-                  height:  2, // px
-                     gap:  1, // px
-         autoWidthMargin:  5 // This attribute is only used if autoWidth (see above) is true.
-        },
-        tape: {
-            height:         4 // px. For thicker tapes, remember to change track height too.
-        },
-        instant: {
-                           icon: Timeline.urlPrefix + "images/dull-blue-circle.png",
-                                 // default icon. Icon can also be specified per event
-                      iconWidth: 10,
-                     iconHeight: 10,
-               impreciseOpacity: 20, // opacity of the tape when durationEvent is false
-            impreciseIconMargin: 3   // A tape and an icon are painted for imprecise instant
-                                     // events. This attribute is the margin between the
-                                     // bottom of the tape and the top of the icon in that
-                                     // case.
-    //        color:             "#58A0DC",
-    //        impreciseColor:    "#58A0DC",
-        },
-        duration: {
-            impreciseOpacity: 20 // tape opacity for imprecise part of duration events
-      //      color:            "#58A0DC",
-      //      impreciseColor:   "#58A0DC",
-        },
-        label: {
-            backgroundOpacity: 50,// only used in detailed painter
-               offsetFromLine:  3 // px left margin amount from icon's right edge
-      //      backgroundColor:   "white",
-      //      lineColor:         "#58A0DC",
-        },
-        highlightColors: [  // Use with getEventPainter().setHighlightMatcher
-                            // See webapp/examples/examples.js
-            "#FFFF00",
-            "#FFC000",
-            "#FF0000",
-            "#0000FF"
-        ],
-        highlightLabelBackground: false, // When highlighting an event, also change the event's label background?
-        bubble: {
-            width:          250, // px
-            maxHeight:        0, // px Maximum height of bubbles. 0 means no max height.
-                                 // scrollbar will be added for taller bubbles
-            titleStyler: function(elmt) {
-                elmt.className = "timeline-event-bubble-title";
-            },
-            bodyStyler: function(elmt) {
-                elmt.className = "timeline-event-bubble-body";
-            },
-            imageStyler: function(elmt) {
-                elmt.className = "timeline-event-bubble-image";
-            },
-            wikiStyler: function(elmt) {
-                elmt.className = "timeline-event-bubble-wiki";
-            },
-            timeStyler: function(elmt) {
-                elmt.className = "timeline-event-bubble-time";
-            }
-        }
-    };
-
-    this.mouseWheel = 'scroll'; // 'default', 'zoom', 'scroll'
-};/*
- *  An "ether" is a object that maps date/time to pixel coordinates.
- *
- */
-
-/*
- *  Linear Ether
- *
- */
-
-Timeline.LinearEther = function(params) {
-    this._params = params;
-    this._interval = params.interval;
-    this._pixelsPerInterval = params.pixelsPerInterval;
-};
-
-Timeline.LinearEther.prototype.initialize = function(band, timeline) {
-    this._band = band;
-    this._timeline = timeline;
-    this._unit = timeline.getUnit();
-
-    if ("startsOn" in this._params) {
-        this._start = this._unit.parseFromObject(this._params.startsOn);
-    } else if ("endsOn" in this._params) {
-        this._start = this._unit.parseFromObject(this._params.endsOn);
-        this.shiftPixels(-this._timeline.getPixelLength());
-    } else if ("centersOn" in this._params) {
-        this._start = this._unit.parseFromObject(this._params.centersOn);
-        this.shiftPixels(-this._timeline.getPixelLength() / 2);
-    } else {
-        this._start = this._unit.makeDefaultValue();
-        this.shiftPixels(-this._timeline.getPixelLength() / 2);
-    }
-};
-
-Timeline.LinearEther.prototype.setDate = function(date) {
-    this._start = this._unit.cloneValue(date);
-};
-
-Timeline.LinearEther.prototype.shiftPixels = function(pixels) {
-    var numeric = this._interval * pixels / this._pixelsPerInterval;
-    this._start = this._unit.change(this._start, numeric);
-};
-
-Timeline.LinearEther.prototype.dateToPixelOffset = function(date) {
-    var numeric = this._unit.compare(date, this._start);
-    return this._pixelsPerInterval * numeric / this._interval;
-};
-
-Timeline.LinearEther.prototype.pixelOffsetToDate = function(pixels) {
-    var numeric = pixels * this._interval / this._pixelsPerInterval;
-    return this._unit.change(this._start, numeric);
-};
-
-Timeline.LinearEther.prototype.zoom = function(zoomIn) {
-  var netIntervalChange = 0;
-  var currentZoomIndex = this._band._zoomIndex;
-  var newZoomIndex = currentZoomIndex;
-
-  if (zoomIn && (currentZoomIndex > 0)) {
-    newZoomIndex = currentZoomIndex - 1;
-  }
-
-  if (!zoomIn && (currentZoomIndex < (this._band._zoomSteps.length - 1))) {
-    newZoomIndex = currentZoomIndex + 1;
-  }
-
-  this._band._zoomIndex = newZoomIndex;
-  this._interval =
-    SimileAjax.DateTime.gregorianUnitLengths[this._band._zoomSteps[newZoomIndex].unit];
-  this._pixelsPerInterval = this._band._zoomSteps[newZoomIndex].pixelsPerInterval;
-  netIntervalChange = this._band._zoomSteps[newZoomIndex].unit -
-    this._band._zoomSteps[currentZoomIndex].unit;
-
-  return netIntervalChange;
-};
-
-
-/*
- *  Hot Zone Ether
- *
- */
-
-Timeline.HotZoneEther = function(params) {
-    this._params = params;
-    this._interval = params.interval;
-    this._pixelsPerInterval = params.pixelsPerInterval;
-    this._theme = params.theme;
-};
-
-Timeline.HotZoneEther.prototype.initialize = function(band, timeline) {
-    this._band = band;
-    this._timeline = timeline;
-    this._unit = timeline.getUnit();
-
-    this._zones = [{
-        startTime:  Number.NEGATIVE_INFINITY,
-        endTime:    Number.POSITIVE_INFINITY,
-        magnify:    1
-    }];
-    var params = this._params;
-    for (var i = 0; i < params.zones.length; i++) {
-        var zone = params.zones[i];
-        var zoneStart = this._unit.parseFromObject(zone.start);
-        var zoneEnd =   this._unit.parseFromObject(zone.end);
-
-        for (var j = 0; j < this._zones.length && this._unit.compare(zoneEnd, zoneStart) > 0; j++) {
-            var zone2 = this._zones[j];
-
-            if (this._unit.compare(zoneStart, zone2.endTime) < 0) {
-                if (this._unit.compare(zoneStart, zone2.startTime) > 0) {
-                    this._zones.splice(j, 0, {
-                        startTime:   zone2.startTime,
-                        endTime:     zoneStart,
-                        magnify:     zone2.magnify
-                    });
-                    j++;
-
-                    zone2.startTime = zoneStart;
-                }
-
-                if (this._unit.compare(zoneEnd, zone2.endTime) < 0) {
-                    this._zones.splice(j, 0, {
-                        startTime:  zoneStart,
-                        endTime:    zoneEnd,
-                        magnify:    zone.magnify * zone2.magnify
-                    });
-                    j++;
-
-                    zone2.startTime = zoneEnd;
-                    zoneStart = zoneEnd;
-                } else {
-                    zone2.magnify *= zone.magnify;
-                    zoneStart = zone2.endTime;
-                }
-            } // else, try the next existing zone
-        }
-    }
-
-    if ("startsOn" in this._params) {
-        this._start = this._unit.parseFromObject(this._params.startsOn);
-    } else if ("endsOn" in this._params) {
-        this._start = this._unit.parseFromObject(this._params.endsOn);
-        this.shiftPixels(-this._timeline.getPixelLength());
-    } else if ("centersOn" in this._params) {
-        this._start = this._unit.parseFromObject(this._params.centersOn);
-        this.shiftPixels(-this._timeline.getPixelLength() / 2);
-    } else {
-        this._start = this._unit.makeDefaultValue();
-        this.shiftPixels(-this._timeline.getPixelLength() / 2);
-    }
-};
-
-Timeline.HotZoneEther.prototype.setDate = function(date) {
-    this._start = this._unit.cloneValue(date);
-};
-
-Timeline.HotZoneEther.prototype.shiftPixels = function(pixels) {
-    this._start = this.pixelOffsetToDate(pixels);
-};
-
-Timeline.HotZoneEther.prototype.dateToPixelOffset = function(date) {
-    return this._dateDiffToPixelOffset(this._start, date);
-};
-
-Timeline.HotZoneEther.prototype.pixelOffsetToDate = function(pixels) {
-    return this._pixelOffsetToDate(pixels, this._start);
-};
-
-Timeline.HotZoneEther.prototype.zoom = function(zoomIn) {
-  var netIntervalChange = 0;
-  var currentZoomIndex = this._band._zoomIndex;
-  var newZoomIndex = currentZoomIndex;
-
-  if (zoomIn && (currentZoomIndex > 0)) {
-    newZoomIndex = currentZoomIndex - 1;
-  }
-
-  if (!zoomIn && (currentZoomIndex < (this._band._zoomSteps.length - 1))) {
-    newZoomIndex = currentZoomIndex + 1;
-  }
-
-  this._band._zoomIndex = newZoomIndex;
-  this._interval =
-    SimileAjax.DateTime.gregorianUnitLengths[this._band._zoomSteps[newZoomIndex].unit];
-  this._pixelsPerInterval = this._band._zoomSteps[newZoomIndex].pixelsPerInterval;
-  netIntervalChange = this._band._zoomSteps[newZoomIndex].unit -
-    this._band._zoomSteps[currentZoomIndex].unit;
-
-  return netIntervalChange;
-};
-
-Timeline.HotZoneEther.prototype._dateDiffToPixelOffset = function(fromDate, toDate) {
-    var scale = this._getScale();
-    var fromTime = fromDate;
-    var toTime = toDate;
-
-    var pixels = 0;
-    if (this._unit.compare(fromTime, toTime) < 0) {
-        var z = 0;
-        while (z < this._zones.length) {
-            if (this._unit.compare(fromTime, this._zones[z].endTime) < 0) {
-                break;
-            }
-            z++;
-        }
-
-        while (this._unit.compare(fromTime, toTime) < 0) {
-            var zone = this._zones[z];
-            var toTime2 = this._unit.earlier(toTime, zone.endTime);
-
-            pixels += (this._unit.compare(toTime2, fromTime) / (scale / zone.magnify));
-
-            fromTime = toTime2;
-            z++;
-        }
-    } else {
-        var z = this._zones.length - 1;
-        while (z >= 0) {
-            if (this._unit.compare(fromTime, this._zones[z].startTime) > 0) {
-                break;
-            }
-            z--;
-        }
-
-        while (this._unit.compare(fromTime, toTime) > 0) {
-            var zone = this._zones[z];
-            var toTime2 = this._unit.later(toTime, zone.startTime);
-
-            pixels += (this._unit.compare(toTime2, fromTime) / (scale / zone.magnify));
-
-            fromTime = toTime2;
-            z--;
-        }
-    }
-    return pixels;
-};
-
-Timeline.HotZoneEther.prototype._pixelOffsetToDate = function(pixels, fromDate) {
-    var scale = this._getScale();
-    var time = fromDate;
-    if (pixels > 0) {
-        var z = 0;
-        while (z < this._zones.length) {
-            if (this._unit.compare(time, this._zones[z].endTime) < 0) {
-                break;
-            }
-            z++;
-        }
-
-        while (pixels > 0) {
-            var zone = this._zones[z];
-            var scale2 = scale / zone.magnify;
-
-            if (zone.endTime == Number.POSITIVE_INFINITY) {
-                time = this._unit.change(time, pixels * scale2);
-                pixels = 0;
-            } else {
-                var pixels2 = this._unit.compare(zone.endTime, time) / scale2;
-                if (pixels2 > pixels) {
-                    time = this._unit.change(time, pixels * scale2);
-                    pixels = 0;
-                } else {
-                    time = zone.endTime;
-                    pixels -= pixels2;
-                }
-            }
-            z++;
-        }
-    } else {
-        var z = this._zones.length - 1;
-        while (z >= 0) {
-            if (this._unit.compare(time, this._zones[z].startTime) > 0) {
-                break;
-            }
-            z--;
-        }
-
-        pixels = -pixels;
-        while (pixels > 0) {
-            var zone = this._zones[z];
-            var scale2 = scale / zone.magnify;
-
-            if (zone.startTime == Number.NEGATIVE_INFINITY) {
-                time = this._unit.change(time, -pixels * scale2);
-                pixels = 0;
-            } else {
-                var pixels2 = this._unit.compare(time, zone.startTime) / scale2;
-                if (pixels2 > pixels) {
-                    time = this._unit.change(time, -pixels * scale2);
-                    pixels = 0;
-                } else {
-                    time = zone.startTime;
-                    pixels -= pixels2;
-                }
-            }
-            z--;
-        }
-    }
-    return time;
-};
-
-Timeline.HotZoneEther.prototype._getScale = function() {
-    return this._interval / this._pixelsPerInterval;
-};
-/*
- *  Gregorian Ether Painter
- *
- */
-
-Timeline.GregorianEtherPainter = function(params) {
-    this._params = params;
-    this._theme = params.theme;
-    this._unit = params.unit;
-    this._multiple = ("multiple" in params) ? params.multiple : 1;
-};
-
-Timeline.GregorianEtherPainter.prototype.initialize = function(band, timeline) {
-    this._band = band;
-    this._timeline = timeline;
-
-    this._backgroundLayer = band.createLayerDiv(0);
-    this._backgroundLayer.setAttribute("name", "ether-background"); // for debugging
-    this._backgroundLayer.className = 'timeline-ether-bg';
-  //  this._backgroundLayer.style.background = this._theme.ether.backgroundColors[band.getIndex()];
-
-
-    this._markerLayer = null;
-    this._lineLayer = null;
-
-    var align = ("align" in this._params && this._params.align != undefined) ? this._params.align :
-        this._theme.ether.interval.marker[timeline.isHorizontal() ? "hAlign" : "vAlign"];
-    var showLine = ("showLine" in this._params) ? this._params.showLine :
-        this._theme.ether.interval.line.show;
-
-    this._intervalMarkerLayout = new Timeline.EtherIntervalMarkerLayout(
-        this._timeline, this._band, this._theme, align, showLine);
-
-    this._highlight = new Timeline.EtherHighlight(
-        this._timeline, this._band, this._theme, this._backgroundLayer);
-}
-
-Timeline.GregorianEtherPainter.prototype.setHighlight = function(startDate, endDate) {
-    this._highlight.position(startDate, endDate);
-}
-
-Timeline.GregorianEtherPainter.prototype.paint = function() {
-    if (this._markerLayer) {
-        this._band.removeLayerDiv(this._markerLayer);
-    }
-    this._markerLayer = this._band.createLayerDiv(100);
-    this._markerLayer.setAttribute("name", "ether-markers"); // for debugging
-    this._markerLayer.style.display = "none";
-
-    if (this._lineLayer) {
-        this._band.removeLayerDiv(this._lineLayer);
-    }
-    this._lineLayer = this._band.createLayerDiv(1);
-    this._lineLayer.setAttribute("name", "ether-lines"); // for debugging
-    this._lineLayer.style.display = "none";
-
-    var minDate = this._band.getMinDate();
-    var maxDate = this._band.getMaxDate();
-
-    var timeZone = this._band.getTimeZone();
-    var labeller = this._band.getLabeller();
-
-    SimileAjax.DateTime.roundDownToInterval(minDate, this._unit, timeZone, this._multiple, this._theme.firstDayOfWeek);
-
-    var p = this;
-    var incrementDate = function(date) {
-        for (var i = 0; i < p._multiple; i++) {
-            SimileAjax.DateTime.incrementByInterval(date, p._unit);
-        }
-    };
-
-    while (minDate.getTime() < maxDate.getTime()) {
-        this._intervalMarkerLayout.createIntervalMarker(
-            minDate, labeller, this._unit, this._markerLayer, this._lineLayer);
-
-        incrementDate(minDate);
-    }
-    this._markerLayer.style.display = "block";
-    this._lineLayer.style.display = "block";
-};
-
-Timeline.GregorianEtherPainter.prototype.softPaint = function() {
-};
-
-Timeline.GregorianEtherPainter.prototype.zoom = function(netIntervalChange) {
-  if (netIntervalChange != 0) {
-    this._unit += netIntervalChange;
-  }
-};
-
-
-/*
- *  Hot Zone Gregorian Ether Painter
- *
- */
-
-Timeline.HotZoneGregorianEtherPainter = function(params) {
-    this._params = params;
-    this._theme = params.theme;
-
-    this._zones = [{
-        startTime:  Number.NEGATIVE_INFINITY,
-        endTime:    Number.POSITIVE_INFINITY,
-        unit:       params.unit,
-        multiple:   1
-    }];
-    for (var i = 0; i < params.zones.length; i++) {
-        var zone = params.zones[i];
-        var zoneStart = SimileAjax.DateTime.parseGregorianDateTime(zone.start).getTime();
-        var zoneEnd = SimileAjax.DateTime.parseGregorianDateTime(zone.end).getTime();
-
-        for (var j = 0; j < this._zones.length && zoneEnd > zoneStart; j++) {
-            var zone2 = this._zones[j];
-
-            if (zoneStart < zone2.endTime) {
-                if (zoneStart > zone2.startTime) {
-                    this._zones.splice(j, 0, {
-                        startTime:   zone2.startTime,
-                        endTime:     zoneStart,
-                        unit:        zone2.unit,
-                        multiple:    zone2.multiple
-                    });
-                    j++;
-
-                    zone2.startTime = zoneStart;
-                }
-
-                if (zoneEnd < zone2.endTime) {
-                    this._zones.splice(j, 0, {
-                        startTime:  zoneStart,
-                        endTime:    zoneEnd,
-                        unit:       zone.unit,
-                        multiple:   (zone.multiple) ? zone.multiple : 1
-                    });
-                    j++;
-
-                    zone2.startTime = zoneEnd;
-                    zoneStart = zoneEnd;
-                } else {
-                    zone2.multiple = zone.multiple;
-                    zone2.unit = zone.unit;
-                    zoneStart = zone2.endTime;
-                }
-            } // else, try the next existing zone
-        }
-    }
-};
-
-Timeline.HotZoneGregorianEtherPainter.prototype.initialize = function(band, timeline) {
-    this._band = band;
-    this._timeline = timeline;
-
-    this._backgroundLayer = band.createLayerDiv(0);
-    this._backgroundLayer.setAttribute("name", "ether-background"); // for debugging
-    this._backgroundLayer.className ='timeline-ether-bg';
-    //this._backgroundLayer.style.background = this._theme.ether.backgroundColors[band.getIndex()];
-
-    this._markerLayer = null;
-    this._lineLayer = null;
-
-    var align = ("align" in this._params && this._params.align != undefined) ? this._params.align :
-        this._theme.ether.interval.marker[timeline.isHorizontal() ? "hAlign" : "vAlign"];
-    var showLine = ("showLine" in this._params) ? this._params.showLine :
-        this._theme.ether.interval.line.show;
-
-    this._intervalMarkerLayout = new Timeline.EtherIntervalMarkerLayout(
-        this._timeline, this._band, this._theme, align, showLine);
-
-    this._highlight = new Timeline.EtherHighlight(
-        this._timeline, this._band, this._theme, this._backgroundLayer);
-}
-
-Timeline.HotZoneGregorianEtherPainter.prototype.setHighlight = function(startDate, endDate) {
-    this._highlight.position(startDate, endDate);
-}
-
-Timeline.HotZoneGregorianEtherPainter.prototype.paint = function() {
-    if (this._markerLayer) {
-        this._band.removeLayerDiv(this._markerLayer);
-    }
-    this._markerLayer = this._band.createLayerDiv(100);
-    this._markerLayer.setAttribute("name", "ether-markers"); // for debugging
-    this._markerLayer.style.display = "none";
-
-    if (this._lineLayer) {
-        this._band.removeLayerDiv(this._lineLayer);
-    }
-    this._lineLayer = this._band.createLayerDiv(1);
-    this._lineLayer.setAttribute("name", "ether-lines"); // for debugging
-    this._lineLayer.style.display = "none";
-
-    var minDate = this._band.getMinDate();
-    var maxDate = this._band.getMaxDate();
-
-    var timeZone = this._band.getTimeZone();
-    var labeller = this._band.getLabeller();
-
-    var p = this;
-    var incrementDate = function(date, zone) {
-        for (var i = 0; i < zone.multiple; i++) {
-            SimileAjax.DateTime.incrementByInterval(date, zone.unit);
-        }
-    };
-
-    var zStart = 0;
-    while (zStart < this._zones.length) {
-        if (minDate.getTime() < this._zones[zStart].endTime) {
-            break;
-        }
-        zStart++;
-    }
-    var zEnd = this._zones.length - 1;
-    while (zEnd >= 0) {
-        if (maxDate.getTime() > this._zones[zEnd].startTime) {
-            break;
-        }
-        zEnd--;
-    }
-
-    for (var z = zStart; z <= zEnd; z++) {
-        var zone = this._zones[z];
-
-        var minDate2 = new Date(Math.max(minDate.getTime(), zone.startTime));
-        var maxDate2 = new Date(Math.min(maxDate.getTime(), zone.endTime));
-
-        SimileAjax.DateTime.roundDownToInterval(minDate2, zone.unit, timeZone, zone.multiple, this._theme.firstDayOfWeek);
-        SimileAjax.DateTime.roundUpToInterval(maxDate2, zone.unit, timeZone, zone.multiple, this._theme.firstDayOfWeek);
-
-        while (minDate2.getTime() < maxDate2.getTime()) {
-            this._intervalMarkerLayout.createIntervalMarker(
-                minDate2, labeller, zone.unit, this._markerLayer, this._lineLayer);
-
-            incrementDate(minDate2, zone);
-        }
-    }
-    this._markerLayer.style.display = "block";
-    this._lineLayer.style.display = "block";
-};
-
-Timeline.HotZoneGregorianEtherPainter.prototype.softPaint = function() {
-};
-
-Timeline.HotZoneGregorianEtherPainter.prototype.zoom = function(netIntervalChange) {
-  if (netIntervalChange != 0) {
-    for (var i = 0; i < this._zones.length; ++i) {
-      if (this._zones[i]) {
-        this._zones[i].unit += netIntervalChange;
-      }
-    }
-  }
-};
-
-/*
- *  Year Count Ether Painter
- *
- */
-
-Timeline.YearCountEtherPainter = function(params) {
-    this._params = params;
-    this._theme = params.theme;
-    this._startDate = SimileAjax.DateTime.parseGregorianDateTime(params.startDate);
-    this._multiple = ("multiple" in params) ? params.multiple : 1;
-};
-
-Timeline.YearCountEtherPainter.prototype.initialize = function(band, timeline) {
-    this._band = band;
-    this._timeline = timeline;
-
-    this._backgroundLayer = band.createLayerDiv(0);
-    this._backgroundLayer.setAttribute("name", "ether-background"); // for debugging
-    this._backgroundLayer.className = 'timeline-ether-bg';
-   // this._backgroundLayer.style.background = this._theme.ether.backgroundColors[band.getIndex()];
-
-    this._markerLayer = null;
-    this._lineLayer = null;
-
-    var align = ("align" in this._params) ? this._params.align :
-        this._theme.ether.interval.marker[timeline.isHorizontal() ? "hAlign" : "vAlign"];
-    var showLine = ("showLine" in this._params) ? this._params.showLine :
-        this._theme.ether.interval.line.show;
-
-    this._intervalMarkerLayout = new Timeline.EtherIntervalMarkerLayout(
-        this._timeline, this._band, this._theme, align, showLine);
-
-    this._highlight = new Timeline.EtherHighlight(
-        this._timeline, this._band, this._theme, this._backgroundLayer);
-};
-
-Timeline.YearCountEtherPainter.prototype.setHighlight = function(startDate, endDate) {
-    this._highlight.position(startDate, endDate);
-};
-
-Timeline.YearCountEtherPainter.prototype.paint = function() {
-    if (this._markerLayer) {
-        this._band.removeLayerDiv(this._markerLayer);
-    }
-    this._markerLayer = this._band.createLayerDiv(100);
-    this._markerLayer.setAttribute("name", "ether-markers"); // for debugging
-    this._markerLayer.style.display = "none";
-
-    if (this._lineLayer) {
-        this._band.removeLayerDiv(this._lineLayer);
-    }
-    this._lineLayer = this._band.createLayerDiv(1);
-    this._lineLayer.setAttribute("name", "ether-lines"); // for debugging
-    this._lineLayer.style.display = "none";
-
-    var minDate = new Date(this._startDate.getTime());
-    var maxDate = this._band.getMaxDate();
-    var yearDiff = this._band.getMinDate().getUTCFullYear() - this._startDate.getUTCFullYear();
-    minDate.setUTCFullYear(this._band.getMinDate().getUTCFullYear() - yearDiff % this._multiple);
-
-    var p = this;
-    var incrementDate = function(date) {
-        for (var i = 0; i < p._multiple; i++) {
-            SimileAjax.DateTime.incrementByInterval(date, SimileAjax.DateTime.YEAR);
-        }
-    };
-    var labeller = {
-        labelInterval: function(date, intervalUnit) {
-            var diff = date.getUTCFullYear() - p._startDate.getUTCFullYear();
-            return {
-                text: diff,
-                emphasized: diff == 0
-            };
-        }
-    };
-
-    while (minDate.getTime() < maxDate.getTime()) {
-        this._intervalMarkerLayout.createIntervalMarker(
-            minDate, labeller, SimileAjax.DateTime.YEAR, this._markerLayer, this._lineLayer);
-
-        incrementDate(minDate);
-    }
-    this._markerLayer.style.display = "block";
-    this._lineLayer.style.display = "block";
-};
-
-Timeline.YearCountEtherPainter.prototype.softPaint = function() {
-};
-
-/*
- *  Quarterly Ether Painter
- *
- */
-
-Timeline.QuarterlyEtherPainter = function(params) {
-    this._params = params;
-    this._theme = params.theme;
-    this._startDate = SimileAjax.DateTime.parseGregorianDateTime(params.startDate);
-};
-
-Timeline.QuarterlyEtherPainter.prototype.initialize = function(band, timeline) {
-    this._band = band;
-    this._timeline = timeline;
-
-    this._backgroundLayer = band.createLayerDiv(0);
-    this._backgroundLayer.setAttribute("name", "ether-background"); // for debugging
-    this._backgroundLayer.className = 'timeline-ether-bg';
- //   this._backgroundLayer.style.background = this._theme.ether.backgroundColors[band.getIndex()];
-
-    this._markerLayer = null;
-    this._lineLayer = null;
-
-    var align = ("align" in this._params) ? this._params.align :
-        this._theme.ether.interval.marker[timeline.isHorizontal() ? "hAlign" : "vAlign"];
-    var showLine = ("showLine" in this._params) ? this._params.showLine :
-        this._theme.ether.interval.line.show;
-
-    this._intervalMarkerLayout = new Timeline.EtherIntervalMarkerLayout(
-        this._timeline, this._band, this._theme, align, showLine);
-
-    this._highlight = new Timeline.EtherHighlight(
-        this._timeline, this._band, this._theme, this._backgroundLayer);
-};
-
-Timeline.QuarterlyEtherPainter.prototype.setHighlight = function(startDate, endDate) {
-    this._highlight.position(startDate, endDate);
-};
-
-Timeline.QuarterlyEtherPainter.prototype.paint = function() {
-    if (this._markerLayer) {
-        this._band.removeLayerDiv(this._markerLayer);
-    }
-    this._markerLayer = this._band.createLayerDiv(100);
-    this._markerLayer.setAttribute("name", "ether-markers"); // for debugging
-    this._markerLayer.style.display = "none";
-
-    if (this._lineLayer) {
-        this._band.removeLayerDiv(this._lineLayer);
-    }
-    this._lineLayer = this._band.createLayerDiv(1);
-    this._lineLayer.setAttribute("name", "ether-lines"); // for debugging
-    this._lineLayer.style.display = "none";
-
-    var minDate = new Date(0);
-    var maxDate = this._band.getMaxDate();
-
-    minDate.setUTCFullYear(Math.max(this._startDate.getUTCFullYear(), this._band.getMinDate().getUTCFullYear()));
-    minDate.setUTCMonth(this._startDate.getUTCMonth());
-
-    var p = this;
-    var incrementDate = function(date) {
-        date.setUTCMonth(date.getUTCMonth() + 3);
-    };
-    var labeller = {
-        labelInterval: function(date, intervalUnit) {
-            var quarters = (4 + (date.getUTCMonth() - p._startDate.getUTCMonth()) / 3) % 4;
-            if (quarters != 0) {
-                return { text: "Q" + (quarters + 1), emphasized: false };
-            } else {
-                return { text: "Y" + (date.getUTCFullYear() - p._startDate.getUTCFullYear() + 1), emphasized: true };
-            }
-        }
-    };
-
-    while (minDate.getTime() < maxDate.getTime()) {
-        this._intervalMarkerLayout.createIntervalMarker(
-            minDate, labeller, SimileAjax.DateTime.YEAR, this._markerLayer, this._lineLayer);
-
-        incrementDate(minDate);
-    }
-    this._markerLayer.style.display = "block";
-    this._lineLayer.style.display = "block";
-};
-
-Timeline.QuarterlyEtherPainter.prototype.softPaint = function() {
-};
-
-/*
- *  Ether Interval Marker Layout
- *
- */
-
-Timeline.EtherIntervalMarkerLayout = function(timeline, band, theme, align, showLine) {
-    var horizontal = timeline.isHorizontal();
-    if (horizontal) {
-        if (align == "Top") {
-            this.positionDiv = function(div, offset) {
-                div.style.left = offset + "px";
-                div.style.top = "0px";
-            };
-        } else {
-            this.positionDiv = function(div, offset) {
-                div.style.left = offset + "px";
-                div.style.bottom = "0px";
-            };
-        }
-    } else {
-        if (align == "Left") {
-            this.positionDiv = function(div, offset) {
-                div.style.top = offset + "px";
-                div.style.left = "0px";
-            };
-        } else {
-            this.positionDiv = function(div, offset) {
-                div.style.top = offset + "px";
-                div.style.right = "0px";
-            };
-        }
-    }
-
-    var markerTheme = theme.ether.interval.marker;
-    var lineTheme = theme.ether.interval.line;
-    var weekendTheme = theme.ether.interval.weekend;
-
-    var stylePrefix = (horizontal ? "h" : "v") + align;
-    var labelStyler = markerTheme[stylePrefix + "Styler"];
-    var emphasizedLabelStyler = markerTheme[stylePrefix + "EmphasizedStyler"];
-    var day = SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.DAY];
-
-    this.createIntervalMarker = function(date, labeller, unit, markerDiv, lineDiv) {
-        var offset = Math.round(band.dateToPixelOffset(date));
-
-        if (showLine && unit != SimileAjax.DateTime.WEEK) {
-            var divLine = timeline.getDocument().createElement("div");
-            divLine.className = "timeline-ether-lines";
-
-            if (lineTheme.opacity < 100) {
-                SimileAjax.Graphics.setOpacity(divLine, lineTheme.opacity);
-            }
-
-            if (horizontal) {
-				//divLine.className += " timeline-ether-lines-vertical";
-				divLine.style.left = offset + "px";
-            } else {
-				//divLine.className += " timeline-ether-lines-horizontal";
-                divLine.style.top = offset + "px";
-            }
-            lineDiv.appendChild(divLine);
-        }
-        if (unit == SimileAjax.DateTime.WEEK) {
-            var firstDayOfWeek = theme.firstDayOfWeek;
-
-            var saturday = new Date(date.getTime() + (6 - firstDayOfWeek - 7) * day);
-            var monday = new Date(saturday.getTime() + 2 * day);
-
-            var saturdayPixel = Math.round(band.dateToPixelOffset(saturday));
-            var mondayPixel = Math.round(band.dateToPixelOffset(monday));
-            var length = Math.max(1, mondayPixel - saturdayPixel);
-
-            var divWeekend = timeline.getDocument().createElement("div");
-			divWeekend.className = 'timeline-ether-weekends'
-
-            if (weekendTheme.opacity < 100) {
-                SimileAjax.Graphics.setOpacity(divWeekend, weekendTheme.opacity);
-            }
-
-            if (horizontal) {
-                divWeekend.style.left = saturdayPixel + "px";
-                divWeekend.style.width = length + "px";
-            } else {
-                divWeekend.style.top = saturdayPixel + "px";
-                divWeekend.style.height = length + "px";
-            }
-            lineDiv.appendChild(divWeekend);
-        }
-
-        var label = labeller.labelInterval(date, unit);
-
-        var div = timeline.getDocument().createElement("div");
-        div.innerHTML = label.text;
-
-
-
-		div.className = 'timeline-date-label'
-		if(label.emphasized) div.className += ' timeline-date-label-em'
-
-        this.positionDiv(div, offset);
-        markerDiv.appendChild(div);
-
-        return div;
-    };
-};
-
-/*
- *  Ether Highlight Layout
- *
- */
-
-Timeline.EtherHighlight = function(timeline, band, theme, backgroundLayer) {
-    var horizontal = timeline.isHorizontal();
-
-    this._highlightDiv = null;
-    this._createHighlightDiv = function() {
-        if (this._highlightDiv == null) {
-            this._highlightDiv = timeline.getDocument().createElement("div");
-            this._highlightDiv.setAttribute("name", "ether-highlight"); // for debugging
-            this._highlightDiv.className = 'timeline-ether-highlight'
-
-            var opacity = theme.ether.highlightOpacity;
-            if (opacity < 100) {
-                SimileAjax.Graphics.setOpacity(this._highlightDiv, opacity);
-            }
-
-            backgroundLayer.appendChild(this._highlightDiv);
-        }
-    }
-
-    this.position = function(startDate, endDate) {
-        this._createHighlightDiv();
-
-        var startPixel = Math.round(band.dateToPixelOffset(startDate));
-        var endPixel = Math.round(band.dateToPixelOffset(endDate));
-        var length = Math.max(endPixel - startPixel, 3);
-        if (horizontal) {
-            this._highlightDiv.style.left = startPixel + "px";
-            this._highlightDiv.style.width = length + "px";
-            this._highlightDiv.style.height = (band.getViewWidth() - 4) + "px";
-        } else {
-            this._highlightDiv.style.top = startPixel + "px";
-            this._highlightDiv.style.height = length + "px";
-            this._highlightDiv.style.width = (band.getViewWidth() - 4) + "px";
-        }
-    }
-};
-/*
- *  Event Utils
- *
- */
-Timeline.EventUtils = {};
-
-Timeline.EventUtils.getNewEventID = function() {
-    // global across page
-    if (this._lastEventID == null) {
-        this._lastEventID = 0;
-    }
-
-    this._lastEventID += 1;
-    return "e" + this._lastEventID;
-};
-
-Timeline.EventUtils.decodeEventElID = function(elementID) {
-    /*
-     *
-     * Use this function to decode an event element's id on a band (label div,
-     * tape div or icon img).
-     *
-     * Returns {band: <bandObj>, evt: <eventObj>}
-     *
-     * To enable a single event listener to monitor everything
-     * on a Timeline, a set format is used for the id's of the
-     * elements on the Timeline--
-     *
-     * element id format for labels, icons, tapes:
-     *   labels: label-tl-<timelineID>-<band_index>-<evt.id>
-     *    icons: icon-tl-<timelineID>-<band_index>-<evt.id>
-     *    tapes: tape1-tl-<timelineID>-<band_index>-<evt.id>
-     *           tape2-tl-<timelineID>-<band_index>-<evt.id>
-     *           // some events have more than one tape
-     *    highlight: highlight1-tl-<timelineID>-<band_index>-<evt.id>
-     *               highlight2-tl-<timelineID>-<band_index>-<evt.id>
-     *           // some events have more than one highlight div (future)
-     * Note: use split('-') to get array of the format's parts
-     *
-     * You can then retrieve the timeline object and event object
-     * by using Timeline.getTimeline, Timeline.getBand, or
-     * Timeline.getEvent and passing in the element's id
-     *
-     *
-     */
-
-    var parts = elementID.split('-');
-    if (parts[1] != 'tl') {
-        alert("Internal Timeline problem 101, please consult support");
-        return {band: null, evt: null}; // early return
-    }
-
-    var timeline = Timeline.getTimelineFromID(parts[2]);
-    var band = timeline.getBand(parts[3]);
-    var evt = band.getEventSource.getEvent(parts[4]);
-
-    return {band: band, evt: evt};
-};
-
-Timeline.EventUtils.encodeEventElID = function(timeline, band, elType, evt) {
-    // elType should be one of {label | icon | tapeN | highlightN}
-    return elType + "-tl-" + timeline.timelineID +
-       "-" + band.getIndex() + "-" + evt.getID();
-};/*
- *  Gregorian Date Labeller
- *
- */
-
-Timeline.GregorianDateLabeller = function(locale, timeZone) {
-    this._locale = locale;
-    this._timeZone = timeZone;
-};
-
-Timeline.GregorianDateLabeller.monthNames = [];
-Timeline.GregorianDateLabeller.dayNames = [];
-Timeline.GregorianDateLabeller.labelIntervalFunctions = [];
-
-Timeline.GregorianDateLabeller.getMonthName = function(month, locale) {
-    return Timeline.GregorianDateLabeller.monthNames[locale][month];
-};
-
-Timeline.GregorianDateLabeller.prototype.labelInterval = function(date, intervalUnit) {
-    var f = Timeline.GregorianDateLabeller.labelIntervalFunctions[this._locale];
-    if (f == null) {
-        f = Timeline.GregorianDateLabeller.prototype.defaultLabelInterval;
-    }
-    return f.call(this, date, intervalUnit);
-};
-
-Timeline.GregorianDateLabeller.prototype.labelPrecise = function(date) {
-    return SimileAjax.DateTime.removeTimeZoneOffset(
-        date,
-        this._timeZone //+ (new Date().getTimezoneOffset() / 60)
-    ).toUTCString();
-};
-
-Timeline.GregorianDateLabeller.prototype.defaultLabelInterval = function(date, intervalUnit) {
-    var text;
-    var emphasized = false;
-
-    date = SimileAjax.DateTime.removeTimeZoneOffset(date, this._timeZone);
-
-    switch(intervalUnit) {
-    case SimileAjax.DateTime.MILLISECOND:
-        text = date.getUTCMilliseconds();
-        break;
-    case SimileAjax.DateTime.SECOND:
-        text = date.getUTCSeconds();
-        break;
-    case SimileAjax.DateTime.MINUTE:
-        var m = date.getUTCMinutes();
-        if (m == 0) {
-            text = date.getUTCHours() + ":00";
-            emphasized = true;
-        } else {
-            text = m;
-        }
-        break;
-    case SimileAjax.DateTime.HOUR:
-        text = date.getUTCHours() + "hr";
-        break;
-    case SimileAjax.DateTime.DAY:
-        text = Timeline.GregorianDateLabeller.getMonthName(date.getUTCMonth(), this._locale) + " " + date.getUTCDate();
-        break;
-    case SimileAjax.DateTime.WEEK:
-        text = Timeline.GregorianDateLabeller.getMonthName(date.getUTCMonth(), this._locale) + " " + date.getUTCDate();
-        break;
-    case SimileAjax.DateTime.MONTH:
-        var m = date.getUTCMonth();
-        if (m != 0) {
-            text = Timeline.GregorianDateLabeller.getMonthName(m, this._locale);
-            break;
-        } // else, fall through
-    case SimileAjax.DateTime.YEAR:
-    case SimileAjax.DateTime.DECADE:
-    case SimileAjax.DateTime.CENTURY:
-    case SimileAjax.DateTime.MILLENNIUM:
-        var y = date.getUTCFullYear();
-        if (y > 0) {
-            text = date.getUTCFullYear();
-        } else {
-            text = (1 - y) + "BC";
-        }
-        emphasized =
-            (intervalUnit == SimileAjax.DateTime.MONTH) ||
-            (intervalUnit == SimileAjax.DateTime.DECADE && y % 100 == 0) ||
-            (intervalUnit == SimileAjax.DateTime.CENTURY && y % 1000 == 0);
-        break;
-    default:
-        text = date.toUTCString();
-    }
-    return { text: text, emphasized: emphasized };
-}
-
-/*
- *  Default Event Source
- *
- */
-
-
-Timeline.DefaultEventSource = function(eventIndex) {
-    this._events = (eventIndex instanceof Object) ? eventIndex : new SimileAjax.EventIndex();
-    this._listeners = [];
-};
-
-Timeline.DefaultEventSource.prototype.addListener = function(listener) {
-    this._listeners.push(listener);
-};
-
-Timeline.DefaultEventSource.prototype.removeListener = function(listener) {
-    for (var i = 0; i < this._listeners.length; i++) {
-        if (this._listeners[i] == listener) {
-            this._listeners.splice(i, 1);
-            break;
-        }
-    }
-};
-
-Timeline.DefaultEventSource.prototype.loadXML = function(xml, url) {
-    var base = this._getBaseURL(url);
-
-    var wikiURL = xml.documentElement.getAttribute("wiki-url");
-    var wikiSection = xml.documentElement.getAttribute("wiki-section");
-
-    var dateTimeFormat = xml.documentElement.getAttribute("date-time-format");
-    var parseDateTimeFunction = this._events.getUnit().getParser(dateTimeFormat);
-
-    var node = xml.documentElement.firstChild;
-    var added = false;
-    while (node != null) {
-        if (node.nodeType == 1) {
-            var description = "";
-            if (node.firstChild != null && node.firstChild.nodeType == 3) {
-                description = node.firstChild.nodeValue;
-            }
-            // instant event: default is true. Or use values from isDuration or durationEvent
-            var instant = (node.getAttribute("isDuration")    === null &&
-                           node.getAttribute("durationEvent") === null) ||
-                          node.getAttribute("isDuration") == "false" ||
-                          node.getAttribute("durationEvent") == "false";
-
-            var evt = new Timeline.DefaultEventSource.Event( {
-                          id: node.getAttribute("id"),
-                       start: parseDateTimeFunction(node.getAttribute("start")),
-                         end: parseDateTimeFunction(node.getAttribute("end")),
-                 latestStart: parseDateTimeFunction(node.getAttribute("latestStart")),
-                 earliestEnd: parseDateTimeFunction(node.getAttribute("earliestEnd")),
-                     instant: instant,
-                        text: node.getAttribute("title"),
-                 description: description,
-                       image: this._resolveRelativeURL(node.getAttribute("image"), base),
-                        link: this._resolveRelativeURL(node.getAttribute("link") , base),
-                        icon: this._resolveRelativeURL(node.getAttribute("icon") , base),
-                       color: node.getAttribute("color"),
-                   textColor: node.getAttribute("textColor"),
-                   hoverText: node.getAttribute("hoverText"),
-                   classname: node.getAttribute("classname"),
-                   tapeImage: node.getAttribute("tapeImage"),
-                  tapeRepeat: node.getAttribute("tapeRepeat"),
-                     caption: node.getAttribute("caption"),
-                     eventID: node.getAttribute("eventID"),
-                    trackNum: node.getAttribute("trackNum")
-            });
-
-            evt._node = node;
-            evt.getProperty = function(name) {
-                return this._node.getAttribute(name);
-            };
-            evt.setWikiInfo(wikiURL, wikiSection);
-
-            this._events.add(evt);
-
-            added = true;
-        }
-        node = node.nextSibling;
-    }
-
-    if (added) {
-        this._fire("onAddMany", []);
-    }
-};
-
-
-Timeline.DefaultEventSource.prototype.loadJSON = function(data, url) {
-    var base = this._getBaseURL(url);
-    var added = false;
-    if (data && data.events){
-        var wikiURL = ("wikiURL" in data) ? data.wikiURL : null;
-        var wikiSection = ("wikiSection" in data) ? data.wikiSection : null;
-
-        var dateTimeFormat = ("dateTimeFormat" in data) ? data.dateTimeFormat : null;
-        var parseDateTimeFunction = this._events.getUnit().getParser(dateTimeFormat);
-
-        for (var i=0; i < data.events.length; i++){
-            var event = data.events[i];
-            // Fixing issue 33:
-            // instant event: default (for JSON only) is false. Or use values from isDuration or durationEvent
-            // isDuration was negated (see issue 33, so keep that interpretation
-            var instant = event.isDuration || (event.durationEvent != null && !event.durationEvent);
-
-            var evt = new Timeline.DefaultEventSource.Event({
-                          id: ("id" in event) ? event.id : undefined,
-                       start: parseDateTimeFunction(event.start),
-                         end: parseDateTimeFunction(event.end),
-                 latestStart: parseDateTimeFunction(event.latestStart),
-                 earliestEnd: parseDateTimeFunction(event.earliestEnd),
-                     instant: instant,
-                        text: event.title,
-                 description: event.description,
-                       image: this._resolveRelativeURL(event.image, base),
-                        link: this._resolveRelativeURL(event.link , base),
-                        icon: this._resolveRelativeURL(event.icon , base),
-                       color: event.color,
-                   textColor: event.textColor,
-                   hoverText: event.hoverText,
-                   classname: event.classname,
-                   tapeImage: event.tapeImage,
-                  tapeRepeat: event.tapeRepeat,
-                     caption: event.caption,
-                     eventID: event.eventID,
-                    trackNum: event.trackNum
-            });
-            evt._obj = event;
-            evt.getProperty = function(name) {
-                return this._obj[name];
-            };
-            evt.setWikiInfo(wikiURL, wikiSection);
-
-            this._events.add(evt);
-            added = true;
-        }
-    }
-
-    if (added) {
-        this._fire("onAddMany", []);
-    }
-};
-
-/*
- *  Contributed by Morten Frederiksen, http://www.wasab.dk/morten/
- */
-Timeline.DefaultEventSource.prototype.loadSPARQL = function(xml, url) {
-    var base = this._getBaseURL(url);
-
-    var dateTimeFormat = 'iso8601';
-    var parseDateTimeFunction = this._events.getUnit().getParser(dateTimeFormat);
-
-    if (xml == null) {
-        return;
-    }
-
-    /*
-     *  Find <results> tag
-     */
-    var node = xml.documentElement.firstChild;
-    while (node != null && (node.nodeType != 1 || node.nodeName != 'results')) {
-        node = node.nextSibling;
-    }
-
-    var wikiURL = null;
-    var wikiSection = null;
-    if (node != null) {
-        wikiURL = node.getAttribute("wiki-url");
-        wikiSection = node.getAttribute("wiki-section");
-
-        node = node.firstChild;
-    }
-
-    var added = false;
-    while (node != null) {
-        if (node.nodeType == 1) {
-            var bindings = { };
-            var binding = node.firstChild;
-            while (binding != null) {
-                if (binding.nodeType == 1 &&
-                    binding.firstChild != null &&
-                    binding.firstChild.nodeType == 1 &&
-                    binding.firstChild.firstChild != null &&
-                    binding.firstChild.firstChild.nodeType == 3) {
-                    bindings[binding.getAttribute('name')] = binding.firstChild.firstChild.nodeValue;
-                }
-                binding = binding.nextSibling;
-            }
-
-            if (bindings["start"] == null && bindings["date"] != null) {
-                bindings["start"] = bindings["date"];
-            }
-
-            // instant event: default is true. Or use values from isDuration or durationEvent
-            var instant = (bindings["isDuration"]    === null &&
-                           bindings["durationEvent"] === null) ||
-                          bindings["isDuration"] == "false" ||
-                          bindings["durationEvent"] == "false";
-
-            var evt = new Timeline.DefaultEventSource.Event({
-                          id: bindings["id"],
-                       start: parseDateTimeFunction(bindings["start"]),
-                         end: parseDateTimeFunction(bindings["end"]),
-                 latestStart: parseDateTimeFunction(bindings["latestStart"]),
-                 earliestEnd: parseDateTimeFunction(bindings["earliestEnd"]),
-                     instant: instant, // instant
-                        text: bindings["title"], // text
-                 description: bindings["description"],
-                       image: this._resolveRelativeURL(bindings["image"], base),
-                        link: this._resolveRelativeURL(bindings["link"] , base),
-                        icon: this._resolveRelativeURL(bindings["icon"] , base),
-                       color: bindings["color"],
-                   textColor: bindings["textColor"],
-                   hoverText: bindings["hoverText"],
-                     caption: bindings["caption"],
-                   classname: bindings["classname"],
-                   tapeImage: bindings["tapeImage"],
-                  tapeRepeat: bindings["tapeRepeat"],
-                     eventID: bindings["eventID"],
-                    trackNum: bindings["trackNum"]
-            });
-            evt._bindings = bindings;
-            evt.getProperty = function(name) {
-                return this._bindings[name];
-            };
-            evt.setWikiInfo(wikiURL, wikiSection);
-
-            this._events.add(evt);
-            added = true;
-        }
-        node = node.nextSibling;
-    }
-
-    if (added) {
-        this._fire("onAddMany", []);
-    }
-};
-
-Timeline.DefaultEventSource.prototype.add = function(evt) {
-    this._events.add(evt);
-    this._fire("onAddOne", [evt]);
-};
-
-Timeline.DefaultEventSource.prototype.addMany = function(events) {
-    for (var i = 0; i < events.length; i++) {
-        this._events.add(events[i]);
-    }
-    this._fire("onAddMany", []);
-};
-
-Timeline.DefaultEventSource.prototype.clear = function() {
-    this._events.removeAll();
-    this._fire("onClear", []);
-};
-
-Timeline.DefaultEventSource.prototype.getEvent = function(id) {
-    return this._events.getEvent(id);
-};
-
-Timeline.DefaultEventSource.prototype.getEventIterator = function(startDate, endDate) {
-    return this._events.getIterator(startDate, endDate);
-};
-
-Timeline.DefaultEventSource.prototype.getEventReverseIterator = function(startDate, endDate) {
-    return this._events.getReverseIterator(startDate, endDate);
-};
-
-Timeline.DefaultEventSource.prototype.getAllEventIterator = function() {
-    return this._events.getAllIterator();
-};
-
-Timeline.DefaultEventSource.prototype.getCount = function() {
-    return this._events.getCount();
-};
-
-Timeline.DefaultEventSource.prototype.getEarliestDate = function() {
-    return this._events.getEarliestDate();
-};
-
-Timeline.DefaultEventSource.prototype.getLatestDate = function() {
-    return this._events.getLatestDate();
-};
-
-Timeline.DefaultEventSource.prototype._fire = function(handlerName, args) {
-    for (var i = 0; i < this._listeners.length; i++) {
-        var listener = this._listeners[i];
-        if (handlerName in listener) {
-            try {
-                listener[handlerName].apply(listener, args);
-            } catch (e) {
-                SimileAjax.Debug.exception(e);
-            }
-        }
-    }
-};
-
-Timeline.DefaultEventSource.prototype._getBaseURL = function(url) {
-    if (url.indexOf("://") < 0) {
-        var url2 = this._getBaseURL(document.location.href);
-        if (url.substr(0,1) == "/") {
-            url = url2.substr(0, url2.indexOf("/", url2.indexOf("://") + 3)) + url;
-        } else {
-            url = url2 + url;
-        }
-    }
-
-    var i = url.lastIndexOf("/");
-    if (i < 0) {
-        return "";
-    } else {
-        return url.substr(0, i+1);
-    }
-};
-
-Timeline.DefaultEventSource.prototype._resolveRelativeURL = function(url, base) {
-    if (url == null || url == "") {
-        return url;
-    } else if (url.indexOf("://") > 0) {
-        return url;
-    } else if (url.substr(0,1) == "/") {
-        return base.substr(0, base.indexOf("/", base.indexOf("://") + 3)) + url;
-    } else {
-        return base + url;
-    }
-};
-
-
-Timeline.DefaultEventSource.Event = function(args) {
-  //
-  // Attention developers!
-  // If you add a new event attribute, please be sure to add it to
-  // all three load functions: loadXML, loadSPARCL, loadJSON.
-  // Thanks!
-  //
-  // args is a hash/object. It supports the following keys. Most are optional
-  //   id            -- an internal id. Really shouldn't be used by events.
-  //                    Timeline library clients should use eventID
-  //   eventID       -- For use by library client when writing custom painters or
-  //                    custom fillInfoBubble
-  //   start
-  //   end
-  //   latestStart
-  //   earliestEnd
-  //   instant      -- boolean. Controls precise/non-precise logic & duration/instant issues
-  //   text         -- event source attribute 'title' -- used as the label on Timelines and in bubbles.
-  //   description  -- used in bubbles
-  //   image        -- used in bubbles
-  //   link         -- used in bubbles
-  //   icon         -- on the Timeline
-  //   color        -- Timeline label and tape color
-  //   textColor    -- Timeline label color, overrides color attribute
-  //   hoverText    -- deprecated, here for backwards compatibility.
-  //                   Superceeded by caption
-  //   caption      -- tooltip-like caption on the Timeline. Uses HTML title attribute
-  //   classname    -- used to set classname in Timeline. Enables better CSS selector rules
-  //   tapeImage    -- background image of the duration event's tape div on the Timeline
-  //   tapeRepeat   -- repeat attribute for tapeImage. {repeat | repeat-x | repeat-y }
-
-  function cleanArg(arg) {
-      // clean up an arg
-      return (args[arg] != null && args[arg] != "") ? args[arg] : null;
-  }
-
-  var id = args.id ? args.id.trim() : "";
-  this._id = id.length > 0 ? id : Timeline.EventUtils.getNewEventID();
-
-  this._instant = args.instant || (args.end == null);
-
-  this._start = args.start;
-  this._end = (args.end != null) ? args.end : args.start;
-
-  this._latestStart = (args.latestStart != null) ?
-                       args.latestStart : (args.instant ? this._end : this._start);
-  this._earliestEnd = (args.earliestEnd != null) ? args.earliestEnd : this._end;
-
-  // check sanity of dates since incorrect dates will later cause calculation errors
-  // when painting
-  var err=[];
-  if (this._start > this._latestStart) {
-          this._latestStart = this._start;
-          err.push("start is > latestStart");}
-  if (this._start > this._earliestEnd) {
-          this._earliestEnd = this._latestStart;
-          err.push("start is > earliestEnd");}
-  if (this._start > this._end) {
-          this._end = this._earliestEnd;
-          err.push("start is > end");}
-  if (this._latestStart > this._earliestEnd) {
-          this._earliestEnd = this._latestStart;
-          err.push("latestStart is > earliestEnd");}
-  if (this._latestStart > this._end) {
-          this._end = this._earliestEnd;
-          err.push("latestStart is > end");}
-  if (this._earliestEnd > this._end) {
-          this._end = this._earliestEnd;
-          err.push("earliestEnd is > end");}
-
-  this._eventID = cleanArg('eventID');
-  this._text = (args.text != null) ? SimileAjax.HTML.deEntify(args.text) : ""; // Change blank titles to ""
-  if (err.length > 0) {
-          this._text += " PROBLEM: " + err.join(", ");
-  }
-
-  this._description = SimileAjax.HTML.deEntify(args.description);
-  this._image = cleanArg('image');
-  this._link =  cleanArg('link');
-  this._title = cleanArg('hoverText');
-  this._title = cleanArg('caption');
-
-  this._icon = cleanArg('icon');
-  this._color = cleanArg('color');
-  this._textColor = cleanArg('textColor');
-  this._classname = cleanArg('classname');
-  this._tapeImage = cleanArg('tapeImage');
-  this._tapeRepeat = cleanArg('tapeRepeat');
-  this._trackNum = cleanArg('trackNum');
-  if (this._trackNum != null) {
-      this._trackNum = parseInt(this._trackNum);
-  }
-
-  this._wikiURL = null;
-  this._wikiSection = null;
-};
-
-Timeline.DefaultEventSource.Event.prototype = {
-    getID:          function() { return this._id; },
-
-    isInstant:      function() { return this._instant; },
-    isImprecise:    function() { return this._start != this._latestStart || this._end != this._earliestEnd; },
-
-    getStart:       function() { return this._start; },
-    getEnd:         function() { return this._end; },
-    getLatestStart: function() { return this._latestStart; },
-    getEarliestEnd: function() { return this._earliestEnd; },
-
-    getEventID:     function() { return this._eventID; },
-    getText:        function() { return this._text; }, // title
-    getDescription: function() { return this._description; },
-    getImage:       function() { return this._image; },
-    getLink:        function() { return this._link; },
-
-    getIcon:        function() { return this._icon; },
-    getColor:       function() { return this._color; },
-    getTextColor:   function() { return this._textColor; },
-    getClassName:   function() { return this._classname; },
-    getTapeImage:   function() { return this._tapeImage; },
-    getTapeRepeat:  function() { return this._tapeRepeat; },
-    getTrackNum:    function() { return this._trackNum; },
-
-    getProperty:    function(name) { return null; },
-
-    getWikiURL:     function() { return this._wikiURL; },
-    getWikiSection: function() { return this._wikiSection; },
-    setWikiInfo: function(wikiURL, wikiSection) {
-        this._wikiURL = wikiURL;
-        this._wikiSection = wikiSection;
-    },
-
-    fillDescription: function(elmt) {
-        elmt.innerHTML = this._description;
-    },
-    fillWikiInfo: function(elmt) {
-        // Many bubbles will not support a wiki link.
-        //
-        // Strategy: assume no wiki link. If we do have
-        // enough parameters for one, then create it.
-        elmt.style.display = "none"; // default
-
-        if (this._wikiURL == null || this._wikiSection == null) {
-          return; // EARLY RETURN
-        }
-
-        // create the wikiID from the property or from the event text (the title)
-        var wikiID = this.getProperty("wikiID");
-        if (wikiID == null || wikiID.length == 0) {
-            wikiID = this.getText(); // use the title as the backup wiki id
-        }
-
-        if (wikiID == null || wikiID.length == 0) {
-          return; // No wikiID. Thus EARLY RETURN
-        }
-
-        // ready to go...
-        elmt.style.display = "inline";
-        wikiID = wikiID.replace(/\s/g, "_");
-        var url = this._wikiURL + this._wikiSection.replace(/\s/g, "_") + "/" + wikiID;
-        var a = document.createElement("a");
-        a.href = url;
-        a.target = "new";
-        a.innerHTML = Timeline.strings[Timeline.clientLocale].wikiLinkLabel;
-
-        elmt.appendChild(document.createTextNode("["));
-        elmt.appendChild(a);
-        elmt.appendChild(document.createTextNode("]"));
-    },
-
-    fillTime: function(elmt, labeller) {
-        if (this._instant) {
-            if (this.isImprecise()) {
-                elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._start)));
-                elmt.appendChild(elmt.ownerDocument.createElement("br"));
-                elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._end)));
-            } else {
-                elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._start)));
-            }
-        } else {
-            if (this.isImprecise()) {
-                elmt.appendChild(elmt.ownerDocument.createTextNode(
-                    labeller.labelPrecise(this._start) + " ~ " + labeller.labelPrecise(this._latestStart)));
-                elmt.appendChild(elmt.ownerDocument.createElement("br"));
-                elmt.appendChild(elmt.ownerDocument.createTextNode(
-                    labeller.labelPrecise(this._earliestEnd) + " ~ " + labeller.labelPrecise(this._end)));
-            } else {
-                elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._start)));
-                elmt.appendChild(elmt.ownerDocument.createElement("br"));
-                elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._end)));
-            }
-        }
-    },
-
-    fillInfoBubble: function(elmt, theme, labeller) {
-        var doc = elmt.ownerDocument;
-
-        var title = this.getText();
-        var link = this.getLink();
-        var image = this.getImage();
-
-        if (image != null) {
-            var img = doc.createElement("img");
-            img.src = image;
-
-            theme.event.bubble.imageStyler(img);
-            elmt.appendChild(img);
-        }
-
-        var divTitle = doc.createElement("div");
-        var textTitle = doc.createTextNode(title);
-        if (link != null) {
-            var a = doc.createElement("a");
-            a.href = link;
-            a.appendChild(textTitle);
-            divTitle.appendChild(a);
-        } else {
-            divTitle.appendChild(textTitle);
-        }
-        theme.event.bubble.titleStyler(divTitle);
-        elmt.appendChild(divTitle);
-
-        var divBody = doc.createElement("div");
-        this.fillDescription(divBody);
-        theme.event.bubble.bodyStyler(divBody);
-        elmt.appendChild(divBody);
-
-        var divTime = doc.createElement("div");
-        this.fillTime(divTime, labeller);
-        theme.event.bubble.timeStyler(divTime);
-        elmt.appendChild(divTime);
-
-        var divWiki = doc.createElement("div");
-        this.fillWikiInfo(divWiki);
-        theme.event.bubble.wikiStyler(divWiki);
-        elmt.appendChild(divWiki);
-    }
-};
-
-
-/*
- *  Original Event Painter
- *
- */
-
-/*
- *
- * To enable a single event listener to monitor everything
- * on a Timeline, we need a way to map from an event's icon,
- * label or tape element to the associated timeline, band and
- * specific event.
- *
- * Thus a set format is used for the id's of the
- * events' elements on the Timeline--
- *
- * element id format for labels, icons, tapes:
- *   labels: label-tl-<timelineID>-<band_index>-<evt.id>
- *    icons: icon-tl-<timelineID>-<band_index>-<evt.id>
- *    tapes: tape1-tl-<timelineID>-<band_index>-<evt.id>
- *           tape2-tl-<timelineID>-<band_index>-<evt.id>
- *           // some events have more than one tape
- *    highlight: highlight1-tl-<timelineID>-<band_index>-<evt.id>
- *               highlight2-tl-<timelineID>-<band_index>-<evt.id>
- *           // some events have more than one highlight div (future)
- * You can then retrieve the band/timeline objects and event object
- * by using Timeline.EventUtils.decodeEventElID
- *
- *
- */
-
-/*
- *    eventPaintListener functions receive calls about painting.
- *    function(band, op, evt, els)
- *       context: 'this' will be an OriginalEventPainter object.
- *                It has properties and methods for obtaining
- *                the relevant band, timeline, etc
- *       band = the band being painted
- *       op = 'paintStarting' // the painter is about to remove
- *            all previously painted events, if any. It will
- *            then start painting all of the visible events that
- *            pass the filter.
- *            evt = null, els = null
- *       op = 'paintEnded' // the painter has finished painting
- *            all of the visible events that passed the filter
- *            evt = null, els = null
- *       op = 'paintedEvent' // the painter just finished painting an event
- *            evt = event just painted
- *            els = array of painted elements' divs. Depending on the event,
- *                  the array could be just a tape or icon (if no label).
- *                  Or could include label, multiple tape divs (imprecise event),
- *                  highlight divs. The array is not ordered. The meaning of
- *                  each el is available by decoding the el's id
- *      Note that there may be no paintedEvent calls if no events were visible
- *      or passed the filter.
- */
-
-Timeline.OriginalEventPainter = function(params) {
-    this._params = params;
-    this._onSelectListeners = [];
-    this._eventPaintListeners = [];
-
-    this._filterMatcher = null;
-    this._highlightMatcher = null;
-    this._frc = null;
-
-    this._eventIdToElmt = {};
-};
-
-Timeline.OriginalEventPainter.prototype.initialize = function(band, timeline) {
-    this._band = band;
-    this._timeline = timeline;
-
-    this._backLayer = null;
-    this._eventLayer = null;
-    this._lineLayer = null;
-    this._highlightLayer = null;
-
-    this._eventIdToElmt = null;
-};
-
-Timeline.OriginalEventPainter.prototype.getType = function() {
-    return 'original';
-};
-
-Timeline.OriginalEventPainter.prototype.addOnSelectListener = function(listener) {
-    this._onSelectListeners.push(listener);
-};
-
-Timeline.OriginalEventPainter.prototype.removeOnSelectListener = function(listener) {
-    for (var i = 0; i < this._onSelectListeners.length; i++) {
-        if (this._onSelectListeners[i] == listener) {
-            this._onSelectListeners.splice(i, 1);
-            break;
-        }
-    }
-};
-
-Timeline.OriginalEventPainter.prototype.addEventPaintListener = function(listener) {
-    this._eventPaintListeners.push(listener);
-};
-
-Timeline.OriginalEventPainter.prototype.removeEventPaintListener = function(listener) {
-    for (var i = 0; i < this._eventPaintListeners.length; i++) {
-        if (this._eventPaintListeners[i] == listener) {
-            this._eventPaintListeners.splice(i, 1);
-            break;
-        }
-    }
-};
-
-Timeline.OriginalEventPainter.prototype.getFilterMatcher = function() {
-    return this._filterMatcher;
-};
-
-Timeline.OriginalEventPainter.prototype.setFilterMatcher = function(filterMatcher) {
-    this._filterMatcher = filterMatcher;
-};
-
-Timeline.OriginalEventPainter.prototype.getHighlightMatcher = function() {
-    return this._highlightMatcher;
-};
-
-Timeline.OriginalEventPainter.prototype.setHighlightMatcher = function(highlightMatcher) {
-    this._highlightMatcher = highlightMatcher;
-};
-
-Timeline.OriginalEventPainter.prototype.paint = function() {
-    // Paints the events for a given section of the band--what is
-    // visible on screen and some extra.
-    var eventSource = this._band.getEventSource();
-    if (eventSource == null) {
-        return;
-    }
-
-    this._eventIdToElmt = {};
-    this._fireEventPaintListeners('paintStarting', null, null);
-    this._prepareForPainting();
-
-    var metrics = this._computeMetrics();
-    var minDate = this._band.getMinDate();
-    var maxDate = this._band.getMaxDate();
-
-    var filterMatcher = (this._filterMatcher != null) ?
-        this._filterMatcher :
-        function(evt) { return true; };
-    var highlightMatcher = (this._highlightMatcher != null) ?
-        this._highlightMatcher :
-        function(evt) { return -1; };
-
-    var iterator = eventSource.getEventReverseIterator(minDate, maxDate);
-    while (iterator.hasNext()) {
-        var evt = iterator.next();
-        if (filterMatcher(evt)) {
-            this.paintEvent(evt, metrics, this._params.theme, highlightMatcher(evt));
-        }
-    }
-
-    this._highlightLayer.style.display = "block";
-    this._lineLayer.style.display = "block";
-    this._eventLayer.style.display = "block";
-    // update the band object for max number of tracks in this section of the ether
-    this._band.updateEventTrackInfo(this._tracks.length, metrics.trackIncrement);
-    this._fireEventPaintListeners('paintEnded', null, null);
-
-    this._setOrthogonalOffset(metrics);
-};
-
-Timeline.OriginalEventPainter.prototype.softPaint = function() {
-    this._setOrthogonalOffset(this._computeMetrics());
-};
-
-Timeline.OriginalEventPainter.prototype._setOrthogonalOffset = function(metrics) {
-    var actualViewWidth = 2 * metrics.trackOffset + this._tracks.length * metrics.trackIncrement;
-    var minOrthogonalOffset = Math.min(0, this._band.getViewWidth() - actualViewWidth);
-    var orthogonalOffset = Math.max(minOrthogonalOffset, this._band.getViewOrthogonalOffset());
-
-    this._highlightLayer.style.top =
-        this._lineLayer.style.top =
-            this._eventLayer.style.top =
-                orthogonalOffset + "px";
-};
-
-Timeline.OriginalEventPainter.prototype._computeMetrics = function() {
-     var eventTheme = this._params.theme.event;
-     var trackHeight = Math.max(eventTheme.track.height, eventTheme.tape.height +
-                         this._frc.getLineHeight());
-     var metrics = {
-            trackOffset: eventTheme.track.offset,
-            trackHeight: trackHeight,
-               trackGap: eventTheme.track.gap,
-         trackIncrement: trackHeight + eventTheme.track.gap,
-                   icon: eventTheme.instant.icon,
-              iconWidth: eventTheme.instant.iconWidth,
-             iconHeight: eventTheme.instant.iconHeight,
-             labelWidth: eventTheme.label.width,
-           maxLabelChar: eventTheme.label.maxLabelChar,
-    impreciseIconMargin: eventTheme.instant.impreciseIconMargin
-     };
-
-     return metrics;
-};
-
-Timeline.OriginalEventPainter.prototype._prepareForPainting = function() {
-    // Remove everything previously painted: highlight, line and event layers.
-    // Prepare blank layers for painting.
-    var band = this._band;
-
-    if (this._backLayer == null) {
-        this._backLayer = this._band.createLayerDiv(0, "timeline-band-events");
-        this._backLayer.style.visibility = "hidden";
-
-        var eventLabelPrototype = document.createElement("span");
-        eventLabelPrototype.className = "timeline-event-label";
-        this._backLayer.appendChild(eventLabelPrototype);
-        this._frc = SimileAjax.Graphics.getFontRenderingContext(eventLabelPrototype);
-    }
-    this._frc.update();
-    this._tracks = [];
-
-    if (this._highlightLayer != null) {
-        band.removeLayerDiv(this._highlightLayer);
-    }
-    this._highlightLayer = band.createLayerDiv(105, "timeline-band-highlights");
-    this._highlightLayer.style.display = "none";
-
-    if (this._lineLayer != null) {
-        band.removeLayerDiv(this._lineLayer);
-    }
-    this._lineLayer = band.createLayerDiv(110, "timeline-band-lines");
-    this._lineLayer.style.display = "none";
-
-    if (this._eventLayer != null) {
-        band.removeLayerDiv(this._eventLayer);
-    }
-    this._eventLayer = band.createLayerDiv(115, "timeline-band-events");
-    this._eventLayer.style.display = "none";
-};
-
-Timeline.OriginalEventPainter.prototype.paintEvent = function(evt, metrics, theme, highlightIndex) {
-    if (evt.isInstant()) {
-        this.paintInstantEvent(evt, metrics, theme, highlightIndex);
-    } else {
-        this.paintDurationEvent(evt, metrics, theme, highlightIndex);
-    }
-};
-
-Timeline.OriginalEventPainter.prototype.paintInstantEvent = function(evt, metrics, theme, highlightIndex) {
-    if (evt.isImprecise()) {
-        this.paintImpreciseInstantEvent(evt, metrics, theme, highlightIndex);
-    } else {
-        this.paintPreciseInstantEvent(evt, metrics, theme, highlightIndex);
-    }
-}
-
-Timeline.OriginalEventPainter.prototype.paintDurationEvent = function(evt, metrics, theme, highlightIndex) {
-    if (evt.isImprecise()) {
-        this.paintImpreciseDurationEvent(evt, metrics, theme, highlightIndex);
-    } else {
-        this.paintPreciseDurationEvent(evt, metrics, theme, highlightIndex);
-    }
-}
-
-Timeline.OriginalEventPainter.prototype.paintPreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
-    var doc = this._timeline.getDocument();
-    var text = evt.getText();
-
-    var startDate = evt.getStart();
-    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
-    var iconRightEdge = Math.round(startPixel + metrics.iconWidth / 2);
-    var iconLeftEdge = Math.round(startPixel - metrics.iconWidth / 2);
-
-    var labelDivClassName = this._getLabelDivClassName(evt);
-    var labelSize = this._frc.computeSize(text, labelDivClassName);
-    var labelLeft = iconRightEdge + theme.event.label.offsetFromLine;
-    var labelRight = labelLeft + labelSize.width;
-
-    var rightEdge = labelRight;
-    var track = this._findFreeTrack(evt, rightEdge);
-
-    var labelTop = Math.round(
-        metrics.trackOffset + track * metrics.trackIncrement +
-        metrics.trackHeight / 2 - labelSize.height / 2);
-
-    var iconElmtData = this._paintEventIcon(evt, track, iconLeftEdge, metrics, theme, 0);
-    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width,
-        labelSize.height, theme, labelDivClassName, highlightIndex);
-    var els = [iconElmtData.elmt, labelElmtData.elmt];
-
-    var self = this;
-    var clickHandler = function(elmt, domEvt, target) {
-        return self._onClickInstantEvent(iconElmtData.elmt, domEvt, evt);
-    };
-    SimileAjax.DOM.registerEvent(iconElmtData.elmt, "mousedown", clickHandler);
-    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
-
-    var hDiv = this._createHighlightDiv(highlightIndex, iconElmtData, theme, evt);
-    if (hDiv != null) {els.push(hDiv);}
-    this._fireEventPaintListeners('paintedEvent', evt, els);
-
-
-    this._eventIdToElmt[evt.getID()] = iconElmtData.elmt;
-    this._tracks[track] = iconLeftEdge;
-};
-
-Timeline.OriginalEventPainter.prototype.paintImpreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
-    var doc = this._timeline.getDocument();
-    var text = evt.getText();
-
-    var startDate = evt.getStart();
-    var endDate = evt.getEnd();
-    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
-    var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
-
-    var iconRightEdge = Math.round(startPixel + metrics.iconWidth / 2);
-    var iconLeftEdge = Math.round(startPixel - metrics.iconWidth / 2);
-
-    var labelDivClassName = this._getLabelDivClassName(evt);
-    var labelSize = this._frc.computeSize(text, labelDivClassName);
-    var labelLeft = iconRightEdge + theme.event.label.offsetFromLine;
-    var labelRight = labelLeft + labelSize.width;
-
-    var rightEdge = Math.max(labelRight, endPixel);
-    var track = this._findFreeTrack(evt, rightEdge);
-    var tapeHeight = theme.event.tape.height;
-    var labelTop = Math.round(
-        metrics.trackOffset + track * metrics.trackIncrement + tapeHeight);
-
-    var iconElmtData = this._paintEventIcon(evt, track, iconLeftEdge, metrics, theme, tapeHeight);
-    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width,
-                        labelSize.height, theme, labelDivClassName, highlightIndex);
-
-    var color = evt.getColor();
-    color = color != null ? color : theme.event.instant.impreciseColor;
-
-    var tapeElmtData = this._paintEventTape(evt, track, startPixel, endPixel,
-        color, theme.event.instant.impreciseOpacity, metrics, theme, 0);
-    var els = [iconElmtData.elmt, labelElmtData.elmt, tapeElmtData.elmt];
-
-    var self = this;
-    var clickHandler = function(elmt, domEvt, target) {
-        return self._onClickInstantEvent(iconElmtData.elmt, domEvt, evt);
-    };
-    SimileAjax.DOM.registerEvent(iconElmtData.elmt, "mousedown", clickHandler);
-    SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
-    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
-
-    var hDiv = this._createHighlightDiv(highlightIndex, iconElmtData, theme, evt);
-    if (hDiv != null) {els.push(hDiv);}
-    this._fireEventPaintListeners('paintedEvent', evt, els);
-
-    this._eventIdToElmt[evt.getID()] = iconElmtData.elmt;
-    this._tracks[track] = iconLeftEdge;
-};
-
-Timeline.OriginalEventPainter.prototype.paintPreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
-    var doc = this._timeline.getDocument();
-    var text = evt.getText();
-
-    var startDate = evt.getStart();
-    var endDate = evt.getEnd();
-    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
-    var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
-
-    var labelDivClassName = this._getLabelDivClassName(evt);
-    var labelSize = this._frc.computeSize(text, labelDivClassName);
-    var labelLeft = startPixel;
-    var labelRight = labelLeft + labelSize.width;
-
-    var rightEdge = Math.max(labelRight, endPixel);
-    var track = this._findFreeTrack(evt, rightEdge);
-    var labelTop = Math.round(
-        metrics.trackOffset + track * metrics.trackIncrement + theme.event.tape.height);
-
-    var color = evt.getColor();
-    color = color != null ? color : theme.event.duration.color;
-
-    var tapeElmtData = this._paintEventTape(evt, track, startPixel, endPixel, color, 100, metrics, theme, 0);
-    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width,
-      labelSize.height, theme, labelDivClassName, highlightIndex);
-    var els = [tapeElmtData.elmt, labelElmtData.elmt];
-
-    var self = this;
-    var clickHandler = function(elmt, domEvt, target) {
-        return self._onClickDurationEvent(tapeElmtData.elmt, domEvt, evt);
-    };
-    SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
-    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
-
-    var hDiv = this._createHighlightDiv(highlightIndex, tapeElmtData, theme, evt);
-    if (hDiv != null) {els.push(hDiv);}
-    this._fireEventPaintListeners('paintedEvent', evt, els);
-
-    this._eventIdToElmt[evt.getID()] = tapeElmtData.elmt;
-    this._tracks[track] = startPixel;
-};
-
-Timeline.OriginalEventPainter.prototype.paintImpreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
-    var doc = this._timeline.getDocument();
-    var text = evt.getText();
-
-    var startDate = evt.getStart();
-    var latestStartDate = evt.getLatestStart();
-    var endDate = evt.getEnd();
-    var earliestEndDate = evt.getEarliestEnd();
-
-    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
-    var latestStartPixel = Math.round(this._band.dateToPixelOffset(latestStartDate));
-    var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
-    var earliestEndPixel = Math.round(this._band.dateToPixelOffset(earliestEndDate));
-
-    var labelDivClassName = this._getLabelDivClassName(evt);
-    var labelSize = this._frc.computeSize(text, labelDivClassName);
-    var labelLeft = latestStartPixel;
-    var labelRight = labelLeft + labelSize.width;
-
-    var rightEdge = Math.max(labelRight, endPixel);
-    var track = this._findFreeTrack(evt, rightEdge);
-    var labelTop = Math.round(
-        metrics.trackOffset + track * metrics.trackIncrement + theme.event.tape.height);
-
-    var color = evt.getColor();
-    color = color != null ? color : theme.event.duration.color;
-
-    // Imprecise events can have two event tapes
-    // The imprecise dates tape, uses opacity to be dimmer than precise dates
-    var impreciseTapeElmtData = this._paintEventTape(evt, track, startPixel, endPixel,
-        theme.event.duration.impreciseColor,
-        theme.event.duration.impreciseOpacity, metrics, theme, 0);
-    // The precise dates tape, regular (100%) opacity
-    var tapeElmtData = this._paintEventTape(evt, track, latestStartPixel,
-        earliestEndPixel, color, 100, metrics, theme, 1);
-
-    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop,
-        labelSize.width, labelSize.height, theme, labelDivClassName, highlightIndex);
-    var els = [impreciseTapeElmtData.elmt, tapeElmtData.elmt, labelElmtData.elmt];
-
-    var self = this;
-    var clickHandler = function(elmt, domEvt, target) {
-        return self._onClickDurationEvent(tapeElmtData.elmt, domEvt, evt);
-    };
-    SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
-    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
-
-    var hDiv = this._createHighlightDiv(highlightIndex, tapeElmtData, theme, evt);
-    if (hDiv != null) {els.push(hDiv);}
-    this._fireEventPaintListeners('paintedEvent', evt, els);
-
-    this._eventIdToElmt[evt.getID()] = tapeElmtData.elmt;
-    this._tracks[track] = startPixel;
-};
-
-Timeline.OriginalEventPainter.prototype._encodeEventElID = function(elType, evt) {
-    return Timeline.EventUtils.encodeEventElID(this._timeline, this._band, elType, evt);
-};
-
-Timeline.OriginalEventPainter.prototype._findFreeTrack = function(event, rightEdge) {
-    var trackAttribute = event.getTrackNum();
-    if (trackAttribute != null) {
-        return trackAttribute; // early return since event includes track number
-    }
-
-    // normal case: find an open track
-    for (var i = 0; i < this._tracks.length; i++) {
-        var t = this._tracks[i];
-        if (t > rightEdge) {
-            break;
-        }
-    }
-    return i;
-};
-
-Timeline.OriginalEventPainter.prototype._paintEventIcon = function(evt, iconTrack, left, metrics, theme, tapeHeight) {
-    // If no tape, then paint the icon in the middle of the track.
-    // If there is a tape, paint the icon below the tape + impreciseIconMargin
-    var icon = evt.getIcon();
-    icon = icon != null ? icon : metrics.icon;
-
-    var top; // top of the icon
-    if (tapeHeight > 0) {
-        top = metrics.trackOffset + iconTrack * metrics.trackIncrement +
-              tapeHeight + metrics.impreciseIconMargin;
-    } else {
-        var middle = metrics.trackOffset + iconTrack * metrics.trackIncrement +
-                     metrics.trackHeight / 2;
-        top = Math.round(middle - metrics.iconHeight / 2);
-    }
-    var img = SimileAjax.Graphics.createTranslucentImage(icon);
-    var iconDiv = this._timeline.getDocument().createElement("div");
-    iconDiv.className = this._getElClassName('timeline-event-icon', evt, 'icon');
-    iconDiv.id = this._encodeEventElID('icon', evt);
-    iconDiv.style.left = left + "px";
-    iconDiv.style.top = top + "px";
-    iconDiv.appendChild(img);
-
-    if(evt._title != null)
-        iconDiv.title = evt._title;
-
-    this._eventLayer.appendChild(iconDiv);
-
-    return {
-        left:   left,
-        top:    top,
-        width:  metrics.iconWidth,
-        height: metrics.iconHeight,
-        elmt:   iconDiv
-    };
-};
-
-Timeline.OriginalEventPainter.prototype._paintEventLabel = function(evt, text, left, top, width,
-    height, theme, labelDivClassName, highlightIndex) {
-    var doc = this._timeline.getDocument();
-
-    var labelDiv = doc.createElement("div");
-    labelDiv.className = labelDivClassName;
-    labelDiv.id = this._encodeEventElID('label', evt);
-    labelDiv.style.left = left + "px";
-    labelDiv.style.width = width + "px";
-    labelDiv.style.top = top + "px";
-    labelDiv.innerHTML = text;
-
-    if(evt._title != null)
-        labelDiv.title = evt._title;
-
-    var color = evt.getTextColor();
-    if (color == null) {
-        color = evt.getColor();
-    }
-    if (color != null) {
-        labelDiv.style.color = color;
-    }
-    if (theme.event.highlightLabelBackground && highlightIndex >= 0) {
-        labelDiv.style.background = this._getHighlightColor(highlightIndex, theme);
-    }
-
-    this._eventLayer.appendChild(labelDiv);
-
-    return {
-        left:   left,
-        top:    top,
-        width:  width,
-        height: height,
-        elmt:   labelDiv
-    };
-};
-
-Timeline.OriginalEventPainter.prototype._paintEventTape = function(
-    evt, iconTrack, startPixel, endPixel, color, opacity, metrics, theme, tape_index) {
-
-    var tapeWidth = endPixel - startPixel;
-    var tapeHeight = theme.event.tape.height;
-    var top = metrics.trackOffset + iconTrack * metrics.trackIncrement;
-
-    var tapeDiv = this._timeline.getDocument().createElement("div");
-    tapeDiv.className = this._getElClassName('timeline-event-tape', evt, 'tape');
-    tapeDiv.id = this._encodeEventElID('tape' + tape_index, evt);
-    tapeDiv.style.left = startPixel + "px";
-    tapeDiv.style.width = tapeWidth + "px";
-    tapeDiv.style.height = tapeHeight + "px";
-    tapeDiv.style.top = top + "px";
-
-    if(evt._title != null)
-        tapeDiv.title = evt._title;
-
-    if(color != null) {
-        tapeDiv.style.backgroundColor = color;
-    }
-
-    var backgroundImage = evt.getTapeImage();
-    var backgroundRepeat = evt.getTapeRepeat();
-    backgroundRepeat = backgroundRepeat != null ? backgroundRepeat : 'repeat';
-    if(backgroundImage != null) {
-      tapeDiv.style.backgroundImage = "url(" + backgroundImage + ")";
-      tapeDiv.style.backgroundRepeat = backgroundRepeat;
-    }
-
-    SimileAjax.Graphics.setOpacity(tapeDiv, opacity);
-
-    this._eventLayer.appendChild(tapeDiv);
-
-    return {
-        left:   startPixel,
-        top:    top,
-        width:  tapeWidth,
-        height: tapeHeight,
-        elmt:   tapeDiv
-    };
-}
-
-Timeline.OriginalEventPainter.prototype._getLabelDivClassName = function(evt) {
-    return this._getElClassName('timeline-event-label', evt, 'label');
-};
-
-Timeline.OriginalEventPainter.prototype._getElClassName = function(elClassName, evt, prefix) {
-    // Prefix and '_' is added to the event's classname. Set to null for no prefix
-    var evt_classname = evt.getClassName(),
-        pieces = [];
-
-    if (evt_classname) {
-      if (prefix) {pieces.push(prefix + '-' + evt_classname + ' ');}
-      pieces.push(evt_classname + ' ');
-    }
-    pieces.push(elClassName);
-    return(pieces.join(''));
-};
-
-Timeline.OriginalEventPainter.prototype._getHighlightColor = function(highlightIndex, theme) {
-    var highlightColors = theme.event.highlightColors;
-    return highlightColors[Math.min(highlightIndex, highlightColors.length - 1)];
-};
-
-Timeline.OriginalEventPainter.prototype._createHighlightDiv = function(highlightIndex, dimensions, theme, evt) {
-    var div = null;
-    if (highlightIndex >= 0) {
-        var doc = this._timeline.getDocument();
-        var color = this._getHighlightColor(highlightIndex, theme);
-
-        div = doc.createElement("div");
-        div.className = this._getElClassName('timeline-event-highlight', evt, 'highlight');
-        div.id = this._encodeEventElID('highlight0', evt); // in future will have other
-                                                           // highlight divs for tapes + icons
-        div.style.position = "absolute";
-        div.style.overflow = "hidden";
-        div.style.left =    (dimensions.left - 2) + "px";
-        div.style.width =   (dimensions.width + 4) + "px";
-        div.style.top =     (dimensions.top - 2) + "px";
-        div.style.height =  (dimensions.height + 4) + "px";
-        div.style.background = color;
-
-        this._highlightLayer.appendChild(div);
-    }
-    return div;
-};
-
-Timeline.OriginalEventPainter.prototype._onClickInstantEvent = function(icon, domEvt, evt) {
-    var c = SimileAjax.DOM.getPageCoordinates(icon);
-    this._showBubble(
-        c.left + Math.ceil(icon.offsetWidth / 2),
-        c.top + Math.ceil(icon.offsetHeight / 2),
-        evt
-    );
-    this._fireOnSelect(evt.getID());
-
-    domEvt.cancelBubble = true;
-    SimileAjax.DOM.cancelEvent(domEvt);
-    return false;
-};
-
-Timeline.OriginalEventPainter.prototype._onClickDurationEvent = function(target, domEvt, evt) {
-    if ("pageX" in domEvt) {
-        var x = domEvt.pageX;
-        var y = domEvt.pageY;
-    } else {
-        var c = SimileAjax.DOM.getPageCoordinates(target);
-        var x = domEvt.offsetX + c.left;
-        var y = domEvt.offsetY + c.top;
-    }
-    this._showBubble(x, y, evt);
-    this._fireOnSelect(evt.getID());
-
-    domEvt.cancelBubble = true;
-    SimileAjax.DOM.cancelEvent(domEvt);
-    return false;
-};
-
-Timeline.OriginalEventPainter.prototype.showBubble = function(evt) {
-    var elmt = this._eventIdToElmt[evt.getID()];
-    if (elmt) {
-        var c = SimileAjax.DOM.getPageCoordinates(elmt);
-        this._showBubble(c.left + elmt.offsetWidth / 2, c.top + elmt.offsetHeight / 2, evt);
-    }
-};
-
-Timeline.OriginalEventPainter.prototype._showBubble = function(x, y, evt) {
-    var div = document.createElement("div");
-    var themeBubble = this._params.theme.event.bubble;
-    evt.fillInfoBubble(div, this._params.theme, this._band.getLabeller());
-
-    SimileAjax.WindowManager.cancelPopups();
-    SimileAjax.Graphics.createBubbleForContentAndPoint(div, x, y,
-        themeBubble.width, null, themeBubble.maxHeight);
-};
-
-Timeline.OriginalEventPainter.prototype._fireOnSelect = function(eventID) {
-    for (var i = 0; i < this._onSelectListeners.length; i++) {
-        this._onSelectListeners[i](eventID);
-    }
-};
-
-Timeline.OriginalEventPainter.prototype._fireEventPaintListeners = function(op, evt, els) {
-    for (var i = 0; i < this._eventPaintListeners.length; i++) {
-        this._eventPaintListeners[i](this._band, op, evt, els);
-    }
-};
-/*
- *  Detailed Event Painter
- *
- */
-
-// Note: a number of features from original-painter
-//       are not yet implemented in detailed painter.
-//       Eg classname, id attributes for icons, labels, tapes
-
-Timeline.DetailedEventPainter = function(params) {
-    this._params = params;
-    this._onSelectListeners = [];
-
-    this._filterMatcher = null;
-    this._highlightMatcher = null;
-    this._frc = null;
-
-    this._eventIdToElmt = {};
-};
-
-Timeline.DetailedEventPainter.prototype.initialize = function(band, timeline) {
-    this._band = band;
-    this._timeline = timeline;
-
-    this._backLayer = null;
-    this._eventLayer = null;
-    this._lineLayer = null;
-    this._highlightLayer = null;
-
-    this._eventIdToElmt = null;
-};
-
-Timeline.DetailedEventPainter.prototype.getType = function() {
-    return 'detailed';
-};
-
-Timeline.DetailedEventPainter.prototype.addOnSelectListener = function(listener) {
-    this._onSelectListeners.push(listener);
-};
-
-Timeline.DetailedEventPainter.prototype.removeOnSelectListener = function(listener) {
-    for (var i = 0; i < this._onSelectListeners.length; i++) {
-        if (this._onSelectListeners[i] == listener) {
-            this._onSelectListeners.splice(i, 1);
-            break;
-        }
-    }
-};
-
-Timeline.DetailedEventPainter.prototype.getFilterMatcher = function() {
-    return this._filterMatcher;
-};
-
-Timeline.DetailedEventPainter.prototype.setFilterMatcher = function(filterMatcher) {
-    this._filterMatcher = filterMatcher;
-};
-
-Timeline.DetailedEventPainter.prototype.getHighlightMatcher = function() {
-    return this._highlightMatcher;
-};
-
-Timeline.DetailedEventPainter.prototype.setHighlightMatcher = function(highlightMatcher) {
-    this._highlightMatcher = highlightMatcher;
-};
-
-Timeline.DetailedEventPainter.prototype.paint = function() {
-    var eventSource = this._band.getEventSource();
-    if (eventSource == null) {
-        return;
-    }
-
-    this._eventIdToElmt = {};
-    this._prepareForPainting();
-
-    var eventTheme = this._params.theme.event;
-    var trackHeight = Math.max(eventTheme.track.height, this._frc.getLineHeight());
-    var metrics = {
-        trackOffset:    Math.round(this._band.getViewWidth() / 2 - trackHeight / 2),
-        trackHeight:    trackHeight,
-        trackGap:       eventTheme.track.gap,
-        trackIncrement: trackHeight + eventTheme.track.gap,
-        icon:           eventTheme.instant.icon,
-        iconWidth:      eventTheme.instant.iconWidth,
-        iconHeight:     eventTheme.instant.iconHeight,
-        labelWidth:     eventTheme.label.width
-    }
-
-    var minDate = this._band.getMinDate();
-    var maxDate = this._band.getMaxDate();
-
-    var filterMatcher = (this._filterMatcher != null) ?
-        this._filterMatcher :
-        function(evt) { return true; };
-    var highlightMatcher = (this._highlightMatcher != null) ?
-        this._highlightMatcher :
-        function(evt) { return -1; };
-
-    var iterator = eventSource.getEventReverseIterator(minDate, maxDate);
-    while (iterator.hasNext()) {
-        var evt = iterator.next();
-        if (filterMatcher(evt)) {
-            this.paintEvent(evt, metrics, this._params.theme, highlightMatcher(evt));
-        }
-    }
-
-    this._highlightLayer.style.display = "block";
-    this._lineLayer.style.display = "block";
-    this._eventLayer.style.display = "block";
-    // update the band object for max number of tracks in this section of the ether
-    this._band.updateEventTrackInfo(this._lowerTracks.length + this._upperTracks.length,
-                                 metrics.trackIncrement);
-};
-
-Timeline.DetailedEventPainter.prototype.softPaint = function() {
-};
-
-Timeline.DetailedEventPainter.prototype._prepareForPainting = function() {
-    var band = this._band;
-
-    if (this._backLayer == null) {
-        this._backLayer = this._band.createLayerDiv(0, "timeline-band-events");
-        this._backLayer.style.visibility = "hidden";
-
-        var eventLabelPrototype = document.createElement("span");
-        eventLabelPrototype.className = "timeline-event-label";
-        this._backLayer.appendChild(eventLabelPrototype);
-        this._frc = SimileAjax.Graphics.getFontRenderingContext(eventLabelPrototype);
-    }
-    this._frc.update();
-    this._lowerTracks = [];
-    this._upperTracks = [];
-
-    if (this._highlightLayer != null) {
-        band.removeLayerDiv(this._highlightLayer);
-    }
-    this._highlightLayer = band.createLayerDiv(105, "timeline-band-highlights");
-    this._highlightLayer.style.display = "none";
-
-    if (this._lineLayer != null) {
-        band.removeLayerDiv(this._lineLayer);
-    }
-    this._lineLayer = band.createLayerDiv(110, "timeline-band-lines");
-    this._lineLayer.style.display = "none";
-
-    if (this._eventLayer != null) {
-        band.removeLayerDiv(this._eventLayer);
-    }
-    this._eventLayer = band.createLayerDiv(110, "timeline-band-events");
-    this._eventLayer.style.display = "none";
-};
-
-Timeline.DetailedEventPainter.prototype.paintEvent = function(evt, metrics, theme, highlightIndex) {
-    if (evt.isInstant()) {
-        this.paintInstantEvent(evt, metrics, theme, highlightIndex);
-    } else {
-        this.paintDurationEvent(evt, metrics, theme, highlightIndex);
-    }
-};
-
-Timeline.DetailedEventPainter.prototype.paintInstantEvent = function(evt, metrics, theme, highlightIndex) {
-    if (evt.isImprecise()) {
-        this.paintImpreciseInstantEvent(evt, metrics, theme, highlightIndex);
-    } else {
-        this.paintPreciseInstantEvent(evt, metrics, theme, highlightIndex);
-    }
-}
-
-Timeline.DetailedEventPainter.prototype.paintDurationEvent = function(evt, metrics, theme, highlightIndex) {
-    if (evt.isImprecise()) {
-        this.paintImpreciseDurationEvent(evt, metrics, theme, highlightIndex);
-    } else {
-        this.paintPreciseDurationEvent(evt, metrics, theme, highlightIndex);
-    }
-}
-
-Timeline.DetailedEventPainter.prototype.paintPreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
-    var doc = this._timeline.getDocument();
-    var text = evt.getText();
-
-    var startDate = evt.getStart();
-    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
-    var iconRightEdge = Math.round(startPixel + metrics.iconWidth / 2);
-    var iconLeftEdge = Math.round(startPixel - metrics.iconWidth / 2);
-
-    var labelSize = this._frc.computeSize(text);
-    var iconTrack = this._findFreeTrackForSolid(iconRightEdge, startPixel);
-    var iconElmtData = this._paintEventIcon(evt, iconTrack, iconLeftEdge, metrics, theme);
-
-    var labelLeft = iconRightEdge + theme.event.label.offsetFromLine;
-    var labelTrack = iconTrack;
-
-    var iconTrackData = this._getTrackData(iconTrack);
-    if (Math.min(iconTrackData.solid, iconTrackData.text) >= labelLeft + labelSize.width) { // label on the same track, to the right of icon
-        iconTrackData.solid = iconLeftEdge;
-        iconTrackData.text = labelLeft;
-    } else { // label on a different track, below icon
-        iconTrackData.solid = iconLeftEdge;
-
-        labelLeft = startPixel + theme.event.label.offsetFromLine;
-        labelTrack = this._findFreeTrackForText(iconTrack, labelLeft + labelSize.width, function(t) { t.line = startPixel - 2; });
-        this._getTrackData(labelTrack).text = iconLeftEdge;
-
-        this._paintEventLine(evt, startPixel, iconTrack, labelTrack, metrics, theme);
-    }
-
-    var labelTop = Math.round(
-        metrics.trackOffset + labelTrack * metrics.trackIncrement +
-        metrics.trackHeight / 2 - labelSize.height / 2);
-
-    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width, labelSize.height, theme);
-
-    var self = this;
-    var clickHandler = function(elmt, domEvt, target) {
-        return self._onClickInstantEvent(iconElmtData.elmt, domEvt, evt);
-    };
-    SimileAjax.DOM.registerEvent(iconElmtData.elmt, "mousedown", clickHandler);
-    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
-
-    this._createHighlightDiv(highlightIndex, iconElmtData, theme);
-
-    this._eventIdToElmt[evt.getID()] = iconElmtData.elmt;
-};
-
-Timeline.DetailedEventPainter.prototype.paintImpreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
-    var doc = this._timeline.getDocument();
-    var text = evt.getText();
-
-    var startDate = evt.getStart();
-    var endDate = evt.getEnd();
-    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
-    var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
-
-    var iconRightEdge = Math.round(startPixel + metrics.iconWidth / 2);
-    var iconLeftEdge = Math.round(startPixel - metrics.iconWidth / 2);
-
-    var labelSize = this._frc.computeSize(text);
-    var iconTrack = this._findFreeTrackForSolid(endPixel, startPixel);
-
-    var tapeElmtData = this._paintEventTape(evt, iconTrack, startPixel, endPixel,
-        theme.event.instant.impreciseColor, theme.event.instant.impreciseOpacity, metrics, theme);
-    var iconElmtData = this._paintEventIcon(evt, iconTrack, iconLeftEdge, metrics, theme);
-
-    var iconTrackData = this._getTrackData(iconTrack);
-    iconTrackData.solid = iconLeftEdge;
-
-    var labelLeft = iconRightEdge + theme.event.label.offsetFromLine;
-    var labelRight = labelLeft + labelSize.width;
-    var labelTrack;
-    if (labelRight < endPixel) {
-        labelTrack = iconTrack;
-    } else {
-        labelLeft = startPixel + theme.event.label.offsetFromLine;
-        labelRight = labelLeft + labelSize.width;
-
-        labelTrack = this._findFreeTrackForText(iconTrack, labelRight, function(t) { t.line = startPixel - 2; });
-        this._getTrackData(labelTrack).text = iconLeftEdge;
-
-        this._paintEventLine(evt, startPixel, iconTrack, labelTrack, metrics, theme);
-    }
-    var labelTop = Math.round(
-        metrics.trackOffset + labelTrack * metrics.trackIncrement +
-        metrics.trackHeight / 2 - labelSize.height / 2);
-
-    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width, labelSize.height, theme);
-
-    var self = this;
-    var clickHandler = function(elmt, domEvt, target) {
-        return self._onClickInstantEvent(iconElmtData.elmt, domEvt, evt);
-    };
-    SimileAjax.DOM.registerEvent(iconElmtData.elmt, "mousedown", clickHandler);
-    SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
-    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
-
-    this._createHighlightDiv(highlightIndex, iconElmtData, theme);
-
-    this._eventIdToElmt[evt.getID()] = iconElmtData.elmt;
-};
-
-Timeline.DetailedEventPainter.prototype.paintPreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
-    var doc = this._timeline.getDocument();
-    var text = evt.getText();
-
-    var startDate = evt.getStart();
-    var endDate = evt.getEnd();
-    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
-    var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
-
-    var labelSize = this._frc.computeSize(text);
-    var tapeTrack = this._findFreeTrackForSolid(endPixel);
-    var color = evt.getColor();
-    color = color != null ? color : theme.event.duration.color;
-
-    var tapeElmtData = this._paintEventTape(evt, tapeTrack, startPixel, endPixel, color, 100, metrics, theme);
-
-    var tapeTrackData = this._getTrackData(tapeTrack);
-    tapeTrackData.solid = startPixel;
-
-    var labelLeft = startPixel + theme.event.label.offsetFromLine;
-    var labelTrack = this._findFreeTrackForText(tapeTrack, labelLeft + labelSize.width, function(t) { t.line = startPixel - 2; });
-    this._getTrackData(labelTrack).text = startPixel - 2;
-
-    this._paintEventLine(evt, startPixel, tapeTrack, labelTrack, metrics, theme);
-
-    var labelTop = Math.round(
-        metrics.trackOffset + labelTrack * metrics.trackIncrement +
-        metrics.trackHeight / 2 - labelSize.height / 2);
-
-    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width, labelSize.height, theme);
-
-    var self = this;
-    var clickHandler = function(elmt, domEvt, target) {
-        return self._onClickDurationEvent(tapeElmtData.elmt, domEvt, evt);
-    };
-    SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
-    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
-
-    this._createHighlightDiv(highlightIndex, tapeElmtData, theme);
-
-    this._eventIdToElmt[evt.getID()] = tapeElmtData.elmt;
-};
-
-Timeline.DetailedEventPainter.prototype.paintImpreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
-    var doc = this._timeline.getDocument();
-    var text = evt.getText();
-
-    var startDate = evt.getStart();
-    var latestStartDate = evt.getLatestStart();
-    var endDate = evt.getEnd();
-    var earliestEndDate = evt.getEarliestEnd();
-
-    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
-    var latestStartPixel = Math.round(this._band.dateToPixelOffset(latestStartDate));
-    var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
-    var earliestEndPixel = Math.round(this._band.dateToPixelOffset(earliestEndDate));
-
-    var labelSize = this._frc.computeSize(text);
-    var tapeTrack = this._findFreeTrackForSolid(endPixel);
-    var color = evt.getColor();
-    color = color != null ? color : theme.event.duration.color;
-
-    var impreciseTapeElmtData = this._paintEventTape(evt, tapeTrack, startPixel, endPixel,
-        theme.event.duration.impreciseColor, theme.event.duration.impreciseOpacity, metrics, theme);
-    var tapeElmtData = this._paintEventTape(evt, tapeTrack, latestStartPixel, earliestEndPixel, color, 100, metrics, theme);
-
-    var tapeTrackData = this._getTrackData(tapeTrack);
-    tapeTrackData.solid = startPixel;
-
-    var labelLeft = latestStartPixel + theme.event.label.offsetFromLine;
-    var labelTrack = this._findFreeTrackForText(tapeTrack, labelLeft + labelSize.width, function(t) { t.line = latestStartPixel - 2; });
-    this._getTrackData(labelTrack).text = latestStartPixel - 2;
-
-    this._paintEventLine(evt, latestStartPixel, tapeTrack, labelTrack, metrics, theme);
-
-    var labelTop = Math.round(
-        metrics.trackOffset + labelTrack * metrics.trackIncrement +
-        metrics.trackHeight / 2 - labelSize.height / 2);
-
-    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width, labelSize.height, theme);
-
-    var self = this;
-    var clickHandler = function(elmt, domEvt, target) {
-        return self._onClickDurationEvent(tapeElmtData.elmt, domEvt, evt);
-    };
-    SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
-    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
-
-    this._createHighlightDiv(highlightIndex, tapeElmtData, theme);
-
-    this._eventIdToElmt[evt.getID()] = tapeElmtData.elmt;
-};
-
-Timeline.DetailedEventPainter.prototype._findFreeTrackForSolid = function(solidEdge, softEdge) {
-    for (var i = 0; true; i++) {
-        if (i < this._lowerTracks.length) {
-            var t = this._lowerTracks[i];
-            if (Math.min(t.solid, t.text) > solidEdge && (!(softEdge) || t.line > softEdge)) {
-                return i;
-            }
-        } else {
-            this._lowerTracks.push({
-                solid:  Number.POSITIVE_INFINITY,
-                text:   Number.POSITIVE_INFINITY,
-                line:   Number.POSITIVE_INFINITY
-            });
-
-            return i;
-        }
-
-        if (i < this._upperTracks.length) {
-            var t = this._upperTracks[i];
-            if (Math.min(t.solid, t.text) > solidEdge && (!(softEdge) || t.line > softEdge)) {
-                return -1 - i;
-            }
-        } else {
-            this._upperTracks.push({
-                solid:  Number.POSITIVE_INFINITY,
-                text:   Number.POSITIVE_INFINITY,
-                line:   Number.POSITIVE_INFINITY
-            });
-
-            return -1 - i;
-        }
-    }
-};
-
-Timeline.DetailedEventPainter.prototype._findFreeTrackForText = function(fromTrack, edge, occupiedTrackVisitor) {
-    var extendUp;
-    var index;
-    var firstIndex;
-    var result;
-
-    if (fromTrack < 0) {
-        extendUp = true;
-        firstIndex = -fromTrack;
-
-        index = this._findFreeUpperTrackForText(firstIndex, edge);
-        result = -1 - index;
-    } else if (fromTrack > 0) {
-        extendUp = false;
-        firstIndex = fromTrack + 1;
-
-        index = this._findFreeLowerTrackForText(firstIndex, edge);
-        result = index;
-    } else {
-        var upIndex = this._findFreeUpperTrackForText(0, edge);
-        var downIndex = this._findFreeLowerTrackForText(1, edge);
-
-        if (downIndex - 1 <= upIndex) {
-            extendUp = false;
-            firstIndex = 1;
-            index = downIndex;
-            result = index;
-        } else {
-            extendUp = true;
-            firstIndex = 0;
-            index = upIndex;
-            result = -1 - index;
-        }
-    }
-
-    if (extendUp) {
-        if (index == this._upperTracks.length) {
-            this._upperTracks.push({
-                solid:  Number.POSITIVE_INFINITY,
-                text:   Number.POSITIVE_INFINITY,
-                line:   Number.POSITIVE_INFINITY
-            });
-        }
-        for (var i = firstIndex; i < index; i++) {
-            occupiedTrackVisitor(this._upperTracks[i]);
-        }
-    } else {
-        if (index == this._lowerTracks.length) {
-            this._lowerTracks.push({
-                solid:  Number.POSITIVE_INFINITY,
-                text:   Number.POSITIVE_INFINITY,
-                line:   Number.POSITIVE_INFINITY
-            });
-        }
-        for (var i = firstIndex; i < index; i++) {
-            occupiedTrackVisitor(this._lowerTracks[i]);
-        }
-    }
-    return result;
-};
-
-Timeline.DetailedEventPainter.prototype._findFreeLowerTrackForText = function(index, edge) {
-    for (; index < this._lowerTracks.length; index++) {
-        var t = this._lowerTracks[index];
-        if (Math.min(t.solid, t.text) >= edge) {
-            break;
-        }
-    }
-    return index;
-};
-
-Timeline.DetailedEventPainter.prototype._findFreeUpperTrackForText = function(index, edge) {
-    for (; index < this._upperTracks.length; index++) {
-        var t = this._upperTracks[index];
-        if (Math.min(t.solid, t.text) >= edge) {
-            break;
-        }
-    }
-    return index;
-};
-
-Timeline.DetailedEventPainter.prototype._getTrackData = function(index) {
-    return (index < 0) ? this._upperTracks[-index - 1] : this._lowerTracks[index];
-};
-
-Timeline.DetailedEventPainter.prototype._paintEventLine = function(evt, left, startTrack, endTrack, metrics, theme) {
-    var top = Math.round(metrics.trackOffset + startTrack * metrics.trackIncrement + metrics.trackHeight / 2);
-    var height = Math.round(Math.abs(endTrack - startTrack) * metrics.trackIncrement);
-
-    var lineStyle = "1px solid " + theme.event.label.lineColor;
-    var lineDiv = this._timeline.getDocument().createElement("div");
-	lineDiv.style.position = "absolute";
-    lineDiv.style.left = left + "px";
-    lineDiv.style.width = theme.event.label.offsetFromLine + "px";
-    lineDiv.style.height = height + "px";
-    if (startTrack > endTrack) {
-        lineDiv.style.top = (top - height) + "px";
-        lineDiv.style.borderTop = lineStyle;
-    } else {
-        lineDiv.style.top = top + "px";
-        lineDiv.style.borderBottom = lineStyle;
-    }
-    lineDiv.style.borderLeft = lineStyle;
-    this._lineLayer.appendChild(lineDiv);
-};
-
-Timeline.DetailedEventPainter.prototype._paintEventIcon = function(evt, iconTrack, left, metrics, theme) {
-    var icon = evt.getIcon();
-    icon = icon != null ? icon : metrics.icon;
-
-    var middle = metrics.trackOffset + iconTrack * metrics.trackIncrement + metrics.trackHeight / 2;
-    var top = Math.round(middle - metrics.iconHeight / 2);
-
-    var img = SimileAjax.Graphics.createTranslucentImage(icon);
-    var iconDiv = this._timeline.getDocument().createElement("div");
-    iconDiv.style.position = "absolute";
-    iconDiv.style.left = left + "px";
-    iconDiv.style.top = top + "px";
-    iconDiv.appendChild(img);
-    iconDiv.style.cursor = "pointer";
-
-    if(evt._title != null)
-        iconDiv.title = evt._title
-
-    this._eventLayer.appendChild(iconDiv);
-
-    return {
-        left:   left,
-        top:    top,
-        width:  metrics.iconWidth,
-        height: metrics.iconHeight,
-        elmt:   iconDiv
-    };
-};
-
-Timeline.DetailedEventPainter.prototype._paintEventLabel = function(evt, text, left, top, width, height, theme) {
-    var doc = this._timeline.getDocument();
-
-    var labelBackgroundDiv = doc.createElement("div");
-    labelBackgroundDiv.style.position = "absolute";
-    labelBackgroundDiv.style.left = left + "px";
-    labelBackgroundDiv.style.width = width + "px";
-    labelBackgroundDiv.style.top = top + "px";
-    labelBackgroundDiv.style.height = height + "px";
-    labelBackgroundDiv.style.backgroundColor = theme.event.label.backgroundColor;
-    SimileAjax.Graphics.setOpacity(labelBackgroundDiv, theme.event.label.backgroundOpacity);
-    this._eventLayer.appendChild(labelBackgroundDiv);
-
-    var labelDiv = doc.createElement("div");
-    labelDiv.style.position = "absolute";
-    labelDiv.style.left = left + "px";
-    labelDiv.style.width = width + "px";
-    labelDiv.style.top = top + "px";
-    labelDiv.innerHTML = text;
-    labelDiv.style.cursor = "pointer";
-
-    if(evt._title != null)
-        labelDiv.title = evt._title;
-
-    var color = evt.getTextColor();
-    if (color == null) {
-        color = evt.getColor();
-    }
-    if (color != null) {
-        labelDiv.style.color = color;
-    }
-
-    this._eventLayer.appendChild(labelDiv);
-
-    return {
-        left:   left,
-        top:    top,
-        width:  width,
-        height: height,
-        elmt:   labelDiv
-    };
-};
-
-Timeline.DetailedEventPainter.prototype._paintEventTape = function(
-    evt, iconTrack, startPixel, endPixel, color, opacity, metrics, theme) {
-
-    var tapeWidth = endPixel - startPixel;
-    var tapeHeight = theme.event.tape.height;
-    var middle = metrics.trackOffset + iconTrack * metrics.trackIncrement + metrics.trackHeight / 2;
-    var top = Math.round(middle - tapeHeight / 2);
-
-    var tapeDiv = this._timeline.getDocument().createElement("div");
-    tapeDiv.style.position = "absolute";
-    tapeDiv.style.left = startPixel + "px";
-    tapeDiv.style.width = tapeWidth + "px";
-    tapeDiv.style.top = top + "px";
-    tapeDiv.style.height = tapeHeight + "px";
-    tapeDiv.style.backgroundColor = color;
-    tapeDiv.style.overflow = "hidden";
-    tapeDiv.style.cursor = "pointer";
-
-    if(evt._title != null)
-        tapeDiv.title = evt._title;
-
-    SimileAjax.Graphics.setOpacity(tapeDiv, opacity);
-
-    this._eventLayer.appendChild(tapeDiv);
-
-    return {
-        left:   startPixel,
-        top:    top,
-        width:  tapeWidth,
-        height: tapeHeight,
-        elmt:   tapeDiv
-    };
-}
-
-Timeline.DetailedEventPainter.prototype._createHighlightDiv = function(highlightIndex, dimensions, theme) {
-    if (highlightIndex >= 0) {
-        var doc = this._timeline.getDocument();
-        var eventTheme = theme.event;
-
-        var color = eventTheme.highlightColors[Math.min(highlightIndex, eventTheme.highlightColors.length - 1)];
-
-        var div = doc.createElement("div");
-        div.style.position = "absolute";
-        div.style.overflow = "hidden";
-        div.style.left =    (dimensions.left - 2) + "px";
-        div.style.width =   (dimensions.width + 4) + "px";
-        div.style.top =     (dimensions.top - 2) + "px";
-        div.style.height =  (dimensions.height + 4) + "px";
-        div.style.background = color;
-
-        this._highlightLayer.appendChild(div);
-    }
-};
-
-Timeline.DetailedEventPainter.prototype._onClickInstantEvent = function(icon, domEvt, evt) {
-    var c = SimileAjax.DOM.getPageCoordinates(icon);
-    this._showBubble(
-        c.left + Math.ceil(icon.offsetWidth / 2),
-        c.top + Math.ceil(icon.offsetHeight / 2),
-        evt
-    );
-    this._fireOnSelect(evt.getID());
-
-    domEvt.cancelBubble = true;
-    SimileAjax.DOM.cancelEvent(domEvt);
-    return false;
-};
-
-Timeline.DetailedEventPainter.prototype._onClickDurationEvent = function(target, domEvt, evt) {
-    if ("pageX" in domEvt) {
-        var x = domEvt.pageX;
-        var y = domEvt.pageY;
-    } else {
-        var c = SimileAjax.DOM.getPageCoordinates(target);
-        var x = domEvt.offsetX + c.left;
-        var y = domEvt.offsetY + c.top;
-    }
-    this._showBubble(x, y, evt);
-    this._fireOnSelect(evt.getID());
-
-    domEvt.cancelBubble = true;
-    SimileAjax.DOM.cancelEvent(domEvt);
-    return false;
-};
-
-Timeline.DetailedEventPainter.prototype.showBubble = function(evt) {
-    var elmt = this._eventIdToElmt[evt.getID()];
-    if (elmt) {
-        var c = SimileAjax.DOM.getPageCoordinates(elmt);
-        this._showBubble(c.left + elmt.offsetWidth / 2, c.top + elmt.offsetHeight / 2, evt);
-    }
-};
-
-Timeline.DetailedEventPainter.prototype._showBubble = function(x, y, evt) {
-    var div = document.createElement("div");
-    var themeBubble = this._params.theme.event.bubble;
-    evt.fillInfoBubble(div, this._params.theme, this._band.getLabeller());
-
-    SimileAjax.WindowManager.cancelPopups();
-    SimileAjax.Graphics.createBubbleForContentAndPoint(div, x, y,
-       themeBubble.width, null, themeBubble.maxHeight);
-};
-
-Timeline.DetailedEventPainter.prototype._fireOnSelect = function(eventID) {
-    for (var i = 0; i < this._onSelectListeners.length; i++) {
-        this._onSelectListeners[i](eventID);
-    }
-};
-/*
- *  Overview Event Painter
- *
- */
-
-Timeline.OverviewEventPainter = function(params) {
-    this._params = params;
-    this._onSelectListeners = [];
-
-    this._filterMatcher = null;
-    this._highlightMatcher = null;
-};
-
-Timeline.OverviewEventPainter.prototype.initialize = function(band, timeline) {
-    this._band = band;
-    this._timeline = timeline;
-
-    this._eventLayer = null;
-    this._highlightLayer = null;
-};
-
-Timeline.OverviewEventPainter.prototype.getType = function() {
-    return 'overview';
-};
-
-Timeline.OverviewEventPainter.prototype.addOnSelectListener = function(listener) {
-    this._onSelectListeners.push(listener);
-};
-
-Timeline.OverviewEventPainter.prototype.removeOnSelectListener = function(listener) {
-    for (var i = 0; i < this._onSelectListeners.length; i++) {
-        if (this._onSelectListeners[i] == listener) {
-            this._onSelectListeners.splice(i, 1);
-            break;
-        }
-    }
-};
-
-Timeline.OverviewEventPainter.prototype.getFilterMatcher = function() {
-    return this._filterMatcher;
-};
-
-Timeline.OverviewEventPainter.prototype.setFilterMatcher = function(filterMatcher) {
-    this._filterMatcher = filterMatcher;
-};
-
-Timeline.OverviewEventPainter.prototype.getHighlightMatcher = function() {
-    return this._highlightMatcher;
-};
-
-Timeline.OverviewEventPainter.prototype.setHighlightMatcher = function(highlightMatcher) {
-    this._highlightMatcher = highlightMatcher;
-};
-
-Timeline.OverviewEventPainter.prototype.paint = function() {
-    var eventSource = this._band.getEventSource();
-    if (eventSource == null) {
-        return;
-    }
-
-    this._prepareForPainting();
-
-    var eventTheme = this._params.theme.event;
-    var metrics = {
-        trackOffset:    eventTheme.overviewTrack.offset,
-        trackHeight:    eventTheme.overviewTrack.height,
-        trackGap:       eventTheme.overviewTrack.gap,
-        trackIncrement: eventTheme.overviewTrack.height + eventTheme.overviewTrack.gap
-    }
-
-    var minDate = this._band.getMinDate();
-    var maxDate = this._band.getMaxDate();
-
-    var filterMatcher = (this._filterMatcher != null) ?
-        this._filterMatcher :
-        function(evt) { return true; };
-    var highlightMatcher = (this._highlightMatcher != null) ?
-        this._highlightMatcher :
-        function(evt) { return -1; };
-
-    var iterator = eventSource.getEventReverseIterator(minDate, maxDate);
-    while (iterator.hasNext()) {
-        var evt = iterator.next();
-        if (filterMatcher(evt)) {
-            this.paintEvent(evt, metrics, this._params.theme, highlightMatcher(evt));
-        }
-    }
-
-    this._highlightLayer.style.display = "block";
-    this._eventLayer.style.display = "block";
-    // update the band object for max number of tracks in this section of the ether
-    this._band.updateEventTrackInfo(this._tracks.length, metrics.trackIncrement);
-};
-
-Timeline.OverviewEventPainter.prototype.softPaint = function() {
-};
-
-Timeline.OverviewEventPainter.prototype._prepareForPainting = function() {
-    var band = this._band;
-
-    this._tracks = [];
-
-    if (this._highlightLayer != null) {
-        band.removeLayerDiv(this._highlightLayer);
-    }
-    this._highlightLayer = band.createLayerDiv(105, "timeline-band-highlights");
-    this._highlightLayer.style.display = "none";
-
-    if (this._eventLayer != null) {
-        band.removeLayerDiv(this._eventLayer);
-    }
-    this._eventLayer = band.createLayerDiv(110, "timeline-band-events");
-    this._eventLayer.style.display = "none";
-};
-
-Timeline.OverviewEventPainter.prototype.paintEvent = function(evt, metrics, theme, highlightIndex) {
-    if (evt.isInstant()) {
-        this.paintInstantEvent(evt, metrics, theme, highlightIndex);
-    } else {
-        this.paintDurationEvent(evt, metrics, theme, highlightIndex);
-    }
-};
-
-Timeline.OverviewEventPainter.prototype.paintInstantEvent = function(evt, metrics, theme, highlightIndex) {
-    var startDate = evt.getStart();
-    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
-
-    var color = evt.getColor(),
-        klassName = evt.getClassName();
-    if (klassName) {
-      color = null;
-    } else {
-      color = color != null ? color : theme.event.duration.color;
-    }
-
-    var tickElmtData = this._paintEventTick(evt, startPixel, color, 100, metrics, theme);
-
-    this._createHighlightDiv(highlightIndex, tickElmtData, theme);
-};
-
-Timeline.OverviewEventPainter.prototype.paintDurationEvent = function(evt, metrics, theme, highlightIndex) {
-    var latestStartDate = evt.getLatestStart();
-    var earliestEndDate = evt.getEarliestEnd();
-
-    var latestStartPixel = Math.round(this._band.dateToPixelOffset(latestStartDate));
-    var earliestEndPixel = Math.round(this._band.dateToPixelOffset(earliestEndDate));
-
-    var tapeTrack = 0;
-    for (; tapeTrack < this._tracks.length; tapeTrack++) {
-        if (earliestEndPixel < this._tracks[tapeTrack]) {
-            break;
-        }
-    }
-    this._tracks[tapeTrack] = earliestEndPixel;
-
-    var color = evt.getColor(),
-        klassName = evt.getClassName();
-    if (klassName) {
-      color = null;
-    } else {
-      color = color != null ? color : theme.event.duration.color;
-    }
-
-    var tapeElmtData = this._paintEventTape(evt, tapeTrack, latestStartPixel, earliestEndPixel,
-      color, 100, metrics, theme, klassName);
-
-    this._createHighlightDiv(highlightIndex, tapeElmtData, theme);
-};
-
-Timeline.OverviewEventPainter.prototype._paintEventTape = function(
-    evt, track, left, right, color, opacity, metrics, theme, klassName) {
-
-    var top = metrics.trackOffset + track * metrics.trackIncrement;
-    var width = right - left;
-    var height = metrics.trackHeight;
-
-    var tapeDiv = this._timeline.getDocument().createElement("div");
-    tapeDiv.className = 'timeline-small-event-tape'
-    if (klassName) {tapeDiv.className += ' small-' + klassName;}
-    tapeDiv.style.left = left + "px";
-    tapeDiv.style.width = width + "px";
-    tapeDiv.style.top = top + "px";
-    tapeDiv.style.height = height + "px";
-
-    if (color) {
-      tapeDiv.style.backgroundColor = color; // set color here if defined by event. Else use css
-    }
- //   tapeDiv.style.overflow = "hidden";   // now set in css
- //   tapeDiv.style.position = "absolute";
-    if(opacity<100) SimileAjax.Graphics.setOpacity(tapeDiv, opacity);
-
-    this._eventLayer.appendChild(tapeDiv);
-
-    return {
-        left:   left,
-        top:    top,
-        width:  width,
-        height: height,
-        elmt:   tapeDiv
-    };
-}
-
-Timeline.OverviewEventPainter.prototype._paintEventTick = function(
-    evt, left, color, opacity, metrics, theme) {
-
-    var height = theme.event.overviewTrack.tickHeight;
-    var top = metrics.trackOffset - height;
-    var width = 1;
-
-    var tickDiv = this._timeline.getDocument().createElement("div");
-	  tickDiv.className = 'timeline-small-event-icon'
-    tickDiv.style.left = left + "px";
-    tickDiv.style.top = top + "px";
-  //  tickDiv.style.width = width + "px";
-  //  tickDiv.style.position = "absolute";
-  //  tickDiv.style.height = height + "px";
-  //  tickDiv.style.backgroundColor = color;
-  //  tickDiv.style.overflow = "hidden";
-
-    var klassName = evt.getClassName()
-    if (klassName) {tickDiv.className +=' small-' + klassName};
-
-    if(opacity<100) {SimileAjax.Graphics.setOpacity(tickDiv, opacity)};
-
-    this._eventLayer.appendChild(tickDiv);
-
-    return {
-        left:   left,
-        top:    top,
-        width:  width,
-        height: height,
-        elmt:   tickDiv
-    };
-}
-
-Timeline.OverviewEventPainter.prototype._createHighlightDiv = function(highlightIndex, dimensions, theme) {
-    if (highlightIndex >= 0) {
-        var doc = this._timeline.getDocument();
-        var eventTheme = theme.event;
-
-        var color = eventTheme.highlightColors[Math.min(highlightIndex, eventTheme.highlightColors.length - 1)];
-
-        var div = doc.createElement("div");
-        div.style.position = "absolute";
-        div.style.overflow = "hidden";
-        div.style.left =    (dimensions.left - 1) + "px";
-        div.style.width =   (dimensions.width + 2) + "px";
-        div.style.top =     (dimensions.top - 1) + "px";
-        div.style.height =  (dimensions.height + 2) + "px";
-        div.style.background = color;
-
-        this._highlightLayer.appendChild(div);
-    }
-};
-
-Timeline.OverviewEventPainter.prototype.showBubble = function(evt) {
-    // not implemented
-};
-/*
- *  Compact Event Painter
- *
- */
-
-Timeline.CompactEventPainter = function(params) {
-    this._params = params;
-    this._onSelectListeners = [];
-
-    this._filterMatcher = null;
-    this._highlightMatcher = null;
-    this._frc = null;
-
-    this._eventIdToElmt = {};
-};
-
-Timeline.CompactEventPainter.prototype.getType = function() {
-    return 'compact';
-};
-
-Timeline.CompactEventPainter.prototype.initialize = function(band, timeline) {
-    this._band = band;
-    this._timeline = timeline;
-
-    this._backLayer = null;
-    this._eventLayer = null;
-    this._lineLayer = null;
-    this._highlightLayer = null;
-
-    this._eventIdToElmt = null;
-};
-
-Timeline.CompactEventPainter.prototype.addOnSelectListener = function(listener) {
-    this._onSelectListeners.push(listener);
-};
-
-Timeline.CompactEventPainter.prototype.removeOnSelectListener = function(listener) {
-    for (var i = 0; i < this._onSelectListeners.length; i++) {
-        if (this._onSelectListeners[i] == listener) {
-            this._onSelectListeners.splice(i, 1);
-            break;
-        }
-    }
-};
-
-Timeline.CompactEventPainter.prototype.getFilterMatcher = function() {
-    return this._filterMatcher;
-};
-
-Timeline.CompactEventPainter.prototype.setFilterMatcher = function(filterMatcher) {
-    this._filterMatcher = filterMatcher;
-};
-
-Timeline.CompactEventPainter.prototype.getHighlightMatcher = function() {
-    return this._highlightMatcher;
-};
-
-Timeline.CompactEventPainter.prototype.setHighlightMatcher = function(highlightMatcher) {
-    this._highlightMatcher = highlightMatcher;
-};
-
-Timeline.CompactEventPainter.prototype.paint = function() {
-    var eventSource = this._band.getEventSource();
-    if (eventSource == null) {
-        return;
-    }
-
-    this._eventIdToElmt = {};
-    this._prepareForPainting();
-
-    var metrics = this._computeMetrics();
-    var minDate = this._band.getMinDate();
-    var maxDate = this._band.getMaxDate();
-
-    var filterMatcher = (this._filterMatcher != null) ?
-        this._filterMatcher :
-        function(evt) { return true; };
-
-    var highlightMatcher = (this._highlightMatcher != null) ?
-        this._highlightMatcher :
-        function(evt) { return -1; };
-
-    var iterator = eventSource.getEventIterator(minDate, maxDate);
-
-    var stackConcurrentPreciseInstantEvents = "stackConcurrentPreciseInstantEvents" in this._params && typeof this._params.stackConcurrentPreciseInstantEvents == "object";
-    var collapseConcurrentPreciseInstantEvents = "collapseConcurrentPreciseInstantEvents" in this._params && this._params.collapseConcurrentPreciseInstantEvents;
-    if (collapseConcurrentPreciseInstantEvents || stackConcurrentPreciseInstantEvents) {
-        var bufferedEvents = [];
-        var previousInstantEvent = null;
-
-        while (iterator.hasNext()) {
-            var evt = iterator.next();
-            if (filterMatcher(evt)) {
-                if (!evt.isInstant() || evt.isImprecise()) {
-                    this.paintEvent(evt, metrics, this._params.theme, highlightMatcher(evt));
-                } else if (previousInstantEvent != null &&
-                        previousInstantEvent.getStart().getTime() == evt.getStart().getTime()) {
-                    bufferedEvents[bufferedEvents.length - 1].push(evt);
-                } else {
-                    bufferedEvents.push([ evt ]);
-                    previousInstantEvent = evt;
-                }
-            }
-        }
-
-        for (var i = 0; i < bufferedEvents.length; i++) {
-            var compositeEvents = bufferedEvents[i];
-            if (compositeEvents.length == 1) {
-                this.paintEvent(compositeEvents[0], metrics, this._params.theme, highlightMatcher(evt));
-            } else {
-                var match = -1;
-                for (var j = 0; match < 0 && j < compositeEvents.length; j++) {
-                    match = highlightMatcher(compositeEvents[j]);
-                }
-
-                if (stackConcurrentPreciseInstantEvents) {
-                    this.paintStackedPreciseInstantEvents(compositeEvents, metrics, this._params.theme, match);
-                } else {
-                    this.paintCompositePreciseInstantEvents(compositeEvents, metrics, this._params.theme, match);
-                }
-            }
-        }
-    } else {
-        while (iterator.hasNext()) {
-            var evt = iterator.next();
-            if (filterMatcher(evt)) {
-                this.paintEvent(evt, metrics, this._params.theme, highlightMatcher(evt));
-            }
-        }
-    }
-
-    this._highlightLayer.style.display = "block";
-    this._lineLayer.style.display = "block";
-    this._eventLayer.style.display = "block";
-
-    this._setOrthogonalOffset(metrics);
-};
-
-Timeline.CompactEventPainter.prototype.softPaint = function() {
-    this._setOrthogonalOffset(this._computeMetrics());
-};
-
-Timeline.CompactEventPainter.prototype._setOrthogonalOffset = function(metrics) {
-    var actualViewWidth = 2 * metrics.trackOffset + this._tracks.length * metrics.trackHeight;
-    var minOrthogonalOffset = Math.min(0, this._band.getViewWidth() - actualViewWidth);
-    var orthogonalOffset = Math.max(minOrthogonalOffset, this._band.getViewOrthogonalOffset());
-
-    this._highlightLayer.style.top =
-        this._lineLayer.style.top =
-            this._eventLayer.style.top =
-                orthogonalOffset + "px";
-};
-
-Timeline.CompactEventPainter.prototype._computeMetrics = function() {
-    var theme = this._params.theme;
-    var eventTheme = theme.event;
-
-    var metrics = {
-        trackOffset:            "trackOffset" in this._params ? this._params.trackOffset : 10,
-        trackHeight:            "trackHeight" in this._params ? this._params.trackHeight : 10,
-
-        tapeHeight:             theme.event.tape.height,
-        tapeBottomMargin:       "tapeBottomMargin" in this._params ? this._params.tapeBottomMargin : 2,
-
-        labelBottomMargin:      "labelBottomMargin" in this._params ? this._params.labelBottomMargin : 5,
-        labelRightMargin:       "labelRightMargin" in this._params ? this._params.labelRightMargin : 5,
-
-        defaultIcon:            eventTheme.instant.icon,
-        defaultIconWidth:       eventTheme.instant.iconWidth,
-        defaultIconHeight:      eventTheme.instant.iconHeight,
-
-        customIconWidth:        "iconWidth" in this._params ? this._params.iconWidth : eventTheme.instant.iconWidth,
-        customIconHeight:       "iconHeight" in this._params ? this._params.iconHeight : eventTheme.instant.iconHeight,
-
-        iconLabelGap:           "iconLabelGap" in this._params ? this._params.iconLabelGap : 2,
-        iconBottomMargin:       "iconBottomMargin" in this._params ? this._params.iconBottomMargin : 2
-    };
-    if ("compositeIcon" in this._params) {
-        metrics.compositeIcon = this._params.compositeIcon;
-        metrics.compositeIconWidth = this._params.compositeIconWidth || metrics.customIconWidth;
-        metrics.compositeIconHeight = this._params.compositeIconHeight || metrics.customIconHeight;
-    } else {
-        metrics.compositeIcon = metrics.defaultIcon;
-        metrics.compositeIconWidth = metrics.defaultIconWidth;
-        metrics.compositeIconHeight = metrics.defaultIconHeight;
-    }
-    metrics.defaultStackIcon = "icon" in this._params.stackConcurrentPreciseInstantEvents ?
-        this._params.stackConcurrentPreciseInstantEvents.icon : metrics.defaultIcon;
-    metrics.defaultStackIconWidth = "iconWidth" in this._params.stackConcurrentPreciseInstantEvents ?
-        this._params.stackConcurrentPreciseInstantEvents.iconWidth : metrics.defaultIconWidth;
-    metrics.defaultStackIconHeight = "iconHeight" in this._params.stackConcurrentPreciseInstantEvents ?
-        this._params.stackConcurrentPreciseInstantEvents.iconHeight : metrics.defaultIconHeight;
-
-    return metrics;
-};
-
-Timeline.CompactEventPainter.prototype._prepareForPainting = function() {
-    var band = this._band;
-
-    if (this._backLayer == null) {
-        this._backLayer = this._band.createLayerDiv(0, "timeline-band-events");
-        this._backLayer.style.visibility = "hidden";
-
-        var eventLabelPrototype = document.createElement("span");
-        eventLabelPrototype.className = "timeline-event-label";
-        this._backLayer.appendChild(eventLabelPrototype);
-        this._frc = SimileAjax.Graphics.getFontRenderingContext(eventLabelPrototype);
-    }
-    this._frc.update();
-    this._tracks = [];
-
-    if (this._highlightLayer != null) {
-        band.removeLayerDiv(this._highlightLayer);
-    }
-    this._highlightLayer = band.createLayerDiv(105, "timeline-band-highlights");
-    this._highlightLayer.style.display = "none";
-
-    if (this._lineLayer != null) {
-        band.removeLayerDiv(this._lineLayer);
-    }
-    this._lineLayer = band.createLayerDiv(110, "timeline-band-lines");
-    this._lineLayer.style.display = "none";
-
-    if (this._eventLayer != null) {
-        band.removeLayerDiv(this._eventLayer);
-    }
-    this._eventLayer = band.createLayerDiv(115, "timeline-band-events");
-    this._eventLayer.style.display = "none";
-};
-
-Timeline.CompactEventPainter.prototype.paintEvent = function(evt, metrics, theme, highlightIndex) {
-    if (evt.isInstant()) {
-        this.paintInstantEvent(evt, metrics, theme, highlightIndex);
-    } else {
-        this.paintDurationEvent(evt, metrics, theme, highlightIndex);
-    }
-};
-
-Timeline.CompactEventPainter.prototype.paintInstantEvent = function(evt, metrics, theme, highlightIndex) {
-    if (evt.isImprecise()) {
-        this.paintImpreciseInstantEvent(evt, metrics, theme, highlightIndex);
-    } else {
-        this.paintPreciseInstantEvent(evt, metrics, theme, highlightIndex);
-    }
-}
-
-Timeline.CompactEventPainter.prototype.paintDurationEvent = function(evt, metrics, theme, highlightIndex) {
-    if (evt.isImprecise()) {
-        this.paintImpreciseDurationEvent(evt, metrics, theme, highlightIndex);
-    } else {
-        this.paintPreciseDurationEvent(evt, metrics, theme, highlightIndex);
-    }
-}
-
-Timeline.CompactEventPainter.prototype.paintPreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
-    var commonData = {
-        tooltip: evt.getProperty("tooltip") || evt.getText()
-    };
-
-    var iconData = {
-        url: evt.getIcon()
-    };
-    if (iconData.url == null) {
-        iconData.url = metrics.defaultIcon;
-        iconData.width = metrics.defaultIconWidth;
-        iconData.height = metrics.defaultIconHeight;
-        iconData.className = "timeline-event-icon-default";
-    } else {
-        iconData.width = evt.getProperty("iconWidth") || metrics.customIconWidth;
-        iconData.height = evt.getProperty("iconHeight") || metrics.customIconHeight;
-    }
-
-    var labelData = {
-        text:       evt.getText(),
-        color:      evt.getTextColor() || evt.getColor(),
-        className:  evt.getClassName()
-    };
-
-    var result = this.paintTapeIconLabel(
-        evt.getStart(),
-        commonData,
-        null, // no tape data
-        iconData,
-        labelData,
-        metrics,
-        theme,
-        highlightIndex
-    );
-
-    var self = this;
-    var clickHandler = function(elmt, domEvt, target) {
-        return self._onClickInstantEvent(result.iconElmtData.elmt, domEvt, evt);
-    };
-    SimileAjax.DOM.registerEvent(result.iconElmtData.elmt, "mousedown", clickHandler);
-    SimileAjax.DOM.registerEvent(result.labelElmtData.elmt, "mousedown", clickHandler);
-
-    this._eventIdToElmt[evt.getID()] = result.iconElmtData.elmt;
-};
-
-Timeline.CompactEventPainter.prototype.paintCompositePreciseInstantEvents = function(events, metrics, theme, highlightIndex) {
-    var evt = events[0];
-
-    var tooltips = [];
-    for (var i = 0; i < events.length; i++) {
-        tooltips.push(events[i].getProperty("tooltip") || events[i].getText());
-    }
-    var commonData = {
-        tooltip: tooltips.join("; ")
-    };
-
-    var iconData = {
-        url: metrics.compositeIcon,
-        width: metrics.compositeIconWidth,
-        height: metrics.compositeIconHeight,
-        className: "timeline-event-icon-composite"
-    };
-
-    var labelData = {
-        text: String.substitute(this._params.compositeEventLabelTemplate, [ events.length ])
-    };
-
-    var result = this.paintTapeIconLabel(
-        evt.getStart(),
-        commonData,
-        null, // no tape data
-        iconData,
-        labelData,
-        metrics,
-        theme,
-        highlightIndex
-    );
-
-    var self = this;
-    var clickHandler = function(elmt, domEvt, target) {
-        return self._onClickMultiplePreciseInstantEvent(result.iconElmtData.elmt, domEvt, events);
-    };
-
-    SimileAjax.DOM.registerEvent(result.iconElmtData.elmt, "mousedown", clickHandler);
-    SimileAjax.DOM.registerEvent(result.labelElmtData.elmt, "mousedown", clickHandler);
-
-    for (var i = 0; i < events.length; i++) {
-        this._eventIdToElmt[events[i].getID()] = result.iconElmtData.elmt;
-    }
-};
-
-Timeline.CompactEventPainter.prototype.paintStackedPreciseInstantEvents = function(events, metrics, theme, highlightIndex) {
-    var limit = "limit" in this._params.stackConcurrentPreciseInstantEvents ?
-        this._params.stackConcurrentPreciseInstantEvents.limit : 10;
-    var moreMessageTemplate = "moreMessageTemplate" in this._params.stackConcurrentPreciseInstantEvents ?
-        this._params.stackConcurrentPreciseInstantEvents.moreMessageTemplate : "%0 More Events";
-    var showMoreMessage = limit <= events.length - 2; // We want at least 2 more events above the limit.
-                                                      // Otherwise we'd need the singular case of "1 More Event"
-
-    var band = this._band;
-    var getPixelOffset = function(date) {
-        return Math.round(band.dateToPixelOffset(date));
-    };
-    var getIconData = function(evt) {
-        var iconData = {
-            url: evt.getIcon()
-        };
-        if (iconData.url == null) {
-            iconData.url = metrics.defaultStackIcon;
-            iconData.width = metrics.defaultStackIconWidth;
-            iconData.height = metrics.defaultStackIconHeight;
-            iconData.className = "timeline-event-icon-stack timeline-event-icon-default";
-        } else {
-            iconData.width = evt.getProperty("iconWidth") || metrics.customIconWidth;
-            iconData.height = evt.getProperty("iconHeight") || metrics.customIconHeight;
-            iconData.className = "timeline-event-icon-stack";
-        }
-        return iconData;
-    };
-
-    var firstIconData = getIconData(events[0]);
-    var horizontalIncrement = 5;
-    var leftIconEdge = 0;
-    var totalLabelWidth = 0;
-    var totalLabelHeight = 0;
-    var totalIconHeight = 0;
-
-    var records = [];
-    for (var i = 0; i < events.length && (!showMoreMessage || i < limit); i++) {
-        var evt = events[i];
-        var text = evt.getText();
-        var iconData = getIconData(evt);
-        var labelSize = this._frc.computeSize(text);
-        var record = {
-            text:       text,
-            iconData:   iconData,
-            labelSize:  labelSize,
-            iconLeft:   firstIconData.width + i * horizontalIncrement - iconData.width
-        };
-        record.labelLeft = firstIconData.width + i * horizontalIncrement + metrics.iconLabelGap;
-        record.top = totalLabelHeight;
-        records.push(record);
-
-        leftIconEdge = Math.min(leftIconEdge, record.iconLeft);
-        totalLabelHeight += labelSize.height;
-        totalLabelWidth = Math.max(totalLabelWidth, record.labelLeft + labelSize.width);
-        totalIconHeight = Math.max(totalIconHeight, record.top + iconData.height);
-    }
-    if (showMoreMessage) {
-        var moreMessage = String.substitute(moreMessageTemplate, [ events.length - limit ]);
-
-        var moreMessageLabelSize = this._frc.computeSize(moreMessage);
-        var moreMessageLabelLeft = firstIconData.width + (limit - 1) * horizontalIncrement + metrics.iconLabelGap;
-        var moreMessageLabelTop = totalLabelHeight;
-
-        totalLabelHeight += moreMessageLabelSize.height;
-        totalLabelWidth = Math.max(totalLabelWidth, moreMessageLabelLeft + moreMessageLabelSize.width);
-    }
-    totalLabelWidth += metrics.labelRightMargin;
-    totalLabelHeight += metrics.labelBottomMargin;
-    totalIconHeight += metrics.iconBottomMargin;
-
-    var anchorPixel = getPixelOffset(events[0].getStart());
-    var newTracks = [];
-
-    var trackCount = Math.ceil(Math.max(totalIconHeight, totalLabelHeight) / metrics.trackHeight);
-    var rightIconEdge = firstIconData.width + (events.length - 1) * horizontalIncrement;
-    for (var i = 0; i < trackCount; i++) {
-        newTracks.push({ start: leftIconEdge, end: rightIconEdge });
-    }
-    var labelTrackCount = Math.ceil(totalLabelHeight / metrics.trackHeight);
-    for (var i = 0; i < labelTrackCount; i++) {
-        var track = newTracks[i];
-        track.end = Math.max(track.end, totalLabelWidth);
-    }
-
-    var firstTrack = this._fitTracks(anchorPixel, newTracks);
-    var verticalPixelOffset = firstTrack * metrics.trackHeight + metrics.trackOffset;
-
-    var iconStackDiv = this._timeline.getDocument().createElement("div");
-    iconStackDiv.className = 'timeline-event-icon-stack';
-    iconStackDiv.style.position = "absolute";
-    iconStackDiv.style.overflow = "visible";
-    iconStackDiv.style.left = anchorPixel + "px";
-    iconStackDiv.style.top = verticalPixelOffset + "px";
-    iconStackDiv.style.width = rightIconEdge + "px";
-    iconStackDiv.style.height = totalIconHeight + "px";
-    iconStackDiv.innerHTML = "<div style='position: relative'></div>";
-    this._eventLayer.appendChild(iconStackDiv);
-
-    var self = this;
-    var onMouseOver = function(domEvt) {
-        try {
-            var n = parseInt(this.getAttribute("index"));
-            var childNodes = iconStackDiv.firstChild.childNodes;
-            for (var i = 0; i < childNodes.length; i++) {
-                var child = childNodes[i];
-                if (i == n) {
-                    child.style.zIndex = childNodes.length;
-                } else {
-                    child.style.zIndex = childNodes.length - i;
-                }
-            }
-        } catch (e) {
-        }
-    };
-    var paintEvent = function(index) {
-        var record = records[index];
-        var evt = events[index];
-        var tooltip = evt.getProperty("tooltip") || evt.getText();
-
-        var labelElmtData = self._paintEventLabel(
-            { tooltip: tooltip },
-            { text: record.text },
-            anchorPixel + record.labelLeft,
-            verticalPixelOffset + record.top,
-            record.labelSize.width,
-            record.labelSize.height,
-            theme
-        );
-        labelElmtData.elmt.setAttribute("index", index);
-        labelElmtData.elmt.onmouseover = onMouseOver;
-
-        var img = SimileAjax.Graphics.createTranslucentImage(record.iconData.url);
-        var iconDiv = self._timeline.getDocument().createElement("div");
-        iconDiv.className = 'timeline-event-icon' + ("className" in record.iconData ? (" " + record.iconData.className) : "");
-        iconDiv.style.left = record.iconLeft + "px";
-        iconDiv.style.top = record.top + "px";
-        iconDiv.style.zIndex = (records.length - index);
-        iconDiv.appendChild(img);
-        iconDiv.setAttribute("index", index);
-        iconDiv.onmouseover = onMouseOver;
-
-        iconStackDiv.firstChild.appendChild(iconDiv);
-
-        var clickHandler = function(elmt, domEvt, target) {
-            return self._onClickInstantEvent(labelElmtData.elmt, domEvt, evt);
-        };
-
-        SimileAjax.DOM.registerEvent(iconDiv, "mousedown", clickHandler);
-        SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
-
-        self._eventIdToElmt[evt.getID()] = iconDiv;
-    };
-    for (var i = 0; i < records.length; i++) {
-        paintEvent(i);
-    }
-
-    if (showMoreMessage) {
-        var moreEvents = events.slice(limit);
-        var moreMessageLabelElmtData = this._paintEventLabel(
-            { tooltip: moreMessage },
-            { text: moreMessage },
-            anchorPixel + moreMessageLabelLeft,
-            verticalPixelOffset + moreMessageLabelTop,
-            moreMessageLabelSize.width,
-            moreMessageLabelSize.height,
-            theme
-        );
-
-        var moreMessageClickHandler = function(elmt, domEvt, target) {
-            return self._onClickMultiplePreciseInstantEvent(moreMessageLabelElmtData.elmt, domEvt, moreEvents);
-        };
-        SimileAjax.DOM.registerEvent(moreMessageLabelElmtData.elmt, "mousedown", moreMessageClickHandler);
-
-        for (var i = 0; i < moreEvents.length; i++) {
-            this._eventIdToElmt[moreEvents[i].getID()] = moreMessageLabelElmtData.elmt;
-        }
-    }
-    //this._createHighlightDiv(highlightIndex, iconElmtData, theme);
-};
-
-Timeline.CompactEventPainter.prototype.paintImpreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
-    var commonData = {
-        tooltip: evt.getProperty("tooltip") || evt.getText()
-    };
-
-    var tapeData = {
-        start:          evt.getStart(),
-        end:            evt.getEnd(),
-        latestStart:    evt.getLatestStart(),
-        earliestEnd:    evt.getEarliestEnd(),
-        isInstant:      true
-    };
-
-    var iconData = {
-        url: evt.getIcon()
-    };
-    if (iconData.url == null) {
-        iconData = null;
-    } else {
-        iconData.width = evt.getProperty("iconWidth") || metrics.customIconWidth;
-        iconData.height = evt.getProperty("iconHeight") || metrics.customIconHeight;
-    }
-
-    var labelData = {
-        text:       evt.getText(),
-        color:      evt.getTextColor() || evt.getColor(),
-        className:  evt.getClassName()
-    };
-
-    var result = this.paintTapeIconLabel(
-        evt.getStart(),
-        commonData,
-        tapeData, // no tape data
-        iconData,
-        labelData,
-        metrics,
-        theme,
-        highlightIndex
-    );
-
-    var self = this;
-    var clickHandler = iconData != null ?
-        function(elmt, domEvt, target) {
-            return self._onClickInstantEvent(result.iconElmtData.elmt, domEvt, evt);
-        } :
-        function(elmt, domEvt, target) {
-            return self._onClickInstantEvent(result.labelElmtData.elmt, domEvt, evt);
-        };
-
-    SimileAjax.DOM.registerEvent(result.labelElmtData.elmt, "mousedown", clickHandler);
-    SimileAjax.DOM.registerEvent(result.impreciseTapeElmtData.elmt, "mousedown", clickHandler);
-
-    if (iconData != null) {
-        SimileAjax.DOM.registerEvent(result.iconElmtData.elmt, "mousedown", clickHandler);
-        this._eventIdToElmt[evt.getID()] = result.iconElmtData.elmt;
-    } else {
-        this._eventIdToElmt[evt.getID()] = result.labelElmtData.elmt;
-    }
-};
-
-Timeline.CompactEventPainter.prototype.paintPreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
-    var commonData = {
-        tooltip: evt.getProperty("tooltip") || evt.getText()
-    };
-
-    var tapeData = {
-        start:          evt.getStart(),
-        end:            evt.getEnd(),
-        isInstant:      false
-    };
-
-    var iconData = {
-        url: evt.getIcon()
-    };
-    if (iconData.url == null) {
-        iconData = null;
-    } else {
-        iconData.width = evt.getProperty("iconWidth") || metrics.customIconWidth;
-        iconData.height = evt.getProperty("iconHeight") || metrics.customIconHeight;
-    }
-
-    var labelData = {
-        text:       evt.getText(),
-        color:      evt.getTextColor() || evt.getColor(),
-        className:  evt.getClassName()
-    };
-
-    var result = this.paintTapeIconLabel(
-        evt.getLatestStart(),
-        commonData,
-        tapeData, // no tape data
-        iconData,
-        labelData,
-        metrics,
-        theme,
-        highlightIndex
-    );
-
-    var self = this;
-    var clickHandler = iconData != null ?
-        function(elmt, domEvt, target) {
-            return self._onClickInstantEvent(result.iconElmtData.elmt, domEvt, evt);
-        } :
-        function(elmt, domEvt, target) {
-            return self._onClickInstantEvent(result.labelElmtData.elmt, domEvt, evt);
-        };
-
-    SimileAjax.DOM.registerEvent(result.labelElmtData.elmt, "mousedown", clickHandler);
-    SimileAjax.DOM.registerEvent(result.tapeElmtData.elmt, "mousedown", clickHandler);
-
-    if (iconData != null) {
-        SimileAjax.DOM.registerEvent(result.iconElmtData.elmt, "mousedown", clickHandler);
-        this._eventIdToElmt[evt.getID()] = result.iconElmtData.elmt;
-    } else {
-        this._eventIdToElmt[evt.getID()] = result.labelElmtData.elmt;
-    }
-};
-
-Timeline.CompactEventPainter.prototype.paintImpreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
-    var commonData = {
-        tooltip: evt.getProperty("tooltip") || evt.getText()
-    };
-
-    var tapeData = {
-        start:          evt.getStart(),
-        end:            evt.getEnd(),
-        latestStart:    evt.getLatestStart(),
-        earliestEnd:    evt.getEarliestEnd(),
-        isInstant:      false
-    };
-
-    var iconData = {
-        url: evt.getIcon()
-    };
-    if (iconData.url == null) {
-        iconData = null;
-    } else {
-        iconData.width = evt.getProperty("iconWidth") || metrics.customIconWidth;
-        iconData.height = evt.getProperty("iconHeight") || metrics.customIconHeight;
-    }
-
-    var labelData = {
-        text:       evt.getText(),
-        color:      evt.getTextColor() || evt.getColor(),
-        className:  evt.getClassName()
-    };
-
-    var result = this.paintTapeIconLabel(
-        evt.getLatestStart(),
-        commonData,
-        tapeData, // no tape data
-        iconData,
-        labelData,
-        metrics,
-        theme,
-        highlightIndex
-    );
-
-    var self = this;
-    var clickHandler = iconData != null ?
-        function(elmt, domEvt, target) {
-            return self._onClickInstantEvent(result.iconElmtData.elmt, domEvt, evt);
-        } :
-        function(elmt, domEvt, target) {
-            return self._onClickInstantEvent(result.labelElmtData.elmt, domEvt, evt);
-        };
-
-    SimileAjax.DOM.registerEvent(result.labelElmtData.elmt, "mousedown", clickHandler);
-    SimileAjax.DOM.registerEvent(result.tapeElmtData.elmt, "mousedown", clickHandler);
-
-    if (iconData != null) {
-        SimileAjax.DOM.registerEvent(result.iconElmtData.elmt, "mousedown", clickHandler);
-        this._eventIdToElmt[evt.getID()] = result.iconElmtData.elmt;
-    } else {
-        this._eventIdToElmt[evt.getID()] = result.labelElmtData.elmt;
-    }
-};
-
-Timeline.CompactEventPainter.prototype.paintTapeIconLabel = function(
-    anchorDate,
-    commonData,
-    tapeData,
-    iconData,
-    labelData,
-    metrics,
-    theme,
-    highlightIndex
-) {
-    var band = this._band;
-    var getPixelOffset = function(date) {
-        return Math.round(band.dateToPixelOffset(date));
-    };
-
-    var anchorPixel = getPixelOffset(anchorDate);
-    var newTracks = [];
-
-    var tapeHeightOccupied = 0;         // how many pixels (vertically) the tape occupies, including bottom margin
-    var tapeTrackCount = 0;             // how many tracks the tape takes up, usually just 1
-    var tapeLastTrackExtraSpace = 0;    // on the last track that the tape occupies, how many pixels are left (for icon and label to occupy as well)
-    if (tapeData != null) {
-        tapeHeightOccupied = metrics.tapeHeight + metrics.tapeBottomMargin;
-        tapeTrackCount = Math.ceil(metrics.tapeHeight / metrics.trackHeight);
-
-        var tapeEndPixelOffset = getPixelOffset(tapeData.end) - anchorPixel;
-        var tapeStartPixelOffset = getPixelOffset(tapeData.start) - anchorPixel;
-
-        for (var t = 0; t < tapeTrackCount; t++) {
-            newTracks.push({ start: tapeStartPixelOffset, end: tapeEndPixelOffset });
-        }
-
-        tapeLastTrackExtraSpace = metrics.trackHeight - (tapeHeightOccupied % metrics.tapeHeight);
-    }
-
-    var iconStartPixelOffset = 0;        // where the icon starts compared to the anchor pixel;
-                                         // this can be negative if the icon is center-aligned around the anchor
-    var iconHorizontalSpaceOccupied = 0; // how many pixels the icon take up from the anchor pixel,
-                                         // including the gap between the icon and the label
-    if (iconData != null) {
-        if ("iconAlign" in iconData && iconData.iconAlign == "center") {
-            iconStartPixelOffset = -Math.floor(iconData.width / 2);
-        }
-        iconHorizontalSpaceOccupied = iconStartPixelOffset + iconData.width + metrics.iconLabelGap;
-
-        if (tapeTrackCount > 0) {
-            newTracks[tapeTrackCount - 1].end = Math.max(newTracks[tapeTrackCount - 1].end, iconHorizontalSpaceOccupied);
-        }
-
-        var iconHeight = iconData.height + metrics.iconBottomMargin + tapeLastTrackExtraSpace;
-        while (iconHeight > 0) {
-            newTracks.push({ start: iconStartPixelOffset, end: iconHorizontalSpaceOccupied });
-            iconHeight -= metrics.trackHeight;
-        }
-    }
-
-    var text = labelData.text;
-    var labelSize = this._frc.computeSize(text);
-    var labelHeight = labelSize.height + metrics.labelBottomMargin + tapeLastTrackExtraSpace;
-    var labelEndPixelOffset = iconHorizontalSpaceOccupied + labelSize.width + metrics.labelRightMargin;
-    if (tapeTrackCount > 0) {
-        newTracks[tapeTrackCount - 1].end = Math.max(newTracks[tapeTrackCount - 1].end, labelEndPixelOffset);
-    }
-    for (var i = 0; labelHeight > 0; i++) {
-        if (tapeTrackCount + i < newTracks.length) {
-            var track = newTracks[tapeTrackCount + i];
-            track.end = labelEndPixelOffset;
-        } else {
-            newTracks.push({ start: 0, end: labelEndPixelOffset });
-        }
-        labelHeight -= metrics.trackHeight;
-    }
-
-    /*
-     *  Try to fit the new track on top of the existing tracks, then
-     *  render the various elements.
-     */
-    var firstTrack = this._fitTracks(anchorPixel, newTracks);
-    var verticalPixelOffset = firstTrack * metrics.trackHeight + metrics.trackOffset;
-    var result = {};
-
-    result.labelElmtData = this._paintEventLabel(
-        commonData,
-        labelData,
-        anchorPixel + iconHorizontalSpaceOccupied,
-        verticalPixelOffset + tapeHeightOccupied,
-        labelSize.width,
-        labelSize.height,
-        theme
-    );
-
-    if (tapeData != null) {
-        if ("latestStart" in tapeData || "earliestEnd" in tapeData) {
-            result.impreciseTapeElmtData = this._paintEventTape(
-                commonData,
-                tapeData,
-                metrics.tapeHeight,
-                verticalPixelOffset,
-                getPixelOffset(tapeData.start),
-                getPixelOffset(tapeData.end),
-                theme.event.duration.impreciseColor,
-                theme.event.duration.impreciseOpacity,
-                metrics,
-                theme
-            );
-        }
-        if (!tapeData.isInstant && "start" in tapeData && "end" in tapeData) {
-            result.tapeElmtData = this._paintEventTape(
-                commonData,
-                tapeData,
-                metrics.tapeHeight,
-                verticalPixelOffset,
-                anchorPixel,
-                getPixelOffset("earliestEnd" in tapeData ? tapeData.earliestEnd : tapeData.end),
-                tapeData.color,
-                100,
-                metrics,
-                theme
-            );
-        }
-    }
-
-    if (iconData != null) {
-        result.iconElmtData = this._paintEventIcon(
-            commonData,
-            iconData,
-            verticalPixelOffset + tapeHeightOccupied,
-            anchorPixel + iconStartPixelOffset,
-            metrics,
-            theme
-        );
-    }
-    //this._createHighlightDiv(highlightIndex, iconElmtData, theme);
-
-    return result;
-};
-
-Timeline.CompactEventPainter.prototype._fitTracks = function(anchorPixel, newTracks) {
-    var firstTrack;
-    for (firstTrack = 0; firstTrack < this._tracks.length; firstTrack++) {
-        var fit = true;
-        for (var j = 0; j < newTracks.length && (firstTrack + j) < this._tracks.length; j++) {
-            var existingTrack = this._tracks[firstTrack + j];
-            var newTrack = newTracks[j];
-            if (anchorPixel + newTrack.start < existingTrack) {
-                fit = false;
-                break;
-            }
-        }
-
-        if (fit) {
-            break;
-        }
-    }
-    for (var i = 0; i < newTracks.length; i++) {
-        this._tracks[firstTrack + i] = anchorPixel + newTracks[i].end;
-    }
-
-    return firstTrack;
-};
-
-
-Timeline.CompactEventPainter.prototype._paintEventIcon = function(commonData, iconData, top, left, metrics, theme) {
-    var img = SimileAjax.Graphics.createTranslucentImage(iconData.url);
-    var iconDiv = this._timeline.getDocument().createElement("div");
-    iconDiv.className = 'timeline-event-icon' + ("className" in iconData ? (" " + iconData.className) : "");
-    iconDiv.style.left = left + "px";
-    iconDiv.style.top = top + "px";
-    iconDiv.appendChild(img);
-
-    if ("tooltip" in commonData && typeof commonData.tooltip == "string") {
-        iconDiv.title = commonData.tooltip;
-    }
-
-    this._eventLayer.appendChild(iconDiv);
-
-    return {
-        left:   left,
-        top:    top,
-        width:  metrics.iconWidth,
-        height: metrics.iconHeight,
-        elmt:   iconDiv
-    };
-};
-
-Timeline.CompactEventPainter.prototype._paintEventLabel = function(commonData, labelData, left, top, width, height, theme) {
-    var doc = this._timeline.getDocument();
-
-    var labelDiv = doc.createElement("div");
-    labelDiv.className = 'timeline-event-label';
-
-    labelDiv.style.left = left + "px";
-    labelDiv.style.width = (width + 1) + "px";
-    labelDiv.style.top = top + "px";
-    labelDiv.innerHTML = labelData.text;
-
-    if ("tooltip" in commonData && typeof commonData.tooltip == "string") {
-        labelDiv.title = commonData.tooltip;
-    }
-    if ("color" in labelData && typeof labelData.color == "string") {
-        labelDiv.style.color = labelData.color;
-    }
-    if ("className" in labelData && typeof labelData.className == "string") {
-        labelDiv.className += ' ' + labelData.className;
-    }
-
-    this._eventLayer.appendChild(labelDiv);
-
-    return {
-        left:   left,
-        top:    top,
-        width:  width,
-        height: height,
-        elmt:   labelDiv
-    };
-};
-
-Timeline.CompactEventPainter.prototype._paintEventTape = function(
-    commonData, tapeData, height, top, startPixel, endPixel, color, opacity, metrics, theme) {
-
-    var width = endPixel - startPixel;
-
-    var tapeDiv = this._timeline.getDocument().createElement("div");
-    tapeDiv.className = "timeline-event-tape"
-
-    tapeDiv.style.left = startPixel + "px";
-    tapeDiv.style.top = top + "px";
-    tapeDiv.style.width = width + "px";
-    tapeDiv.style.height = height + "px";
-
-    if ("tooltip" in commonData && typeof commonData.tooltip == "string") {
-        tapeDiv.title = commonData.tooltip;
-    }
-    if (color != null && typeof tapeData.color == "string") {
-        tapeDiv.style.backgroundColor = color;
-    }
-
-    if ("backgroundImage" in tapeData && typeof tapeData.backgroundImage == "string") {
-        tapeDiv.style.backgroundImage = "url(" + backgroundImage + ")";
-        tapeDiv.style.backgroundRepeat =
-            ("backgroundRepeat" in tapeData && typeof tapeData.backgroundRepeat == "string")
-                ? tapeData.backgroundRepeat : 'repeat';
-    }
-
-    SimileAjax.Graphics.setOpacity(tapeDiv, opacity);
-
-    if ("className" in tapeData && typeof tapeData.className == "string") {
-        tapeDiv.className += ' ' + tapeData.className;
-    }
-
-    this._eventLayer.appendChild(tapeDiv);
-
-    return {
-        left:   startPixel,
-        top:    top,
-        width:  width,
-        height: height,
-        elmt:   tapeDiv
-    };
-}
-
-Timeline.CompactEventPainter.prototype._createHighlightDiv = function(highlightIndex, dimensions, theme) {
-    if (highlightIndex >= 0) {
-        var doc = this._timeline.getDocument();
-        var eventTheme = theme.event;
-
-        var color = eventTheme.highlightColors[Math.min(highlightIndex, eventTheme.highlightColors.length - 1)];
-
-        var div = doc.createElement("div");
-        div.style.position = "absolute";
-        div.style.overflow = "hidden";
-        div.style.left =    (dimensions.left - 2) + "px";
-        div.style.width =   (dimensions.width + 4) + "px";
-        div.style.top =     (dimensions.top - 2) + "px";
-        div.style.height =  (dimensions.height + 4) + "px";
-//        div.style.background = color;
-
-        this._highlightLayer.appendChild(div);
-    }
-};
-
-Timeline.CompactEventPainter.prototype._onClickMultiplePreciseInstantEvent = function(icon, domEvt, events) {
-    var c = SimileAjax.DOM.getPageCoordinates(icon);
-    this._showBubble(
-        c.left + Math.ceil(icon.offsetWidth / 2),
-        c.top + Math.ceil(icon.offsetHeight / 2),
-        events
-    );
-
-    var ids = [];
-    for (var i = 0; i < events.length; i++) {
-        ids.push(events[i].getID());
-    }
-    this._fireOnSelect(ids);
-
-    domEvt.cancelBubble = true;
-    SimileAjax.DOM.cancelEvent(domEvt);
-
-    return false;
-};
-
-Timeline.CompactEventPainter.prototype._onClickInstantEvent = function(icon, domEvt, evt) {
-    var c = SimileAjax.DOM.getPageCoordinates(icon);
-    this._showBubble(
-        c.left + Math.ceil(icon.offsetWidth / 2),
-        c.top + Math.ceil(icon.offsetHeight / 2),
-        [evt]
-    );
-    this._fireOnSelect([evt.getID()]);
-
-    domEvt.cancelBubble = true;
-    SimileAjax.DOM.cancelEvent(domEvt);
-    return false;
-};
-
-Timeline.CompactEventPainter.prototype._onClickDurationEvent = function(target, domEvt, evt) {
-    if ("pageX" in domEvt) {
-        var x = domEvt.pageX;
-        var y = domEvt.pageY;
-    } else {
-        var c = SimileAjax.DOM.getPageCoordinates(target);
-        var x = domEvt.offsetX + c.left;
-        var y = domEvt.offsetY + c.top;
-    }
-    this._showBubble(x, y, [evt]);
-    this._fireOnSelect([evt.getID()]);
-
-    domEvt.cancelBubble = true;
-    SimileAjax.DOM.cancelEvent(domEvt);
-    return false;
-};
-
-Timeline.CompactEventPainter.prototype.showBubble = function(evt) {
-    var elmt = this._eventIdToElmt[evt.getID()];
-    if (elmt) {
-        var c = SimileAjax.DOM.getPageCoordinates(elmt);
-        this._showBubble(c.left + elmt.offsetWidth / 2, c.top + elmt.offsetHeight / 2, [evt]);
-    }
-};
-
-Timeline.CompactEventPainter.prototype._showBubble = function(x, y, evts) {
-    var div = document.createElement("div");
-
-    evts = ("fillInfoBubble" in evts) ? [evts] : evts;
-    for (var i = 0; i < evts.length; i++) {
-        var div2 = document.createElement("div");
-        div.appendChild(div2);
-
-        evts[i].fillInfoBubble(div2, this._params.theme, this._band.getLabeller());
-    }
-
-    SimileAjax.WindowManager.cancelPopups();
-    SimileAjax.Graphics.createBubbleForContentAndPoint(div, x, y, this._params.theme.event.bubble.width);
-};
-
-Timeline.CompactEventPainter.prototype._fireOnSelect = function(eventIDs) {
-    for (var i = 0; i < this._onSelectListeners.length; i++) {
-        this._onSelectListeners[i](eventIDs);
-    }
-};
-/*
- *  Span Highlight Decorator
- *
- */
-
-Timeline.SpanHighlightDecorator = function(params) {
-    // When evaluating params, test against null. Not "p in params". Testing against
-    // null enables caller to explicitly request the default. Testing against "in" means
-    // that the param has to be ommitted to get the default.
-    this._unit = params.unit != null ? params.unit : SimileAjax.NativeDateUnit;
-    this._startDate = (typeof params.startDate == "string") ?
-        this._unit.parseFromObject(params.startDate) : params.startDate;
-    this._endDate = (typeof params.endDate == "string") ?
-        this._unit.parseFromObject(params.endDate) : params.endDate;
-    this._startLabel = params.startLabel != null ? params.startLabel : ""; // not null!
-    this._endLabel   = params.endLabel   != null ? params.endLabel   : ""; // not null!
-    this._color = params.color;
-    this._cssClass = params.cssClass != null ? params.cssClass : null;
-    this._opacity = params.opacity != null ? params.opacity : 100;
-         // Default z is 10, behind everything but background grid.
-         // If inFront, then place just behind events, in front of everything else
-    this._zIndex = (params.inFront != null && params.inFront) ? 113 : 10;
-};
-
-Timeline.SpanHighlightDecorator.prototype.initialize = function(band, timeline) {
-    this._band = band;
-    this._timeline = timeline;
-
-    this._layerDiv = null;
-};
-
-Timeline.SpanHighlightDecorator.prototype.paint = function() {
-    if (this._layerDiv != null) {
-        this._band.removeLayerDiv(this._layerDiv);
-    }
-    this._layerDiv = this._band.createLayerDiv(this._zIndex);
-    this._layerDiv.setAttribute("name", "span-highlight-decorator"); // for debugging
-    this._layerDiv.style.display = "none";
-
-    var minDate = this._band.getMinDate();
-    var maxDate = this._band.getMaxDate();
-
-    if (this._unit.compare(this._startDate, maxDate) < 0 &&
-        this._unit.compare(this._endDate, minDate) > 0) {
-
-        minDate = this._unit.later(minDate, this._startDate);
-        maxDate = this._unit.earlier(maxDate, this._endDate);
-
-        var minPixel = this._band.dateToPixelOffset(minDate);
-        var maxPixel = this._band.dateToPixelOffset(maxDate);
-
-        var doc = this._timeline.getDocument();
-
-        var createTable = function() {
-            var table = doc.createElement("table");
-            table.insertRow(0).insertCell(0);
-            return table;
-        };
-
-        var div = doc.createElement("div");
-        div.className='timeline-highlight-decorator'
-        if(this._cssClass) {
-        	  div.className += ' ' + this._cssClass;
-        }
-        if(this._color != null) {
-        	  div.style.backgroundColor = this._color;
-        }
-        if (this._opacity < 100) {
-            SimileAjax.Graphics.setOpacity(div, this._opacity);
-        }
-        this._layerDiv.appendChild(div);
-
-        var tableStartLabel = createTable();
-        tableStartLabel.className = 'timeline-highlight-label timeline-highlight-label-start'
-        var tdStart =  tableStartLabel.rows[0].cells[0]
-        tdStart.innerHTML = this._startLabel;
-        if (this._cssClass) {
-        	  tdStart.className = 'label_' + this._cssClass;
-        }
-        this._layerDiv.appendChild(tableStartLabel);
-
-        var tableEndLabel = createTable();
-        tableEndLabel.className = 'timeline-highlight-label timeline-highlight-label-end'
-        var tdEnd = tableEndLabel.rows[0].cells[0]
-        tdEnd.innerHTML = this._endLabel;
-        if (this._cssClass) {
-        	   tdEnd.className = 'label_' + this._cssClass;
-        }
-        this._layerDiv.appendChild(tableEndLabel);
-
-        if (this._timeline.isHorizontal()){
-            div.style.left = minPixel + "px";
-            div.style.width = (maxPixel - minPixel) + "px";
-
-            tableStartLabel.style.right = (this._band.getTotalViewLength() - minPixel) + "px";
-            tableStartLabel.style.width = (this._startLabel.length) + "em";
-
-            tableEndLabel.style.left = maxPixel + "px";
-            tableEndLabel.style.width = (this._endLabel.length) + "em";
-
-        } else {
-            div.style.top = minPixel + "px";
-            div.style.height = (maxPixel - minPixel) + "px";
-
-            tableStartLabel.style.bottom = minPixel + "px";
-            tableStartLabel.style.height = "1.5px";
-
-            tableEndLabel.style.top = maxPixel + "px";
-            tableEndLabel.style.height = "1.5px";
-        }
-    }
-    this._layerDiv.style.display = "block";
-};
-
-Timeline.SpanHighlightDecorator.prototype.softPaint = function() {
-};
-
-/*
- *  Point Highlight Decorator
- *
- */
-
-Timeline.PointHighlightDecorator = function(params) {
-    this._unit = params.unit != null ? params.unit : SimileAjax.NativeDateUnit;
-    this._date = (typeof params.date == "string") ?
-        this._unit.parseFromObject(params.date) : params.date;
-    this._width = params.width != null ? params.width : 10;
-      // Since the width is used to calculate placements (see minPixel, below), we
-      // specify width here, not in css.
-    this._color = params.color;
-    this._cssClass = params.cssClass != null ? params.cssClass : '';
-    this._opacity = params.opacity != null ? params.opacity : 100;
-};
-
-Timeline.PointHighlightDecorator.prototype.initialize = function(band, timeline) {
-    this._band = band;
-    this._timeline = timeline;
-    this._layerDiv = null;
-};
-
-Timeline.PointHighlightDecorator.prototype.paint = function() {
-    if (this._layerDiv != null) {
-        this._band.removeLayerDiv(this._layerDiv);
-    }
-    this._layerDiv = this._band.createLayerDiv(10);
-    this._layerDiv.setAttribute("name", "span-highlight-decorator"); // for debugging
-    this._layerDiv.style.display = "none";
-
-    var minDate = this._band.getMinDate();
-    var maxDate = this._band.getMaxDate();
-
-    if (this._unit.compare(this._date, maxDate) < 0 &&
-        this._unit.compare(this._date, minDate) > 0) {
-
-        var pixel = this._band.dateToPixelOffset(this._date);
-        var minPixel = pixel - Math.round(this._width / 2);
-
-        var doc = this._timeline.getDocument();
-
-        var div = doc.createElement("div");
-        div.className='timeline-highlight-point-decorator';
-        div.className += ' ' + this._cssClass;
-
-        if(this._color != null) {
-        	  div.style.backgroundColor = this._color;
-        }
-        if (this._opacity < 100) {
-            SimileAjax.Graphics.setOpacity(div, this._opacity);
-        }
-        this._layerDiv.appendChild(div);
-
-        if (this._timeline.isHorizontal()) {
-            div.style.left = minPixel + "px";
-            div.style.width = this._width;
-        } else {
-            div.style.top = minPixel + "px";
-            div.style.height = this._width;
-        }
-    }
-    this._layerDiv.style.display = "block";
-};
-
-Timeline.PointHighlightDecorator.prototype.softPaint = function() {
-};
-/*
- *  Default Unit
- *
- */
-
-Timeline.NativeDateUnit = new Object();
-
-Timeline.NativeDateUnit.createLabeller = function(locale, timeZone) {
-    return new Timeline.GregorianDateLabeller(locale, timeZone);
-};
-
-Timeline.NativeDateUnit.makeDefaultValue = function() {
-    return new Date();
-};
-
-Timeline.NativeDateUnit.cloneValue = function(v) {
-    return new Date(v.getTime());
-};
-
-Timeline.NativeDateUnit.getParser = function(format) {
-    if (typeof format == "string") {
-        format = format.toLowerCase();
-    }
-    return (format == "iso8601" || format == "iso 8601") ?
-        Timeline.DateTime.parseIso8601DateTime : 
-        Timeline.DateTime.parseGregorianDateTime;
-};
-
-Timeline.NativeDateUnit.parseFromObject = function(o) {
-    return Timeline.DateTime.parseGregorianDateTime(o);
-};
-
-Timeline.NativeDateUnit.toNumber = function(v) {
-    return v.getTime();
-};
-
-Timeline.NativeDateUnit.fromNumber = function(n) {
-    return new Date(n);
-};
-
-Timeline.NativeDateUnit.compare = function(v1, v2) {
-    var n1, n2;
-    if (typeof v1 == "object") {
-        n1 = v1.getTime();
-    } else {
-        n1 = Number(v1);
-    }
-    if (typeof v2 == "object") {
-        n2 = v2.getTime();
-    } else {
-        n2 = Number(v2);
-    }
-
-    return n1 - n2;
-};
-
-Timeline.NativeDateUnit.earlier = function(v1, v2) {
-    return Timeline.NativeDateUnit.compare(v1, v2) < 0 ? v1 : v2;
-};
-
-Timeline.NativeDateUnit.later = function(v1, v2) {
-    return Timeline.NativeDateUnit.compare(v1, v2) > 0 ? v1 : v2;
-};
-
-Timeline.NativeDateUnit.change = function(v, n) {
-    return new Date(v.getTime() + n);
-};
-
-/*
- *  Common localization strings
- *
- */
-
-Timeline.strings["fr"] = {
-    wikiLinkLabel:  "Discute"
-};
-
-/*
- *  Localization of labellers.js
- *
- */
-
-Timeline.GregorianDateLabeller.monthNames["fr"] = [
-    "jan", "fev", "mar", "avr", "mai", "jui", "jui", "aou", "sep", "oct", "nov", "dec"
-];
-/*
- *  Common localization strings
- *
- */
-
-Timeline.strings["en"] = {
-    wikiLinkLabel:  "Discuss"
-};
-
-/*
- *  Localization of labellers.js
- *
- */
-
-Timeline.GregorianDateLabeller.monthNames["en"] = [
-    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-];
-
-Timeline.GregorianDateLabeller.dayNames["en"] = [
-    "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
-];
--- a/web/data/cubicweb.timeline-ext.js	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,108 +0,0 @@
-/**
- *  :organization: Logilab
- *  :copyright: 2008-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
- *  :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
- *
- */
-
-/**
- * provide our own custom date parser since the default
- * one only understands iso8601 and gregorian dates
- */
-SimileAjax.NativeDateUnit.getParser = Timeline.NativeDateUnit.getParser = function(format) {
-    if (typeof format == "string") {
-        if (format.indexOf('%') != - 1) {
-            return function(datestring) {
-                if (datestring) {
-                    return strptime(datestring, format);
-                }
-                return null;
-            };
-        }
-        format = format.toLowerCase();
-    }
-    if (format == "iso8601" || format == "iso 8601") {
-        return Timeline.DateTime.parseIso8601DateTime;
-    }
-    return Timeline.DateTime.parseGregorianDateTime;
-};
-
-/*** CUBICWEB EVENT PAINTER *****************************************************/
-Timeline.CubicWebEventPainter = function(params) {
-    //  Timeline.OriginalEventPainter.apply(this, arguments);
-    this._params = params;
-    this._onSelectListeners = [];
-
-    this._filterMatcher = null;
-    this._highlightMatcher = null;
-    this._frc = null;
-
-    this._eventIdToElmt = {};
-};
-
-Timeline.CubicWebEventPainter.prototype = new Timeline.OriginalEventPainter();
-
-Timeline.CubicWebEventPainter.prototype._paintEventLabel = function(
-evt, text, left, top, width, height, theme) {
-    var doc = this._timeline.getDocument();
-
-    var labelDiv = doc.createElement("div");
-    labelDiv.className = 'timeline-event-label';
-
-    labelDiv.style.left = left + "px";
-    labelDiv.style.width = width + "px";
-    labelDiv.style.top = top + "px";
-
-    if (evt._obj.onclick) {
-        labelDiv.appendChild(A({
-            'href': evt._obj.onclick
-        },
-        text));
-    } else if (evt._obj.image) {
-        labelDiv.appendChild(IMG({
-            src: evt._obj.image,
-            width: '30px',
-            height: '30px'
-        }));
-    } else {
-        labelDiv.innerHTML = text;
-    }
-
-    if (evt._title != null) labelDiv.title = evt._title;
-
-    var color = evt.getTextColor();
-    if (color == null) {
-        color = evt.getColor();
-    }
-    if (color != null) {
-        labelDiv.style.color = color;
-    }
-    var classname = evt.getClassName();
-    if (classname) labelDiv.className += ' ' + classname;
-
-    this._eventLayer.appendChild(labelDiv);
-
-    return {
-        left: left,
-        top: top,
-        width: width,
-        height: height,
-        elmt: labelDiv
-    };
-};
-
-Timeline.CubicWebEventPainter.prototype._showBubble = function(x, y, evt) {
-    var div = DIV({
-        id: 'xxx'
-    });
-    var width = this._params.theme.event.bubble.width;
-    if (!evt._obj.bubbleUrl) {
-        evt.fillInfoBubble(div, this._params.theme, this._band.getLabeller());
-    }
-    SimileAjax.WindowManager.cancelPopups();
-    SimileAjax.Graphics.createBubbleForContentAndPoint(div, x, y, width);
-    if (evt._obj.bubbleUrl) {
-        jQuery('#xxx').loadxhtml(evt._obj.bubbleUrl, null, 'post', 'replace');
-    }
-};
-
--- a/web/data/cubicweb.widgets.js	Mon May 09 17:24:03 2016 +0200
+++ b/web/data/cubicweb.widgets.js	Tue Jun 21 07:42:30 2016 +0200
@@ -347,62 +347,6 @@
 });
 
 /**
- * .. class:: Widgets.SuggestForm
- *
- * suggestform displays a suggest field and associated validate / cancel buttons
- * constructor's argumemts are the same that BaseSuggestField widget
- */
-Widgets.SuggestForm = defclass("SuggestForm", null, {
-
-    __init__: function(inputid, initfunc, varargs, validatefunc, options) {
-        this.validatefunc = validatefunc || $.noop;
-        this.sgfield = new Widgets.BaseSuggestField(inputid, initfunc, varargs, options);
-        this.oklabel = options.oklabel || 'ok';
-        this.cancellabel = options.cancellabel || 'cancel';
-        bindMethods(this);
-        connect(this.sgfield, 'validate', this, this.entryValidated);
-    },
-
-    show: function(parentnode) {
-        var sgnode = this.sgfield.builddom();
-        var buttons = DIV({
-            'class': "sgformbuttons"
-        },
-        [A({
-            'href': "javascript: $.noop();",
-            'onclick': this.onValidateClicked
-        },
-        this.oklabel), ' / ', A({
-            'href': "javascript: $.noop();",
-            'onclick': this.destroy
-        },
-        escapeHTML(this.cancellabel))]);
-        var formnode = DIV({
-            'class': "sgform"
-        },
-        [sgnode, buttons]);
-        appendChildNodes(parentnode, formnode);
-        this.sgfield.textinput.focus();
-        this.formnode = formnode;
-        return formnode;
-    },
-
-    destroy: function() {
-        signal(this, 'destroy');
-        this.sgfield.destroy();
-        removeElement(this.formnode);
-    },
-
-    onValidateClicked: function() {
-        this.validatefunc(this, this.sgfield.taglist());
-    },
-    /* just an indirection to pass the form instead of the sgfield as first parameter */
-    entryValidated: function(sgfield, taglist) {
-        this.validatefunc(this, taglist);
-    }
-});
-
-/**
  * .. function:: toggleTree(event)
  *
  * called when the use clicks on a tree node
@@ -428,41 +372,6 @@
     }
 }
 
-/**
- * .. class:: Widgets.TimelineWidget
- *
- * widget based on SIMILE's timeline widget
- * http://code.google.com/p/simile-widgets/
- *
- * Beware not to mess with SIMILE's Timeline JS namepsace !
- */
-
-Widgets.TimelineWidget = defclass("TimelineWidget", null, {
-    __init__: function(wdgnode) {
-        var tldiv = DIV({
-            id: "tl",
-            style: 'height: 200px; border: 1px solid #ccc;'
-        });
-        wdgnode.appendChild(tldiv);
-        var tlunit = wdgnode.getAttribute('cubicweb:tlunit') || 'YEAR';
-        var eventSource = new Timeline.DefaultEventSource();
-        var bandData = {
-            eventPainter: Timeline.CubicWebEventPainter,
-            eventSource: eventSource,
-            width: "100%",
-            intervalUnit: Timeline.DateTime[tlunit.toUpperCase()],
-            intervalPixels: 100
-        };
-        var bandInfos = [Timeline.createBandInfo(bandData)];
-        this.tl = Timeline.create(tldiv, bandInfos);
-        var loadurl = wdgnode.getAttribute('cubicweb:loadurl');
-        Timeline.loadJSON(loadurl, function(json, url) {
-            eventSource.loadJSON(json, url);
-        });
-
-    }
-});
-
 Widgets.TemplateTextField = defclass("TemplateTextField", null, {
 
     __init__: function(wdgnode) {
--- a/web/data/timeline-bundle.css	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,401 +0,0 @@
-div.simileAjax-bubble-container {
-    margin:     0px;
-    padding:    0px;
-    border:     none;
-    position:   absolute;
-    z-index:    1000;
-}
-
-div.simileAjax-bubble-innerContainer {
-    margin:     0px;
-    padding:    0px;
-    border:     none;
-    position:   relative;
-    width:      100%;
-    height:     100%;
-    overflow:   visible;
-}
-
-div.simileAjax-bubble-contentContainer {
-    margin:     0px;
-    padding:    0px;
-    border:     none;
-    position:   absolute;
-    left:       0px;
-    top:        0px;
-    width:      100%;
-    height:     100%;
-    overflow:   auto;
-    background: white;
-}
-
-div.simileAjax-bubble-border-left {
-    position:   absolute;
-    left:       -50px;
-    top:        0px;
-    width:      50px;
-    height:     100%;
-}
-div.simileAjax-bubble-border-left-pngTranslucent {
-    background: url(../images/bubble-left.png) top right repeat-y;
-}
-
-div.simileAjax-bubble-border-right {
-    position:   absolute;
-    right:      -50px;
-    top:        0px;
-    width:      50px;
-    height:     100%;
-}
-.simileAjax-bubble-border-right-pngTranslucent {
-    background: url(../images/bubble-right.png) top left repeat-y;
-}
-
-div.simileAjax-bubble-border-top {
-    position:   absolute;
-    top:        -50px;
-    left:       0px;
-    width:      100%;
-    height:     50px;
-}
-.simileAjax-bubble-border-top-pngTranslucent {
-    background: url(../images/bubble-top.png) bottom left repeat-x;
-}
-
-div.simileAjax-bubble-border-bottom {
-    position:   absolute;
-    bottom:     -50px;
-    left:       0px;
-    width:      100%;
-    height:     50px;
-}
-.simileAjax-bubble-border-bottom-pngTranslucent {
-    background: url(../images/bubble-bottom.png) top left repeat-x;
-}
-
-div.simileAjax-bubble-border-top-left {
-    position:   absolute;
-    top:        -50px;
-    left:       -50px;
-    width:      50px;
-    height:     50px;
-}
-.simileAjax-bubble-border-top-left-pngTranslucent {
-    background: url(../images/bubble-top-left.png) bottom right no-repeat;
-}
-
-div.simileAjax-bubble-border-top-right {
-    position:   absolute;
-    top:        -50px;
-    right:      -50px;
-    width:      50px;
-    height:     50px;
-}
-.simileAjax-bubble-border-top-right-pngTranslucent {
-    background: url(../images/bubble-top-right.png) bottom left no-repeat;
-}
-
-div.simileAjax-bubble-border-bottom-left {
-    position:   absolute;
-    bottom:     -50px;
-    left:       -50px;
-    width:      50px;
-    height:     50px;
-}
-.simileAjax-bubble-border-bottom-left-pngTranslucent {
-    background: url(../images/bubble-bottom-left.png) top right no-repeat;
-}
-
-div.simileAjax-bubble-border-bottom-right {
-    position:   absolute;
-    bottom:     -50px;
-    right:      -50px;
-    width:      50px;
-    height:     50px;
-}
-.simileAjax-bubble-border-bottom-right-pngTranslucent {
-    background: url(../images/bubble-bottom-right.png) top left no-repeat;
-}
-
-div.simileAjax-bubble-arrow-point-left {
-    position:   absolute;
-    left:       -100px;
-    width:      100px;
-    height:     49px;
-}
-.simileAjax-bubble-arrow-point-left-pngTranslucent {
-    background: url(../images/bubble-arrow-point-left.png) center right no-repeat;
-}
-
-div.simileAjax-bubble-arrow-point-right {
-    position:   absolute;
-    right:      -100px;
-    width:      100px;
-    height:     49px;
-}
-.simileAjax-bubble-arrow-point-right-pngTranslucent {
-    background: url(../images/bubble-arrow-point-right.png) center left no-repeat;
-}
-
-div.simileAjax-bubble-arrow-point-up {
-    position:   absolute;
-    top:        -100px;
-    width:      49px;
-    height:     100px;
-}
-.simileAjax-bubble-arrow-point-up-pngTranslucent {
-    background: url(../images/bubble-arrow-point-up.png) bottom center no-repeat;
-}
-
-div.simileAjax-bubble-arrow-point-down {
-    position:   absolute;
-    bottom:     -100px;
-    width:      49px;
-    height:     100px;
-}
-.simileAjax-bubble-arrow-point-down-pngTranslucent {
-    background: url(../images/bubble-arrow-point-down.png) bottom center no-repeat;
-}
-
-
-div.simileAjax-bubble-close {
-    position:   absolute;
-    right:      -10px;
-    top:        -12px;
-    width:      16px;
-    height:     16px;
-    cursor:     pointer;
-}
-.simileAjax-bubble-close-pngTranslucent {
-    background: url(../images/close-button.png) no-repeat;
-}
-.timeline-container {
-    position: relative;
-    overflow: hidden;
-}
-
-.timeline-copyright {
-    position: absolute;
-    bottom: 0px;
-    left: 0px;
-    z-index: 1000;
-    cursor: pointer;
-}
-
-.timeline-message-container {
-    position:   absolute;
-    top:        30%;
-    left:       35%;
-    right:      35%;
-    z-index:    1000;
-    display:    none;
-}
-.timeline-message {
-    font-size:      120%;
-    font-weight:    bold;
-    text-align:     center;
-}
-.timeline-message img {
-    vertical-align: middle;
-}
-
-.timeline-band {
-    position:   absolute;
-    background: #eee;
-    z-index:    10;
-}
-
-.timeline-band-inner {
-    position: relative;
-    width: 100%;
-    height: 100%;
-}
-
-.timeline-band-input {
-    position:   absolute;
-    width:      1em;
-    height:     1em;
-    overflow:   hidden;
-    z-index:    0;
-}
-.timeline-band-input input{
-    width:      0;
-}
-
-.timeline-band-layer {
-    position:   absolute;
-    width:      100%;
-    height:     100%;
-}
-
-.timeline-band-layer-inner {
-    position:   relative;
-    width:      100%;
-    height:     100%;
-}
-
-
-
-/*------------------- Horizontal / Vertical lines ----------------*/
-
-/* style for ethers */
-.timeline-ether-lines{border-color:#666; border-style:dotted; position:absolute;}
-.timeline-horizontal .timeline-ether-lines{border-width:0 0 0 1px; height:100%; top: 0; width: 1px;}
-.timeline-vertical .timeline-ether-lines{border-width:1px 0 0; height:1px; left: 0; width: 100%;}
-
-
-
-/*---------------- Weekends ---------------------------*/
-.timeline-ether-weekends{
-	position:absolute;
-	background-color:#FFFFE0;
-}
-
-.timeline-vertical .timeline-ether-weekends{left:0;width:100%;}
-.timeline-horizontal .timeline-ether-weekends{top:0; height:100%;}
-
-
-/*-------------------------- HIGHLIGHT DECORATORS -------------------*/
-/* Used for decorators, not used for Timeline Highlight              */
-.timeline-highlight-decorator,
-.timeline-highlight-point-decorator{
-	position:absolute;
-	overflow:hidden;
-}
-
-/* Width of horizontal decorators and Height of vertical decorators is
-   set in the decorator function params */
-.timeline-horizontal .timeline-highlight-point-decorator,
-.timeline-horizontal .timeline-highlight-decorator{
-	top:0;
-  height:100%;
-}
-
-.timeline-vertical .timeline-highlight-point-decorator,
-.timeline-vertical .timeline-highlight-decorator{
-	width:100%;
-	left:0;
-}
-
-.timeline-highlight-decorator{background-color:#FFC080;}
-.timeline-highlight-point-decorator{background-color:#ff5;}
-
-
-/*---------------------------- LABELS -------------------------*/
-.timeline-highlight-label {
-  position:absolute; overflow:hidden; font-size:200%;
-  font-weight:bold; color:#999; }
-
-
-/*---------------- VERTICAL LABEL -------------------*/
-.timeline-horizontal .timeline-highlight-label {top:0; height:100%;}
-.timeline-horizontal .timeline-highlight-label td {vertical-align:middle;}
-.timeline-horizontal .timeline-highlight-label-start {text-align:right;}
-.timeline-horizontal .timeline-highlight-label-end {text-align:left;}
-
-
-/*---------------- HORIZONTAL LABEL -------------------*/
-.timeline-vertical .timeline-highlight-label {left:0;width:100%;}
-.timeline-vertical .timeline-highlight-label td {vertical-align:top;}
-.timeline-vertical .timeline-highlight-label-start {text-align:center;}
-.timeline-vertical .timeline-highlight-label-end {text-align:center;}
-
-
-/*-------------------------------- DATE LABELS --------------------------------*/
-.timeline-date-label {
-  position: absolute;
-  border: solid #aaa;
-  color: #aaa;
-  width: 5em;
-  height: 1.5em;}
-.timeline-date-label-em {color: #000;}
-
-/* horizontal */
-.timeline-horizontal .timeline-date-label{padding-left:2px;}
-.timeline-horizontal .timeline-date-label{border-width:0 0 0 1px;}
-.timeline-horizontal .timeline-date-label-em{height:2em}
-
-/* vertical */
-.timeline-vertical .timeline-date-label{padding-top:2px;}
-.timeline-vertical .timeline-date-label{border-width:1px 0 0;}
-.timeline-vertical .timeline-date-label-em{width:7em}
-
-
-/*------------------------------- Ether.highlight -------------------------*/
-.timeline-ether-highlight{position:absolute; background-color:#fff;}
-.timeline-horizontal .timeline-ether-highlight{top:2px;}
-.timeline-vertical .timeline-ether-highlight{left:2px;}
-
-
-/*------------------------------ EVENTS ------------------------------------*/
-.timeline-event-icon, .timeline-event-label,.timeline-event-tape{
-	position:absolute;
-	cursor:pointer;
-}
-
-.timeline-event-tape,
-.timeline-small-event-tape,
-.timeline-small-event-icon{
-	background-color:#58A0DC;
-	overflow:hidden;
-}
-
-.timeline-small-event-tape,
-.timeline-small-event-icon{
-	position:absolute;
-}
-
-.timeline-small-event-icon{width:1px; height:6px;}
-
-  
-/*--------------------------------- TIMELINE-------------------------*/
-.timeline-ether-bg{width:100%; height:100%;}
-.timeline-band-0 .timeline-ether-bg{background-color:#eee}
-.timeline-band-1 .timeline-ether-bg{background-color:#ddd}
-.timeline-band-2 .timeline-ether-bg{background-color:#ccc}
-.timeline-band-3 .timeline-ether-bg{background-color:#aaa}
-.timeline-duration-event {
-    position: absolute;
-    overflow: hidden;
-    border: 1px solid blue;
-}
-
-.timeline-instant-event2 {
-    position: absolute;
-    overflow: hidden;
-    border-left: 1px solid blue;
-    padding-left: 2px;
-}
-
-.timeline-instant-event {
-    position: absolute;
-    overflow: hidden;
-}
-
-.timeline-event-bubble-title {
-    font-weight: bold;
-    border-bottom: 1px solid #888;
-    margin-bottom: 0.5em;
-}
-
-.timeline-event-bubble-body {
-}
-
-.timeline-event-bubble-wiki {
-    margin:     0.5em;
-    text-align: right;
-    color:      #A0A040;
-}
-.timeline-event-bubble-wiki a {
-    color:      #A0A040;
-}
-
-.timeline-event-bubble-time {
-    color: #aaa;
-}
-
-.timeline-event-bubble-image {
-    float: right;
-    padding-left: 5px;
-    padding-bottom: 5px;
-}
\ No newline at end of file
Binary file web/data/timeline/blue-circle.png has changed
Binary file web/data/timeline/bubble-arrows.png has changed
Binary file web/data/timeline/bubble-body-and-arrows.png has changed
Binary file web/data/timeline/bubble-body.png has changed
Binary file web/data/timeline/bubble-bottom-arrow.png has changed
Binary file web/data/timeline/bubble-bottom-left.png has changed
Binary file web/data/timeline/bubble-bottom-right.png has changed
Binary file web/data/timeline/bubble-bottom.png has changed
Binary file web/data/timeline/bubble-left-arrow.png has changed
Binary file web/data/timeline/bubble-left.png has changed
Binary file web/data/timeline/bubble-right-arrow.png has changed
Binary file web/data/timeline/bubble-right.png has changed
Binary file web/data/timeline/bubble-top-arrow.png has changed
Binary file web/data/timeline/bubble-top-left.png has changed
Binary file web/data/timeline/bubble-top-right.png has changed
Binary file web/data/timeline/bubble-top.png has changed
Binary file web/data/timeline/close-button.png has changed
Binary file web/data/timeline/copyright-vertical.png has changed
Binary file web/data/timeline/copyright.png has changed
Binary file web/data/timeline/dark-blue-circle.png has changed
Binary file web/data/timeline/dark-green-circle.png has changed
Binary file web/data/timeline/dark-red-circle.png has changed
Binary file web/data/timeline/dull-blue-circle.png has changed
Binary file web/data/timeline/dull-green-circle.png has changed
Binary file web/data/timeline/dull-red-circle.png has changed
Binary file web/data/timeline/gray-circle.png has changed
Binary file web/data/timeline/green-circle.png has changed
Binary file web/data/timeline/message-bottom-left.png has changed
Binary file web/data/timeline/message-bottom-right.png has changed
Binary file web/data/timeline/message-left.png has changed
Binary file web/data/timeline/message-right.png has changed
Binary file web/data/timeline/message-top-left.png has changed
Binary file web/data/timeline/message-top-right.png has changed
Binary file web/data/timeline/message.png has changed
Binary file web/data/timeline/progress-running.gif has changed
Binary file web/data/timeline/red-circle.png has changed
Binary file web/data/timeline/sundial.png has changed
Binary file web/data/timeline/top-bubble.png has changed
--- a/web/facet.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/facet.py	Tue Jun 21 07:42:30 2016 +0200
@@ -1210,7 +1210,7 @@
 
     The image below display the rendering of the slider:
 
-    .. image:: ../images/facet_range.png
+    .. image:: ../../images/facet_range.png
 
     .. _jquery: http://www.jqueryui.com/
     """
@@ -1309,7 +1309,7 @@
 
     The image below display the rendering of the slider for a date range:
 
-    .. image:: ../images/facet_date_range.png
+    .. image:: ../../images/facet_date_range.png
     """
     target_attr_type = 'Date' # only date types are supported
 
@@ -1440,7 +1440,7 @@
     Here is an example of the rendering of thos facet to filter book with image
     and the corresponding code:
 
-    .. image:: ../images/facet_has_image.png
+    .. image:: ../../images/facet_has_image.png
 
     .. sourcecode:: python
 
@@ -1465,15 +1465,17 @@
 
     def add_rql_restrictions(self):
         """add restriction for this facet into the rql syntax tree"""
-        self.select.set_distinct(True) # XXX
         value = self._cw.form.get(self.__regid__)
         if not value: # no value sent for this facet
             return
+        exists = nodes.Exists()
+        self.select.add_restriction(exists)
         var = self.select.make_variable()
         if self.role == 'subject':
-            self.select.add_relation(self.filtered_variable, self.rtype, var)
+            subj, obj = self.filtered_variable, var
         else:
-            self.select.add_relation(var, self.rtype, self.filtered_variable)
+            subj, obj = var, self.filtered_variable
+        exists.add_relation(subj, self.rtype, obj)
 
 
 class BitFieldFacet(AttributeFacet):
--- a/web/formwidgets.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/formwidgets.py	Tue Jun 21 07:42:30 2016 +0200
@@ -712,7 +712,7 @@
 
 
 class JQueryTimePicker(JQueryDatePicker):
-    """Use jquery.timePicker to define a time picker. Will return the time as an
+    """Use jquery.timePicker to define a time picker. Will return the time as a
     unicode string.
     """
     needs_js = ('jquery.timePicker.js',)
--- a/web/htmlwidgets.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/htmlwidgets.py	Tue Jun 21 07:42:30 2016 +0200
@@ -141,7 +141,7 @@
 
 
 class RawBoxItem(HTMLWidget): # XXX deprecated
-    """a simpe box item displaying raw data"""
+    """a simple box item displaying raw data"""
 
     def __init__(self, label, liclass=None):
         self.label = label
--- a/web/httpcache.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/httpcache.py	Tue Jun 21 07:42:30 2016 +0200
@@ -22,10 +22,6 @@
 from time import mktime
 from datetime import datetime
 
-# time delta usable to convert localized time to GMT time
-# XXX this become erroneous after a DST transition!!!
-GMTOFFSET = - (datetime.now() - datetime.utcnow())
-
 class NoHTTPCacheManager(object):
     """default cache manager: set no-cache cache control policy"""
     def __init__(self, view):
--- a/web/propertysheet.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/propertysheet.py	Tue Jun 21 07:42:30 2016 +0200
@@ -63,15 +63,15 @@
                 if not isinstance(self[name], type):
                     msg = "Configuration error: %s.%s should be a %s" % (fpath, name, type)
                     raise Exception(msg)
-        self._propfile_mtime[fpath] = os.stat(fpath)[-2]
+        self._propfile_mtime[fpath] = os.stat(fpath).st_mtime
         self._ordered_propfiles.append(fpath)
 
     def need_reload(self):
         for rid, (adirectory, rdirectory, mtime) in self._cache.items():
-            if os.stat(osp.join(rdirectory, rid))[-2] > mtime:
+            if os.stat(osp.join(rdirectory, rid)).st_mtime > mtime:
                 del self._cache[rid]
         for fpath, mtime in self._propfile_mtime.iteritems():
-            if os.stat(fpath)[-2] > mtime:
+            if os.stat(fpath).st_mtime > mtime:
                 return True
         return False
 
@@ -109,7 +109,7 @@
                 stream.write(content)
                 stream.close()
                 adirectory = self._cache_directory
-            self._cache[rid] = (adirectory, rdirectory, os.stat(sourcefile)[-2])
+            self._cache[rid] = (adirectory, rdirectory, os.stat(sourcefile).st_mtime)
             return adirectory
 
     def compile(self, content):
--- a/web/request.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/request.py	Tue Jun 21 07:42:30 2016 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -38,14 +38,14 @@
 from logilab.common.deprecation import deprecated
 from logilab.mtconverter import xml_escape
 
+from cubicweb import AuthenticationError
 from cubicweb.req import RequestSessionBase
-from cubicweb.dbapi import DBAPIRequest
 from cubicweb.uilib import remove_html_tags, js
 from cubicweb.utils import SizeConstrainedList, HTMLHead, make_uid
 from cubicweb.view import TRANSITIONAL_DOCTYPE_NOEXT
 from cubicweb.web import (INTERNAL_FIELD_VALUE, LOGGER, NothingToEdit,
                           RequestError, StatusResponse)
-from cubicweb.web.httpcache import GMTOFFSET, get_validators
+from cubicweb.web.httpcache import get_validators
 from cubicweb.web.http_headers import Headers, Cookie, parseDateTime
 
 _MARKER = object()
@@ -155,9 +155,7 @@
         #: shared among various components used to publish the request (views,
         #: controller, application...)
         self.data = {}
-        #:  search state: 'normal' or 'linksearch' (eg searching for an object
-        #:  to create a relation with another)
-        self.search_state = ('normal',)
+        self._search_state = None
         #: page id, set by htmlheader template
         self.pageid = None
         self._set_pageid()
@@ -354,21 +352,36 @@
             self.session.data.pop(self._msgid, u'')
             del self._msgid
 
+    def _load_search_state(self, searchstate):
+        if searchstate is None or searchstate == 'normal':
+            self._search_state = ('normal',)
+        else:
+            self._search_state = ('linksearch', searchstate.split(':'))
+            assert len(self._search_state[-1]) == 4, 'invalid searchstate'
+
+    @property
+    def search_state(self):
+        """search state: 'normal' or 'linksearch' (i.e. searching for an object
+        to create a relation with another)"""
+        if self._search_state is None:
+            searchstate = self.session.data.get('search_state', 'normal')
+            self._load_search_state(searchstate)
+        return self._search_state
+
+    @search_state.setter
+    def search_state(self, searchstate):
+        self._search_state = searchstate
+
     def update_search_state(self):
-        """update the current search state"""
+        """update the current search state if needed"""
         searchstate = self.form.get('__mode')
-        if not searchstate:
-            searchstate = self.session.data.get('search_state', 'normal')
-        self.set_search_state(searchstate)
+        if searchstate:
+            self.set_search_state(searchstate)
 
     def set_search_state(self, searchstate):
         """set a new search state"""
-        if searchstate is None or searchstate == 'normal':
-            self.search_state = (searchstate or 'normal',)
-        else:
-            self.search_state = ('linksearch', searchstate.split(':'))
-            assert len(self.search_state[-1]) == 4
         self.session.data['search_state'] = searchstate
+        self._load_search_state(searchstate)
 
     def match_search_state(self, rset):
         """when searching an entity to create a relation, return True if entities in
@@ -385,7 +398,7 @@
 
     def update_breadcrumbs(self):
         """stores the last visisted page in session data"""
-        searchstate = self.session.data.get('search_state')
+        searchstate = self.search_state[0]
         if searchstate == 'normal':
             breadcrumbs = self.session.data.get('breadcrumbs')
             if breadcrumbs is None:
@@ -403,67 +416,6 @@
             return breadcrumbs.pop()
         return self.base_url()
 
-    @deprecated('[3.19] use a traditional ajaxfunc / controller')
-    def user_rql_callback(self, rqlargs, *args, **kwargs):
-        """register a user callback to execute some rql query, and return a URL
-        to call that callback which can be inserted in an HTML view.
-
-        `rqlargs` should be a tuple containing argument to give to the execute function.
-
-        The first argument following rqlargs must be the message to be
-        displayed after the callback is called.
-
-        For other allowed arguments, see :meth:`user_callback` method
-        """
-        def rqlexec(req, rql, args=None, key=None):
-            req.execute(rql, args, key)
-        return self.user_callback(rqlexec, rqlargs, *args, **kwargs)
-
-    @deprecated('[3.19] use a traditional ajaxfunc / controller')
-    def user_callback(self, cb, cbargs, *args, **kwargs):
-        """register the given user callback and return a URL which can
-        be inserted in an HTML view. When the URL is accessed, the
-        callback function will be called (as 'cb(req, \*cbargs)', and a
-        message will be displayed in the web interface. The third
-        positional argument must be 'msg', containing the message.
-
-        You can specify the underlying js function to call using a 'jsfunc'
-        named args, to one of :func:`userCallback`,
-        ':func:`userCallbackThenUpdateUI`, ':func:`userCallbackThenReloadPage`
-        (the default). Take care arguments may vary according to the used
-        function.
-        """
-        self.add_js('cubicweb.ajax.js')
-        jsfunc = kwargs.pop('jsfunc', 'userCallbackThenReloadPage')
-        assert not kwargs, 'dunno what to do with remaining kwargs: %s' % kwargs
-        cbname = self.register_onetime_callback(cb, *cbargs)
-        return "javascript: %s" % getattr(js, jsfunc)(cbname, *args)
-
-    @deprecated('[3.19] use a traditional ajaxfunc / controller')
-    def register_onetime_callback(self, func, *args):
-        cbname = build_cb_uid(func.__name__)
-        def _cb(req):
-            try:
-                return func(req, *args)
-            finally:
-                self.unregister_callback(self.pageid, cbname)
-        self.set_page_data(cbname, _cb)
-        return cbname
-
-    @deprecated('[3.19] use a traditional ajaxfunc / controller')
-    def unregister_callback(self, pageid, cbname):
-        assert pageid is not None
-        assert cbname.startswith('cb_')
-        self.info('unregistering callback %s for pageid %s', cbname, pageid)
-        self.del_page_data(cbname)
-
-    @deprecated('[3.19] use a traditional ajaxfunc / controller')
-    def clear_user_callbacks(self):
-        if self.session is not None: # XXX
-            for key in list(self.session.data):
-                if key.startswith('cb_'):
-                    del self.session.data[key]
-
     # web edition helpers #####################################################
 
     @cached # so it's writed only once
@@ -584,8 +536,8 @@
             # we don't want to handle times before the EPOCH (cause bug on
             # windows). Also use > and not >= else expires == 0 and Cookie think
             # that means no expire...
-            assert expires + GMTOFFSET > date(1970, 1, 1)
-            expires = timegm((expires + GMTOFFSET).timetuple())
+            assert expires > date(1970, 1, 1)
+            expires = timegm(expires.timetuple())
         else:
             expires = None
         # make sure cookie is set on the correct path
@@ -859,8 +811,7 @@
         """
         mtime = self.get_header('If-modified-since', raw=False)
         if mtime:
-            # :/ twisted is returned a localized time stamp
-            return datetime.fromtimestamp(mtime) + GMTOFFSET
+            return datetime.utcfromtimestamp(mtime)
         return None
 
     ### outcoming headers
@@ -1005,29 +956,36 @@
         self.set_default_language(vreg)
 
 
-class DBAPICubicWebRequestBase(_CubicWebRequestBase, DBAPIRequest):
-
-    def set_session(self, session):
-        """method called by the session handler when the user is authenticated
-        or an anonymous connection is open
-        """
-        super(CubicWebRequestBase, self).set_session(session)
-        # set request language
-        self.set_user_language(session.user)
-
-
 def _cnx_func(name):
     def proxy(req, *args, **kwargs):
         return getattr(req.cnx, name)(*args, **kwargs)
     return proxy
 
+class _NeedAuthAccessMock(object):
+
+    def __getattribute__(self, attr):
+        raise AuthenticationError()
+
+    def __nonzero__(self):
+        return False
+
+class _MockAnonymousSession(object):
+    sessionid = 'thisisnotarealsession'
+
+    @property
+    def data(self):
+        return {}
+
+    @property
+    def anonymous_session(self):
+        return True
 
 class ConnectionCubicWebRequestBase(_CubicWebRequestBase):
+    cnx = None
+    session = None
 
     def __init__(self, vreg, https=False, form=None, headers={}):
         """"""
-        self.cnx = None
-        self.session = None
         self.vreg = vreg
         try:
             # no vreg or config which doesn't handle translations
@@ -1036,8 +994,7 @@
             self.translations = {}
         super(ConnectionCubicWebRequestBase, self).__init__(vreg, https=https,
                                                        form=form, headers=headers)
-        from cubicweb.dbapi import DBAPISession, _NeedAuthAccessMock
-        self.session = DBAPISession(None)
+        self.session = _MockAnonymousSession()
         self.cnx = self.user = _NeedAuthAccessMock()
 
     @property
@@ -1045,8 +1002,10 @@
         return self.cnx.transaction_data
 
     def set_cnx(self, cnx):
+        if 'ecache' in cnx.transaction_data:
+            del cnx.transaction_data['ecache']
         self.cnx = cnx
-        self.session = cnx._session
+        self.session = cnx.session
         self._set_user(cnx.user)
         self.set_user_language(cnx.user)
 
@@ -1056,7 +1015,6 @@
         return rset
 
     def set_default_language(self, vreg):
-        # XXX copy from dbapi
         try:
             lang = vreg.property_value('ui.language')
         except Exception: # property may not be registered
--- a/web/test/data/bootstrap_cubes	Mon May 09 17:24:03 2016 +0200
+++ b/web/test/data/bootstrap_cubes	Tue Jun 21 07:42:30 2016 +0200
@@ -1,1 +1,1 @@
-file, blog, tag, folder
+file, blog, tag
--- a/web/test/data/schema.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/test/data/schema.py	Tue Jun 21 07:42:30 2016 +0200
@@ -120,9 +120,9 @@
     object = 'Directory'
     composite = 'object'
 
-# used by windmill for `test_edit_relation`
-from cubes.folder.schema import Folder
-
+class Folder(EntityType):
+    name = String(required=True)
+    filed_under = SubjectRelation('Folder', description=_('parent folder'))
 
 class TreeNode(EntityType):
     name = String(required=True)
--- a/web/test/data/views.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/test/data/views.py	Tue Jun 21 07:42:30 2016 +0200
@@ -16,7 +16,9 @@
 # You should have received a copy of the GNU Lesser General Public License along
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
 
+from cubicweb.predicates import has_related_entities
 from cubicweb.web.views.ajaxcontroller import ajaxfunc
+from cubicweb.web.views.ibreadcrumbs import IBreadCrumbsAdapter
 
 def _recursive_replace_stream_by_content(tree):
     """ Search for streams (i.e. object that have a 'read' method) in a tree
@@ -46,3 +48,10 @@
     except Exception, ex:
         import traceback as tb
         tb.print_exc(ex)
+
+
+class FolderIBreadCrumbsAdapter(IBreadCrumbsAdapter):
+    __select__ = IBreadCrumbsAdapter.__select__ & has_related_entities('filed_under')
+
+    def parent_entity(self):
+        return self.entity.filed_under[0]
--- a/web/test/jstests/ajax_url1.html	Mon May 09 17:24:03 2016 +0200
+++ b/web/test/jstests/ajax_url1.html	Tue Jun 21 07:42:30 2016 +0200
@@ -1,6 +1,6 @@
 <div id="ajaxroot">
   <div class="ajaxHtmlHead">
-    <script src="http://foo.js" type="text/javascript"> </script>
+    <cubicweb:script src="http://foo.js" type="text/javascript"> </cubicweb:script>
   </div>
   <h1>Hello</h1>
 </div>
--- a/web/test/jstests/ajaxresult.json	Mon May 09 17:24:03 2016 +0200
+++ b/web/test/jstests/ajaxresult.json	Tue Jun 21 07:42:30 2016 +0200
@@ -1,1 +1,1 @@
-['foo', 'bar']
+["foo", "bar"]
--- a/web/test/jstests/test_ajax.js	Mon May 09 17:24:03 2016 +0200
+++ b/web/test/jstests/test_ajax.js	Tue Jun 21 07:42:30 2016 +0200
@@ -1,17 +1,17 @@
 $(document).ready(function() {
 
-    module("ajax", {
+    QUnit.module("ajax", {
         setup: function() {
           this.scriptsLength = $('head script[src]').length-1;
           this.cssLength = $('head link[rel=stylesheet]').length-1;
           // re-initialize cw loaded cache so that each tests run in a
           // clean environment, have a lookt at _loadAjaxHtmlHead implementation
           // in cubicweb.ajax.js for more information.
-          cw.loaded_src = [];
-          cw.loaded_href = [];
+          cw.loaded_scripts = [];
+          cw.loaded_links = [];
         },
         teardown: function() {
-          $('head script[src]:gt(' + this.scriptsLength + ')').remove();
+          $('head script[src]:lt(' + ($('head script[src]').length - 1 - this.scriptsLength) + ')').remove();
           $('head link[rel=stylesheet]:gt(' + this.cssLength + ')').remove();
         }
       });
@@ -22,66 +22,66 @@
         });
     }
 
-    test('test simple h1 inclusion (ajax_url0.html)', function() {
-        expect(3);
-        equals(jQuery('#main').children().length, 0);
-        stop();
-        jQuery('#main').loadxhtml('/../ajax_url0.html', {
-            callback: function() {
+    QUnit.test('test simple h1 inclusion (ajax_url0.html)', function (assert) {
+        assert.expect(3);
+        assert.equal($('#qunit-fixture').children().length, 0);
+        var done = assert.async();
+        $('#qunit-fixture').loadxhtml(BASE_URL + 'cwsoftwareroot/web/test/jstests/ajax_url0.html')
+        .addCallback(function() {
                 try {
-                    equals(jQuery('#main').children().length, 1);
-                    equals(jQuery('#main h1').html(), 'Hello');
+                    assert.equal($('#qunit-fixture').children().length, 1);
+                    assert.equal($('#qunit-fixture h1').html(), 'Hello');
                 } finally {
-                    start();
+                    done();
                 };
             }
-        });
+        );
     });
 
-    test('test simple html head inclusion (ajax_url1.html)', function() {
-        expect(6);
+    QUnit.test('test simple html head inclusion (ajax_url1.html)', function (assert) {
+        assert.expect(6);
         var scriptsIncluded = jsSources();
-        equals(jQuery.inArray('http://foo.js', scriptsIncluded), - 1);
-        stop();
-        jQuery('#main').loadxhtml('/../ajax_url1.html', {
-            callback: function() {
+        assert.equal(jQuery.inArray('http://foo.js', scriptsIncluded), - 1);
+        var done = assert.async();
+        $('#qunit-fixture').loadxhtml(BASE_URL + 'cwsoftwareroot/web/test/jstests/ajax_url1.html')
+        .addCallback(function() {
                 try {
                     var origLength = scriptsIncluded.length;
                     scriptsIncluded = jsSources();
-                    // check that foo.js has been *appended* to <head>
-                    equals(scriptsIncluded.length, origLength + 1);
-                    equals(scriptsIncluded[origLength].indexOf('http://foo.js'), 0);
+                    // check that foo.js has been prepended to <head>
+                    assert.equal(scriptsIncluded.length, origLength + 1);
+                    assert.equal(scriptsIncluded.indexOf('http://foo.js'), 0);
                     // check that <div class="ajaxHtmlHead"> has been removed
-                    equals(jQuery('#main').children().length, 1);
-                    equals(jQuery('div.ajaxHtmlHead').length, 0);
-                    equals(jQuery('#main h1').html(), 'Hello');
+                    assert.equal($('#qunit-fixture').children().length, 1);
+                    assert.equal($('div.ajaxHtmlHead').length, 0);
+                    assert.equal($('#qunit-fixture h1').html(), 'Hello');
                 } finally {
-                    start();
+                    done();
                 };
             }
-        });
+        );
     });
 
-    test('test addCallback', function() {
-        expect(3);
-        equals(jQuery('#main').children().length, 0);
-        stop();
-        var d = jQuery('#main').loadxhtml('/../ajax_url0.html');
+    QUnit.test('test addCallback', function (assert) {
+        assert.expect(3);
+        assert.equal($('#qunit-fixture').children().length, 0);
+        var done = assert.async();
+        var d = $('#qunit-fixture').loadxhtml(BASE_URL + 'cwsoftwareroot/web/test/jstests/ajax_url0.html');
         d.addCallback(function() {
             try {
-                equals(jQuery('#main').children().length, 1);
-                equals(jQuery('#main h1').html(), 'Hello');
+                assert.equal($('#qunit-fixture').children().length, 1);
+                assert.equal($('#qunit-fixture h1').html(), 'Hello');
             } finally {
-                start();
+                done();
             };
         });
     });
 
-    test('test callback after synchronous request', function() {
-        expect(1);
+    QUnit.test('test callback after synchronous request', function (assert) {
+        assert.expect(1);
         var deferred = new Deferred();
         var result = jQuery.ajax({
-            url: './ajax_url0.html',
+            url: BASE_URL + 'cwsoftwareroot/web/test/jstests/ajax_url0.html',
             async: false,
             beforeSend: function(xhr) {
                 deferred._req = xhr;
@@ -90,44 +90,44 @@
                 deferred.success(data);
             }
         });
-        stop();
+        var done = assert.async();
         deferred.addCallback(function() {
             try {
                 // add an assertion to ensure the callback is executed
-                ok(true, "callback is executed");
+                assert.ok(true, "callback is executed");
             } finally {
-                start();
+                done();
             };
         });
     });
 
-    test('test addCallback with parameters', function() {
-        expect(3);
-        equals(jQuery('#main').children().length, 0);
-        stop();
-        var d = jQuery('#main').loadxhtml('/../ajax_url0.html');
+    QUnit.test('test addCallback with parameters', function (assert) {
+        assert.expect(3);
+        assert.equal($('#qunit-fixture').children().length, 0);
+        var done = assert.async();
+        var d = $('#qunit-fixture').loadxhtml(BASE_URL + 'cwsoftwareroot/web/test/jstests/ajax_url0.html');
         d.addCallback(function(data, req, arg1, arg2) {
             try {
-                equals(arg1, 'Hello');
-                equals(arg2, 'world');
+                assert.equal(arg1, 'Hello');
+                assert.equal(arg2, 'world');
             } finally {
-                start();
+                done();
             };
         },
         'Hello', 'world');
     });
 
-    test('test callback after synchronous request with parameters', function() {
-        expect(2);
+    QUnit.test('test callback after synchronous request with parameters', function (assert) {
+        assert.expect(3);
         var deferred = new Deferred();
         deferred.addCallback(function(data, req, arg1, arg2) {
             // add an assertion to ensure the callback is executed
             try {
-                ok(true, "callback is executed");
-                equals(arg1, 'Hello');
-                equals(arg2, 'world');
+                assert.ok(true, "callback is executed");
+                assert.equal(arg1, 'Hello');
+                assert.equal(arg2, 'world');
             } finally {
-                start();
+                done();
             };
         },
         'Hello', 'world');
@@ -136,12 +136,12 @@
             try {
                 throw this._error;
             } finally {
-                start();
+                done();
             };
         });
-        stop();
+        var done = assert.async();
         var result = jQuery.ajax({
-            url: '/../ajax_url0.html',
+            url: BASE_URL + 'cwsoftwareroot/web/test/jstests/ajax_url0.html',
             async: false,
             beforeSend: function(xhr) {
                 deferred._req = xhr;
@@ -152,138 +152,123 @@
         });
     });
 
-  test('test addErrback', function() {
-        expect(1);
-        stop();
-        var d = jQuery('#main').loadxhtml('/../ajax_url0.html');
+  QUnit.test('test addErrback', function (assert) {
+        assert.expect(1);
+        var done = assert.async();
+        var d = $('#qunit-fixture').loadxhtml(BASE_URL + 'cwsoftwareroot/web/test/jstests/nonexistent.html');
         d.addCallback(function() {
-            // throw an exception to start errback chain
-            try {
-                throw new Error();
-            } finally {
-                start();
-            };
+            // should not be executed
+            assert.ok(false, "callback is executed");
         });
         d.addErrback(function() {
             try {
-                ok(true, "errback is executed");
+                assert.ok(true, "errback is executed");
             } finally {
-                start();
+                done();
             };
         });
     });
 
-    test('test callback / errback execution order', function() {
-        expect(4);
+    QUnit.test('test callback execution order', function (assert) {
+        assert.expect(3);
         var counter = 0;
-        stop();
-        var d = jQuery('#main').loadxhtml('/../ajax_url0.html', {
-            callback: function() {
-                try {
-                    equals(++counter, 1); // should be executed first
-                } finally {
-                    start();
-                };
-            }
+        var done = assert.async();
+        var d = $('#qunit-fixture').loadxhtml(BASE_URL + 'cwsoftwareroot/web/test/jstests/ajax_url0.html');
+        d.addCallback(function() {
+            assert.equal(++counter, 1); // should be executed first
         });
         d.addCallback(function() {
-            equals(++counter, 2); // should be executed and break callback chain
-            throw new Error();
+            assert.equal(++counter, 2);
         });
         d.addCallback(function() {
-            // should not be executed since second callback raised an error
-            ok(false, "callback is executed");
-        });
-        d.addErrback(function() {
-            // should be executed after the second callback
-            equals(++counter, 3);
-        });
-        d.addErrback(function() {
-            // should be executed after the first errback
-            equals(++counter, 4);
+            try {
+                assert.equal(++counter, 3);
+            } finally {
+                done();
+            }
         });
     });
 
-    test('test already included resources are ignored (ajax_url1.html)', function() {
-        expect(10);
+    QUnit.test('test already included resources are ignored (ajax_url1.html)', function (assert) {
+        assert.expect(10);
         var scriptsIncluded = jsSources();
         // NOTE:
-        equals(jQuery.inArray('http://foo.js', scriptsIncluded), -1);
-        equals(jQuery('head link').length, 1);
+        assert.equal(jQuery.inArray('http://foo.js', scriptsIncluded), -1);
+        assert.equal($('head link').length, 1);
         /* use endswith because in pytest context we have an absolute path */
-        ok(jQuery('head link').attr('href').endswith('/qunit.css'));
-        stop();
-        jQuery('#main').loadxhtml('/../ajax_url1.html', {
-            callback: function() {
+        assert.ok($('head link').attr('href').endswith('/qunit.css'), 'qunit.css is loaded');
+        var done = assert.async();
+        $('#qunit-fixture').loadxhtml(BASE_URL + 'cwsoftwareroot/web/test/jstests/ajax_url1.html')
+        .addCallback(function() {
                 var origLength = scriptsIncluded.length;
                 scriptsIncluded = jsSources();
                 try {
                     // check that foo.js has been inserted in <head>
-                    equals(scriptsIncluded.length, origLength + 1);
-                    equals(scriptsIncluded[origLength].indexOf('http://foo.js'), 0);
+                    assert.equal(scriptsIncluded.length, origLength + 1);
+                    assert.equal(scriptsIncluded.indexOf('http://foo.js'), 0);
                     // check that <div class="ajaxHtmlHead"> has been removed
-                    equals(jQuery('#main').children().length, 1);
-                    equals(jQuery('div.ajaxHtmlHead').length, 0);
-                    equals(jQuery('#main h1').html(), 'Hello');
+                    assert.equal($('#qunit-fixture').children().length, 1);
+                    assert.equal($('div.ajaxHtmlHead').length, 0);
+                    assert.equal($('#qunit-fixture h1').html(), 'Hello');
                     // qunit.css is not added twice
-                    equals(jQuery('head link').length, 1);
+                    assert.equal($('head link').length, 1);
                     /* use endswith because in pytest context we have an absolute path */
-                    ok(jQuery('head link').attr('href').endswith('/qunit.css'));
+                    assert.ok($('head link').attr('href').endswith('/qunit.css'), 'qunit.css is loaded');
                 } finally {
-                    start();
+                    done();
                 }
             }
-        });
+        );
     });
 
-    test('test synchronous request loadRemote', function() {
-        var res = loadRemote('/../ajaxresult.json', {},
+    QUnit.test('test synchronous request loadRemote', function (assert) {
+        var res = loadRemote(BASE_URL + 'cwsoftwareroot/web/test/jstests/ajaxresult.json', {},
         'GET', true);
-        same(res, ['foo', 'bar']);
+        assert.deepEqual(res, ['foo', 'bar']);
     });
 
-    test('test event on CubicWeb', function() {
-        expect(1);
-        stop();
+    QUnit.test('test event on CubicWeb', function (assert) {
+        assert.expect(1);
+        var done = assert.async();
         var events = null;
-        jQuery(CubicWeb).bind('server-response', function() {
+        $(CubicWeb).bind('server-response', function() {
             // check that server-response event on CubicWeb is triggered
             events = 'CubicWeb';
         });
-        jQuery('#main').loadxhtml('/../ajax_url0.html', {
-            callback: function() {
+        $('#qunit-fixture').loadxhtml(BASE_URL + 'cwsoftwareroot/web/test/jstests/ajax_url0.html')
+        .addCallback(function() {
                 try {
-                    equals(events, 'CubicWeb');
+                    assert.equal(events, 'CubicWeb');
                 } finally {
-                    start();
+                    done();
                 };
             }
-        });
+        );
     });
 
-    test('test event on node', function() {
-        expect(3);
-        stop();
+    QUnit.test('test event on node', function (assert) {
+        assert.expect(3);
+        var done = assert.async();
         var nodes = [];
-        jQuery('#main').bind('server-response', function() {
+        $('#qunit-fixture').bind('server-response', function() {
             nodes.push('node');
         });
-        jQuery(CubicWeb).bind('server-response', function() {
+        $(CubicWeb).bind('server-response', function() {
             nodes.push('CubicWeb');
         });
-        jQuery('#main').loadxhtml('/../ajax_url0.html', {
-            callback: function() {
+        $('#qunit-fixture').loadxhtml(BASE_URL + 'cwsoftwareroot/web/test/jstests/ajax_url0.html')
+        .addCallback(function() {
                 try {
-                    equals(nodes.length, 2);
+                    assert.equal(nodes.length, 2);
                     // check that server-response event on CubicWeb is triggered
                     // only once and event server-response on node is triggered
-                    equals(nodes[0], 'CubicWeb');
-                    equals(nodes[1], 'node');
+                    assert.equal(nodes[0], 'CubicWeb');
+                    assert.equal(nodes[1], 'node');
                 } finally {
-                    start();
+                    done();
                 };
             }
-        });
+        );
     });
 });
 
--- a/web/test/jstests/test_htmlhelpers.js	Mon May 09 17:24:03 2016 +0200
+++ b/web/test/jstests/test_htmlhelpers.js	Tue Jun 21 07:42:30 2016 +0200
@@ -1,35 +1,35 @@
 $(document).ready(function() {
 
-    module("module2", {
+    QUnit.module("module2", {
       setup: function() {
-        $('#main').append('<select id="theselect" multiple="multiple" size="2">' +
+        $('#qunit-fixture').append('<select id="theselect" multiple="multiple" size="2">' +
     			'</select>');
       }
     });
 
-    test("test first selected", function() {
+    QUnit.test("test first selected", function (assert) {
         $('#theselect').append('<option value="foo">foo</option>' +
     			     '<option selected="selected" value="bar">bar</option>' +
     			     '<option value="baz">baz</option>' +
     			     '<option selected="selecetd"value="spam">spam</option>');
         var selected = firstSelected(document.getElementById("theselect"));
-        equals(selected.value, 'bar');
+        assert.equal(selected.value, 'bar');
     });
 
-    test("test first selected 2", function() {
+    QUnit.test("test first selected 2", function (assert) {
         $('#theselect').append('<option value="foo">foo</option>' +
     			     '<option value="bar">bar</option>' +
     			     '<option value="baz">baz</option>' +
     			     '<option value="spam">spam</option>');
         var selected = firstSelected(document.getElementById("theselect"));
-        equals(selected, null);
+        assert.equal(selected, null);
     });
 
-    module("visibilty");
-    test('toggleVisibility', function() {
-        $('#main').append('<div id="foo"></div>');
+    QUnit.module("visibilty");
+    QUnit.test('toggleVisibility', function (assert) {
+        $('#qunit-fixture').append('<div id="foo"></div>');
         toggleVisibility('foo');
-        ok($('#foo').hasClass('hidden'), 'check hidden class is set');
+        assert.ok($('#foo').hasClass('hidden'), 'check hidden class is set');
     });
 
 });
--- a/web/test/jstests/test_utils.js	Mon May 09 17:24:03 2016 +0200
+++ b/web/test/jstests/test_utils.js	Tue Jun 21 07:42:30 2016 +0200
@@ -1,66 +1,66 @@
 $(document).ready(function() {
 
-  module("datetime");
+  QUnit.module("datetime");
 
-  test("test full datetime", function() {
-      equals(cw.utils.toISOTimestamp(new Date(1986, 3, 18, 10, 30, 0, 0)),
+  QUnit.test("test full datetime", function (assert) {
+      assert.equal(cw.utils.toISOTimestamp(new Date(1986, 3, 18, 10, 30, 0, 0)),
 	     '1986-04-18 10:30:00');
   });
 
-  test("test only date", function() {
-      equals(cw.utils.toISOTimestamp(new Date(1986, 3, 18)), '1986-04-18 00:00:00');
+  QUnit.test("test only date", function (assert) {
+      assert.equal(cw.utils.toISOTimestamp(new Date(1986, 3, 18)), '1986-04-18 00:00:00');
   });
 
-  test("test null", function() {
-      equals(cw.utils.toISOTimestamp(null), null);
+  QUnit.test("test null", function (assert) {
+      assert.equal(cw.utils.toISOTimestamp(null), null);
   });
 
-  module("parsing");
-  test("test basic number parsing", function() {
+  QUnit.module("parsing");
+  QUnit.test("test basic number parsing", function (assert) {
       var d = strptime('2008/08/08', '%Y/%m/%d');
-      same(datetuple(d), [2008, 8, 8, 0, 0]);
+      assert.deepEqual(datetuple(d), [2008, 8, 8, 0, 0]);
       d = strptime('2008/8/8', '%Y/%m/%d');
-      same(datetuple(d), [2008, 8, 8, 0, 0]);
+      assert.deepEqual(datetuple(d), [2008, 8, 8, 0, 0]);
       d = strptime('8/8/8', '%Y/%m/%d');
-      same(datetuple(d), [8, 8, 8, 0, 0]);
+      assert.deepEqual(datetuple(d), [8, 8, 8, 0, 0]);
       d = strptime('0/8/8', '%Y/%m/%d');
-      same(datetuple(d), [0, 8, 8, 0, 0]);
+      assert.deepEqual(datetuple(d), [0, 8, 8, 0, 0]);
       d = strptime('-10/8/8', '%Y/%m/%d');
-      same(datetuple(d), [-10, 8, 8, 0, 0]);
+      assert.deepEqual(datetuple(d), [-10, 8, 8, 0, 0]);
       d = strptime('-35000', '%Y');
-      same(datetuple(d), [-35000, 1, 1, 0, 0]);
+      assert.deepEqual(datetuple(d), [-35000, 1, 1, 0, 0]);
   });
 
-  test("test custom format parsing", function() {
+  QUnit.test("test custom format parsing", function (assert) {
       var d = strptime('2008-08-08', '%Y-%m-%d');
-      same(datetuple(d), [2008, 8, 8, 0, 0]);
+      assert.deepEqual(datetuple(d), [2008, 8, 8, 0, 0]);
       d = strptime('2008 - !  08: 08', '%Y - !  %m: %d');
-      same(datetuple(d), [2008, 8, 8, 0, 0]);
+      assert.deepEqual(datetuple(d), [2008, 8, 8, 0, 0]);
       d = strptime('2008-08-08 12:14', '%Y-%m-%d %H:%M');
-      same(datetuple(d), [2008, 8, 8, 12, 14]);
+      assert.deepEqual(datetuple(d), [2008, 8, 8, 12, 14]);
       d = strptime('2008-08-08 1:14', '%Y-%m-%d %H:%M');
-      same(datetuple(d), [2008, 8, 8, 1, 14]);
+      assert.deepEqual(datetuple(d), [2008, 8, 8, 1, 14]);
       d = strptime('2008-08-08 01:14', '%Y-%m-%d %H:%M');
-      same(datetuple(d), [2008, 8, 8, 1, 14]);
+      assert.deepEqual(datetuple(d), [2008, 8, 8, 1, 14]);
   });
 
-  module("sliceList");
-  test("test slicelist", function() {
+  QUnit.module("sliceList");
+  QUnit.test("test slicelist", function (assert) {
       var list = ['a', 'b', 'c', 'd', 'e', 'f'];
-      same(cw.utils.sliceList(list, 2),  ['c', 'd', 'e', 'f']);
-      same(cw.utils.sliceList(list, 2, -2), ['c', 'd']);
-      same(cw.utils.sliceList(list, -3), ['d', 'e', 'f']);
-      same(cw.utils.sliceList(list, 0, -2), ['a', 'b', 'c', 'd']);
-      same(cw.utils.sliceList(list),  list);
+      assert.deepEqual(cw.utils.sliceList(list, 2),  ['c', 'd', 'e', 'f']);
+      assert.deepEqual(cw.utils.sliceList(list, 2, -2), ['c', 'd']);
+      assert.deepEqual(cw.utils.sliceList(list, -3), ['d', 'e', 'f']);
+      assert.deepEqual(cw.utils.sliceList(list, 0, -2), ['a', 'b', 'c', 'd']);
+      assert.deepEqual(cw.utils.sliceList(list),  list);
   });
 
-  module("formContents", {
+  QUnit.module("formContents", {
     setup: function() {
-      $('#main').append('<form id="test-form"></form>');
+      $('#qunit-fixture').append('<form id="test-form"></form>');
     }
   });
   // XXX test fckeditor
-  test("test formContents", function() {
+  QUnit.test("test formContents", function (assert) {
       $('#test-form').append('<input name="input-text" ' +
 			     'type="text" value="toto" />');
       $('#test-form').append('<textarea rows="10" cols="30" '+
@@ -83,7 +83,7 @@
 			     'value="one" />');
       $('#test-form').append('<input name="unchecked-choice" type="radio" ' +
 			     'value="two"/>');
-      same(cw.utils.formContents($('#test-form')[0]), [
+      assert.deepEqual(cw.utils.formContents($('#test-form')[0]), [
 	['input-text', 'mytextarea', 'choice', 'check', 'theselect'],
 	['toto', 'Hello World!', 'no', 'no', 'foo']
       ]);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/test/requirements.txt	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,6 @@
+requests
+webtest
+Twisted
+cubicweb-blog
+cubicweb-file
+cubicweb-tag
--- a/web/test/unittest_application.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/test/unittest_application.py	Tue Jun 21 07:42:30 2016 +0200
@@ -29,7 +29,6 @@
 from cubicweb.web import LogOut, Redirect, INTERNAL_FIELD_VALUE
 from cubicweb.web.views.basecontrollers import ViewController
 from cubicweb.web.application import anonymized_request
-from cubicweb.dbapi import DBAPISession, _NeedAuthAccessMock
 from cubicweb import repoapi
 
 class FakeMapping:
@@ -620,15 +619,11 @@
         req.set_request_header('Cookie', cookie[sessioncookie].OutputString(),
                                raw=True)
         clear_cache(req, 'get_authorization')
-        # reset session as if it was a new incoming request
-        req.session = DBAPISession(None)
-        req.user = req.cnx = _NeedAuthAccessMock
-        
 
     def _test_auth_anon(self, req):
         asession = self.app.get_session(req)
         # important otherwise _reset_cookie will not use the right session
-        req.set_cnx(repoapi.ClientConnection(asession))
+        req.set_cnx(repoapi.Connection(asession))
         self.assertEqual(len(self.open_sessions), 1)
         self.assertEqual(asession.login, 'anon')
         self.assertTrue(asession.anonymous_session)
@@ -638,7 +633,7 @@
         self.assertEqual(1, len(self.open_sessions))
         session = self.app.get_session(req)
         # important otherwise _reset_cookie will not use the right session
-        req.set_cnx(repoapi.ClientConnection(session))
+        req.set_cnx(repoapi.Connection(session))
         self.assertEqual(req.message, 'authentication failure')
         self.assertEqual(req.session.anonymous_session, True)
         self.assertEqual(1, len(self.open_sessions))
--- a/web/test/unittest_facet.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/test/unittest_facet.py	Tue Jun 21 07:42:30 2016 +0200
@@ -129,8 +129,6 @@
             self.assertEqual(f.select.as_string(),
                               'DISTINCT Any  WHERE X is CWUser')
 
-
-
     def test_relationattribute(self):
         with self.admin_access.web_request() as req:
             f, (guests, managers) = self._in_group_facet(req, cls=facet.RelationAttributeFacet)
@@ -150,6 +148,20 @@
             self.assertEqual(f.select.as_string(),
                               "DISTINCT Any  WHERE X is CWUser, X in_group E, E name 'guests'")
 
+    def test_hasrelation(self):
+        with self.admin_access.web_request() as req:
+            rset, rqlst, filtered_variable = self.prepare_rqlst(req)
+            f = facet.HasRelationFacet(req, rset=rset,
+                                       select=rqlst.children[0],
+                                       filtered_variable=filtered_variable)
+            f.__regid__ = 'has_group'
+            f.rtype = 'in_group'
+            f.role = 'subject'
+            f._cw.form[f.__regid__] = 'feed me'
+            f.add_rql_restrictions()
+            self.assertEqual(f.select.as_string(),
+                             'DISTINCT Any  WHERE X is CWUser, EXISTS(X in_group A)')
+
     def test_daterange(self):
         with self.admin_access.web_request() as req:
             rset, rqlst, filtered_variable = self.prepare_rqlst(req)
--- a/web/test/unittest_form.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/test/unittest_form.py	Tue Jun 21 07:42:30 2016 +0200
@@ -21,7 +21,7 @@
 from xml.etree.ElementTree import fromstring
 from lxml import html
 
-from logilab.common.testlib import unittest_main, mock_object
+from logilab.common.testlib import unittest_main
 
 from cubicweb import Binary, ValidationError
 from cubicweb.devtools.testlib import CubicWebTC
@@ -39,7 +39,7 @@
     def test_form_field_format(self):
         with self.admin_access.web_request() as req:
             form = FieldsForm(req, None)
-            self.assertEqual(StringField().format(form), 'text/html')
+            self.assertEqual(StringField().format(form), 'text/plain')
             req.cnx.execute('INSERT CWProperty X: X pkey "ui.default-text-format", X value "text/rest", X for_user U WHERE U login "admin"')
             req.cnx.commit()
             self.assertEqual(StringField().format(form), 'text/rest')
--- a/web/test/unittest_formfields.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/test/unittest_formfields.py	Tue Jun 21 07:42:30 2016 +0200
@@ -150,7 +150,7 @@
             self.assertEqual(description_format_field.internationalizable, True)
             self.assertEqual(description_format_field.sort, True)
             # unlike below, initial is bound to form.form_field_format
-            self.assertEqual(description_format_field.value(form), 'text/html')
+            self.assertEqual(description_format_field.value(form), 'text/plain')
             req.cnx.execute('INSERT CWProperty X: X pkey "ui.default-text-format", X value "text/rest", X for_user U WHERE U login "admin"')
             req.cnx.commit()
             self.assertEqual(description_format_field.value(form), 'text/rest')
--- a/web/test/unittest_propertysheet.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/test/unittest_propertysheet.py	Tue Jun 21 07:42:30 2016 +0200
@@ -1,23 +1,37 @@
 import os
 from os.path import join, dirname
 from shutil import rmtree
-
-from logilab.common.testlib import TestCase, unittest_main
+import errno
+import tempfile
+from unittest import TestCase, main
 
 from cubicweb.web.propertysheet import PropertySheet, lazystr
 
+
 DATADIR = join(dirname(__file__), 'data')
-CACHEDIR = join(DATADIR, 'uicache')
+
 
 class PropertySheetTC(TestCase):
 
+    def setUp(self):
+        uicache = join(DATADIR, 'uicache')
+        try:
+            os.makedirs(uicache)
+        except OSError as err:
+            if err.errno != errno.EEXIST:
+                raise
+        self.cachedir = tempfile.mkdtemp(dir=uicache)
+
     def tearDown(self):
-        rmtree(CACHEDIR)
+        rmtree(self.cachedir)
+
+    def data(self, filename):
+        return join(DATADIR, filename)
 
     def test(self):
-        ps = PropertySheet(CACHEDIR, datadir_url='http://cwtest.com')
-        ps.load(join(DATADIR, 'sheet1.py'))
-        ps.load(join(DATADIR, 'sheet2.py'))
+        ps = PropertySheet(self.cachedir, datadir_url='http://cwtest.com')
+        ps.load(self.data('sheet1.py'))
+        ps.load(self.data('sheet2.py'))
         # defined by sheet1
         self.assertEqual(ps['logo'], 'http://cwtest.com/logo.png')
         # defined by sheet1, overriden by sheet2
@@ -34,10 +48,10 @@
         self.assertEqual(ps.compile('a {bgcolor: %(bgcolor)s; size: 1%;}'),
                           'a {bgcolor: #FFFFFF; size: 1%;}')
         self.assertEqual(ps.process_resource(DATADIR, 'pouet.css'),
-                          CACHEDIR)
+                         self.cachedir)
         self.assertIn('pouet.css', ps._cache)
         self.assertFalse(ps.need_reload())
-        os.utime(join(DATADIR, 'sheet1.py'), None)
+        os.utime(self.data('sheet1.py'), None)
         self.assertIn('pouet.css', ps._cache)
         self.assertTrue(ps.need_reload())
         self.assertIn('pouet.css', ps._cache)
@@ -45,9 +59,10 @@
         self.assertNotIn('pouet.css', ps._cache)
         self.assertFalse(ps.need_reload())
         ps.process_resource(DATADIR, 'pouet.css') # put in cache
-        os.utime(join(DATADIR, 'pouet.css'), None)
+        os.utime(self.data('pouet.css'), None)
         self.assertFalse(ps.need_reload())
         self.assertNotIn('pouet.css', ps._cache)
 
+
 if __name__ == '__main__':
-    unittest_main()
+    main()
--- a/web/test/unittest_reledit.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/test/unittest_reledit.py	Tue Jun 21 07:42:30 2016 +0200
@@ -53,7 +53,7 @@
 
     def test_default_forms(self):
         self.skipTest('Need to check if this test should still run post reledit/doreledit merge')
-        doreledit = {'title': """<div id="title-subject-%(eid)s-reledit" onmouseout="jQuery('#title-subject-%(eid)s').addClass('invisible')" onmouseover="jQuery('#title-subject-%(eid)s').removeClass('invisible')" class="releditField"><div id="title-subject-%(eid)s-value" class="editableFieldValue">cubicweb-world-domination</div><form action="http://testing.fr/cubicweb/validateform?__onsuccess=window.parent.cw.reledit.onSuccess" method="post" enctype="application/x-www-form-urlencoded" id="title-subject-%(eid)s-form" onsubmit="return freezeFormButtons(&#39;title-subject-%(eid)s-form&#39;);" class="releditForm" cubicweb:target="eformframe">
+        doreledit = {'title': """<div id="title-subject-%(eid)s-reledit" onmouseout="jQuery('#title-subject-%(eid)s').addClass('invisible')" onmouseover="jQuery('#title-subject-%(eid)s').removeClass('invisible')" class="releditField"><div id="title-subject-%(eid)s-value" class="editableFieldValue">cubicweb-world-domination</div><form action="http://testing.fr/cubicweb/validateform?__onsuccess=window.parent.cw.reledit.onSuccess" method="post" enctype="application/x-www-form-urlencoded" id="title-subject-%(eid)s-form" onsubmit="return freezeFormButtons(&#39;title-subject-%(eid)s-form&#39;);" class="releditForm" target="eformframe">
 <fieldset>
 <input name="__form_id" type="hidden" value="base" />
 <input name="__errorurl" type="hidden" value="http://testing.fr/cubicweb/view?rql=Blop&amp;vid=blop#title-subject-%(eid)s-form" />
@@ -82,9 +82,10 @@
 <td><button class="validateButton" onclick="cw.reledit.cleanupAfterCancel(&#39;title-subject-%(eid)s&#39;)" tabindex="3" type="button" value="button_cancel"><img alt="CANCEL_ICON" src="http://testing.fr/cubicweb/data/cancel.png" />button_cancel</button></td>
 </tr></table>
 </fieldset>
+<iframe width="0px" height="0px" src="javascript: void(0);" name="eformframe" id="eformframe"></iframe>
 </form><div id="title-subject-%(eid)s" class="editableField invisible"><div id="title-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;base&#39;, %(eid)s, &#39;title&#39;, &#39;subject&#39;, &#39;title-subject-%(eid)s&#39;, false, &#39;&#39;);" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
 
-                     'long_desc': """<div id="long_desc-subject-%(eid)s-reledit" onmouseout="jQuery('#long_desc-subject-%(eid)s').addClass('invisible')" onmouseover="jQuery('#long_desc-subject-%(eid)s').removeClass('invisible')" class="releditField"><div id="long_desc-subject-%(eid)s-value" class="editableFieldValue">&lt;not specified&gt;</div><form action="http://testing.fr/cubicweb/validateform?__onsuccess=window.parent.cw.reledit.onSuccess" method="post" enctype="application/x-www-form-urlencoded" id="long_desc-subject-%(eid)s-form" onsubmit="return freezeFormButtons(&#39;long_desc-subject-%(eid)s-form&#39;);" class="releditForm" cubicweb:target="eformframe">
+                     'long_desc': """<div id="long_desc-subject-%(eid)s-reledit" onmouseout="jQuery('#long_desc-subject-%(eid)s').addClass('invisible')" onmouseover="jQuery('#long_desc-subject-%(eid)s').removeClass('invisible')" class="releditField"><div id="long_desc-subject-%(eid)s-value" class="editableFieldValue">&lt;not specified&gt;</div><form action="http://testing.fr/cubicweb/validateform?__onsuccess=window.parent.cw.reledit.onSuccess" method="post" enctype="application/x-www-form-urlencoded" id="long_desc-subject-%(eid)s-form" onsubmit="return freezeFormButtons(&#39;long_desc-subject-%(eid)s-form&#39;);" class="releditForm" target="eformframe">
 <fieldset>
 <input name="__form_id" type="invisible" value="edition" />
 <input name="__errorurl" type="invisible" value="http://testing.fr/cubicweb/view?rql=Blop&amp;vid=blop#long_desc-subject-%(eid)s-form" />
@@ -126,9 +127,10 @@
 <td><button class="validateButton" onclick="cw.reledit.cleanupAfterCancel(&#39;long_desc-subject-%(eid)s&#39;)" tabindex="8" type="button" value="button_cancel"><img alt="CANCEL_ICON" src="http://testing.fr/cubicweb/data/cancel.png" />button_cancel</button></td>
 </tr></table>
 </fieldset>
+<iframe width="0px" height="0px" src="javascript: void(0);" name="eformframe" id="eformframe"></iframe>
 </form><div id="long_desc-subject-%(eid)s" class="editableField invisible"><div id="long_desc-subject-%(eid)s-add" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;edition&#39;, %(eid)s, &#39;long_desc&#39;, &#39;subject&#39;, &#39;long_desc-subject-%(eid)s&#39;, false, &#39;autolimited&#39;);" title="click to add a value"><img title="click to add a value" src="http://testing.fr/cubicweb/data/plus.png" alt="click to add a value"/></div></div></div>""",
 
-                     'manager': """<div id="manager-subject-%(eid)s-reledit" onmouseout="jQuery('#manager-subject-%(eid)s').addClass('invisible')" onmouseover="jQuery('#manager-subject-%(eid)s').removeClass('invisible')" class="releditField"><div id="manager-subject-%(eid)s-value" class="editableFieldValue">&lt;not specified&gt;</div><form action="http://testing.fr/cubicweb/validateform?__onsuccess=window.parent.cw.reledit.onSuccess" method="post" enctype="application/x-www-form-urlencoded" id="manager-subject-%(eid)s-form" onsubmit="return freezeFormButtons(&#39;manager-subject-%(eid)s-form&#39;);" class="releditForm" cubicweb:target="eformframe">
+                     'manager': """<div id="manager-subject-%(eid)s-reledit" onmouseout="jQuery('#manager-subject-%(eid)s').addClass('invisible')" onmouseover="jQuery('#manager-subject-%(eid)s').removeClass('invisible')" class="releditField"><div id="manager-subject-%(eid)s-value" class="editableFieldValue">&lt;not specified&gt;</div><form action="http://testing.fr/cubicweb/validateform?__onsuccess=window.parent.cw.reledit.onSuccess" method="post" enctype="application/x-www-form-urlencoded" id="manager-subject-%(eid)s-form" onsubmit="return freezeFormButtons(&#39;manager-subject-%(eid)s-form&#39;);" class="releditForm" target="eformframe">
 <fieldset>
 <input name="__form_id" type="hidden" value="base" />
 <input name="__errorurl" type="hidden" value="http://testing.fr/cubicweb/view?rql=Blop&amp;vid=blop#manager-subject-%(eid)s-form" />
@@ -162,6 +164,7 @@
 <td><button class="validateButton" onclick="cw.reledit.cleanupAfterCancel(&#39;manager-subject-%(eid)s&#39;)" tabindex="11" type="button" value="button_cancel"><img alt="CANCEL_ICON" src="http://testing.fr/cubicweb/data/cancel.png" />button_cancel</button></td>
 </tr></table>
 </fieldset>
+<iframe width="0px" height="0px" src="javascript: void(0);" name="eformframe" id="eformframe"></iframe>
 </form><div id="manager-subject-%(eid)s" class="editableField invisible"><div id="manager-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;base&#39;, %(eid)s, &#39;manager&#39;, &#39;subject&#39;, &#39;manager-subject-%(eid)s&#39;, false, &#39;autolimited&#39;);" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
                      'composite_card11_2ttypes': """&lt;not specified&gt;""",
                      'concerns': """&lt;not specified&gt;"""
--- a/web/test/unittest_urlpublisher.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/test/unittest_urlpublisher.py	Tue Jun 21 07:42:30 2016 +0200
@@ -66,8 +66,8 @@
             ctrl, rset = self.process(req, 'CWEType')
             self.assertEqual(ctrl, 'view')
             self.assertEqual(rset.description[0][0], 'CWEType')
-            self.assertEqual("Any X,AA,AB ORDERBY AA WHERE X is_instance_of CWEType, "
-                             "X name AA, X modification_date AB",
+            self.assertEqual("Any X,AA,AB ORDERBY AB WHERE X is_instance_of CWEType, "
+                             "X modification_date AA, X name AB",
                              rset.printable_rql())
 
     def test_rest_path_by_attr(self):
@@ -77,8 +77,8 @@
             self.assertEqual(len(rset), 1)
             self.assertEqual(rset.description[0][0], 'CWUser')
             self.assertEqual('Any X,AA,AB,AC,AD WHERE X is_instance_of CWUser, '
-                             'X login AA, X firstname AB, X surname AC, '
-                             'X modification_date AD, X login "admin"',
+                             'X firstname AA, X login AB, X modification_date AC, '
+                             'X surname AD, X login "admin"',
                              rset.printable_rql())
 
     def test_rest_path_unique_attr(self):
@@ -88,8 +88,8 @@
             self.assertEqual(len(rset), 1)
             self.assertEqual(rset.description[0][0], 'CWUser')
             self.assertEqual('Any X,AA,AB,AC,AD WHERE X is_instance_of CWUser, '
-                             'X login AA, X firstname AB, X surname AC, '
-                             'X modification_date AD, X login "admin"',
+                             'X firstname AA, X login AB, X modification_date AC, '
+                             'X surname AD, X login "admin"',
                              rset.printable_rql())
 
     def test_rest_path_eid(self):
@@ -99,8 +99,8 @@
             self.assertEqual(len(rset), 1)
             self.assertEqual(rset.description[0][0], 'CWUser')
             self.assertEqual('Any X,AA,AB,AC,AD WHERE X is_instance_of CWUser, '
-                             'X login AA, X firstname AB, X surname AC, '
-                             'X modification_date AD, X eid %s' % rset[0][0],
+                             'X firstname AA, X login AB, X modification_date AC, '
+                             'X surname AD, X eid %s' % rset[0][0],
                              rset.printable_rql())
 
     def test_rest_path_non_ascii_paths(self):
@@ -110,8 +110,8 @@
             self.assertEqual(len(rset), 1)
             self.assertEqual(rset.description[0][0], 'CWUser')
             self.assertEqual(u'Any X,AA,AB,AC,AD WHERE X is_instance_of CWUser, '
-                             u'X login AA, X firstname AB, X surname AC, '
-                             u'X modification_date AD, X login "\xffsa\xffe"',
+                             u'X firstname AA, X login AB, X modification_date AC, '
+                             u'X surname AD, X login "\xffsa\xffe"',
                              rset.printable_rql())
 
     def test_rest_path_quoted_paths(self):
@@ -121,7 +121,7 @@
             self.assertEqual(len(rset), 1)
             self.assertEqual(rset.description[0][0], 'BlogEntry')
             self.assertEqual(u'Any X,AA,AB,AC WHERE X is_instance_of BlogEntry, '
-                             'X creation_date AA, X title AB, X modification_date AC, '
+                             'X creation_date AA, X modification_date AB, X title AC, '
                              'X title "hell\'o"',
                              rset.printable_rql())
 
--- a/web/test/unittest_views_basecontrollers.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/test/unittest_views_basecontrollers.py	Tue Jun 21 07:42:30 2016 +0200
@@ -156,9 +156,9 @@
 
     def test_user_can_change_its_password(self):
         with self.admin_access.repo_cnx() as cnx:
-            self.create_user(cnx, 'user')
+            self.create_user(cnx, u'user')
             cnx.commit()
-        with self.new_access('user').web_request() as req:
+        with self.new_access(u'user').web_request() as req:
             eid = unicode(req.user.eid)
             req.form = {
                 'eid': eid, '__maineid' : eid,
@@ -287,7 +287,7 @@
 
     def test_edit_multiple_linked(self):
         with self.admin_access.web_request() as req:
-            peid = unicode(self.create_user(req, 'adim').eid)
+            peid = unicode(self.create_user(req, u'adim').eid)
             req.form = {'eid': [peid, 'Y'], '__maineid': peid,
 
                         '__type:'+peid: u'CWUser',
@@ -548,7 +548,7 @@
             self.assertIn('_cwmsgid', params)
             eid = req.create_entity('EmailAddress', address=u'hop@logilab.fr').eid
             req.execute('SET X use_email E WHERE E eid %(e)s, X eid %(x)s',
-                        {'x': self.session.user.eid, 'e': eid})
+                        {'x': req.user.eid, 'e': eid})
             req.cnx.commit()
             req.form = {'eid': unicode(eid), '__type:%s'%eid: 'EmailAddress',
                         '__action_delete': ''}
@@ -692,7 +692,7 @@
 
     def test_nonregr_rollback_on_validation_error(self):
         with self.admin_access.web_request() as req:
-            p = self.create_user(req, "doe")
+            p = self.create_user(req, u"doe")
             # do not try to skip 'primary_email' for this test
             old_skips = p.__class__.skip_copy_for
             p.__class__.skip_copy_for = ()
@@ -754,10 +754,10 @@
 class ReportBugControllerTC(CubicWebTC):
 
     def test_usable_by_guest(self):
-        with self.new_access('anon').web_request() as req:
+        with self.new_access(u'anon').web_request() as req:
             self.assertRaises(NoSelectableObject,
                               self.vreg['controllers'].select, 'reportbug', req)
-        with self.new_access('anon').web_request(description='hop') as req:
+        with self.new_access(u'anon').web_request(description='hop') as req:
             self.vreg['controllers'].select('reportbug', req)
 
 
@@ -836,9 +836,9 @@
             deletes = get_pending_deletes(req)
             self.assertEqual(deletes, [])
             inserts = get_pending_inserts(req)
-            self.assertEqual(inserts, ['12:tags:13', '12:tags:14'])
+            self.assertCountEqual(inserts, ['12:tags:13', '12:tags:14'])
             inserts = get_pending_inserts(req, 12)
-            self.assertEqual(inserts, ['12:tags:13', '12:tags:14'])
+            self.assertCountEqual(inserts, ['12:tags:13', '12:tags:14'])
             inserts = get_pending_inserts(req, 13)
             self.assertEqual(inserts, ['12:tags:13'])
             inserts = get_pending_inserts(req, 14)
@@ -855,9 +855,9 @@
             inserts = get_pending_inserts(req)
             self.assertEqual(inserts, [])
             deletes = get_pending_deletes(req)
-            self.assertEqual(deletes, ['12:tags:13', '12:tags:14'])
+            self.assertCountEqual(deletes, ['12:tags:13', '12:tags:14'])
             deletes = get_pending_deletes(req, 12)
-            self.assertEqual(deletes, ['12:tags:13', '12:tags:14'])
+            self.assertCountEqual(deletes, ['12:tags:13', '12:tags:14'])
             deletes = get_pending_deletes(req, 13)
             self.assertEqual(deletes, ['12:tags:13'])
             deletes = get_pending_deletes(req, 14)
@@ -880,7 +880,7 @@
         with self.remote_calling('add_pending_inserts',
                                  [('12', 'tags', '13'), ('12', 'tags', '14')]) as (_, req):
             inserts = get_pending_inserts(req)
-            self.assertEqual(inserts, ['12:tags:13', '12:tags:14'])
+            self.assertCountEqual(inserts, ['12:tags:13', '12:tags:14'])
             req.remove_pending_operations()
 
 
@@ -1016,8 +1016,8 @@
 
     def setup_database(self):
         with self.admin_access.repo_cnx() as cnx:
-            self.toto = self.create_user(cnx, 'toto',
-                                         password='toto',
+            self.toto = self.create_user(cnx, u'toto',
+                                         password=u'toto',
                                          groups=('users',),
                                          commit=False)
             self.txuuid_toto = cnx.commit()
--- a/web/test/unittest_views_editforms.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/test/unittest_views_editforms.py	Tue Jun 21 07:42:30 2016 +0200
@@ -43,7 +43,7 @@
         with self.admin_access.web_request() as req:
             AFFK.tag_subject_of(('CWUser', 'login', '*'),
                                 {'widget': AutoCompletionWidget(autocomplete_initfunc='get_logins')})
-            form = self.vreg['forms'].select('edition', req, entity=self.user(req))
+            form = self.vreg['forms'].select('edition', req, entity=req.user)
             field = form.field_by_name('login', 'subject')
             self.assertIsInstance(field.widget, AutoCompletionWidget)
             AFFK.del_rtag('CWUser', 'login', '*', 'subject')
@@ -54,18 +54,18 @@
             e = self.vreg['etypes'].etype_class('CWUser')(req)
             # see custom configuration in views.cwuser
             self.assertEqual(rbc(e, 'main', 'attributes'),
-                              [('login', 'subject'),
-                               ('upassword', 'subject'),
-                               ('firstname', 'subject'),
-                               ('surname', 'subject'),
-                               ('in_group', 'subject'),
-                               ])
-            self.assertListEqual(rbc(e, 'muledit', 'attributes'),
+                             [('login', 'subject'),
+                              ('upassword', 'subject'),
+                              ('firstname', 'subject'),
+                              ('surname', 'subject'),
+                              ('in_group', 'subject'),
+                              ])
+            self.assertEqual(rbc(e, 'muledit', 'attributes'),
                                   [('login', 'subject'),
                                    ('upassword', 'subject'),
                                    ('in_group', 'subject'),
                                    ])
-            self.assertListEqual(rbc(e, 'main', 'metadata'),
+            self.assertCountEqual(rbc(e, 'main', 'metadata'),
                                   [('last_login_time', 'subject'),
                                    ('cw_source', 'subject'),
                                    ('creation_date', 'subject'),
@@ -77,7 +77,7 @@
             # XXX skip 'tags' relation here and in the hidden category because
             # of some test interdependancy when pytest is launched on whole cw
             # (appears here while expected in hidden
-            self.assertListEqual([x for x in rbc(e, 'main', 'relations')
+            self.assertCountEqual([x for x in rbc(e, 'main', 'relations')
                                    if x != ('tags', 'object')],
                                   [('connait', 'subject'),
                                    ('custom_workflow', 'subject'),
@@ -125,14 +125,14 @@
             self.assertListEqual(rbc(e, 'muledit', 'attributes'),
                                   [('nom', 'subject'),
                                    ])
-            self.assertListEqual(rbc(e, 'main', 'metadata'),
+            self.assertCountEqual(rbc(e, 'main', 'metadata'),
                                   [('cw_source', 'subject'),
                                    ('creation_date', 'subject'),
                                    ('modification_date', 'subject'),
                                    ('created_by', 'subject'),
                                    ('owned_by', 'subject'),
                                    ])
-            self.assertListEqual(rbc(e, 'main', 'relations'),
+            self.assertCountEqual(rbc(e, 'main', 'relations'),
                                   [('travaille', 'subject'),
                                    ('manager', 'object'),
                                    ('connait', 'object'),
@@ -158,15 +158,15 @@
     def test_attribute_add_permissions(self):
         # https://www.cubicweb.org/ticket/4342844
         with self.admin_access.repo_cnx() as cnx:
-            self.create_user(cnx, 'toto')
+            self.create_user(cnx, u'toto')
             cnx.commit()
-        with self.new_access('toto').web_request() as req:
+        with self.new_access(u'toto').web_request() as req:
             e = self.vreg['etypes'].etype_class('Personne')(req)
             cform = self.vreg['forms'].select('edition', req, entity=e)
             self.assertIn('sexe',
                           [rschema.type
                            for rschema, _ in cform.editable_attributes()])
-            with self.new_access('toto').repo_cnx() as cnx:
+            with self.new_access(u'toto').repo_cnx() as cnx:
                 person_eid = cnx.create_entity('Personne', nom=u'Robert').eid
                 cnx.commit()
             person = req.entity_from_eid(person_eid)
--- a/web/test/unittest_views_xmlrss.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/test/unittest_views_xmlrss.py	Tue Jun 21 07:42:30 2016 +0200
@@ -11,13 +11,13 @@
                 req.user.view('xml'),
                 '''\
 <CWUser eid="6" cwuri="http://testing.fr/cubicweb/6" cwsource="system">
+  <creation_date>%(cdate)s</creation_date>
+  <firstname/>
+  <last_login_time/>
   <login>admin</login>
-  <upassword/>
-  <firstname/>
+  <modification_date>%(mdate)s</modification_date>
   <surname/>
-  <last_login_time/>
-  <creation_date>%(cdate)s</creation_date>
-  <modification_date>%(mdate)s</modification_date>
+  <upassword/>
   <tags role="object">
   </tags>
   <in_group role="subject">
--- a/web/test/unittest_viewselector.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/test/unittest_viewselector.py	Tue Jun 21 07:42:30 2016 +0200
@@ -33,7 +33,6 @@
                                 cwproperties, cwsources, xmlrss, rdf, csvexport, json,
                                 undohistory)
 
-from cubes.folder import views as folderviews
 
 USERACTIONS = [actions.UserPreferencesAction,
                actions.UserInfoAction,
@@ -49,7 +48,7 @@
                  debug.SiteInfoAction]
 
 if hasattr(rdf, 'RDFView'): # not available if rdflib not installed
-    RDFVIEWS = [('rdf', rdf.RDFView)]
+    RDFVIEWS = [('rdf', rdf.RDFView), ('n3rdf', rdf.RDFN3View)]
 else:
     RDFVIEWS = []
 
@@ -101,7 +100,6 @@
                                   ('schema', schema.SchemaView),
                                   ('siteinfo', debug.SiteInfoView),
                                   ('systempropertiesform', cwproperties.SystemCWPropertiesForm),
-                                  ('tree', folderviews.FolderTreeView),
                                   ('undohistory', undohistory.UndoHistoryView)])
 
     def test_possible_views_noresult(self):
@@ -117,50 +115,51 @@
     def test_possible_views_one_egroup(self):
         with self.admin_access.web_request() as req:
             rset = req.execute('CWGroup X WHERE X name "managers"')
-            self.assertListEqual(self.pviews(req, rset),
-                                 [('csvexport', csvexport.CSVRsetView),
-                                  ('ecsvexport', csvexport.CSVEntityView),
-                                  ('ejsonexport', json.JsonEntityView),
-                                  ('filetree', treeview.FileTreeView),
-                                  ('jsonexport', json.JsonRsetView),
-                                  ('list', baseviews.ListView),
-                                  ('oneline', baseviews.OneLineView),
-                                  ('owlabox', owl.OWLABOXView),
-                                  ('primary', cwuser.CWGroupPrimaryView)] + \
-                                 RDFVIEWS + \
-                                 [('rsetxml', xmlrss.XMLRsetView),
-                                  ('rss', xmlrss.RSSView),
-                                  ('sameetypelist', baseviews.SameETypeListView),
-                                  ('security', management.SecurityManagementView),
-                                  ('table', tableview.RsetTableView),
-                                  ('text', baseviews.TextView),
-                                  ('treeview', treeview.TreeView),
-                                  ('xbel', xbel.XbelView),
-                                  ('xml', xmlrss.XMLView)])
+            self.assertCountEqual(self.pviews(req, rset),
+                                  RDFVIEWS +
+                                  [('csvexport', csvexport.CSVRsetView),
+                                   ('ecsvexport', csvexport.CSVEntityView),
+                                   ('ejsonexport', json.JsonEntityView),
+                                   ('filetree', treeview.FileTreeView),
+                                   ('jsonexport', json.JsonRsetView),
+                                   ('list', baseviews.ListView),
+                                   ('oneline', baseviews.OneLineView),
+                                   ('owlabox', owl.OWLABOXView),
+                                   ('primary', cwuser.CWGroupPrimaryView),
+                                   ('rsetxml', xmlrss.XMLRsetView),
+                                   ('rss', xmlrss.RSSView),
+                                   ('sameetypelist', baseviews.SameETypeListView),
+                                   ('security', management.SecurityManagementView),
+                                   ('table', tableview.RsetTableView),
+                                   ('text', baseviews.TextView),
+                                   ('treeview', treeview.TreeView),
+                                   ('xbel', xbel.XbelView),
+                                   ('xml', xmlrss.XMLView)])
 
     def test_possible_views_multiple_egroups(self):
         with self.admin_access.web_request() as req:
             rset = req.execute('CWGroup X')
-            self.assertListEqual(self.pviews(req, rset),
-                                 [('csvexport', csvexport.CSVRsetView),
-                                  ('ecsvexport', csvexport.CSVEntityView),
-                                  ('ejsonexport', json.JsonEntityView),
-                                  ('filetree', treeview.FileTreeView),
-                                  ('jsonexport', json.JsonRsetView),
-                                  ('list', baseviews.ListView),
-                                  ('oneline', baseviews.OneLineView),
-                                  ('owlabox', owl.OWLABOXView),
-                                  ('primary', cwuser.CWGroupPrimaryView)] + RDFVIEWS + [
-                                  ('rsetxml', xmlrss.XMLRsetView),
-                                  ('rss', xmlrss.RSSView),
-                                  ('sameetypelist', baseviews.SameETypeListView),
-                                  ('security', management.SecurityManagementView),
-                                  ('table', tableview.RsetTableView),
-                                  ('text', baseviews.TextView),
-                                  ('treeview', treeview.TreeView),
-                                  ('xbel', xbel.XbelView),
-                                  ('xml', xmlrss.XMLView),
-                                  ])
+            self.assertCountEqual(self.pviews(req, rset),
+                                  RDFVIEWS +
+                                  [('csvexport', csvexport.CSVRsetView),
+                                   ('ecsvexport', csvexport.CSVEntityView),
+                                   ('ejsonexport', json.JsonEntityView),
+                                   ('filetree', treeview.FileTreeView),
+                                   ('jsonexport', json.JsonRsetView),
+                                   ('list', baseviews.ListView),
+                                   ('oneline', baseviews.OneLineView),
+                                   ('owlabox', owl.OWLABOXView),
+                                   ('primary', cwuser.CWGroupPrimaryView),
+                                   ('rsetxml', xmlrss.XMLRsetView),
+                                   ('rss', xmlrss.RSSView),
+                                   ('sameetypelist', baseviews.SameETypeListView),
+                                   ('security', management.SecurityManagementView),
+                                   ('table', tableview.RsetTableView),
+                                   ('text', baseviews.TextView),
+                                   ('treeview', treeview.TreeView),
+                                   ('xbel', xbel.XbelView),
+                                   ('xml', xmlrss.XMLView),
+                                   ])
 
     def test_propertiesform_admin(self):
         assert self.vreg['views']['propertiesform']
@@ -172,7 +171,7 @@
             self.assertTrue(self.vreg['views'].select('propertiesform', req, rset=rset2))
 
     def test_propertiesform_anon(self):
-        with self.new_access('anon').web_request() as req:
+        with self.new_access(u'anon').web_request() as req:
             rset1 = req.execute('CWUser X WHERE X login "admin"')
             self.assertRaises(NoSelectableObject, self.vreg['views'].select, 'propertiesform', req, rset=None)
             self.assertRaises(NoSelectableObject, self.vreg['views'].select, 'propertiesform', req, rset=rset1)
@@ -181,9 +180,9 @@
 
     def test_propertiesform_jdoe(self):
         with self.admin_access.repo_cnx() as cnx:
-            self.create_user(cnx, 'jdoe')
+            self.create_user(cnx, u'jdoe')
             cnx.commit()
-        with self.new_access('jdoe').web_request() as req:
+        with self.new_access(u'jdoe').web_request() as req:
             rset1 = req.execute('CWUser X WHERE X login "admin"')
             rset2 = req.execute('CWUser X WHERE X login "jdoe"')
             self.assertTrue(self.vreg['views'].select('propertiesform', req, rset=None))
@@ -193,24 +192,25 @@
     def test_possible_views_multiple_different_types(self):
         with self.admin_access.web_request() as req:
             rset = req.execute('Any X')
-            self.assertListEqual(self.pviews(req, rset),
-                                 [('csvexport', csvexport.CSVRsetView),
-                                  ('ecsvexport', csvexport.CSVEntityView),
-                                  ('ejsonexport', json.JsonEntityView),
-                                  ('filetree', treeview.FileTreeView),
-                                  ('jsonexport', json.JsonRsetView),
-                                  ('list', baseviews.ListView),
-                                  ('oneline', baseviews.OneLineView),
-                                  ('owlabox', owl.OWLABOXView),
-                                  ('primary', primary.PrimaryView),] + RDFVIEWS + [
-                                  ('rsetxml', xmlrss.XMLRsetView),
-                                  ('rss', xmlrss.RSSView),
-                                  ('security', management.SecurityManagementView),
-                                  ('table', tableview.RsetTableView),
-                                  ('text', baseviews.TextView),
-                                  ('treeview', treeview.TreeView),
-                                  ('xbel', xbel.XbelView),
-                                  ('xml', xmlrss.XMLView),
+            self.assertCountEqual(self.pviews(req, rset),
+                                  RDFVIEWS +
+                                  [('csvexport', csvexport.CSVRsetView),
+                                   ('ecsvexport', csvexport.CSVEntityView),
+                                   ('ejsonexport', json.JsonEntityView),
+                                   ('filetree', treeview.FileTreeView),
+                                   ('jsonexport', json.JsonRsetView),
+                                   ('list', baseviews.ListView),
+                                   ('oneline', baseviews.OneLineView),
+                                   ('owlabox', owl.OWLABOXView),
+                                   ('primary', primary.PrimaryView),
+                                   ('rsetxml', xmlrss.XMLRsetView),
+                                   ('rss', xmlrss.RSSView),
+                                   ('security', management.SecurityManagementView),
+                                   ('table', tableview.RsetTableView),
+                                   ('text', baseviews.TextView),
+                                   ('treeview', treeview.TreeView),
+                                   ('xbel', xbel.XbelView),
+                                   ('xml', xmlrss.XMLView),
                                   ])
 
     def test_possible_views_any_rset(self):
@@ -226,28 +226,29 @@
     def test_possible_views_multiple_eusers(self):
         with self.admin_access.web_request() as req:
             rset = req.execute('CWUser X')
-            self.assertListEqual(self.pviews(req, rset),
-                                 [('csvexport', csvexport.CSVRsetView),
-                                  ('ecsvexport', csvexport.CSVEntityView),
-                                  ('ejsonexport', json.JsonEntityView),
-                                  ('filetree', treeview.FileTreeView),
-                                  ('foaf', cwuser.FoafView),
-                                  ('jsonexport', json.JsonRsetView),
-                                  ('list', baseviews.ListView),
-                                  ('oneline', baseviews.OneLineView),
-                                  ('owlabox', owl.OWLABOXView),
-                                  ('primary', primary.PrimaryView)] + RDFVIEWS + [
-                                  ('rsetxml', xmlrss.XMLRsetView),
-                                  ('rss', xmlrss.RSSView),
-                                  ('sameetypelist', baseviews.SameETypeListView),
-                                  ('security', management.SecurityManagementView),
-                                  ('table', tableview.RsetTableView),
-                                  ('text', baseviews.TextView),
-                                  ('treeview', treeview.TreeView),
-                                  ('vcard', vcard.VCardCWUserView),
-                                  ('xbel', xbel.XbelView),
-                                  ('xml', xmlrss.XMLView),
-                                  ])
+            self.assertCountEqual(self.pviews(req, rset),
+                                  RDFVIEWS +
+                                  [('csvexport', csvexport.CSVRsetView),
+                                   ('ecsvexport', csvexport.CSVEntityView),
+                                   ('ejsonexport', json.JsonEntityView),
+                                   ('filetree', treeview.FileTreeView),
+                                   ('foaf', cwuser.FoafView),
+                                   ('jsonexport', json.JsonRsetView),
+                                   ('list', baseviews.ListView),
+                                   ('oneline', baseviews.OneLineView),
+                                   ('owlabox', owl.OWLABOXView),
+                                   ('primary', primary.PrimaryView),
+                                   ('rsetxml', xmlrss.XMLRsetView),
+                                   ('rss', xmlrss.RSSView),
+                                   ('sameetypelist', baseviews.SameETypeListView),
+                                   ('security', management.SecurityManagementView),
+                                   ('table', tableview.RsetTableView),
+                                   ('text', baseviews.TextView),
+                                   ('treeview', treeview.TreeView),
+                                   ('vcard', vcard.VCardCWUserView),
+                                   ('xbel', xbel.XbelView),
+                                   ('xml', xmlrss.XMLView),
+                                   ])
 
     def test_possible_actions_none_rset(self):
         with self.admin_access.web_request() as req:
--- a/web/test/unittest_web.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/test/unittest_web.py	Tue Jun 21 07:42:30 2016 +0200
@@ -126,17 +126,24 @@
         self.assertIn('HttpOnly', webreq.getheader('set-cookie'))
 
 
-class LogQueriesTC(CubicWebServerTC):
+class MiscOptionsTC(CubicWebServerTC):
     @classmethod
     def init_config(cls, config):
-        super(LogQueriesTC, cls).init_config(config)
+        super(MiscOptionsTC, cls).init_config(config)
         cls.logfile = tempfile.NamedTemporaryFile()
         config.global_set_option('query-log-file', cls.logfile.name)
+        config.global_set_option('datadir-url', '//static.testing.fr/')
+        # call load_configuration again to let the config reset its datadir_url
+        config.load_configuration()
 
     def test_log_queries(self):
         self.web_request()
         self.assertTrue(self.logfile.read())
 
+    def test_datadir_url(self):
+        webreq = self.web_request()
+        self.assertNotIn('/data/', webreq.read())
+
     @classmethod
     def tearDownClass(cls):
         cls.logfile.close()
--- a/web/views/ajaxcontroller.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/views/ajaxcontroller.py	Tue Jun 21 07:42:30 2016 +0200
@@ -427,26 +427,6 @@
     """returns the URL of the external resource named `resource`"""
     return self._cw.uiprops[resource]
 
-@ajaxfunc(output_type='json', check_pageid=True)
-def user_callback(self, cbname):
-    """execute the previously registered user callback `cbname`.
-
-    If matching callback is not found, return None
-    """
-    page_data = self._cw.session.data.get(self._cw.pageid, {})
-    try:
-        cb = page_data[cbname]
-    except KeyError:
-        self.warning('unable to find user callback %s', cbname)
-        return None
-    return cb(self._cw)
-
-
-@ajaxfunc
-def unregister_user_callback(self, cbname):
-    """unregister user callback `cbname`"""
-    self._cw.unregister_callback(self._cw.pageid, cbname)
-
 @ajaxfunc
 def unload_page_data(self):
     """remove user's session data associated to current pageid"""
--- a/web/views/authentication.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/views/authentication.py	Tue Jun 21 07:42:30 2016 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -26,9 +26,8 @@
 
 from cubicweb import AuthenticationError, BadConnectionId
 from cubicweb.view import Component
-from cubicweb.dbapi import _repo_connect, ConnectionProperties
 from cubicweb.web import InvalidSession
-from cubicweb.web.application import AbstractAuthenticationManager
+
 
 class NoAuthInfo(Exception): pass
 
@@ -102,6 +101,36 @@
     '("ie" instead of "ei")')
 
 
+class AbstractAuthenticationManager(Component):
+    """authenticate user associated to a request and check session validity"""
+    __abstract__ = True
+    __regid__ = 'authmanager'
+
+    def __init__(self, repo):
+        self.vreg = repo.vreg
+
+    def validate_session(self, req, session):
+        """check session validity, reconnecting it to the repository if the
+        associated connection expired in the repository side (hence the
+        necessity for this method).
+
+        raise :exc:`InvalidSession` if session is corrupted for a reason or
+        another and should be closed
+        """
+        raise NotImplementedError()
+
+    def authenticate(self, req):
+        """authenticate user using connection information found in the request,
+        and return corresponding a :class:`~cubicweb.dbapi.Connection` instance,
+        as well as login and authentication information dictionary used to open
+        the connection.
+
+        raise :exc:`cubicweb.AuthenticationError` if authentication failed
+        (no authentication info found or wrong user/password)
+        """
+        raise NotImplementedError()
+
+
 class RepositoryAuthenticationManager(AbstractAuthenticationManager):
     """authenticate user associated to a request and check session validity"""
 
--- a/web/views/autoform.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/views/autoform.py	Tue Jun 21 07:42:30 2016 +0200
@@ -746,15 +746,6 @@
     # action on the form tag
     _default_form_action_path = 'validateform'
 
-    @deprecated('[3.18] you should override form_action()')
-    def set_action(self, action):
-        self._action = action
-
-    @deprecated('[3.18] use form_action()')
-    def get_action(self):
-        return self._action
-
-
     @iclassmethod
     def field_by_name(cls_or_self, name, role=None, eschema=None):
         """return field with the given name and role. If field is not explicitly
--- a/web/views/basecomponents.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/views/basecomponents.py	Tue Jun 21 07:42:30 2016 +0200
@@ -55,7 +55,7 @@
         else:
             rset = self.cw_rset
         # display multilines query as one line
-        rql = rset is not None and rset.printable_rql(encoded=False) or req.form.get('rql', '')
+        rql = rset is not None and rset.printable_rql() or req.form.get('rql', '')
         rql = rql.replace(u"\n", u" ")
         rql_suggestion_comp = self._cw.vreg['components'].select_or_none('rql.suggestions', self._cw)
         if rql_suggestion_comp is not None:
--- a/web/views/basecontrollers.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/views/basecontrollers.py	Tue Jun 21 07:42:30 2016 +0200
@@ -92,7 +92,7 @@
     def publish(self, rset=None):
         """log in the instance"""
         path = self._cw.form.get('postlogin_path', '')
-        # redirect expect a URL, not a path. Also path may contains a query
+        # Redirect expects a URL, not a path. Also path may contain a query
         # string, hence should not be given to _cw.build_url()
         raise Redirect(self._cw.base_url() + path)
 
--- a/web/views/basetemplates.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/views/basetemplates.py	Tue Jun 21 07:42:30 2016 +0200
@@ -490,7 +490,7 @@
 
 
 class LogFormView(View):
-    # XXX an awfull lot of hardcoded assumptions there
+    # XXX an awful lot of hardcoded assumptions there
     #     makes it unobvious to reuse/specialize
     __regid__ = 'logform'
     __select__ = match_kwargs('id', 'klass')
@@ -516,10 +516,6 @@
         if config['auth-mode'] != 'http':
             self.login_form(id) # Cookie authentication
         w(u'</div>')
-        if self._cw.https and config.anonymous_user()[0] and config['https-deny-anonymous']:
-            path = xml_escape(config['base-url'] + self._cw.relative_path())
-            w(u'<div class="loginMessage"><a href="%s">%s</a></div>\n'
-              % (path, self._cw._('No account? Try public access at %s') % path))
         w(u'</div>\n')
 
     def login_form(self, id):
--- a/web/views/cwsources.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/views/cwsources.py	Tue Jun 21 07:42:30 2016 +0200
@@ -231,6 +231,7 @@
     __regid__ = 'cwsource'
     title = _('data sources')
     category = 'manage'
+    order = 100
 
 
 class CWSourcesManagementView(StartupView):
--- a/web/views/debug.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/views/debug.py	Tue Jun 21 07:42:30 2016 +0200
@@ -24,7 +24,6 @@
 
 from logilab.mtconverter import xml_escape
 
-from cubicweb import BadConnectionId
 from cubicweb.predicates import none_rset, match_user_groups
 from cubicweb.view import StartupView
 from cubicweb.web.views import actions, tabs
@@ -98,6 +97,13 @@
         w(u'<h3>%s</h3>' % _('resources usage'))
         w(u'<table>')
         stats = self._cw.call_service('repo_stats')
+        stats['looping_tasks'] = ', '.join('%s (%s seconds)' % (n, i) for n, i in stats['looping_tasks'])
+        stats['threads'] = ', '.join(sorted(stats['threads']))
+        for k in stats:
+            if k in ('extid_cache_size', 'type_source_cache_size'):
+                continue
+            if k.endswith('_cache_size'):
+                stats[k] = '%s / %s' % (stats[k]['size'], stats[k]['maxsize'])
         for element in sorted(stats):
             w(u'<tr><th align="left">%s</th><td>%s %s</td></tr>'
                    % (element, xml_escape(unicode(stats[element])),
@@ -179,31 +185,13 @@
     cache_max_age = 0
 
     def call(self, **kwargs):
-        from cubicweb._gcdebug import gc_info
-        from rql.stmts import Union
-        from cubicweb.appobject import AppObject
-        from cubicweb.rset import ResultSet
-        from cubicweb.dbapi import Connection, Cursor
-        from cubicweb.web.request import CubicWebRequestBase
-        lookupclasses = (AppObject,
-                         Union, ResultSet,
-                         Connection, Cursor,
-                         CubicWebRequestBase)
-        try:
-            from cubicweb.server.session import Session, InternalSession
-            lookupclasses += (InternalSession, Session)
-        except ImportError:
-            pass # no server part installed
+        stats = self._cw.call_service('repo_gc_stats')
         self.w(u'<h2>%s</h2>' % _('Garbage collection information'))
-        counters, ocounters, garbage = gc_info(lookupclasses,
-                                               viewreferrersclasses=())
         self.w(u'<h3>%s</h3>' % self._cw._('Looked up classes'))
-        values = sorted(counters.iteritems(), key=lambda x: x[1], reverse=True)
-        self.wview('pyvaltable', pyvalue=values)
+        self.wview('pyvaltable', pyvalue=stats['lookupclasses'])
         self.w(u'<h3>%s</h3>' % self._cw._('Most referenced classes'))
-        values = sorted(ocounters.iteritems(), key=lambda x: x[1], reverse=True)
-        self.wview('pyvaltable', pyvalue=values[:self._cw.form.get('nb', 20)])
-        if garbage:
+        self.wview('pyvaltable', pyvalue=stats['referenced'])
+        if stats['unreachable']:
             self.w(u'<h3>%s</h3>' % self._cw._('Unreachable objects'))
-            values = sorted(xml_escape(repr(o)) for o in garbage)
+            values = [xml_escape(val) for val in stats['unreachable']]
             self.wview('pyvallist', pyvalue=values)
--- a/web/views/formrenderers.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/views/formrenderers.py	Tue Jun 21 07:42:30 2016 +0200
@@ -196,7 +196,7 @@
         if form.cssclass:
             attrs.setdefault('class', form.cssclass)
         if form.cwtarget:
-            attrs.setdefault('cubicweb:target', form.cwtarget)
+            attrs.setdefault('target', form.cwtarget)
         if not form.autocomplete:
             attrs.setdefault('autocomplete', 'off')
         return '<form %s>' % uilib.sgml_attributes(attrs)
@@ -206,7 +206,13 @@
         for form renderers overriding open_form to use something else or more than
         and <form>
         """
-        return u'</form>'
+        out = u'</form>'
+        if form.cwtarget:
+            attrs = {'name': form.cwtarget, 'id': form.cwtarget,
+                     'width': '0px', 'height': '0px',
+                     'src': 'javascript: void(0);'}
+            out =  (u'<iframe %s></iframe>\n' % uilib.sgml_attributes(attrs)) + out
+        return out
 
     def render_fields(self, w, form, values):
         fields = self._render_hidden_fields(w, form)
--- a/web/views/forms.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/views/forms.py	Tue Jun 21 07:42:30 2016 +0200
@@ -95,7 +95,7 @@
       value for the "style" attribute of the <form> tag
 
     :attr:`cwtarget`
-      value for the "cubicweb:target" attribute of the <form> tag
+      value for the "target" attribute of the <form> tag
 
     :attr:`redirect_path`
       relative to redirect to after submitting the form
@@ -241,10 +241,7 @@
 
     _default_form_action_path = 'edit'
     def form_action(self):
-        try:
-            action = self.get_action() # avoid spurious warning w/ autoform bw compat property
-        except AttributeError:
-            action = self.action
+        action = self.action
         if action is None:
             return self._cw.build_url(self._default_form_action_path)
         return action
--- a/web/views/management.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/views/management.py	Tue Jun 21 07:42:30 2016 +0200
@@ -187,6 +187,13 @@
 
     def call(self):
         stats = self._cw.call_service('repo_stats')
+        stats['looping_tasks'] = ', '.join('%s (%s seconds)' % (n, i) for n, i in stats['looping_tasks'])
+        stats['threads'] = ', '.join(sorted(stats['threads']))
+        for k in stats:
+            if k in ('extid_cache_size', 'type_source_cache_size'):
+                continue
+            if k.endswith('_cache_size'):
+                stats[k] = '%s / %s' % (stats[k]['size'], stats[k]['maxsize'])
         results = []
         for element in stats:
             results.append(u'%s %s' % (element, stats[element]))
--- a/web/views/rdf.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/views/rdf.py	Tue Jun 21 07:42:30 2016 +0200
@@ -47,6 +47,8 @@
         __regid__ = 'rdf'
         title = _('rdf export')
         templatable = False
+        binary = True
+        format = 'xml'
         content_type = 'text/xml' # +rdf
 
         def call(self):
@@ -57,7 +59,7 @@
             for i in xrange(self.cw_rset.rowcount):
                 entity = self.cw_rset.complete_entity(i, 0)
                 self.entity2graph(graph, entity)
-            self.w(graph.serialize().decode('utf-8'))
+            self.w(graph.serialize(format=self.format))
 
         def entity_call(self, entity):
             self.call()
@@ -100,3 +102,8 @@
                             else:
                                 add( (URIRef(related.cwuri), CW[rtype], cwuri) )
 
+
+    class RDFN3View(RDFView):
+        __regid__ = 'n3rdf'
+        format = 'n3'
+        content_type = 'text/n3'
--- a/web/views/sessions.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/views/sessions.py	Tue Jun 21 07:42:30 2016 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -15,20 +15,84 @@
 #
 # You should have received a copy of the GNU Lesser General Public License along
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""web session component: by dfault the session is actually the db connection
-object :/
-"""
-
+"""web session: by default the session is actually the db connection """
 __docformat__ = "restructuredtext en"
 
 from time import time
 
-from cubicweb import (RepositoryError, Unauthorized, AuthenticationError,
-                      BadConnectionId)
-from cubicweb.web import InvalidSession, Redirect
-from cubicweb.web.application import AbstractSessionManager
-from cubicweb.dbapi import ProgrammingError, DBAPISession
-from cubicweb import repoapi
+from cubicweb import RepositoryError, Unauthorized, BadConnectionId
+from cubicweb.web import InvalidSession, component
+
+
+class AbstractSessionManager(component.Component):
+    """manage session data associated to a session identifier"""
+    __abstract__ = True
+    __regid__ = 'sessionmanager'
+
+    def __init__(self, repo):
+        vreg = repo.vreg
+        self.session_time = vreg.config['http-session-time'] or None
+        self.authmanager = vreg['components'].select('authmanager', repo=repo)
+        interval = (self.session_time or 0) / 2.
+        if vreg.config.anonymous_user()[0] is not None:
+            self.cleanup_anon_session_time = vreg.config['cleanup-anonymous-session-time'] or 5 * 60
+            assert self.cleanup_anon_session_time > 0
+            if self.session_time is not None:
+                self.cleanup_anon_session_time = min(self.session_time,
+                                                     self.cleanup_anon_session_time)
+            interval = self.cleanup_anon_session_time / 2.
+        # we don't want to check session more than once every 5 minutes
+        self.clean_sessions_interval = max(5 * 60, interval)
+
+    def clean_sessions(self):
+        """cleanup sessions which has not been unused since a given amount of
+        time. Return the number of sessions which have been closed.
+        """
+        self.debug('cleaning http sessions')
+        session_time = self.session_time
+        closed, total = 0, 0
+        for session in self.current_sessions():
+            total += 1
+            try:
+                last_usage_time = session.cnx.check()
+            except AttributeError:
+                last_usage_time = session.mtime
+            except BadConnectionId:
+                self.close_session(session)
+                closed += 1
+                continue
+
+            no_use_time = (time() - last_usage_time)
+            if session.anonymous_session:
+                if no_use_time >= self.cleanup_anon_session_time:
+                    self.close_session(session)
+                    closed += 1
+            elif session_time is not None and no_use_time >= session_time:
+                self.close_session(session)
+                closed += 1
+        return closed, total - closed
+
+    def current_sessions(self):
+        """return currently open sessions"""
+        raise NotImplementedError()
+
+    def get_session(self, req, sessionid):
+        """return existing session for the given session identifier"""
+        raise NotImplementedError()
+
+    def open_session(self, req):
+        """open and return a new session for the given request.
+
+        raise :exc:`cubicweb.AuthenticationError` if authentication failed
+        (no authentication info found or wrong user/password)
+        """
+        raise NotImplementedError()
+
+    def close_session(self, session):
+        """close session on logout or on invalid session detected (expired out,
+        corrupted...)
+        """
+        raise NotImplementedError()
 
 
 class InMemoryRepositorySessionManager(AbstractSessionManager):
@@ -97,8 +161,7 @@
         # XXX should properly detect missing permission / non writeable source
         # and avoid "except (RepositoryError, Unauthorized)" below
         try:
-            cnx = repoapi.ClientConnection(session)
-            with cnx:
+            with session.new_cnx() as cnx:
                 cnx.execute('SET X last_login_time NOW WHERE X eid %(x)s',
                            {'x' : session.user.eid})
                 cnx.commit()
--- a/web/views/staticcontrollers.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/views/staticcontrollers.py	Tue Jun 21 07:42:30 2016 +0200
@@ -188,7 +188,6 @@
     def __init__(self, *args, **kwargs):
         super(DataController, self).__init__(*args, **kwargs)
         config = self._cw.vreg.config
-        md5_version = config.instance_md5_version()
         self.base_datapath = config.data_relpath()
         self.data_modconcat_basepath = '%s??' % self.base_datapath
         self.concat_files_registry = ConcatFilesHandler(config)
--- a/web/views/tableview.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/views/tableview.py	Tue Jun 21 07:42:30 2016 +0200
@@ -992,8 +992,10 @@
 
     @cachedproperty
     def initial_load(self):
-        """We detect a bit heuristically if we are built for the first time of
-        from subsequent calls by the form filter or by the pagination hooks
+        """We detect a bit heuristically if we are built for the first time or
+        from subsequent calls by the form filter or by the pagination
+        hooks.
+
         """
         form = self._cw.form
         return 'fromformfilter' not in form and '__start' not in form
--- a/web/views/timeline.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/views/timeline.py	Tue Jun 21 07:42:30 2016 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -15,124 +15,20 @@
 #
 # You should have received a copy of the GNU Lesser General Public License along
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""basic support for SIMILE's timeline widgets
 
-cf. http://code.google.com/p/simile-widgets/
-"""
-
-__docformat__ = "restructuredtext en"
-
-from logilab.mtconverter import xml_escape
-from logilab.common.date import ustrftime
-
-from cubicweb.predicates import adaptable
-from cubicweb.view import EntityView, StartupView
-from cubicweb.utils import json_dumps
-
-_ = unicode
-
-class TimelineJsonView(EntityView):
-    """generates a json file to feed Timeline.loadJSON()
-    NOTE: work in progress (image_url, bubbleUrl and so on
-    should be properties of entity classes or subviews)
-    """
-    __regid__ = 'timeline-json'
-    __select__ = adaptable('ICalendarable')
-
-    binary = True
-    templatable = False
-    content_type = 'application/json'
-
-    date_fmt = '%Y/%m/%d'
-
-    def call(self):
-        events = []
-        for entity in self.cw_rset.entities():
-            event = self.build_event(entity)
-            if event is not None:
-                events.append(event)
-        timeline_data = {'dateTimeFormat': self.date_fmt,
-                         'events': events}
-        self.w(json_dumps(timeline_data))
-
-    # FIXME: those properties should be defined by the entity class
-    def onclick_url(self, entity):
-        return entity.absolute_url()
-
-    def onclick(self, entity):
-        url = self.onclick_url(entity)
-        if url:
-            return u"javascript: document.location.href='%s'" % url
-        return None
+try:
+    from cubes.timeline.views import (
+            TimelineJsonView,
+            TimelineViewMixIn,
+            TimelineView,
+            StaticTimelineView)
 
-    def build_event(self, entity):
-        """converts `entity` into a JSON object
-        {'start': '1891',
-        'end': '1915',
-        'title': 'Portrait of Horace Brodsky',
-        'description': 'by Henri Gaudier-Brzeska, French Sculptor, 1891-1915',
-        'image': 'http://imagecache2.allposters.com/images/BRGPOD/102770_b.jpg',
-        'link': 'http://www.allposters.com/-sp/Portrait-of-Horace-Brodsky-Posters_i1584413_.htm'
-        }
-        """
-        icalendarable = entity.cw_adapt_to('ICalendarable')
-        start = icalendarable.start
-        stop = icalendarable.stop
-        start = start or stop
-        if start is None and stop is None:
-            return None
-        event_data = {'start': ustrftime(start, self.date_fmt),
-                      'title': xml_escape(entity.dc_title()),
-                      'description': entity.dc_description(format='text/html'),
-                      'link': entity.absolute_url(),
-                      }
-        onclick = self.onclick(entity)
-        if onclick:
-            event_data['onclick'] = onclick
-        if stop:
-            event_data['end'] = ustrftime(stop, self.date_fmt)
-        return event_data
-
-
-class TimelineViewMixIn(object):
-    widget_class = 'TimelineWidget'
-    jsfiles = ('cubicweb.timeline-bundle.js', 'cubicweb.widgets.js',
-               'cubicweb.timeline-ext.js', 'cubicweb.ajax.js')
+except ImportError:
+    pass
+else:
+    from logilab.common.deprecation import class_moved
 
-    def render_url(self, loadurl, tlunit=None):
-        tlunit = tlunit or self._cw.form.get('tlunit')
-        self._cw.add_js(self.jsfiles)
-        self._cw.add_css('timeline-bundle.css')
-        if tlunit:
-            additional = u' cubicweb:tlunit="%s"' % tlunit
-        else:
-            additional = u''
-        self.w(u'<div class="widget" cubicweb:wdgtype="%s" '
-               u'cubicweb:loadtype="auto" cubicweb:loadurl="%s" %s >' %
-               (self.widget_class, xml_escape(loadurl),
-                additional))
-        self.w(u'</div>')
-
-
-class TimelineView(TimelineViewMixIn, EntityView):
-    """builds a cubicweb timeline widget node"""
-    __regid__ = 'timeline'
-    title = _('timeline')
-    __select__ = adaptable('ICalendarable')
-    paginable = False
-    def call(self, tlunit=None):
-        self._cw.html_headers.define_var('Timeline_urlPrefix', self._cw.datadir_url)
-        rql = self.cw_rset.printable_rql()
-        loadurl = self._cw.build_url(rql=rql, vid='timeline-json')
-        self.render_url(loadurl, tlunit)
-
-
-class StaticTimelineView(TimelineViewMixIn, StartupView):
-    """similar to `TimelineView` but loads data from a static
-    JSON file instead of one after a RQL query.
-    """
-    __regid__ = 'static-timeline'
-
-    def call(self, loadurl, tlunit=None, wdgclass=None):
-        self.widget_class = wdgclass or self.widget_class
-        self.render_url(loadurl, tlunit)
+    TimelineJsonView = class_moved(TimelineJsonView, 'TimelineJsonView')
+    TimelineViewMixIn = class_moved(TimelineViewMixIn, 'TimelineViewMixIn')
+    TimelineView = class_moved(TimelineView, 'TimelineView')
+    StaticTimelineView = class_moved(StaticTimelineView, 'StaticTimelineView')
--- a/web/views/urlpublishing.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/views/urlpublishing.py	Tue Jun 21 07:42:30 2016 +0200
@@ -97,7 +97,7 @@
         self.evaluators = sorted(evaluators, key=lambda x: x.priority)
 
     def process(self, req, path):
-        """Given a URL (essentialy caracterized by a path on the
+        """Given a URL (essentially characterized by a path on the
         server, but additional information may be found in the request
         object), return a publishing method identifier
         (e.g. controller) and an optional result set.
--- a/web/views/xmlrss.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/views/xmlrss.py	Tue Jun 21 07:42:30 2016 +0200
@@ -79,7 +79,7 @@
         self.w(u'<%s eid="%s" cwuri="%s" cwsource="%s">\n'
                % (entity.cw_etype, entity.eid, xml_escape(entity.cwuri),
                   xml_escape(source)))
-        for rschema, attrschema in entity.e_schema.attribute_definitions():
+        for rschema, attrschema in sorted(entity.e_schema.attribute_definitions()):
             attr = rschema.type
             if attr in ('eid', 'cwuri'):
                 continue
--- a/web/webconfig.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/webconfig.py	Tue Jun 21 07:42:30 2016 +0200
@@ -49,7 +49,7 @@
       }),
     # user web ui configuration
     ('fckeditor',
-     {'type' : 'yn', 'default': True,
+     {'type' : 'yn', 'default': False,
       'help': _('should html fields being edited using fckeditor (a HTML '
                 'WYSIWYG editor).  You should also select text/html as default '
                 'text format to actually get fckeditor.'),
@@ -124,16 +124,13 @@
           'where the cubicweb web server is listening on port 8080.',
           'group': 'main', 'level': 3,
           }),
-        ('https-deny-anonymous',
-         {'type': 'yn',
-          'default': False,
-          'help': 'Prevent anonymous user to browse through https version of '
-                  'the site (https-url). Login form will then be displayed '
-                  'until logged',
+        ('datadir-url',
+         {'type': 'string', 'default': None,
+          'help': ('base url for static data, if different from "${base-url}/data/".  '
+                   'If served from a different domain, that domain should allow '
+                   'cross-origin requests.'),
           'group': 'web',
-          'level': 2
-         }
-          ),
+          }),
         ('auth-mode',
          {'type' : 'choice',
           'choices' : ('cookie', 'http'),
@@ -378,9 +375,9 @@
             if exists(fpath):
                 yield join(fpath)
 
-    def load_configuration(self):
+    def load_configuration(self, **kw):
         """load instance's configuration files"""
-        super(WebConfiguration, self).load_configuration()
+        super(WebConfiguration, self).load_configuration(**kw)
         # load external resources definition
         self._init_base_url()
         self._build_ui_properties()
@@ -392,6 +389,14 @@
             baseurl += '/'
         if not (self.repairing or self.creating):
             self.global_set_option('base-url', baseurl)
+        self.datadir_url = self['datadir-url']
+        if self.datadir_url:
+            if self.datadir_url[-1] != '/':
+                self.datadir_url += '/'
+            if self.mode != 'test':
+                self.datadir_url += '%s/' % self.instance_md5_version()
+            self.https_datadir_url = self.datadir_url
+            return
         httpsurl = self['https-url']
         data_relpath = self.data_relpath()
         if httpsurl:
@@ -477,8 +482,3 @@
     def static_file_del(self, rpath):
         if self.static_file_exists(rpath):
             os.remove(join(self.static_directory, rpath))
-
-    @deprecated('[3.9] use _cw.uiprops.get(rid)')
-    def has_resource(self, rid):
-        """return true if an external resource is defined"""
-        return bool(self.uiprops.get(rid))
--- a/web/webctl.py	Mon May 09 17:24:03 2016 +0200
+++ b/web/webctl.py	Tue Jun 21 07:42:30 2016 +0200
@@ -46,9 +46,6 @@
         if not automatic:
             print '\n' + underline_title('Generic web configuration')
             config = self.config
-            if config['repository-uri'].startswith('pyro://') or config.pyro_enabled():
-                print '\n' + underline_title('Pyro configuration')
-                config.input_config('pyro', inputlevel)
             config.input_config('web', inputlevel)
             if ASK.confirm('Allow anonymous access ?', False):
                 config.global_set_option('anonymous-user', 'anon')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wsgi/test/requirements.txt	Tue Jun 21 07:42:30 2016 +0200
@@ -0,0 +1,1 @@
+webtest
--- a/zmqclient.py	Mon May 09 17:24:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of CubicWeb.
-#
-# CubicWeb is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""Source to query another RQL repository using pyro"""
-
-__docformat__ = "restructuredtext en"
-_ = unicode
-
-from functools import partial
-import zmq
-
-from cubicweb.server.cwzmq import cwproto_to_zmqaddr
-
-# XXX hack to overpass old zmq limitation that force to have
-# only one context per python process
-try:
-    from cubicweb.server.cwzmq import ctx
-except ImportError:
-    ctx = zmq.Context()
-
-class ZMQRepositoryClient(object):
-    """
-    This class delegates the overall repository stuff to a remote source.
-
-    So calling a method of this repository will result on calling the
-    corresponding method of the remote source repository.
-
-    Any raised exception on the remote source is propagated locally.
-
-    ZMQ is used as the transport layer and cPickle is used to serialize data.
-    """
-
-    def __init__(self, zmq_address):
-        """A zmq address provided here will be like
-        `zmqpickle-tcp://127.0.0.1:42000`.  W
-
-        We chop the prefix to get a real zmq address.
-        """
-        self.socket = ctx.socket(zmq.REQ)
-        self.socket.connect(cwproto_to_zmqaddr(zmq_address))
-
-    def __zmqcall__(self, name, *args, **kwargs):
-         self.socket.send_pyobj([name, args, kwargs])
-         result = self.socket.recv_pyobj()
-         if isinstance(result, BaseException):
-             raise result
-         return result
-
-    def __getattr__(self, name):
-        return partial(self.__zmqcall__, name)