--- a/.hgtags Thu Jul 02 10:36:25 2009 +0200
+++ b/.hgtags Mon Jul 06 19:55:18 2009 +0200
@@ -43,3 +43,8 @@
a356da3e725bfcb59d8b48a89d04be05ea261fd3 3.3.1
e3aeb6e6c3bb5c18e8dcf61bae9d654beda6c036 cubicweb-version-3_3_2
bef5e74e53f9de8220451dca4b5863a24a0216fb cubicweb-debian-version-3_3_2-1
+1cf9e44e2f1f4415253b8892a0adfbd3b69e84fd cubicweb-version-3_3_3
+81973c897c9e78e5e52643e03628654916473196 cubicweb-debian-version-3_3_3-1
+1cf9e44e2f1f4415253b8892a0adfbd3b69e84fd cubicweb-version-3_3_3
+47b5236774a0cf3b1cfe75f6d4bd2ec989644ace cubicweb-version-3_3_3
+2ba27ce8ecd9828693ec53c517e1c8810cbbe33e cubicweb-debian-version-3_3_3-2
--- a/__pkginfo__.py Thu Jul 02 10:36:25 2009 +0200
+++ b/__pkginfo__.py Mon Jul 06 19:55:18 2009 +0200
@@ -7,7 +7,7 @@
distname = "cubicweb"
modname = "cubicweb"
-numversion = (3, 3, 2)
+numversion = (3, 3, 3)
version = '.'.join(str(num) for num in numversion)
license = 'LGPL v2'
--- a/common/migration.py Thu Jul 02 10:36:25 2009 +0200
+++ b/common/migration.py Mon Jul 06 19:55:18 2009 +0200
@@ -20,24 +20,6 @@
from cubicweb import ConfigurationError
-def migration_files(config, toupgrade):
- """return an orderer list of path of scripts to execute to upgrade
- an installed application according to installed cube and cubicweb versions
- """
- merged = []
- for cube, fromversion, toversion in toupgrade:
- if cube == 'cubicweb':
- migrdir = config.migration_scripts_dir()
- else:
- migrdir = config.cube_migration_scripts_dir(cube)
- scripts = filter_scripts(config, migrdir, fromversion, toversion)
- merged += [s[1] for s in scripts]
- if config.accept_mode('Any'):
- migrdir = config.migration_scripts_dir()
- merged.insert(0, join(migrdir, 'bootstrapmigration_repository.py'))
- return merged
-
-
def filter_scripts(config, directory, fromversion, toversion, quiet=True):
"""return a list of paths of migration files to consider to upgrade
from a version to a greater one
@@ -126,6 +108,18 @@
'interactive_mode': interactive,
}
+ def __getattribute__(self, name):
+ try:
+ return object.__getattribute__(self, name)
+ except AttributeError:
+ cmd = 'cmd_%s' % name
+ if hasattr(self, cmd):
+ meth = getattr(self, cmd)
+ return lambda *args, **kwargs: self.interact(args, kwargs,
+ meth=meth)
+ raise
+ raise AttributeError(name)
+
def repo_connect(self):
return self.config.repository()
@@ -144,31 +138,35 @@
return False
return orig_accept_mode(mode)
self.config.accept_mode = accept_mode
- scripts = migration_files(self.config, toupgrade)
- if scripts:
- vmap = dict( (pname, (fromver, tover)) for pname, fromver, tover in toupgrade)
- self.__context.update({'applcubicwebversion': vcconf['cubicweb'],
- 'cubicwebversion': self.config.cubicweb_version(),
- 'versions_map': vmap})
- self.scripts_session(scripts)
- else:
- print 'no migration script to execute'
+ # may be an iterator
+ toupgrade = tuple(toupgrade)
+ vmap = dict( (cube, (fromver, tover)) for cube, fromver, tover in toupgrade)
+ ctx = self.__context
+ ctx['versions_map'] = vmap
+ if self.config.accept_mode('Any') and 'cubicweb' in vmap:
+ migrdir = self.config.migration_scripts_dir()
+ self.process_script(join(migrdir, 'bootstrapmigration_repository.py'))
+ for cube, fromversion, toversion in toupgrade:
+ if cube == 'cubicweb':
+ migrdir = self.config.migration_scripts_dir()
+ else:
+ migrdir = self.config.cube_migration_scripts_dir(cube)
+ scripts = filter_scripts(self.config, migrdir, fromversion, toversion)
+ if scripts:
+ for version, script in scripts:
+ self.process_script(script)
+ self.cube_upgraded(cube, version)
+ if version != toversion:
+ self.cube_upgraded(cube, toversion)
+ else:
+ self.cube_upgraded(cube, toversion)
+
+ def cube_upgraded(self, cube, version):
+ pass
def shutdown(self):
pass
- def __getattribute__(self, name):
- try:
- return object.__getattribute__(self, name)
- except AttributeError:
- cmd = 'cmd_%s' % name
- if hasattr(self, cmd):
- meth = getattr(self, cmd)
- return lambda *args, **kwargs: self.interact(args, kwargs,
- meth=meth)
- raise
- raise AttributeError(name)
-
def interact(self, args, kwargs, meth):
"""execute the given method according to user's confirmation"""
msg = 'execute command: %s(%s) ?' % (
@@ -205,7 +203,6 @@
if answer in ('r', 'retry'):
return 2
if answer in ('a', 'abort'):
- self.rollback()
raise SystemExit(1)
if shell and answer in ('s', 'shell'):
self.interactive_shell()
@@ -284,16 +281,6 @@
return None
return func(*args, **kwargs)
- def scripts_session(self, migrscripts):
- """execute some scripts in a transaction"""
- try:
- for migrscript in migrscripts:
- self.process_script(migrscript)
- self.commit()
- except:
- self.rollback()
- raise
-
def cmd_option_renamed(self, oldname, newname):
"""a configuration option has been renamed"""
self._option_changes.append(('renamed', oldname, newname))
--- a/common/test/unittest_migration.py Thu Jul 02 10:36:25 2009 +0200
+++ b/common/test/unittest_migration.py Mon Jul 06 19:55:18 2009 +0200
@@ -13,7 +13,8 @@
from cubicweb.devtools.apptest import TestEnvironment
from cubicweb.cwconfig import CubicWebConfiguration
-from cubicweb.common.migration import migration_files, filter_scripts
+from cubicweb.common.migration import MigrationHelper, filter_scripts
+from cubicweb.server.migractions import ServerMigrationHelper
class Schema(dict):
@@ -39,82 +40,53 @@
self.config.__class__.cubicweb_vobject_path = frozenset()
self.config.__class__.cube_vobject_path = frozenset()
- def test_migration_files_base(self):
- self.assertListEquals(migration_files(self.config, [('cubicweb', (2,3,0), (2,4,0)),
- ('TEMPLATE', (0,0,2), (0,0,3))]),
- [SMIGRDIR+'bootstrapmigration_repository.py',
- TMIGRDIR+'0.0.3_Any.py'])
- self.assertListEquals(migration_files(self.config, [('cubicweb', (2,4,0), (2,5,0)),
- ('TEMPLATE', (0,0,2), (0,0,3))]),
- [SMIGRDIR+'bootstrapmigration_repository.py',
- SMIGRDIR+'2.5.0_Any.sql',
- TMIGRDIR+'0.0.3_Any.py'])
- self.assertListEquals(migration_files(self.config, [('cubicweb', (2,5,0), (2,6,0)),
- ('TEMPLATE', (0,0,3), (0,0,4))]),
- [SMIGRDIR+'bootstrapmigration_repository.py',
- SMIGRDIR+'2.6.0_Any.sql',
- TMIGRDIR+'0.0.4_Any.py'])
+ def test_filter_scripts_base(self):
+ self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,3,0), (2,4,0)),
+ [])
+ self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,4,0), (2,5,0)),
+ [((2, 5, 0), SMIGRDIR+'2.5.0_Any.sql')])
+ self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,5,0), (2,6,0)),
+ [((2, 6, 0), SMIGRDIR+'2.6.0_Any.sql')])
+ self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,4,0), (2,6,0)),
+ [((2, 5, 0), SMIGRDIR+'2.5.0_Any.sql'),
+ ((2, 6, 0), SMIGRDIR+'2.6.0_Any.sql')])
+ self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,5,0), (2,5,1)),
+ [])
+ self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,5,0), (2,10,2)),
+ [((2, 6, 0), SMIGRDIR+'2.6.0_Any.sql'),
+ ((2, 10, 2), SMIGRDIR+'2.10.2_Any.sql')])
+ self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,5,1), (2,6,0)),
+ [((2, 6, 0), SMIGRDIR+'2.6.0_Any.sql')])
-## def test_migration_files_overlap(self):
-## self.assertListEquals(migration_files(self.config, (2,4,0), (2,10,2),
-## (0,0,2), (0,1,2)),
-## [SMIGRDIR+'bootstrapmigration_repository.py',
-## TMIGRDIR+'0.0.3_Any.py',
-## TMIGRDIR+'0.0.4_Any.py',
-## SMIGRDIR+'2.4.0_2.5.0_Any.sql',
-## SMIGRDIR+'2.5.1_2.6.0_Any.sql',
-## TMIGRDIR+'0.1.0_Any.py',
-## TMIGRDIR+'0.1.0_common.py',
-## TMIGRDIR+'0.1.0_repository.py',
-## TMIGRDIR+'0.1.2_Any.py',
-## SMIGRDIR+'2.10.1_2.10.2_Any.sql'])
+ self.assertListEquals(filter_scripts(self.config, TMIGRDIR, (0,0,2), (0,0,3)),
+ [((0, 0, 3), TMIGRDIR+'0.0.3_Any.py')])
+ self.assertListEquals(filter_scripts(self.config, TMIGRDIR, (0,0,2), (0,0,4)),
+ [((0, 0, 3), TMIGRDIR+'0.0.3_Any.py'),
+ ((0, 0, 4), TMIGRDIR+'0.0.4_Any.py')])
- def test_migration_files_for_mode(self):
- from cubicweb.server.migractions import ServerMigrationHelper
+ def test_filter_scripts_for_mode(self):
self.assertIsInstance(self.config.migration_handler(), ServerMigrationHelper)
- from cubicweb.common.migration import MigrationHelper
config = CubicWebConfiguration('data')
config.verbosity = 0
self.assert_(not isinstance(config.migration_handler(), ServerMigrationHelper))
self.assertIsInstance(config.migration_handler(), MigrationHelper)
config = self.config
config.__class__.name = 'twisted'
- self.assertListEquals(migration_files(config, [('TEMPLATE', (0,0,4), (0,1,0))]),
- [TMIGRDIR+'0.1.0_common.py',
- TMIGRDIR+'0.1.0_web.py'])
- config.__class__.name = 'repository'
- self.assertListEquals(migration_files(config, [('TEMPLATE', (0,0,4), (0,1,0))]),
- [SMIGRDIR+'bootstrapmigration_repository.py',
- TMIGRDIR+'0.1.0_Any.py',
- TMIGRDIR+'0.1.0_common.py',
- TMIGRDIR+'0.1.0_repository.py'])
- config.__class__.name = 'all-in-one'
- self.assertListEquals(migration_files(config, [('TEMPLATE', (0,0,4), (0,1,0))]),
- [SMIGRDIR+'bootstrapmigration_repository.py',
- TMIGRDIR+'0.1.0_Any.py',
- TMIGRDIR+'0.1.0_common.py',
- TMIGRDIR+'0.1.0_repository.py',
- TMIGRDIR+'0.1.0_web.py'])
+ self.assertListEquals(filter_scripts(config, TMIGRDIR, (0,0,4), (0,1,0)),
+ [((0, 1 ,0), TMIGRDIR+'0.1.0_common.py'),
+ ((0, 1 ,0), TMIGRDIR+'0.1.0_web.py')])
config.__class__.name = 'repository'
-
- def test_filter_scripts(self):
- self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,4,0), (2,5,0)),
- [((2, 5, 0), SMIGRDIR+'2.5.0_Any.sql')])
- self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,4,0), (2,6,0)),
- [((2, 5, 0), SMIGRDIR+'2.5.0_Any.sql'),
- ((2, 6, 0), SMIGRDIR+'2.6.0_Any.sql')])
- self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,5,0), (2,5,1)),
- [])
- self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,5,0), (2,6,0)),
- [((2, 6, 0), SMIGRDIR+'2.6.0_Any.sql')])
- self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,5,0), (2,10,2)),
- [((2, 6, 0), SMIGRDIR+'2.6.0_Any.sql'),
- ((2, 10, 2), SMIGRDIR+'2.10.2_Any.sql')])
- self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,5,1), (2,6,0)),
- [((2, 6, 0), SMIGRDIR+'2.6.0_Any.sql')])
- self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,5,1), (2,10,2)),
- [((2, 6, 0), SMIGRDIR+'2.6.0_Any.sql'),
- ((2, 10, 2), SMIGRDIR+'2.10.2_Any.sql')])
+ self.assertListEquals(filter_scripts(config, TMIGRDIR, (0,0,4), (0,1,0)),
+ [((0, 1 ,0), TMIGRDIR+'0.1.0_Any.py'),
+ ((0, 1 ,0), TMIGRDIR+'0.1.0_common.py'),
+ ((0, 1 ,0), TMIGRDIR+'0.1.0_repository.py')])
+ config.__class__.name = 'all-in-one'
+ self.assertListEquals(filter_scripts(config, TMIGRDIR, (0,0,4), (0,1,0)),
+ [((0, 1 ,0), TMIGRDIR+'0.1.0_Any.py'),
+ ((0, 1 ,0), TMIGRDIR+'0.1.0_common.py'),
+ ((0, 1 ,0), TMIGRDIR+'0.1.0_repository.py'),
+ ((0, 1 ,0), TMIGRDIR+'0.1.0_web.py')])
+ config.__class__.name = 'repository'
from cubicweb.devtools import ApptestConfiguration, init_test_database, cleanup_sqlite
--- a/cwconfig.py Thu Jul 02 10:36:25 2009 +0200
+++ b/cwconfig.py Mon Jul 06 19:55:18 2009 +0200
@@ -194,6 +194,12 @@
'help': 'web server root url',
'group': 'main', 'inputlevel': 1,
}),
+ ('allow-email-login',
+ {'type' : 'yn',
+ 'default': False,
+ 'help': 'allow users to login with their primary email if set',
+ 'group': 'main', 'inputlevel': 2,
+ }),
('use-request-subdomain',
{'type' : 'yn',
'default': None,
--- a/cwctl.py Thu Jul 02 10:36:25 2009 +0200
+++ b/cwctl.py Mon Jul 06 19:55:18 2009 +0200
@@ -655,7 +655,7 @@
else:
applcubicwebversion = vcconf.get('cubicweb')
if cubicwebversion > applcubicwebversion:
- toupgrade.append( ('cubicweb', applcubicwebversion, cubicwebversion) )
+ toupgrade.append(('cubicweb', applcubicwebversion, cubicwebversion))
if not self.config.fs_only and not toupgrade:
print 'no software migration needed for application %s' % appid
return
@@ -682,7 +682,6 @@
'continue anyway ?'):
print 'migration not completed'
return
- mih.rewrite_vcconfiguration()
mih.shutdown()
print
print 'application migrated'
@@ -733,7 +732,8 @@
config.set_sources_mode(sources)
mih = config.migration_handler()
if args:
- mih.scripts_session(args)
+ for arg in args:
+ mih.process_script(script)
else:
mih.interactive_shell()
mih.shutdown()
--- a/cwvreg.py Thu Jul 02 10:36:25 2009 +0200
+++ b/cwvreg.py Mon Jul 06 19:55:18 2009 +0200
@@ -96,6 +96,11 @@
clear_cache(self, 'rqlhelper')
# now we can load application's web objects
self.register_objects(self.config.vregistry_path())
+ # map lowered entity type names to their actual name
+ self.case_insensitive_etypes = {}
+ for etype in self.schema.entities():
+ etype = str(etype)
+ self.case_insensitive_etypes[etype.lower()] = etype
def update_schema(self, schema):
"""update .schema attribute on registered objects, necessary for some
--- a/dbapi.py Thu Jul 02 10:36:25 2009 +0200
+++ b/dbapi.py Mon Jul 06 19:55:18 2009 +0200
@@ -63,19 +63,19 @@
'application' % nsid)
return core.getProxyForURI(uri)
-def repo_connect(repo, user, password, cnxprops=None):
+def repo_connect(repo, login, password, cnxprops=None):
"""Constructor to create a new connection to the CubicWeb repository.
Returns a Connection instance.
"""
cnxprops = cnxprops or ConnectionProperties('inmemory')
- cnxid = repo.connect(unicode(user), password, cnxprops=cnxprops)
+ cnxid = repo.connect(unicode(login), password, cnxprops=cnxprops)
cnx = Connection(repo, cnxid, cnxprops)
if cnxprops.cnxtype == 'inmemory':
cnx.vreg = repo.vreg
return cnx
-def connect(database=None, user=None, password=None, host=None,
+def connect(database=None, login=None, password=None, host=None,
group=None, cnxprops=None, port=None, setvreg=True, mulcnx=True,
initlog=True):
"""Constructor for creating a connection to the CubicWeb repository.
@@ -111,11 +111,11 @@
vreg.set_schema(schema)
else:
vreg = None
- cnx = repo_connect(repo, user, password, cnxprops)
+ cnx = repo_connect(repo, login, password, cnxprops)
cnx.vreg = vreg
return cnx
-def in_memory_cnx(config, user, password):
+def in_memory_cnx(config, login, password):
"""usefull method for testing and scripting to get a dbapi.Connection
object connected to an in-memory repository instance
"""
@@ -128,7 +128,7 @@
repo = get_repository('inmemory', config=config, vreg=vreg)
# connection to the CubicWeb repository
cnxprops = ConnectionProperties('inmemory')
- cnx = repo_connect(repo, user, password, cnxprops=cnxprops)
+ cnx = repo_connect(repo, login, password, cnxprops=cnxprops)
return repo, cnx
@@ -245,7 +245,7 @@
@property
def user(self):
if self._user is None and self.cnx:
- self.set_user(self.cnx.user(self))
+ self.set_user(self.cnx.user(self, {'lang': self.lang}))
return self._user
def set_user(self, user):
@@ -367,6 +367,10 @@
"""raise `BadSessionId` if the connection is no more valid"""
self._repo.check_session(self.sessionid)
+ def set_session_props(self, **props):
+ """raise `BadSessionId` if the connection is no more valid"""
+ self._repo.set_session_props(self.sessionid, props)
+
def get_shared_data(self, key, default=None, pop=False):
"""return value associated to `key` in shared data"""
return self._repo.get_shared_data(self.sessionid, key, default, pop)
@@ -434,7 +438,8 @@
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)
+ eid, login, groups, properties = self._repo.user_info(self.sessionid,
+ props)
if req is None:
req = self.request()
rset = req.eid_rset(eid, 'CWUser')
--- a/debian/changelog Thu Jul 02 10:36:25 2009 +0200
+++ b/debian/changelog Mon Jul 06 19:55:18 2009 +0200
@@ -1,3 +1,16 @@
+cubicweb (3.3.3-2) unstable; urgency=low
+
+ * re-release with "from __future__ import with_statement" commented out to
+ avoid broken installation if 2.4 is installed
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Mon, 06 Jul 2009 17:33:15 +0200
+
+cubicweb (3.3.3-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Mon, 06 Jul 2009 13:24:29 +0200
+
cubicweb (3.3.2-1) unstable; urgency=low
* new upstream release
--- a/devtools/apptest.py Thu Jul 02 10:36:25 2009 +0200
+++ b/devtools/apptest.py Mon Jul 06 19:55:18 2009 +0200
@@ -462,14 +462,13 @@
self.__close = repo.close
self.cnxid = self.cnx.sessionid
self.session = repo._sessions[self.cnxid]
- # XXX copy schema since hooks may alter it and it may be not fully
- # cleaned (missing some schema synchronization support)
- try:
- origschema = repo.__schema
- except AttributeError:
- origschema = repo.schema
- repo.__schema = origschema
if self.copy_schema:
+ # XXX copy schema since hooks may alter it and it may be not fully
+ # cleaned (missing some schema synchronization support)
+ try:
+ origschema = repo.__schema
+ except AttributeError:
+ repo.__schema = origschema = repo.schema
repo.schema = deepcopy(origschema)
repo.set_schema(repo.schema) # reset hooks
repo.vreg.update_schema(repo.schema)
--- a/doc/book/en/admin/setup.rst Thu Jul 02 10:36:25 2009 +0200
+++ b/doc/book/en/admin/setup.rst Mon Jul 06 19:55:18 2009 +0200
@@ -46,6 +46,13 @@
There is also a wide variety of cubes listed on http://www.cubicweb.org/Project available as debian packages and tarball.
+The repositories are signed with `Logilab's gnupg key`_. To avoid warning on "apt-get update":
+1. become root using sudo
+2. download http://ftp.logilab.org/dists/logilab-dists-key.asc using e.g. wget
+3. run "apt-key add logilab-dists-key.asc"
+4. re-run apt-get update (manually or through the package manager, whichever you prefer)
+
+.. `Logilab's gnupg key` _http://ftp.logilab.org/dists/logilab-dists-key.asc
Install from source
```````````````````
--- a/doc/book/en/development/cubes/cc-newcube.rst Thu Jul 02 10:36:25 2009 +0200
+++ b/doc/book/en/development/cubes/cc-newcube.rst Mon Jul 06 19:55:18 2009 +0200
@@ -13,16 +13,15 @@
hg add .
hg ci
-If all went well, you should see the cube you just create in the list
-returned by `cubicweb-ctl list` in the section *Available components*,
+If all went well, you should see the cube you just created in the list
+returned by ``cubicweb-ctl list`` in the section *Available components*,
and if it is not the case please refer to :ref:`ConfigurationEnv`.
-To use a cube, you have to list it in the variable ``__use__``
-of the file ``__pkginfo__.py`` of the instance.
-This variable is used for the instance packaging (dependencies
-handled by system utility tools such as APT) and the usable cubes
-at the time the base is created (import_erschema('MyCube') will
-not properly work otherwise).
+To reuse an existing cube, add it to the list named ``__use__`` and defined in
+:file:`__pkginfo__.py`. This variable is used for the instance packaging
+(dependencies handled by system utility tools such as APT) and the usable cubes
+at the time the base is created (import_erschema('MyCube') will not properly
+work otherwise).
.. note::
Please note that if you do not wish to use default directory
--- a/doc/book/en/intro/concepts/index.rst Thu Jul 02 10:36:25 2009 +0200
+++ b/doc/book/en/intro/concepts/index.rst Mon Jul 06 19:55:18 2009 +0200
@@ -1,5 +1,7 @@
.. -*- coding: utf-8 -*-
+.. _Concepts:
+
The Core Concepts of CubicWeb
=============================
@@ -13,8 +15,9 @@
Cubes
-----
-A cube is a software component composed of three parts: its data model (schema),
-its logic (entities) and its user interface (views).
+A cube is a software component made of three parts: its data model
+(:file:`schema`), its logic (:file:`entities`) and its user interface
+(:file:`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`_
@@ -24,12 +27,14 @@
The `CubicWeb Forge`_ offers a large number of cubes developed by the community
and available under a free software license.
-Available cubes on your system are usually stored in the directory
-:file:`/usr/share/cubicweb/cubes` when using a unix system wide
-installation. During development, the cubes are found in the
-:file:`/path/to/cubicweb_forest/cubes` directory. You can specify additional
-locations using the :envvar:`CW_CUBES_PATH` environment variable, using ':' as a
-separator.
+The command ``cubicweb-ctl list`` displays the list of cubes installed on your
+system.
+
+On a Unix system, the available cubes are usually stored in the directory
+:file:`/usr/share/cubicweb/cubes`. During development, the cubes are commonly
+found in the directory :file:`/path/to/cubicweb_forest/cubes`. The environment
+variable :envvar:`CW_CUBES_PATH` gives additionnal locations where to search for
+cubes.
.. _`CubicWeb Forge`: http://www.cubicweb.org/project/
.. _`cubicweb-blog`: http://www.cubicweb.org/project/cubicweb-blog
@@ -37,124 +42,113 @@
Instances
-----------
+---------
An instance is a runnable application installed on a computer and based on a
cube.
-The instance directory includes the configuration files. Several instances can
-be created based on the same cube. For exemple, several software forges can be
-set up on one computer system based on the `cubicweb-forge`_ 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 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.
+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 ``cubicweb-ctl list`` displays the list of instances installed on
+your system.
+
+On a Unix system, the instances are usually stored in the directory
+:file:`/etc/cubicweb.d/`. During development, the :file:`~/etc/cubicweb.d/`
+directory is looked up, as well as the paths in :envvar:`CW_REGISTRY`
+environment variable.
+
The term application can refer to an instance or to a cube, depending on the
context. This book will try to avoid using this term and use *cube* and
*instance* as appropriate.
-(Data) Repository
-~~~~~~~~~~~~~~~~~~
+Data Repository
+---------------
-The repository (Be carefull not to get confused with a Mercurial repository or a
-debian repository!) manages all interactions with various data sources by
-providing access to them using uniformly using the Relation Query Language (RQL). The
-web interface and the repository communicate using this language.
+The data repository [#]_ provides access to one or more data sources (including
+SQL databases, LDAP repositories, Mercurial or Subversion version control
+systems, other CubicWeb repositories, GAE's DataStore, etc).
-Usually, the web server and repository sides are integrated in the same process and
-interact directly, without the need for distant calls using Pyro. But, it is
-important to note that those two sides, client/server, are disjointed and it is
-possible to execute a couple of calls in distinct processes to balance the load
-of your web site on one or more machines.
-
+All interactions with the repository are done using the Relation Query Language
+(RQL). The repository federates the data sources and hides them from the
+querier, which does not realize when a query spans accross several data sources
+and requires running sub-queries and merges to complete.
-A data source is a container of data integrated in the *CubicWeb* repository. A
-repository has at least one source, named `system`, which contains the schema of
-the application, plain-text index and other vital informations for the
-system. You'll find source for SQL databases, LDAP servers, other RQL
-repositories and even mercurial /svn repositories or `Google App Engine`'s
-datastore.
+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 server.
-Web interface
-~~~~~~~~~~~~~
-By default the web server provides a generated interface based on its schema.
-Entities can be created, displayed, updated and deleted. As display views are not
-very fancy, it is usually necessary to develop your own.
+Some logic can be attached to events that happen in 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 `Hooks` below.
+
+.. _[#]: not to be confused with a Mercurial repository or a Debian repository.
+.. _`Python Remote Objects`: http://pyro.sourceforge.net/
-Instances are defined on your system in the directory :file:`/etc/cubicweb.d` when
-using a system wide installation. For people using the mercurial repository of
-cubicweb, the :file:`etc` directory is searched in the user home directory. You can
-also specify an alternative directory using the :envvar:`CW_REGISTRY` environment
-variable.
-
-
+Web Engine
+----------
-Schema
-------
-** *CubicWeb* is schema driven **
+The web engine replies to http requests and runs the user interface and most of
+the application logic.
-The schema describes the persistent data model using entities and
-relations. It is modeled with a comprehensive language made of Python classes based on
-the `yams`_ library.
+By default the web engine provides a generated 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.
-When you create a new cubicweb instance, the schema is stored in the database,
-and it will usually evolves as you upgrade cubicweb and used cubes.
+Schema (Data Model)
+-------------------
-*CubicWeb* provides a certain number of system entities included
-sytematically (necessary for the core of *CubicWeb*, notably the schema itself).
-You will also find a library of cubes which defines more piece of schema for standard needs.
-necessary.
+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.
-*CubicWeb* add some metadata to every entity type, such as the eid (a global
- identifier, unique into an instance), entity's creation date...
-
+.. _yams: http://www.logilab.org/project/yams/
-Attributes may be of the following types:
- `String`, `Int`, `Float`, `Boolean`, `Date`, `Time`, `Datetime`,
- `Interval`, `Password`, `Bytes`.
-
-New in 3.2: RichString
+An `entity type` defines a set of attributes and is used in some relations.
+Attributes may be of the following types: `String`, `Int`, `Float`, `Boolean`,
+`Date`, `Time`, `Datetime`, `Interval`, `Password`, `Bytes`, `RichString`. See
+:ref:`yams.BASE_TYPES` for details.
-see :ref:`yams.BASE_TYPES`
-
-Data level security is defined by setting permissions on entity and relation types.
-
-A schema consist of parts detailed below.
+A `relation type` is used to define a binary oriented relation between two
+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.
-Entity type
-~~~~~~~~~~~
-An *entity type* defines set of attributes and is used in some relations. It may
-have some permissions telling who can read/add/update/delete entities of this type.
-
-Relation type
-~~~~~~~~~~~~~
-A *relation type* is used to define a semantic relation between two entity types.
-It may have some permissions telling who can read/add/delete relation of this type.
+Permissions can be set on entity types and relation types to control who will be
+able to create, read, update or delete entities and relations.
-In *CubicWeb* relations are ordered and binary: by convention we name the first
-item of a relation the `subject` and the second the `object`.
+Some meta-data necessary to the system is 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.
-Relation definition
-~~~~~~~~~~~~~~~~~~~
-A *relation definition* is a 3-uple (*subject entity type*, *relation type*, *object
-entity type*), with an associated set of property such as cardinality, constraints...
+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.
-
+Registries and Objects
+----------------------
-Dynamic objects for reusable components
----------------------------------------
-** Dynamic objects management or how CubicWeb provides really reusable components **
+XXX registry, register, registries, registers ???
Application objects
~~~~~~~~~~~~~~~~~~~
+
Beside a few core functionalities, almost every feature of the framework is
-acheived by dynamic objects (`application objects` or `appobjects`) stored in a
+achieved by dynamic objects (`application objects` or `appobjects`) stored in a
two-levels registry (the `vregistry`). 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, At runtime, appobjects are selected in the
@@ -168,6 +162,7 @@
The `vregistry`
~~~~~~~~~~~~~~~
+
At startup, the `registry` or registers base, inspects a number of directories
looking for compatible classes definition. After a recording process, the objects
are assigned to registers so that they can be selected dynamically while the
@@ -175,23 +170,22 @@
Selectors
~~~~~~~~~
-Each appobject has a selector, which is used to score how well it suits to a
-given context by returning a score. A score of 0 means the object doesn't apply
-to the context. The score is used to choose the most pertinent object: the "more"
-the appobject suits the context the higher the score.
+
+Each appobject has a selector, that is used to compute how well the object fits
+a given context. The better the object fits the context, the higher the score.
-CubicWeb provides a set of basic selectors which may be parametrized and combined
-using binary `&` and `|` operators to provide a custom selector (which can be
-itself reused...).
+CubicWeb provides a set of basic selectors that may be parametrized. Selectors
+can be combined with the binary operators `&` and `|` to build more complex
+selector that can be combined too.
-There is 3 current ways to retreive some appobject from the repository:
+There are three common ways to retrieve some appobject from the repository:
* get the most appropriate objects by specifying a registry and an identifier. In
that case, the object with the greatest score is selected. There should always
be a single appobject with a greater score than others.
* get all appobjects applying to a context by specifying a registry.In
- that case, every objects with the a postive score are selected.
+ that case, every object with the a postive score is selected.
* get the object within a particular registry/identifier. In that case no
selection process is involved, the vregistry will expect to find a single
@@ -218,9 +212,10 @@
The RQL query language
----------------------
-**No needs for a complicated ORM when you've a powerful query language**
-All the persistant data in a CubicWeb application is retreived and modified by using the
+**No need for a complicated ORM when you have a powerful query language**
+
+All the persistant data in a CubicWeb application is retrieved and modified by using the
Relation Query Language.
This query language is inspired by SQL but is on a higher level in order to
@@ -228,16 +223,19 @@
db-api
~~~~~~
+
The repository exposes a `db-api`_ like api but using the RQL instead of SQL.
XXX feed me
Result set
~~~~~~~~~~
+
XXX feed me
Views
-----
+
** *CubicWeb* is data driven **
XXX feed me.
@@ -248,7 +246,3 @@
** *CubicWeb* provides an extensible data repository **
XXX feed me.
-
-
-.. _`Python Remote Object`: http://pyro.sourceforge.net/
-.. _`yams`: http://www.logilab.org/project/yams/
--- a/doc/book/en/intro/tutorial/components.rst Thu Jul 02 10:36:25 2009 +0200
+++ b/doc/book/en/intro/tutorial/components.rst Mon Jul 06 19:55:18 2009 +0200
@@ -23,6 +23,8 @@
* classtags: Tag (to tag anything)
+* comment: Comment (to attach comment threads to entities)
+
* file: File (to allow users to upload and store binary or text files)
* link: Link (to collect links to web resources)
@@ -37,10 +39,6 @@
* zone: Zone (to define places within larger places, for example a
city in a state in a country)
-The available system entities are:
-
-* comment: Comment (to attach comment threads to entities)
-
Adding comments to BlogDemo
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--- a/i18n/en.po Thu Jul 02 10:36:25 2009 +0200
+++ b/i18n/en.po Mon Jul 06 19:55:18 2009 +0200
@@ -154,6 +154,12 @@
msgid "1?"
msgstr "1 0..1"
+#, python-format
+msgid ""
+"<div>This schema of the data model <em>excludes</em> the meta-data, but you "
+"can also display a <a href=\"%s\">complete schema with meta-data</a>.</div>"
+msgstr ""
+
msgid "?*"
msgstr "0..1 0..n"
@@ -169,12 +175,18 @@
msgid "AND"
msgstr ""
+msgid "Add permissions"
+msgstr ""
+
msgid "Any"
msgstr ""
msgid "Application"
msgstr ""
+msgid "Attributes"
+msgstr ""
+
msgid "Bookmark"
msgstr "Bookmark"
@@ -284,6 +296,9 @@
msgid "Decimal_plural"
msgstr "Decimal numbers"
+msgid "Delete permissions"
+msgstr ""
+
msgid "Do you want to delete the following element(s) ?"
msgstr ""
@@ -395,6 +410,9 @@
msgid "RQLExpression_plural"
msgstr "RQL expressions"
+msgid "Read permissions"
+msgstr ""
+
msgid "Recipients:"
msgstr ""
@@ -408,6 +426,9 @@
msgid "Schema %s"
msgstr ""
+msgid "Schema of the data model"
+msgstr ""
+
msgid "Search for"
msgstr ""
@@ -449,6 +470,9 @@
msgid "The view %s could not be found"
msgstr ""
+msgid "There is no workflow defined for this entity."
+msgstr ""
+
#, python-format
msgid "This %s"
msgstr ""
@@ -526,6 +550,9 @@
msgid "Unable to find anything named \"%s\" in the schema !"
msgstr ""
+msgid "Update permissions"
+msgstr ""
+
msgid "Used by:"
msgstr ""
@@ -981,6 +1008,9 @@
msgid "bookmarks"
msgstr ""
+msgid "bookmarks are used to have user's specific internal links"
+msgstr ""
+
msgid "boxes"
msgstr ""
@@ -1111,6 +1141,9 @@
msgid "changes applied"
msgstr ""
+msgid "click here to see created entity"
+msgstr ""
+
msgid "click on the box to cancel the deletion"
msgstr ""
@@ -1132,12 +1165,6 @@
msgid "components_appliname_description"
msgstr "display the application title in the page's header"
-msgid "components_applmessages"
-msgstr "application messages"
-
-msgid "components_applmessages_description"
-msgstr "display the application messages"
-
msgid "components_breadcrumbs"
msgstr "breadcrumbs"
@@ -1419,6 +1446,18 @@
msgid "currently attached file: %s"
msgstr ""
+msgid "cwetype-schema-image"
+msgstr "schema"
+
+msgid "cwetype-schema-permissions"
+msgstr "permissions"
+
+msgid "cwetype-schema-text"
+msgstr "description"
+
+msgid "cwetype-workflow"
+msgstr "workflow"
+
msgid "data directory url"
msgstr ""
@@ -1538,9 +1577,6 @@
msgid "detach attached file %s"
msgstr ""
-msgid "detailed schema view"
-msgstr ""
-
msgid "display order of the action"
msgstr ""
@@ -1624,6 +1660,9 @@
msgid "entity edited"
msgstr ""
+msgid "entity linked"
+msgstr ""
+
msgid "entity type"
msgstr ""
@@ -1676,6 +1715,18 @@
msgid "facets_created_by-facet_description"
msgstr ""
+msgid "facets_cwfinal-facet"
+msgstr "\"final entity or relation type\" facet"
+
+msgid "facets_cwfinal-facet_description"
+msgstr ""
+
+msgid "facets_cwmeta-facet"
+msgstr ""
+
+msgid "facets_cwmeta-facet_description"
+msgstr ""
+
msgid "facets_etype-facet"
msgstr "\"entity type\" facet"
@@ -1820,9 +1871,6 @@
msgid "hide filter form"
msgstr ""
-msgid "hide meta-data"
-msgstr "hide meta entities and relations"
-
msgid "home"
msgstr ""
@@ -2484,6 +2532,12 @@
msgid "schema's permissions definitions"
msgstr ""
+msgid "schema-image"
+msgstr "schema"
+
+msgid "schema-text"
+msgstr "description"
+
msgid "search"
msgstr ""
@@ -2569,9 +2623,6 @@
msgid "show filter form"
msgstr ""
-msgid "show meta-data"
-msgstr "show the complete schema"
-
msgid "sioc"
msgstr ""
@@ -2685,6 +2736,9 @@
msgid "thursday"
msgstr ""
+msgid "timeline"
+msgstr ""
+
msgid "timestamp"
msgstr ""
@@ -2957,14 +3011,26 @@
#~ msgid "add a Card"
#~ msgstr "add a card"
+#~ msgid "components_applmessages"
+#~ msgstr "application messages"
+
+#~ msgid "components_applmessages_description"
+#~ msgstr "display the application messages"
+
#~ msgid "content_format"
#~ msgstr "content format"
+#~ msgid "hide meta-data"
+#~ msgstr "hide meta entities and relations"
+
#~ msgid "planned_delivery"
#~ msgstr "planned delivery"
#~ msgid "remove this Card"
#~ msgstr "remove this card"
+#~ msgid "show meta-data"
+#~ msgstr "show the complete schema"
+
#~ msgid "wikiid"
#~ msgstr "wiki identifier"
--- a/i18n/es.po Thu Jul 02 10:36:25 2009 +0200
+++ b/i18n/es.po Mon Jul 06 19:55:18 2009 +0200
@@ -105,7 +105,7 @@
#, python-format
msgid "%d years"
-msgstr ""
+msgstr "%d años"
#, python-format
msgid "%s error report"
@@ -159,6 +159,14 @@
msgid "1?"
msgstr "1 0..1"
+#, python-format
+msgid ""
+"<div>This schema of the data model <em>excludes</em> the meta-data, but you "
+"can also display a <a href=\"%s\">complete schema with meta-data</a>.</div>"
+msgstr ""
+"<div>Este esquema del modelo de datos <em>no incluye</em> los meta-datos, pero "
+"se puede ver a un <a href=\"%s\">modelo completo con meta-datos</a>.</div>"
+
msgid "?*"
msgstr "0..1 0..n"
@@ -174,12 +182,18 @@
msgid "AND"
msgstr "Y"
+msgid "Add permissions"
+msgstr "Añadir autorizaciónes"
+
msgid "Any"
msgstr "Cualquiera"
msgid "Application"
msgstr "Aplicación"
+msgid "Attributes"
+msgstr "Atributos"
+
msgid "Bookmark"
msgstr "Favorito"
@@ -289,6 +303,9 @@
msgid "Decimal_plural"
msgstr "Decimales"
+msgid "Delete permissions"
+msgstr "Autorización de suprimir"
+
msgid "Do you want to delete the following element(s) ?"
msgstr "Desea suprimir el(los) elemento(s) siguiente(s)"
@@ -311,7 +328,7 @@
msgstr "Números flotantes"
msgid "From:"
-msgstr ""
+msgstr "De: "
msgid "Int"
msgstr "Número entero"
@@ -400,8 +417,11 @@
msgid "RQLExpression_plural"
msgstr "Expresiones RQL"
+msgid "Read permissions"
+msgstr "Autorización de leer"
+
msgid "Recipients:"
-msgstr ""
+msgstr "Destinatarios"
msgid "Relations"
msgstr "Relaciones"
@@ -413,6 +433,9 @@
msgid "Schema %s"
msgstr "Esquema %s"
+msgid "Schema of the data model"
+msgstr "Esquema del modelo de datos"
+
msgid "Search for"
msgstr "Buscar"
@@ -435,7 +458,7 @@
msgstr "Cadenas de caracteres"
msgid "Subject:"
-msgstr ""
+msgstr "Sujeto:"
msgid "Submit bug report"
msgstr "Enviar un reporte de error (bug)"
@@ -454,6 +477,9 @@
msgid "The view %s could not be found"
msgstr "La vista %s no ha podido ser encontrada"
+msgid "There is no workflow defined for this entity."
+msgstr "No hay workflow para este entidad"
+
#, python-format
msgid "This %s"
msgstr "Este %s"
@@ -531,6 +557,9 @@
msgid "Unable to find anything named \"%s\" in the schema !"
msgstr "No encontramos el nombre \"%s\" en el esquema"
+msgid "Update permissions"
+msgstr "Autorización de modificar"
+
msgid "Used by:"
msgstr "Utilizado por :"
@@ -683,7 +712,7 @@
msgstr ""
msgid "actions_managepermission"
-msgstr ""
+msgstr "Administración de autorizaciónes"
msgid "actions_managepermission_description"
msgstr ""
@@ -973,7 +1002,7 @@
msgstr "Atributo"
msgid "attributes with modified permissions:"
-msgstr ""
+msgstr "atributos con autorizaciónes modificadas:"
msgid "august"
msgstr "Agosto"
@@ -1008,6 +1037,9 @@
msgid "bookmarks"
msgstr "Favoritos"
+msgid "bookmarks are used to have user's specific internal links"
+msgstr "favoritos son usados para que un usuario recorde ligas"
+
msgid "boxes"
msgstr "Cajas"
@@ -1132,7 +1164,7 @@
msgstr "cardinalidad"
msgid "category"
-msgstr ""
+msgstr "categoria"
#, python-format
msgid "changed state of %(etype)s #%(eid)s (%(title)s)"
@@ -1141,6 +1173,9 @@
msgid "changes applied"
msgstr "Cambios realizados"
+msgid "click here to see created entity"
+msgstr "ver la entidad creada"
+
msgid "click on the box to cancel the deletion"
msgstr "Seleccione la zona de edición para cancelar la eliminación"
@@ -1162,12 +1197,6 @@
msgid "components_appliname_description"
msgstr "Muestra el título de la aplicación en el encabezado de la página"
-msgid "components_applmessages"
-msgstr "Mensajes de la aplicación"
-
-msgid "components_applmessages_description"
-msgstr "Muestra los mensajes de la aplicación"
-
msgid "components_breadcrumbs"
msgstr "Ruta de Navegación"
@@ -1474,7 +1503,19 @@
#, python-format
msgid "currently attached file: %s"
-msgstr ""
+msgstr "archivo adjunto: %s"
+
+msgid "cwetype-schema-image"
+msgstr "Esquema"
+
+msgid "cwetype-schema-permissions"
+msgstr "Autorizaciónes"
+
+msgid "cwetype-schema-text"
+msgstr "Modelo de datos"
+
+msgid "cwetype-workflow"
+msgstr "Workflow"
msgid "data directory url"
msgstr "Url del repertorio de datos"
@@ -1608,9 +1649,6 @@
msgid "detach attached file %s"
msgstr "Quitar archivo adjunto %s"
-msgid "detailed schema view"
-msgstr "Vista detallada del esquema"
-
msgid "display order of the action"
msgstr "Orden de aparición de la acción"
@@ -1685,16 +1723,19 @@
msgstr "Entidades eliminadas"
msgid "entity copied"
-msgstr ""
+msgstr "entidad copiada"
msgid "entity created"
-msgstr ""
+msgstr "entidad creada"
msgid "entity deleted"
msgstr "Entidad eliminada"
msgid "entity edited"
-msgstr ""
+msgstr "entidad modificada"
+
+msgid "entity linked"
+msgstr "entidad asociada"
msgid "entity type"
msgstr "Tipo de entidad"
@@ -1753,6 +1794,18 @@
msgid "facets_created_by-facet_description"
msgstr "faceta creado por"
+msgid "facets_cwfinal-facet"
+msgstr "faceta \"final\""
+
+msgid "facets_cwfinal-facet_description"
+msgstr "faceta para las entidades \"finales\""
+
+msgid "facets_cwmeta-facet"
+msgstr "faceta \"meta\""
+
+msgid "facets_cwmeta-facet_description"
+msgstr "faceta para las entidades \"meta\""
+
msgid "facets_etype-facet"
msgstr "faceta \"es de tipo\""
@@ -1809,7 +1862,7 @@
#, python-format
msgid "from %(date)s"
-msgstr ""
+msgstr "de %(date)s"
msgid "from_entity"
msgstr "De la entidad"
@@ -1836,7 +1889,7 @@
msgstr "Trazado de curbas estándares"
msgid "generic relation to link one entity to another"
-msgstr ""
+msgstr "relación generica para ligar entidades"
msgid "go back to the index page"
msgstr "Regresar a la página de inicio"
@@ -1897,9 +1950,6 @@
msgid "hide filter form"
msgstr "Esconder el filtro"
-msgid "hide meta-data"
-msgstr "Esconder los meta-datos"
-
msgid "home"
msgstr "Inicio"
@@ -2106,7 +2156,8 @@
msgid ""
"link a permission to the entity. This permission should be used in the "
"security definition of the entity's type to be useful."
-msgstr ""
+msgstr "relaciónar una autorización con la entidad. Este autorización debe "
+"ser usada en la definición de la entidad para ser utíl."
msgid ""
"link a property to the user which want this property customization. Unless "
@@ -2150,7 +2201,7 @@
msgstr "Clave de acesso"
msgid "login or email"
-msgstr ""
+msgstr "Clave de acesso o dirección de correo"
msgid "login_action"
msgstr "Ingresa tus datos"
@@ -2259,15 +2310,19 @@
msgid "navigation.combobox-limit"
msgstr ""
+# msgstr "Navegación: numero maximo de elementos en una caja de elección (combobox)"
msgid "navigation.page-size"
msgstr ""
+# msgstr "Navegación: numero maximo de elementos por pagina"
msgid "navigation.related-limit"
msgstr ""
+# msgstr "Navegación: numero maximo de elementos relacionados"
msgid "navigation.short-line-size"
msgstr ""
+#msgstr "Navegación: numero maximo de caracteres en una linéa corta"
msgid "navtop"
msgstr "Encabezado del contenido principal"
@@ -2282,7 +2337,7 @@
msgstr "no"
msgid "no associated permissions"
-msgstr ""
+msgstr "no autorización relacionada"
msgid "no possible transition"
msgstr "transición no posible"
@@ -2322,7 +2377,7 @@
msgstr "objeto"
msgid "object_plural:"
-msgstr ""
+msgstr "objetos:"
msgid "october"
msgstr "octubre"
@@ -2340,7 +2395,7 @@
msgstr "solo estan permitidas consultas de lectura"
msgid "open all"
-msgstr ""
+msgstr "abrir todos"
msgid "order"
msgstr "orden"
@@ -2349,10 +2404,10 @@
msgstr "orden"
msgid "owl"
-msgstr ""
+msgstr "owl"
msgid "owlabox"
-msgstr ""
+msgstr "owlabox"
msgid "owned_by"
msgstr "pertenece a"
@@ -2385,10 +2440,10 @@
msgstr "Permiso"
msgid "permissions for entities"
-msgstr ""
+msgstr "autorizaciónes para entidades"
msgid "permissions for relations"
-msgstr ""
+msgstr "autorizaciónes para relaciones"
msgid "permissions for this entity"
msgstr "Permisos para esta entidad"
@@ -2400,7 +2455,7 @@
msgstr "Seleccione los favoritos existentes"
msgid "pkey"
-msgstr ""
+msgstr "pkey"
msgid "please correct errors below"
msgstr "Favor de corregir errores"
@@ -2458,7 +2513,7 @@
msgstr "Definición"
msgid "relations"
-msgstr ""
+msgstr "relaciones"
msgid "relations deleted"
msgstr "Relaciones eliminadas"
@@ -2518,16 +2573,16 @@
msgstr "Eliminar esta transición"
msgid "require_group"
-msgstr "Requiere_grupo"
+msgstr "Requiere grupo"
msgid "require_group_object"
-msgstr "Objeto_grupo_requerido"
+msgstr "Requerido por grupo"
msgid "require_permission"
-msgstr ""
+msgstr "Requiere autorización"
msgid "require_permission_object"
-msgstr ""
+msgstr "Requerido por autorización"
msgid "required attribute"
msgstr "Atributo requerido"
@@ -2582,6 +2637,12 @@
msgid "schema's permissions definitions"
msgstr "definiciones de permisos del esquema"
+msgid "schema-image"
+msgstr "esquema imagen"
+
+msgid "schema-text"
+msgstr "esquema text"
+
msgid "search"
msgstr "buscar"
@@ -2601,7 +2662,7 @@
msgstr "Ver todos"
msgid "see_also"
-msgstr ""
+msgstr "Ver tambíen"
msgid "select"
msgstr "Seleccionar"
@@ -2610,7 +2671,7 @@
msgstr "seleccione un"
msgid "select a key first"
-msgstr ""
+msgstr "seleccione una clave"
msgid "select a relation"
msgstr "seleccione una relación"
@@ -2670,9 +2731,6 @@
msgid "show filter form"
msgstr "afficher le filtre"
-msgid "show meta-data"
-msgstr "mostrar meta-data"
-
msgid "sioc"
msgstr ""
@@ -2730,7 +2788,7 @@
msgstr "cardinalidad sujeto/objeto"
msgid "subject_plural:"
-msgstr ""
+msgstr "sujetos:"
msgid "sunday"
msgstr "domingo"
@@ -2787,6 +2845,9 @@
msgid "thursday"
msgstr "jueves"
+msgid "timeline"
+msgstr ""
+
msgid "timestamp"
msgstr "fecha"
@@ -2804,7 +2865,7 @@
#, python-format
msgid "to %(date)s"
-msgstr ""
+msgstr "a %(date)s"
msgid "to associate with"
msgstr "a asociar con"
@@ -2825,7 +2886,7 @@
msgstr "a hacer por"
msgid "toggle check boxes"
-msgstr ""
+msgstr "cambiar valor"
msgid "transition is not allowed"
msgstr "transition no permitida"
@@ -2894,7 +2955,7 @@
msgstr "propiedad desconocida"
msgid "up"
-msgstr ""
+msgstr "arriba"
msgid "upassword"
msgstr "clave de acceso"
@@ -3055,7 +3116,7 @@
msgstr "ha terminado la sesion"
msgid "you should probably delete that property"
-msgstr ""
+msgstr "deberia probablamente suprimir esta propriedad"
#~ msgid "%s constraint failed"
#~ msgstr "La contrainte %s n'est pas satisfaite"
@@ -3121,6 +3182,12 @@
#~ msgid "cancel edition"
#~ msgstr "annuler l'Èdition"
+#~ msgid "components_applmessages"
+#~ msgstr "Mensajes de la aplicación"
+
+#~ msgid "components_applmessages_description"
+#~ msgstr "Muestra los mensajes de la aplicación"
+
#~ msgid "components_rss_feed_url"
#~ msgstr "RSS FEED URL"
@@ -3140,6 +3207,9 @@
#~ "langue par dÈfaut (regarder le rÈpertoire i18n de l'application pour voir "
#~ "les langues disponibles)"
+#~ msgid "detailed schema view"
+#~ msgstr "Vista detallada del esquema"
+
#~ msgid "filter"
#~ msgstr "filtrer"
@@ -3149,6 +3219,9 @@
#~ msgid "header"
#~ msgstr "en-tÍte de page"
+#~ msgid "hide meta-data"
+#~ msgstr "Esconder los meta-datos"
+
#~ msgid "iCal"
#~ msgstr "iCal"
@@ -3190,6 +3263,9 @@
#~ msgid "see also"
#~ msgstr "voir aussi"
+#~ msgid "show meta-data"
+#~ msgstr "mostrar meta-data"
+
#~ msgid "status will change from %s to %s"
#~ msgstr "l'Ètat va passer de %s ‡ %s"
--- a/i18n/fr.po Thu Jul 02 10:36:25 2009 +0200
+++ b/i18n/fr.po Mon Jul 06 19:55:18 2009 +0200
@@ -159,6 +159,14 @@
msgid "1?"
msgstr "1 0..1"
+#, python-format
+msgid ""
+"<div>This schema of the data model <em>excludes</em> the meta-data, but you "
+"can also display a <a href=\"%s\">complete schema with meta-data</a>.</div>"
+msgstr ""
+"<div>Ce schéma du modèle de données <em>exclue</em> les méta-données, mais vous "
+"pouvez afficher un <a href=\"%s\">schéma complet</a>.</div>"
+
msgid "?*"
msgstr "0..1 0..n"
@@ -174,12 +182,18 @@
msgid "AND"
msgstr "ET"
+msgid "Add permissions"
+msgstr "Permissions d'ajouter"
+
msgid "Any"
msgstr "N'importe"
msgid "Application"
msgstr "Application"
+msgid "Attributes"
+msgstr "Attributs"
+
msgid "Bookmark"
msgstr "Signet"
@@ -289,6 +303,9 @@
msgid "Decimal_plural"
msgstr "Nombres décimaux"
+msgid "Delete permissions"
+msgstr "Permissions de supprimer"
+
msgid "Do you want to delete the following element(s) ?"
msgstr "Voulez vous supprimer le(s) élément(s) suivant(s)"
@@ -400,6 +417,9 @@
msgid "RQLExpression_plural"
msgstr "Expressions RQL"
+msgid "Read permissions"
+msgstr "Permissions de lire"
+
msgid "Recipients:"
msgstr "Destinataires :"
@@ -413,6 +433,9 @@
msgid "Schema %s"
msgstr "Schéma %s"
+msgid "Schema of the data model"
+msgstr "Schéma du modèle de données"
+
msgid "Search for"
msgstr "Rechercher"
@@ -454,6 +477,9 @@
msgid "The view %s could not be found"
msgstr "La vue %s est introuvable"
+msgid "There is no workflow defined for this entity."
+msgstr "Il n'y a pas de workflow défini pour ce type d'entité"
+
#, python-format
msgid "This %s"
msgstr "Ce %s"
@@ -531,6 +557,9 @@
msgid "Unable to find anything named \"%s\" in the schema !"
msgstr "Rien de nommé \"%s\" dans le schéma"
+msgid "Update permissions"
+msgstr "Permissions de modifier"
+
msgid "Used by:"
msgstr "Utilisé par :"
@@ -604,6 +633,9 @@
"invalidate the cache (typically in hooks). Also, checkout the AppRsetObject."
"get_cache() method."
msgstr ""
+"une simple entité de cache, caractérisées par un nom et une date de validité. L'application "
+"est responsable de la mise à jour de la date quand il est nécessaire d'invalider le cache (typiquement dans les crochets). "
+"Voir aussi la méthode get_cache() sur la classe AppRsetObject."
msgid "about this site"
msgstr "à propos de ce site"
@@ -645,7 +677,7 @@
msgstr ""
msgid "actions_download_as_owl"
-msgstr ""
+msgstr "télécharger en owl"
msgid "actions_download_as_owl_description"
msgstr ""
@@ -681,7 +713,7 @@
msgstr ""
msgid "actions_managepermission"
-msgstr ""
+msgstr "gestion des permissions"
msgid "actions_managepermission_description"
msgstr ""
@@ -964,7 +996,7 @@
#, python-format
msgid "at least one relation %(rtype)s is required on %(etype)s (%(eid)s)"
msgstr ""
-"L'entité #%(eid)s de type %(etype)s doit nécessairement être reliée à une\n"
+"l'entité #%(eid)s de type %(etype)s doit nécessairement être reliée à une\n"
"autre via la relation %(rtype)s"
msgid "attribute"
@@ -1006,6 +1038,9 @@
msgid "bookmarks"
msgstr "signets"
+msgid "bookmarks are used to have user's specific internal links"
+msgstr "les signets sont utilisés pour gérer des liens internes par utilisateur"
+
msgid "boxes"
msgstr "boîtes"
@@ -1139,6 +1174,9 @@
msgid "changes applied"
msgstr "changements appliqués"
+msgid "click here to see created entity"
+msgstr "cliquez ici pour voir l'entité créée"
+
msgid "click on the box to cancel the deletion"
msgstr "cliquer dans la zone d'édition pour annuler la suppression"
@@ -1160,12 +1198,6 @@
msgid "components_appliname_description"
msgstr "affiche le titre de l'application dans l'en-tête de page"
-msgid "components_applmessages"
-msgstr "messages applicatifs"
-
-msgid "components_applmessages_description"
-msgstr "affiche les messages applicatifs"
-
msgid "components_breadcrumbs"
msgstr "fil d'ariane"
@@ -1475,6 +1507,18 @@
msgid "currently attached file: %s"
msgstr "fichie actuellement attaché %s"
+msgid "cwetype-schema-image"
+msgstr "schéma"
+
+msgid "cwetype-schema-permissions"
+msgstr "permissions"
+
+msgid "cwetype-schema-text"
+msgstr "description"
+
+msgid "cwetype-workflow"
+msgstr "workflow"
+
msgid "data directory url"
msgstr "url du répertoire de données"
@@ -1606,9 +1650,6 @@
msgid "detach attached file %s"
msgstr "détacher le fichier existant %s"
-msgid "detailed schema view"
-msgstr "vue détaillée du schéma"
-
msgid "display order of the action"
msgstr "ordre d'affichage de l'action"
@@ -1694,6 +1735,9 @@
msgid "entity edited"
msgstr "entité éditée"
+msgid "entity linked"
+msgstr "entité liée"
+
msgid "entity type"
msgstr "type d'entité"
@@ -1750,6 +1794,18 @@
msgid "facets_created_by-facet_description"
msgstr ""
+msgid "facets_cwfinal-facet"
+msgstr "facette \"type d'entité ou de relation final\""
+
+msgid "facets_cwfinal-facet_description"
+msgstr ""
+
+msgid "facets_cwmeta-facet"
+msgstr ""
+
+msgid "facets_cwmeta-facet_description"
+msgstr ""
+
msgid "facets_etype-facet"
msgstr "facette \"est de type\""
@@ -1895,9 +1951,6 @@
msgid "hide filter form"
msgstr "cacher le filtre"
-msgid "hide meta-data"
-msgstr "cacher les entités et relations \"méta\""
-
msgid "home"
msgstr "maison"
@@ -2106,8 +2159,8 @@
"link a permission to the entity. This permission should be used in the "
"security definition of the entity's type to be useful."
msgstr ""
-"lie une permission à une entité. Cette permission doit généralement être utilisée "
-"dans la définition de sécurité du type d'entité pour être utile."
+"lie une permission à une entité. Cette permission doit généralement être "
+"utilisée dans la définition de sécurité du type d'entité pour être utile."
msgid ""
"link a property to the user which want this property customization. Unless "
@@ -2590,6 +2643,12 @@
msgid "schema's permissions definitions"
msgstr "permissions définies dans le schéma"
+msgid "schema-image"
+msgstr "schéma"
+
+msgid "schema-text"
+msgstr "description"
+
msgid "search"
msgstr "rechercher"
@@ -2678,9 +2737,6 @@
msgid "show filter form"
msgstr "afficher le filtre"
-msgid "show meta-data"
-msgstr "afficher le schéma complet"
-
msgid "sioc"
msgstr "sioc"
@@ -2795,6 +2851,9 @@
msgid "thursday"
msgstr "jeudi"
+msgid "timeline"
+msgstr "échelle de temps"
+
msgid "timestamp"
msgstr "date"
@@ -3133,6 +3192,12 @@
#~ msgid "close all"
#~ msgstr "tout fermer"
+#~ msgid "components_applmessages"
+#~ msgstr "messages applicatifs"
+
+#~ msgid "components_applmessages_description"
+#~ msgstr "affiche les messages applicatifs"
+
#~ msgid "components_rss_feed_url"
#~ msgstr "syndication rss"
@@ -3149,6 +3214,9 @@
#~ "langue par défaut (regarder le répertoire i18n de l'application pour voir "
#~ "les langues disponibles)"
+#~ msgid "detailed schema view"
+#~ msgstr "vue détaillée du schéma"
+
#~ msgid "filter"
#~ msgstr "filtrer"
@@ -3158,6 +3226,9 @@
#~ msgid "header"
#~ msgstr "en-tête de page"
+#~ msgid "hide meta-data"
+#~ msgstr "cacher les entités et relations \"méta\""
+
#~ msgid "iCal"
#~ msgstr "iCal"
@@ -3193,6 +3264,9 @@
#~ msgid "see also"
#~ msgstr "voir aussi"
+#~ msgid "show meta-data"
+#~ msgstr "afficher le schéma complet"
+
#~ msgid "status will change from %s to %s"
#~ msgstr "l'état va passer de %s à %s"
--- a/misc/migration/bootstrapmigration_repository.py Thu Jul 02 10:36:25 2009 +0200
+++ b/misc/migration/bootstrapmigration_repository.py Mon Jul 06 19:55:18 2009 +0200
@@ -8,6 +8,8 @@
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
"""
+applcubicwebversion, cubicwebversion = versions_map['cubicweb']
+
if applcubicwebversion < (3, 2, 2) and cubicwebversion >= (3, 2, 1):
from base64 import b64encode
for table in ('entities', 'deleted_entities'):
--- a/schema.py Thu Jul 02 10:36:25 2009 +0200
+++ b/schema.py Mon Jul 06 19:55:18 2009 +0200
@@ -354,6 +354,7 @@
"""rql expression factory"""
return ERQLExpression(expression, mainvars, eid)
+
class CubicWebRelationSchema(RelationSchema):
RelationSchema._RPROPERTIES['eid'] = None
_perms_checked = False
--- a/selectors.py Thu Jul 02 10:36:25 2009 +0200
+++ b/selectors.py Mon Jul 06 19:55:18 2009 +0200
@@ -482,8 +482,7 @@
def __call__(self, cls, req, *args, **kwargs):
score = 0
for param in self.expected:
- val = req.form.get(param)
- if not val:
+ if not param in req.form:
return 0
score += 1
return len(self.expected)
@@ -623,6 +622,14 @@
etype = kwargs['etype']
except KeyError:
return 0
+ else:
+ # only check this is a known type if etype comes from req.form,
+ # else we want the error to propagate
+ try:
+ etype = cls.vreg.case_insensitive_etypes[etype.lower()]
+ req.form['etype'] = etype
+ except KeyError:
+ return 0
return self.score_class(cls.vreg.etype_class(etype), req)
--- a/server/checkintegrity.py Thu Jul 02 10:36:25 2009 +0200
+++ b/server/checkintegrity.py Mon Jul 06 19:55:18 2009 +0200
@@ -71,6 +71,14 @@
uniquecstrcheck_before_modification)
from cubicweb.server.repository import FTIndexEntityOp
repo = session.repo
+ cursor = session.pool['system']
+ if not repo.system_source.indexer.has_fti_table(cursor):
+ from indexer import get_indexer
+ print 'no text index table'
+ indexer = get_indexer(repo.system_source.dbdriver)
+ # XXX indexer.init_fti(cursor) once index 0.7 is out
+ indexer.init_extensions(cursor)
+ cursor.execute(indexer.sql_init_fti())
repo.hm.unregister_hook(setmtime_before_update_entity,
'before_update_entity', '')
repo.hm.unregister_hook(uniquecstrcheck_before_modification,
--- a/server/hooks.py Thu Jul 02 10:36:25 2009 +0200
+++ b/server/hooks.py Mon Jul 06 19:55:18 2009 +0200
@@ -222,9 +222,10 @@
return
if self.session.unsafe_execute(*self._rql()).rowcount < 1:
etype = self.session.describe(self.eid)[0]
- msg = self.session._('at least one relation %(rtype)s is required on %(etype)s (%(eid)s)')
- raise ValidationError(self.eid, {self.rtype: msg % {'rtype': self.rtype,
- 'etype': etype,
+ _ = self.session._
+ msg = _('at least one relation %(rtype)s is required on %(etype)s (%(eid)s)')
+ raise ValidationError(self.eid, {self.rtype: msg % {'rtype': _(self.rtype),
+ 'etype': _(etype),
'eid': self.eid}})
def commit_event(self):
--- a/server/migractions.py Thu Jul 02 10:36:25 2009 +0200
+++ b/server/migractions.py Mon Jul 06 19:55:18 2009 +0200
@@ -64,25 +64,48 @@
self.fs_schema = schema
self._synchronized = set()
+ # overriden from base MigrationHelper ######################################
+
@cached
def repo_connect(self):
self.repo = get_repository(method='inmemory', config=self.config)
return self.repo
+ def cube_upgraded(self, cube, version):
+ self.cmd_set_property('system.version.%s' % cube.lower(),
+ unicode(version))
+ self.commit()
+
def shutdown(self):
if self.repo is not None:
self.repo.shutdown()
- def rewrite_vcconfiguration(self):
- """write current installed versions (of cubicweb software
- and of each used cube) into the database
+ def migrate(self, vcconf, toupgrade, options):
+ if not options.fs_only:
+ if options.backup_db is None:
+ self.backup_database()
+ elif options.backup_db:
+ self.backup_database(askconfirm=False)
+ super(ServerMigrationHelper, self).migrate(vcconf, toupgrade, options)
+
+ def process_script(self, migrscript, funcname=None, *args, **kwargs):
+ """execute a migration script
+ in interactive mode, display the migration script path, ask for
+ confirmation and execute it if confirmed
"""
- self.cmd_set_property('system.version.cubicweb',
- self.config.cubicweb_version())
- for pkg in self.config.cubes():
- pkgversion = self.config.cube_version(pkg)
- self.cmd_set_property('system.version.%s' % pkg.lower(), pkgversion)
- self.commit()
+ try:
+ if migrscript.endswith('.sql'):
+ if self.execscript_confirm(migrscript):
+ sqlexec(open(migrscript).read(), self.session.system_sql)
+ else:
+ return super(ServerMigrationHelper, self).process_script(
+ migrscript, funcname, *args, **kwargs)
+ self.commit()
+ except:
+ self.rollback()
+ raise
+
+ # server specific migration methods ########################################
def backup_database(self, backupfile=None, askconfirm=True):
config = self.config
@@ -142,26 +165,6 @@
break
print 'database restored'
- def migrate(self, vcconf, toupgrade, options):
- if not options.fs_only:
- if options.backup_db is None:
- self.backup_database()
- elif options.backup_db:
- self.backup_database(askconfirm=False)
- super(ServerMigrationHelper, self).migrate(vcconf, toupgrade, options)
-
- def process_script(self, migrscript, funcname=None, *args, **kwargs):
- """execute a migration script
- in interactive mode, display the migration script path, ask for
- confirmation and execute it if confirmed
- """
- if migrscript.endswith('.sql'):
- if self.execscript_confirm(migrscript):
- sqlexec(open(migrscript).read(), self.session.system_sql)
- else:
- return super(ServerMigrationHelper, self).process_script(
- migrscript, funcname, *args, **kwargs)
-
@property
def cnx(self):
"""lazy connection"""
--- a/server/repository.py Thu Jul 02 10:36:25 2009 +0200
+++ b/server/repository.py Mon Jul 06 19:55:18 2009 +0200
@@ -392,10 +392,23 @@
except ZeroDivisionError:
pass
+ def _login_from_email(self, login):
+ session = self.internal_session()
+ try:
+ rset = session.execute('Any L WHERE U login L, U primary_email M, '
+ 'M address %(login)s', {'login': login})
+ if rset.rowcount == 1:
+ login = rset[0][0]
+ finally:
+ session.close()
+ return login
+
def authenticate_user(self, session, login, password):
"""validate login / password, raise AuthenticationError on failure
return associated CWUser instance on success
"""
+ if self.vreg.config['allow-email-login'] and '@' in login:
+ login = self._login_from_email(login)
for source in self.sources:
if source.support_entity('CWUser'):
try:
@@ -405,11 +418,11 @@
continue
else:
raise AuthenticationError('authentication failed with all sources')
- euser = self._build_user(session, eid)
+ cwuser = self._build_user(session, eid)
if self.config.consider_user_state and \
- not euser.state in euser.AUTHENTICABLE_STATES:
+ not cwuser.state in cwuser.AUTHENTICABLE_STATES:
raise AuthenticationError('user is not in authenticable state')
- return euser
+ return cwuser
def _build_user(self, session, eid):
"""return a CWUser entity for user with the given eid"""
@@ -417,13 +430,13 @@
rql = cls.fetch_rql(session.user, ['X eid %(x)s'])
rset = session.execute(rql, {'x': eid}, 'x')
assert len(rset) == 1, rset
- euser = rset.get_entity(0, 0)
+ cwuser = rset.get_entity(0, 0)
# pylint: disable-msg=W0104
- # prefetch / cache euser's groups and properties. This is especially
+ # prefetch / cache cwuser's groups and properties. This is especially
# useful for internal sessions to avoid security insertions
- euser.groups
- euser.properties
- return euser
+ cwuser.groups
+ cwuser.properties
+ return cwuser
# public (dbapi) interface ################################################
@@ -664,13 +677,22 @@
custom properties)
"""
session = self._get_session(sessionid, setpool=False)
- if props:
- # update session properties
- for prop, value in props.items():
- session.change_property(prop, value)
+ if props is not None:
+ self.set_session_props(sessionid, props)
user = session.user
return user.eid, user.login, user.groups, user.properties
+ def set_session_props(self, sessionid, props):
+ """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)
+ """
+ session = self._get_session(sessionid, setpool=False)
+ # update session properties
+ for prop, value in props.items():
+ session.change_property(prop, value)
+
# public (inter-repository) interface #####################################
def entities_modified_since(self, etypes, mtime):
--- a/server/schemahooks.py Thu Jul 02 10:36:25 2009 +0200
+++ b/server/schemahooks.py Mon Jul 06 19:55:18 2009 +0200
@@ -247,8 +247,7 @@
"""actually add the entity type to the application's schema"""
eid = None # make pylint happy
def commit_event(self):
- eschema = self.schema.add_entity_type(self.kobj)
- eschema.eid = self.eid
+ self.schema.add_entity_type(self.kobj)
def before_add_eetype(session, entity):
"""before adding a CWEType entity:
@@ -299,7 +298,8 @@
# register operation to modify the schema on commit
# this have to be done before adding other relations definitions
# or permission settings
- AddCWETypeOp(session, etype, eid=entity.eid)
+ etype.eid = entity.eid
+ AddCWETypeOp(session, etype)
# add meta creation_date, modification_date and owned_by relations
for rql, kwargs in relrqls:
session.execute(rql, kwargs)
@@ -311,7 +311,6 @@
def commit_event(self):
rschema = self.schema.add_relation_type(self.kobj)
rschema.set_default_groups()
- rschema.eid = self.eid
def before_add_ertype(session, entity):
"""before adding a CWRType entity:
@@ -331,12 +330,13 @@
schema on commit
We don't know yeat this point if a table is necessary
"""
- AddCWRTypeOp(session, RelationType(name=entity['name'],
- description=entity.get('description'),
- meta=entity.get('meta', False),
- inlined=entity.get('inlined', False),
- symetric=entity.get('symetric', False)),
- eid=entity.eid)
+ rtype = RelationType(name=entity['name'],
+ description=entity.get('description'),
+ meta=entity.get('meta', False),
+ inlined=entity.get('inlined', False),
+ symetric=entity.get('symetric', False))
+ rtype.eid = entity.eid
+ AddCWRTypeOp(session, rtype)
class AddErdefOp(EarlySchemaOperation):
--- a/server/sources/pyrorql.py Thu Jul 02 10:36:25 2009 +0200
+++ b/server/sources/pyrorql.py Mon Jul 06 19:55:18 2009 +0200
@@ -164,7 +164,12 @@
"""
self.info('synchronizing pyro source %s', self.uri)
cnx = self.get_connection()
- extrepo = cnx._repo
+ try:
+ extrepo = cnx._repo
+ except AttributeError:
+ # fake connection wrapper returned when we can't connect to the
+ # external source (hence we've no chance to synchronize...)
+ return
etypes = self.support_entities.keys()
if mtime is None:
mtime = self.last_update_time()
@@ -212,7 +217,7 @@
nsgroup = self.config.get('pyro-ns-group') or self.repo.config['pyro-ns-group']
#cnxprops = ConnectionProperties(cnxtype=self.config['cnx-type'])
return dbapi.connect(database=self.config['pyro-ns-id'],
- user=self.config['cubicweb-user'],
+ login=self.config['cubicweb-user'],
password=self.config['cubicweb-password'],
host=nshost, port=nsport, group=nsgroup,
setvreg=False) #cnxprops=cnxprops)
--- a/server/test/unittest_hooks.py Thu Jul 02 10:36:25 2009 +0200
+++ b/server/test/unittest_hooks.py Mon Jul 06 19:55:18 2009 +0200
@@ -242,7 +242,7 @@
class SchemaModificationHooksTC(RepositoryBasedTC):
- copy_schema = True
+ #copy_schema = True
def setUp(self):
if not hasattr(self, '_repo'):
@@ -471,6 +471,10 @@
self.commit()
# should not be able anymore to add personne without prenom
self.assertRaises(ValidationError, self.execute, 'INSERT Personne X: X nom "toto"')
+ self.execute('SET DEF cardinality "?1" '
+ 'WHERE DEF relation_type RT, DEF from_entity E,'
+ 'RT name "prenom", E name "Personne"')
+ self.commit()
class WorkflowHooksTC(RepositoryBasedTC):
--- a/server/test/unittest_migractions.py Thu Jul 02 10:36:25 2009 +0200
+++ b/server/test/unittest_migractions.py Mon Jul 06 19:55:18 2009 +0200
@@ -23,7 +23,7 @@
class MigrationCommandsTC(RepositoryBasedTC):
- copy_schema = True
+ copy_schema = False
def setUp(self):
if not hasattr(self, '_repo'):
@@ -146,7 +146,7 @@
for cstr in eschema.constraints('name'):
self.failUnless(hasattr(cstr, 'eid'))
- def test_drop_entity_type(self):
+ def test_add_drop_entity_type(self):
self.mh.cmd_add_entity_type('Folder2')
todoeid = self.mh.cmd_add_state(u'todo', 'Folder2', initial=True)
doneeid = self.mh.cmd_add_state(u'done', 'Folder2')
@@ -161,7 +161,7 @@
self.failIf(self.execute('State X WHERE NOT X state_of ET'))
self.failIf(self.execute('Transition X WHERE NOT X transition_of ET'))
- def test_add_relation_type(self):
+ def test_add_drop_relation_type(self):
self.mh.cmd_add_entity_type('Folder2', auto=False)
self.mh.cmd_add_relation_type('filed_under2')
self.failUnless('filed_under2' in self.schema)
@@ -169,52 +169,40 @@
['Affaire', 'Card', 'Division', 'Email', 'EmailThread', 'File',
'Folder2', 'Image', 'Note', 'Personne', 'Societe', 'SubDivision'])
self.assertEquals(self.schema['filed_under2'].objects(), ('Folder2',))
-
-
- def test_drop_relation_type(self):
- self.mh.cmd_add_entity_type('Folder2', auto=False)
- self.mh.cmd_add_relation_type('filed_under2')
- self.failUnless('filed_under2' in self.schema)
self.mh.cmd_drop_relation_type('filed_under2')
self.failIf('filed_under2' in self.schema)
- def test_add_relation_definition(self):
- self.mh.cmd_add_relation_definition('Societe', 'in_state', 'State')
- self.assertEquals(sorted(str(x) for x in self.schema['in_state'].subjects()),
- ['Affaire', 'CWUser', 'Division', 'Note', 'Societe', 'SubDivision'])
- self.assertEquals(self.schema['in_state'].objects(), ('State',))
-
def test_add_relation_definition_nortype(self):
self.mh.cmd_add_relation_definition('Personne', 'concerne2', 'Affaire')
self.assertEquals(self.schema['concerne2'].subjects(),
('Personne',))
self.assertEquals(self.schema['concerne2'].objects(), ('Affaire',))
+ self.mh.cmd_drop_relation_definition('Personne', 'concerne2', 'Affaire')
+ self.failIf('concerne2' in self.schema)
- def test_drop_relation_definition1(self):
- self.failUnless('concerne' in self.schema)
+ def test_drop_relation_definition_existant_rtype(self):
self.assertEquals(sorted(str(e) for e in self.schema['concerne'].subjects()), ['Affaire', 'Personne'])
self.assertEquals(sorted(str(e) for e in self.schema['concerne'].objects()), ['Affaire', 'Division', 'Note', 'Societe', 'SubDivision'])
self.mh.cmd_drop_relation_definition('Personne', 'concerne', 'Affaire')
self.assertEquals(sorted(str(e) for e in self.schema['concerne'].subjects()), ['Affaire'])
self.assertEquals(sorted(str(e) for e in self.schema['concerne'].objects()), ['Division', 'Note', 'Societe', 'SubDivision'])
+ self.mh.cmd_add_relation_definition('Personne', 'concerne', 'Affaire')
+ self.assertEquals(sorted(str(e) for e in self.schema['concerne'].subjects()), ['Affaire', 'Personne'])
+ self.assertEquals(sorted(str(e) for e in self.schema['concerne'].objects()), ['Affaire', 'Division', 'Note', 'Societe', 'SubDivision'])
+ # trick: overwrite self.maxeid to avoid deletion of just reintroduced types
+ self.maxeid = self.execute('Any MAX(X)')[0][0]
def test_drop_relation_definition_with_specialization(self):
- self.failUnless('concerne' in self.schema)
self.assertEquals(sorted(str(e) for e in self.schema['concerne'].subjects()), ['Affaire', 'Personne'])
self.assertEquals(sorted(str(e) for e in self.schema['concerne'].objects()), ['Affaire', 'Division', 'Note', 'Societe', 'SubDivision'])
self.mh.cmd_drop_relation_definition('Affaire', 'concerne', 'Societe')
- self.mh.cmd_drop_relation_definition('None', 'concerne', 'Societe')
self.assertEquals(sorted(str(e) for e in self.schema['concerne'].subjects()), ['Affaire', 'Personne'])
self.assertEquals(sorted(str(e) for e in self.schema['concerne'].objects()), ['Affaire', 'Note'])
-
- def test_drop_relation_definition2(self):
- self.failUnless('evaluee' in self.schema)
- self.mh.cmd_drop_relation_definition('Personne', 'evaluee', 'Note')
- self.failUnless('evaluee' in self.schema)
- self.assertEquals(sorted(self.schema['evaluee'].subjects()),
- ['CWUser', 'Division', 'Societe', 'SubDivision'])
- self.assertEquals(sorted(self.schema['evaluee'].objects()),
- ['Note'])
+ self.mh.cmd_add_relation_definition('Affaire', 'concerne', 'Societe')
+ self.assertEquals(sorted(str(e) for e in self.schema['concerne'].subjects()), ['Affaire', 'Personne'])
+ self.assertEquals(sorted(str(e) for e in self.schema['concerne'].objects()), ['Affaire', 'Division', 'Note', 'Societe', 'SubDivision'])
+ # trick: overwrite self.maxeid to avoid deletion of just reintroduced types
+ self.maxeid = self.execute('Any MAX(X)')[0][0]
def test_rename_relation(self):
self.skip('implement me')
@@ -455,7 +443,6 @@
ex = self.assertRaises(ConfigurationError, self.mh.cmd_remove_cube, 'file')
self.assertEquals(str(ex), "can't remove cube file, used as a dependency")
-
def test_set_state(self):
user = self.session.user
self.mh.set_state(user.eid, 'deactivated')
--- a/test/unittest_rset.py Thu Jul 02 10:36:25 2009 +0200
+++ b/test/unittest_rset.py Mon Jul 06 19:55:18 2009 +0200
@@ -6,7 +6,7 @@
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
"""
-from __future__ import with_statement
+#from __future__ import with_statement
from logilab.common.testlib import TestCase, unittest_main
--- a/utils.py Thu Jul 02 10:36:25 2009 +0200
+++ b/utils.py Mon Jul 06 19:55:18 2009 +0200
@@ -206,8 +206,13 @@
def add_post_inline_script(self, content):
self.post_inlined_scripts.append(content)
- def add_onload(self, jscode):
- self.add_post_inline_script(u"""jQuery(document).ready(function () {
+ def add_onload(self, jscode, jsoncall=False):
+ if jsoncall:
+ self.add_post_inline_script(u"""jQuery(CubicWeb).bind('ajax-loaded', function(event) {
+%s
+});""" % jscode)
+ else:
+ self.add_post_inline_script(u"""jQuery(document).ready(function () {
%s
});""" % jscode)
--- a/web/application.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/application.py Mon Jul 06 19:55:18 2009 +0200
@@ -21,8 +21,7 @@
from cubicweb.web import LOGGER, component
from cubicweb.web import (
StatusResponse, DirectResponse, Redirect, NotFound,
- RemoteCallFailed, ExplicitLogin, InvalidSession)
-from cubicweb.web.component import Component
+ RemoteCallFailed, ExplicitLogin, InvalidSession, RequestError)
# make session manager available through a global variable so the debug view can
# print information about web session
@@ -340,7 +339,7 @@
raise
except ValidationError, ex:
self.validation_error_handler(req, ex)
- except (Unauthorized, BadRQLQuery), ex:
+ except (Unauthorized, BadRQLQuery, RequestError), ex:
self.error_handler(req, ex, tb=False)
except Exception, ex:
self.error_handler(req, ex, tb=True)
@@ -396,8 +395,8 @@
return self.vreg.main_template(req, template, view=view)
def main_template_id(self, req):
- template = req.property_value('ui.main-template')
- if template not in self.vreg.registry('views') :
+ template = req.form.get('__template', req.property_value('ui.main-template'))
+ if template not in self.vreg.registry('views'):
template = 'main-template'
return template
--- a/web/component.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/component.py Mon Jul 06 19:55:18 2009 +0200
@@ -37,7 +37,7 @@
property_defs = {
_('visible'): dict(type='Boolean', default=True,
- help=_('display the box or not')),
+ help=_('display the component or not')),
_('order'): dict(type='Int', default=99,
help=_('display order of the component')),
_('context'): dict(type='String', default='header',
--- a/web/data/cubicweb.ajax.js Thu Jul 02 10:36:25 2009 +0200
+++ b/web/data/cubicweb.ajax.js Mon Jul 06 19:55:18 2009 +0200
@@ -11,15 +11,20 @@
function _loadAjaxHtmlHead(node, head, tag, srcattr) {
var loaded = [];
- jQuery('head ' + tag).each(function(i) {
+ var jqtagfilter = tag + '[' + srcattr + ']';
+ jQuery('head ' + jqtagfilter).each(function(i) {
loaded.push(this.getAttribute(srcattr));
});
node.find(tag).each(function(i) {
- if (!loaded.contains(this.getAttribute(srcattr))) {
+ if (this.getAttribute(srcattr)) {
+ if (!loaded.contains(this.getAttribute(srcattr))) {
+ jQuery(this).appendTo(head);
+ }
+ } else {
jQuery(this).appendTo(head);
}
});
- node.find(tag).remove();
+ node.find(jqtagfilter).remove();
}
/*
--- a/web/data/cubicweb.css Thu Jul 02 10:36:25 2009 +0200
+++ b/web/data/cubicweb.css Mon Jul 06 19:55:18 2009 +0200
@@ -448,14 +448,11 @@
background: #cfceb7;
display: block;
font: bold 100% Georgia;
- padding : 2px 0;
}
div.sideBox {
padding: 0 0 0.2em;
margin-bottom: 0.5em;
- min-width: 21em;
- max-width: 50em;
}
ul.sideBox li{
@@ -575,8 +572,6 @@
}
div.primaryRight{
- float:right;
-
}
div.metadata {
@@ -687,7 +682,7 @@
/***************************************/
table.listing {
- margin: 10px 0em;
+ padding: 10px 0em;
color: #000;
width: 100%;
border-right: 1px solid #dfdfdf;
--- a/web/data/cubicweb.edition.js Thu Jul 02 10:36:25 2009 +0200
+++ b/web/data/cubicweb.edition.js Mon Jul 06 19:55:18 2009 +0200
@@ -305,7 +305,9 @@
}
function _clearPreviousErrors(formid) {
+ jQuery('#' + formid + 'ErrorMessage').remove();
jQuery('#' + formid + ' span.error').remove();
+ jQuery('#' + formid + ' .error').removeClass('error');
}
function _displayValidationerrors(formid, eid, errors) {
@@ -324,7 +326,7 @@
field.before(span);
} else {
firsterrfield = formid;
- globalerrors.push(fieldname + ': ' + errmsg);
+ globalerrors.push(_(fieldname) + ' : ' + errmsg);
}
}
if (globalerrors.length) {
@@ -334,7 +336,7 @@
var innernode = UL(null, map(LI, globalerrors));
}
// insert DIV and innernode before the form
- var div = DIV({'class' : "errorMessage"});
+ var div = DIV({'class' : "errorMessage", 'id': formid + 'ErrorMessage'});
div.appendChild(innernode);
jQuery('#' + formid).before(div);
}
--- a/web/formfields.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/formfields.py Mon Jul 06 19:55:18 2009 +0200
@@ -7,6 +7,7 @@
"""
__docformat__ = "restructuredtext en"
+from warnings import warn
from datetime import datetime
from logilab.mtconverter import html_escape
--- a/web/request.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/request.py Mon Jul 06 19:55:18 2009 +0200
@@ -61,6 +61,7 @@
class CubicWebRequestBase(DBAPIRequest):
"""abstract HTTP request, should be extended according to the HTTP backend"""
+ json_request = False # to be set to True by json controllers
def __init__(self, vreg, https, form=None):
super(CubicWebRequestBase, self).__init__(vreg)
@@ -91,7 +92,7 @@
or an anonymous connection is open
"""
super(CubicWebRequestBase, self).set_connection(cnx, user)
- # get request language:
+ # set request language
vreg = self.vreg
if self.user:
try:
@@ -114,6 +115,7 @@
def set_language(self, lang):
self._ = self.__ = self.translations[lang]
self.lang = lang
+ self.cnx.set_session_props(lang=lang)
self.debug('request language: %s', lang)
# input form parameters management ########################################
@@ -336,7 +338,6 @@
params[name] = value
params['eid'] = eid
if len(params) < minparams:
- print eid, params
raise RequestError(self._('missing parameters for entity %s') % eid)
return params
@@ -452,6 +453,9 @@
# high level methods for HTML headers management ##########################
+ def add_onload(self, jscode):
+ self.html_headers.add_onload(jscode, self.json_request)
+
def add_js(self, jsfiles, localfile=True):
"""specify a list of JS files to include in the HTML headers
:param jsfiles: a JS filename or a list of JS filenames
--- a/web/test/unittest_application.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/test/unittest_application.py Mon Jul 06 19:55:18 2009 +0200
@@ -307,7 +307,7 @@
self.assertEquals(cnx.password, origcnx.password)
self.assertEquals(cnx.anonymous_connection, False)
self.assertEquals(path, 'view')
- self.assertEquals(params, {'__message': 'welcome %s !' % origcnx.login})
+ self.assertEquals(params, {'__message': 'welcome %s !' % cnx.user().login})
def _test_auth_fail(self, req):
self.assertRaises(AuthenticationError, self.app.connect, req)
@@ -351,8 +351,8 @@
req.form['__password'] = origcnx.password
self._test_auth_fail(req)
# option allow-email-login set
+ origcnx.login = address
self.set_option('allow-email-login', True)
- req, origcnx = self._init_auth('cookie')
req.form['__login'] = address
req.form['__password'] = origcnx.password
self._test_auth_succeed(req, origcnx)
--- a/web/test/unittest_form.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/test/unittest_form.py Mon Jul 06 19:55:18 2009 +0200
@@ -5,7 +5,7 @@
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
"""
-from __future__ import with_statement
+#from __future__ import with_statement
from xml.etree.ElementTree import fromstring
--- a/web/views/authentication.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/views/authentication.py Mon Jul 06 19:55:18 2009 +0200
@@ -36,7 +36,10 @@
# calling cnx.user() check connection validity, raise
# BadConnectionId on failure
user = cnx.user(req)
- if login and user.login != login:
+ # check cnx.login and not user.login, since in case of login by
+ # email, login and cnx.login are the email while user.login is the
+ # actual user login
+ if login and cnx.login != login:
cnx.close()
raise InvalidSession('login mismatch')
except BadConnectionId:
@@ -53,18 +56,6 @@
req.set_connection(cnx, user)
return cnx
- def login_from_email(self, login):
- # XXX should not be called from web interface
- session = self.repo.internal_session()
- try:
- rset = session.execute('Any L WHERE U login L, U primary_email M, '
- 'M address %(login)s', {'login': login})
- if rset.rowcount == 1:
- login = rset[0][0]
- finally:
- session.close()
- return login
-
def authenticate(self, req, _login=None, _password=None):
"""authenticate user and return corresponding user object
@@ -79,8 +70,6 @@
login, password = _login, _password
else:
login, password = req.get_authorization()
- if self.vreg.config['allow-email-login'] and '@' in (login or u''):
- login = self.login_from_email(login)
if not login:
# No session and no login -> try anonymous
login, password = self.vreg.config.anonymous_user()
--- a/web/views/basecontrollers.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/views/basecontrollers.py Mon Jul 06 19:55:18 2009 +0200
@@ -178,37 +178,50 @@
req.execute(rql, {'x': eid, 'y': typed_eid(teid)}, ('x', 'y'))
+def _validation_error(req, ex):
+ req.cnx.rollback()
+ forminfo = req.get_session_data(req.form.get('__errorurl'), pop=True)
+ foreid = ex.entity
+ eidmap = req.data.get('eidmap', {})
+ for var, eid in eidmap.items():
+ if foreid == eid:
+ foreid = var
+ break
+ return (foreid, ex.errors)
+
+def _validate_form(req, vreg):
+ # XXX should use the `RemoteCallFailed` mechanism
+ try:
+ ctrl = vreg.select(vreg.registry_objects('controllers', 'edit'),
+ req=req)
+ except NoSelectableObject:
+ return (False, {None: req._('not authorized')})
+ try:
+ ctrl.publish(None)
+ except ValidationError, ex:
+ return (False, _validation_error(req, ex))
+ except Redirect, ex:
+ try:
+ req.cnx.commit() # ValidationError may be raise on commit
+ except ValidationError, ex:
+ return (False, _validation_error(req, ex))
+ else:
+ return (True, ex.location)
+ except Exception, ex:
+ req.cnx.rollback()
+ req.exception('unexpected error while validating form')
+ return (False, req._(str(ex).decode('utf-8')))
+ return (False, '???')
+
+
class FormValidatorController(Controller):
id = 'validateform'
def publish(self, rset=None):
- vreg = self.vreg
- try:
- ctrl = vreg.select('controllers', 'edit', self.req,
- appli=self.appli)
- except NoSelectableObject:
- status, args = (False, {None: self.req._('not authorized')})
- else:
- try:
- ctrl.publish(None, fromjson=True)
- except ValidationError, err:
- status, args = self.validation_error(err)
- except Redirect, err:
- try:
- self.req.cnx.commit() # ValidationError may be raise on commit
- except ValidationError, err:
- status, args = self.validation_error(err)
- else:
- status, args = (True, err.location)
- except Exception, err:
- self.req.cnx.rollback()
- self.exception('unexpected error in validateform')
- try:
- status, args = (False, self.req._(unicode(err)))
- except UnicodeError:
- status, args = (False, repr(err))
- else:
- status, args = (False, '???')
+ self.req.json_request = True
+ # XXX unclear why we have a separated controller here vs
+ # js_validate_form on the json controller
+ status, args = _validate_form(self.req, self.vreg)
self.req.set_content_type('text/html')
jsarg = simplejson.dumps( (status, args) )
domid = self.req.form.get('__domid', 'entityForm').encode(
@@ -217,14 +230,6 @@
window.parent.handleFormValidationResponse('%s', null, null, %s);
</script>""" % (domid, simplejson.dumps( (status, args) ))
- def validation_error(self, err):
- self.req.cnx.rollback()
- try:
- eid = err.entity.eid
- except AttributeError:
- eid = err.entity
- return (False, (eid, err.errors))
-
class JSonController(Controller):
id = 'json'
@@ -238,6 +243,7 @@
note: it's the responsability of js_* methods to set the correct
response content type
"""
+ self.req.json_request = True
self.req.pageid = self.req.form.get('pageid')
try:
fname = self.req.form['fname']
@@ -377,26 +383,8 @@
return self.validate_form(action, names, values)
def validate_form(self, action, names, values):
- # XXX this method (and correspoding js calls) should use the new
- # `RemoteCallFailed` mechansim
self.req.form = self._rebuild_posted_form(names, values, action)
- vreg = self.vreg
- try:
- ctrl = vreg.select('controllers', 'edit', self.req)
- except NoSelectableObject:
- return (False, {None: self.req._('not authorized')})
- try:
- ctrl.publish(None, fromjson=True)
- except ValidationError, err:
- self.req.cnx.rollback()
- return (False, (err.entity, err.errors))
- except Redirect, redir:
- return (True, redir.location)
- except Exception, err:
- self.req.cnx.rollback()
- self.exception('unexpected error in js_validateform')
- return (False, self.req._(str(err).decode('utf-8')))
- return (False, '???')
+ return _validate_form(self.req, self.vreg)
@jsonize
def js_edit_field(self, action, names, values, rtype, eid, default):
--- a/web/views/boxes.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/views/boxes.py Mon Jul 06 19:55:18 2009 +0200
@@ -18,10 +18,10 @@
from logilab.mtconverter import html_escape
-from cubicweb.rtags import RelationTags
from cubicweb.selectors import match_user_groups, non_final_entity
+from cubicweb.view import EntityView
+from cubicweb.schema import display_name
from cubicweb.web.htmlwidgets import BoxWidget, BoxMenu, BoxHtml, RawBoxItem
-from cubicweb.view import EntityView
from cubicweb.web import uicfg
from cubicweb.web.box import BoxTemplate
--- a/web/views/editcontroller.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/views/editcontroller.py Mon Jul 06 19:55:18 2009 +0200
@@ -25,9 +25,8 @@
class EditController(ViewController):
id = 'edit'
- def publish(self, rset=None, fromjson=False):
+ def publish(self, rset=None):
"""edit / create / copy / delete entity / relations"""
- self.fromjson = fromjson
for key in self.req.form:
# There should be 0 or 1 action
if key.startswith('__action_'):
@@ -113,9 +112,8 @@
entity = execute(rql, formparams).get_entity(0, 0)
eid = entity.eid
except ValidationError, ex:
- # ex.entity may be an int or an entity instance
self._to_create[formparams['eid']] = ex.entity
- if self.fromjson:
+ if self.req.json_request: # XXX (syt) why?
ex.entity = formparams['eid']
raise
self._to_create[formparams['eid']] = eid
--- a/web/views/editforms.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/views/editforms.py Mon Jul 06 19:55:18 2009 +0200
@@ -368,8 +368,13 @@
divid = '%s-%s-%s' % (peid, rtype, entity.eid)
title = self.schema.rschema(rtype).display_name(self.req, role)
removejs = self.removejs % (peid, rtype,entity.eid)
+ countkey = '%s_count' % rtype
+ try:
+ self.req.data[countkey] += 1
+ except:
+ self.req.data[countkey] = 1
self.w(form.form_render(divid=divid, title=title, removejs=removejs,
- **kwargs))
+ counter=self.req.data[countkey], **kwargs))
def add_hiddens(self, form, entity, peid, rtype, role):
# to ease overriding (see cubes.vcsfile.views.forms for instance)
--- a/web/views/facets.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/views/facets.py Mon Jul 06 19:55:18 2009 +0200
@@ -41,6 +41,8 @@
needs_css = 'cubicweb.facets.css'
needs_js = ('cubicweb.ajax.js', 'cubicweb.formfilter.js')
+ bkLinkBox_template = u'<div class="facetTitle">%s</div>'
+
def facetargs(self):
"""this method returns the list of extra arguments that should
be used by the facet
@@ -80,18 +82,8 @@
widgets.append(wdg)
if not widgets:
return
+ self.displayBookmarkLink(rset)
w = self.w
- eschema = self.schema.eschema('Bookmark')
- if eschema.has_perm(req, 'add'):
- bk_path = 'view?rql=%s' % rset.printable_rql()
- bk_title = req._('my custom search')
- linkto = 'bookmarked_by:%s:subject' % self.req.user.eid
- bk_add_url = self.build_url('add/Bookmark', path=bk_path, title=bk_title, __linkto=linkto)
- bk_base_url = self.build_url('add/Bookmark', title=bk_title, __linkto=linkto)
- w(u'<div class="facetTitle"><a cubicweb:target="%s" id="facetBkLink" href="%s">%s</a></div>' % (
- html_escape(bk_base_url),
- html_escape(bk_add_url),
- req._('bookmark this search')))
w(u'<form method="post" id="%sForm" cubicweb:facetargs="%s" action="">' % (
divid, html_escape(dumps([divid, vid, paginate, self.facetargs()]))))
w(u'<fieldset>')
@@ -109,6 +101,20 @@
import cubicweb
cubicweb.info('after facets with rql: %s' % repr(rqlst))
+ def displayBookmarkLink(self, rset):
+ eschema = self.schema.eschema('Bookmark')
+ if eschema.has_perm(self.req, 'add'):
+ bk_path = 'view?rql=%s' % rset.printable_rql()
+ bk_title = self.req._('my custom search')
+ linkto = 'bookmarked_by:%s:subject' % self.req.user.eid
+ bk_add_url = self.build_url('add/Bookmark', path=bk_path, title=bk_title, __linkto=linkto)
+ bk_base_url = self.build_url('add/Bookmark', title=bk_title, __linkto=linkto)
+ bk_link = u'<a cubicweb:target="%s" id="facetBkLink" href="%s">%s</a>' % (
+ html_escape(bk_base_url),
+ html_escape(bk_add_url),
+ self.req._('bookmark this search'))
+ self.w(self.bkLinkBox_template % bk_link)
+
def get_facets(self, rset, mainvar):
return self.vreg.possible_vobjects('facets', self.req, rset=rset,
context='facetbox',
--- a/web/views/formrenderers.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/views/formrenderers.py Mon Jul 06 19:55:18 2009 +0200
@@ -503,7 +503,7 @@
w(u'<div class="iformBody">')
values['removemsg'] = self.req.__('remove this %s' % form.edited_entity.e_schema)
w(u'<div class="iformTitle"><span>%(title)s</span> '
- '#<span class="icounter">1</span> '
+ '#<span class="icounter">%(counter)s</span> '
'[<a href="javascript: %(removejs)s;noop();">%(removemsg)s</a>]</div>'
% values)
# cleanup values
--- a/web/views/forms.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/views/forms.py Mon Jul 06 19:55:18 2009 +0200
@@ -119,8 +119,8 @@
def form_add_hidden(self, name, value=None, **kwargs):
"""add an hidden field to the form"""
- field = StringField(name=name, widget=fwdgs.HiddenInput, initial=value,
- **kwargs)
+ kwargs.setdefault('widget', fwdgs.HiddenInput)
+ field = StringField(name=name, initial=value, **kwargs)
if 'id' in kwargs:
# by default, hidden input don't set id attribute. If one is
# explicitly specified, ensure it will be set
--- a/web/views/management.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/views/management.py Mon Jul 06 19:55:18 2009 +0200
@@ -14,7 +14,7 @@
from cubicweb.selectors import yes, none_rset, match_user_groups, authenticated_user
from cubicweb.view import AnyRsetView, StartupView, EntityView
from cubicweb.common.uilib import html_traceback, rest_traceback
-from cubicweb.web import formwidgets
+from cubicweb.web import formwidgets as wdgs
from cubicweb.web.formfields import guess_field
SUBMIT_MSGID = _('Submit bug report')
@@ -160,11 +160,9 @@
self.w(self.req._('no associated permissions'))
def require_permission_edit_form(self, entity):
- w = self.w
- _ = self.req._
newperm = self.vreg.etype_class('CWPermission')(self.req, None)
newperm.eid = self.req.varmaker.next()
- w(u'<p>%s</p>' % _('add a new permission'))
+ self.w(u'<p>%s</p>' % self.req._('add a new permission'))
form = self.vreg.select('forms', 'base', self.req, entity=newperm,
form_buttons=[formwidgets.SubmitButton()],
domid='reqperm%s' % entity.eid,
@@ -176,7 +174,7 @@
cwpermschema = newperm.e_schema
if permnames is not None:
field = guess_field(cwpermschema, self.schema.rschema('name'),
- widget=formwidgets.Select({'size': 1}),
+ widget=wdgs.Select({'size': 1}),
choices=permnames)
else:
field = guess_field(cwpermschema, self.schema.rschema('name'))
@@ -246,15 +244,17 @@
form = self.vreg.select('forms', 'base', self.req, rset=None,
mainform=False)
binfo = text_error_description(ex, excinfo, req, eversion, cversions)
- form.form_add_hidden('description', binfo)
+ form.form_add_hidden('description', binfo,
+ # we must use a text area to keep line breaks
+ widget=wdgs.TextArea({'class': 'hidden'}))
form.form_add_hidden('__bugreporting', '1')
if submitmail:
- form.form_buttons = [formwidgets.SubmitButton(MAIL_SUBMIT_MSGID)]
+ form.form_buttons = [wdgs.SubmitButton(MAIL_SUBMIT_MSGID)]
form.action = req.build_url('reportbug')
w(form.form_render())
if submiturl:
form.form_add_hidden('description_format', 'text/rest')
- form.form_buttons = [formwidgets.SubmitButton(SUBMIT_MSGID)]
+ form.form_buttons = [wdgs.SubmitButton(SUBMIT_MSGID)]
form.action = submiturl
w(form.form_render())
--- a/web/views/primary.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/views/primary.py Mon Jul 06 19:55:18 2009 +0200
@@ -52,7 +52,6 @@
boxes = self._prepare_side_boxes(entity)
if boxes or hasattr(self, 'render_side_related'):
self.w(u'<table width="100%"><tr><td style="width: 75%">')
- self.w(u'<div>')
self.w(u'<div class="mainInfo">')
self.content_navigation_components('navcontenttop')
try:
@@ -63,7 +62,6 @@
warn('siderelations argument of render_entity_attributes is '
'deprecated (%s)' % self.__class__)
self.render_entity_attributes(entity, [])
- self.w(u'</div>')
if self.main_related_section:
try:
self.render_entity_relations(entity)
@@ -74,9 +72,9 @@
'deprecated')
self.render_entity_relations(entity, [])
self.w(u'</div>')
+ # side boxes
if boxes or hasattr(self, 'render_side_related'):
self.w(u'</td><td>')
- # side boxes
self.w(u'<div class="primaryRight">')
if hasattr(self, 'render_side_related'):
warn('render_side_related is deprecated')
--- a/web/views/schema.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/views/schema.py Mon Jul 06 19:55:18 2009 +0200
@@ -248,12 +248,7 @@
### facets
-class CWMetaFacet(AttributeFacet):
- id = 'cwmeta-facet'
- __select__ = AttributeFacet.__select__ & implements('CWEType')
- rtype = 'meta'
-
class CWFinalFacet(AttributeFacet):
id = 'cwfinal-facet'
- __select__ = AttributeFacet.__select__ & implements('CWEType')
+ __select__ = AttributeFacet.__select__ & implements('CWEType', 'CWRType')
rtype = 'final'
--- a/web/views/startup.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/views/startup.py Mon Jul 06 19:55:18 2009 +0200
@@ -14,7 +14,7 @@
from cubicweb.view import StartupView
from cubicweb.selectors import match_user_groups, implements
-from cubicweb.schema import META_RELATIONS_TYPES
+from cubicweb.schema import META_RELATIONS_TYPES, display_name
from cubicweb.common.uilib import ureport_as_html
from cubicweb.web import ajax_replace_url, uicfg, httpcache
from cubicweb.web.views import tabs, management, schema as schemamod
--- a/web/views/tabs.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/views/tabs.py Mon Jul 06 19:55:18 2009 +0200
@@ -24,7 +24,7 @@
"""
def _prepare_bindings(self, vid, reloadable):
- self.req.html_headers.add_onload(u"""
+ self.req.add_onload(u"""
jQuery('#lazy-%(vid)s').bind('%(event)s', function(event) {
load_now('#lazy-%(vid)s', '#%(vid)s-hole', %(reloadable)s);
});""" % {'event': 'load_%s' % vid, 'vid': vid,
@@ -59,7 +59,7 @@
on dom readyness
"""
self.req.add_js('cubicweb.lazy.js')
- self.req.html_headers.add_onload("trigger_load('%s');" % vid)
+ self.req.add_onload("trigger_load('%s');" % vid)
class TabsMixin(LazyViewMixin):
@@ -93,22 +93,11 @@
return selected_tabs
def render_tabs(self, tabs, default, entity=None):
- # tabbed views do no support concatenation
- # hence we delegate to the default tab if there is more than on entity
- # in the result set
+ # delegate to the default tab if there is more than one entity
+ # in the result set (tabs are pretty useless there)
if entity and len(self.rset) > 1:
entity.view(default, w=self.w)
return
- # XXX (syt) fix below add been introduced at some point to fix something
- # (http://intranet.logilab.fr/jpl/ticket/32174 ?) but this is not a clean
- # way. We must not consider form['rql'] here since it introduces some
- # other failures on non rql queries (plain text, shortcuts,... handled by
- # magicsearch) which has a single result whose primary view is using tabs
- # (https://www.logilab.net/cwo/ticket/342789)
- #rql = self.req.form.get('rql')
- #if rql:
- # self.req.execute(rql).get_entity(0,0).view(default, w=self.w)
- # return
self.req.add_css('tabs-no-images.css')
self.req.add_js(('jquery.tools.min.js', 'cubicweb.htmlhelpers.js',
'cubicweb.ajax.js', 'cubicweb.tabs.js', 'cubicweb.lazy.js'))
@@ -145,11 +134,11 @@
w(u'</div>')
# call the set_tab() JS function *after* each tab is generated
# because the callback binding needs to be done before
- self.req.html_headers.add_onload(u"""
+ self.req.add_onload(u'''
jQuery(function() {
jQuery("#tabs-%(eeid)s").tabs("#panes-%(eeid)s > div", {initialIndex: %(tabindex)s});
set_tab('%(vid)s', '%(cookiename)s');
- });""" % {'eeid' : entity.eid,
+ });''' % {'eeid' : entity.eid,
'vid' : active_tab,
'cookiename' : self.cookie_name,
'tabindex' : tabs.index(active_tab)})
--- a/web/views/urlpublishing.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/views/urlpublishing.py Mon Jul 06 19:55:18 2009 +0200
@@ -144,18 +144,12 @@
<etype>[[/<attribute name>]/<attribute value>]*
"""
priority = 2
- def __init__(self, urlpublisher):
- super(RestPathEvaluator, self).__init__(urlpublisher)
- self.etype_map = {}
- for etype in self.schema.entities():
- etype = str(etype)
- self.etype_map[etype.lower()] = etype
def evaluate_path(self, req, parts):
if not (0 < len(parts) < 4):
raise PathDontMatch()
try:
- etype = self.etype_map[parts.pop(0).lower()]
+ etype = self.vreg.case_insensitive_etypes[parts.pop(0).lower()]
except KeyError:
raise PathDontMatch()
cls = self.vreg.etype_class(etype)
--- a/web/webconfig.py Thu Jul 02 10:36:25 2009 +0200
+++ b/web/webconfig.py Mon Jul 06 19:55:18 2009 +0200
@@ -80,12 +80,6 @@
'if anonymous-user is set',
'group': 'main', 'inputlevel': 1,
}),
- ('allow-email-login',
- {'type' : 'yn',
- 'default': False,
- 'help': 'allow users to login with their primary email if set',
- 'group': 'main', 'inputlevel': 2,
- }),
('query-log-file',
{'type' : 'string',
'default': None,