--- a/devtools/__init__.py Fri Jun 17 18:50:13 2011 +0200
+++ b/devtools/__init__.py Fri Jun 17 18:53:33 2011 +0200
@@ -528,11 +528,19 @@
return get_db_helper('postgres')
@property
- @cached
def dbcnx(self):
- from cubicweb.server.serverctl import _db_sys_cnx
- return _db_sys_cnx(self.system_source, 'CREATE DATABASE and / or USER',
- interactive=False)
+ try:
+ return self._cnx
+ except AttributeError:
+ from cubicweb.server.serverctl import _db_sys_cnx
+ try:
+ self._cnx = _db_sys_cnx(
+ self.system_source, 'CREATE DATABASE and / or USER',
+ interactive=False)
+ return self._cnx
+ except Exception:
+ self._cnx = None
+ raise
@property
@cached
@@ -570,17 +578,19 @@
cnx.close()
init_repository(self.config, interactive=False)
except:
- self.dbcnx.rollback()
+ if self.dbcnx is not None:
+ self.dbcnx.rollback()
print >> sys.stderr, 'building', self.dbname, 'failed'
#self._drop(self.dbname)
raise
def helper_clear_cache(self):
- self.dbcnx.commit()
- self.dbcnx.close()
- clear_cache(self, 'dbcnx')
+ if self.dbcnx is not None:
+ self.dbcnx.commit()
+ self.dbcnx.close()
+ del self._cnx
+ clear_cache(self, 'cursor')
clear_cache(self, 'helper')
- clear_cache(self, 'cursor')
def __del__(self):
self.helper_clear_cache()
--- a/doc/book/en/admin/config.rst Fri Jun 17 18:50:13 2011 +0200
+++ b/doc/book/en/admin/config.rst Fri Jun 17 18:53:33 2011 +0200
@@ -209,6 +209,8 @@
Pyro configuration
------------------
+Pyro name server
+~~~~~~~~~~~~~~~~
If you want to use Pyro to access your instance remotely, or to have multi-source
or distributed configuration, it is required to have a Pyro name server running
on your network. By default it is detected by a broadcast request, but you can
@@ -216,9 +218,13 @@
To do so, you need to :
+* be sure to have installed it (see :ref:`InstallDependencies`)
+
* launch the pyro name server with `pyro-nsd start` before starting cubicweb
* under debian, edit the file :file:`/etc/default/pyro-nsd` so that the name
server pyro will be launched automatically when the machine fire up
+Note that you can use the pyro server without a running pyro nameserver.
+Refer to `pyro-ns-host` server configuration option for details.
--- a/doc/book/en/admin/index.rst Fri Jun 17 18:50:13 2011 +0200
+++ b/doc/book/en/admin/index.rst Fri Jun 17 18:53:33 2011 +0200
@@ -14,6 +14,8 @@
:numbered:
setup
+ setup-windows
+ config
cubicweb-ctl
create-instance
instance-config
--- a/doc/book/en/annexes/faq.rst Fri Jun 17 18:50:13 2011 +0200
+++ b/doc/book/en/annexes/faq.rst Fri Jun 17 18:53:33 2011 +0200
@@ -185,12 +185,6 @@
recommended also for other attribute types). By default it expects to generate
HTML, so it deals with rich text formating, xml escaping...
-How do I translate an msg id defined (and translated) in another cube ?
------------------------------------------------------------------------
-
-You should put these translations in the `i18n/static-messages.pot`
-file of your own cube.
-
How to update a database after a schema modification ?
------------------------------------------------------
--- a/doc/book/en/annexes/rql/debugging.rst Fri Jun 17 18:50:13 2011 +0200
+++ b/doc/book/en/annexes/rql/debugging.rst Fri Jun 17 18:53:33 2011 +0200
@@ -8,26 +8,15 @@
Available levels
~~~~~~~~~~~~~~~~
-:DBG_NONE:
- no debug information (current mode)
-
-:DBG_RQL:
- rql execution information
-
-:DBG_SQL:
- executed sql
+Server debugging flags. They may be combined using binary operators.
-:DBG_REPO:
- repository events
-
-:DBG_MS:
- multi-sources
-
-:DBG_MORE:
- more verbosity
-
-:DBG_ALL:
- all level enabled
+.. autodata:: cubicweb.server.DBG_NONE
+.. autodata:: cubicweb.server.DBG_RQL
+.. autodata:: cubicweb.server.DBG_SQL
+.. autodata:: cubicweb.server.DBG_REPO
+.. autodata:: cubicweb.server.DBG_MS
+.. autodata:: cubicweb.server.DBG_MORE
+.. autodata:: cubicweb.server.DBG_ALL
Enable verbose output
@@ -40,6 +29,8 @@
from cubicweb import server
server.set_debug(server.DBG_RQL|server.DBG_SQL|server.DBG_ALL)
+.. autofunction:: cubicweb.server.set_debug
+
Detect largest RQL queries
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -50,7 +41,5 @@
API
~~~
-.. autofunction:: cubicweb.server.set_debug
-
.. autoclass:: cubicweb.server.debugged
--- a/doc/book/en/annexes/rql/intro.rst Fri Jun 17 18:50:13 2011 +0200
+++ b/doc/book/en/annexes/rql/intro.rst Fri Jun 17 18:53:33 2011 +0200
@@ -159,3 +159,4 @@
.. _Datalog: http://en.wikipedia.org/wiki/Datalog
.. _intensional: http://en.wikipedia.org/wiki/Intensional_definition
.. _extensional: http://en.wikipedia.org/wiki/Extension_(predicate_logic)
+
--- a/doc/book/en/annexes/rql/language.rst Fri Jun 17 18:50:13 2011 +0200
+++ b/doc/book/en/annexes/rql/language.rst Fri Jun 17 18:53:33 2011 +0200
@@ -152,6 +152,10 @@
- Aggregate Functions: COUNT, MIN, MAX, AVG, SUM, GROUP_CONCAT
+.. note::
+ Aggregate functions will return None if there is no result row.
+
+
Having
```````
--- a/doc/book/en/devrepo/vreg.rst Fri Jun 17 18:50:13 2011 +0200
+++ b/doc/book/en/devrepo/vreg.rst Fri Jun 17 18:53:33 2011 +0200
@@ -79,7 +79,7 @@
.. autoclass:: cubicweb.selectors.has_add_permission
.. autoclass:: cubicweb.selectors.has_mimetype
.. autoclass:: cubicweb.selectors.is_in_state
-.. autoclass:: cubicweb.selectors.on_fire_transition
+.. autofunction:: cubicweb.selectors.on_fire_transition
.. autoclass:: cubicweb.selectors.implements
--- a/doc/book/en/devweb/controllers.rst Fri Jun 17 18:50:13 2011 +0200
+++ b/doc/book/en/devweb/controllers.rst Fri Jun 17 18:53:33 2011 +0200
@@ -16,11 +16,11 @@
`Browsing`:
* the View controller is associated with most browsing actions within a
- CubicWeb application: it always instantiates a :ref:`the_main_template` and
- lets the ResultSet/Views dispatch system build up the whole content; it
- handles :exc:`ObjectNotFound` and :exc:`NoSelectableObject` errors that may
- bubble up to its entry point, in an end-user-friendly way (but other
- programming errors will slip through)
+ CubicWeb application: it always instantiates a
+ :ref:`the_main_template_layout` and lets the ResultSet/Views dispatch system
+ build up the whole content; it handles :exc:`ObjectNotFound` and
+ :exc:`NoSelectableObject` errors that may bubble up to its entry point, in an
+ end-user-friendly way (but other programming errors will slip through)
* the JSon controller (same module) provides services for Ajax calls,
typically using JSON as a serialization format for input, and
--- a/doc/book/en/devweb/httpcaching.rst Fri Jun 17 18:50:13 2011 +0200
+++ b/doc/book/en/devweb/httpcaching.rst Fri Jun 17 18:53:33 2011 +0200
@@ -1,3 +1,21 @@
HTTP cache management
----------------------
-XXX feedme
\ No newline at end of file
+=====================
+
+.. automodule:: cubicweb.web.httpcache
+
+Cache policies
+--------------
+.. autoclass:: cubicweb.web.httpcache.NoHTTPCacheManager
+.. autoclass:: cubicweb.web.httpcache.MaxAgeHTTPCacheManager
+.. autoclass:: cubicweb.web.httpcache.EtagHTTPCacheManager
+.. autoclass:: cubicweb.web.httpcache.EntityHTTPCacheManager
+
+Exception
+---------
+.. autoexception:: cubicweb.web.httpcache.NoEtag
+
+Helper functions
+----------------
+.. autofunction:: cubicweb.web.httpcache.set_http_cache_headers
+
+.. NOT YET AVAILABLE IN STABLE autofunction:: cubicweb.web.httpcache.lastmodified
--- a/doc/book/en/devweb/index.rst Fri Jun 17 18:50:13 2011 +0200
+++ b/doc/book/en/devweb/index.rst Fri Jun 17 18:53:33 2011 +0200
@@ -17,5 +17,6 @@
edition/index
facets
internationalization
-.. property
+ property
httpcaching
+ resource
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/en/devweb/resource.rst Fri Jun 17 18:53:33 2011 +0200
@@ -0,0 +1,16 @@
+Locate resources
+----------------
+
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.locate_resource
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.locate_doc_file
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.locate_all_files
+
+Static files handling
+---------------------
+
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.static_directory
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.static_file_exists
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.static_file_open
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.static_file_add
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.static_file_del
+
--- a/doc/book/en/devweb/views/idownloadable.rst Fri Jun 17 18:50:13 2011 +0200
+++ b/doc/book/en/devweb/views/idownloadable.rst Fri Jun 17 18:53:33 2011 +0200
@@ -1,5 +1,23 @@
-The 'download' view
--------------------
+The 'download' views
+====================
+
+.. automodule:: cubicweb.web.views.idownloadable
+
+Components
+----------
+
+.. autoclass:: cubicweb.web.views.idownloadable.DownloadBox
-(:mod:`cubicweb.web.views.idownloadable`)
+Download views
+--------------
+.. autoclass:: cubicweb.web.views.idownloadable.DownloadView
+.. autoclass:: cubicweb.web.views.idownloadable.DownloadLinkView
+.. autoclass:: cubicweb.web.views.idownloadable.IDownloadablePrimaryView
+.. autoclass:: cubicweb.web.views.idownloadable.IDownloadableLineView
+
+Embedded views
+--------------
+
+.. autoclass:: cubicweb.web.views.idownloadable.ImageView
+.. autoclass:: cubicweb.web.views.idownloadable.EHTMLView
--- a/doc/book/en/devweb/views/index.rst Fri Jun 17 18:50:13 2011 +0200
+++ b/doc/book/en/devweb/views/index.rst Fri Jun 17 18:53:33 2011 +0200
@@ -25,7 +25,7 @@
urlpublish
breadcrumbs
-.. wdoc
+ idownloadable
+ wdoc
.. embedding
-.. idownloadable
--- a/doc/book/en/devweb/views/wdoc.rst Fri Jun 17 18:50:13 2011 +0200
+++ b/doc/book/en/devweb/views/wdoc.rst Fri Jun 17 18:53:33 2011 +0200
@@ -1,9 +1,17 @@
.. -*- coding: utf-8 -*-
Online documentation system
----------------------------
+===========================
+
+.. automodule:: cubicweb.web.views.wdoc
-(:mod:`cubicweb.web.views.wdoc`)
+Help views
+----------
+.. autoclass:: cubicweb.web.views.wdoc.InlineHelpView
+.. autoclass:: cubicweb.web.views.wdoc.ChangeLogView
-XXX describe the on-line documentation system
-
+Actions
+-------
+.. autoclass:: cubicweb.web.views.wdoc.HelpAction
+.. autoclass:: cubicweb.web.views.wdoc.ChangeLogAction
+.. autoclass:: cubicweb.web.views.wdoc.AboutAction
--- a/doc/book/en/tutorials/advanced/part05_ui-advanced.rst Fri Jun 17 18:50:13 2011 +0200
+++ b/doc/book/en/tutorials/advanced/part05_ui-advanced.rst Fri Jun 17 18:53:33 2011 +0200
@@ -3,6 +3,8 @@
We'll now see how to benefit from features introduced in 3.9 and 3.10 releases of CubicWeb
+.. _uiprops:
+
Step 1: tired of the default look?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--- a/doc/book/en/tutorials/tools/windmill.rst Fri Jun 17 18:50:13 2011 +0200
+++ b/doc/book/en/tutorials/tools/windmill.rst Fri Jun 17 18:53:33 2011 +0200
@@ -23,23 +23,21 @@
environment, take a look to the `virtualenv
<http://pypi.python.org/pypi/virtualenv>`_ project as well)::
- pip install windmill
- curl -O http://github.com/windmill/windmill/tarball/master
+ $ pip install windmill
+ $ curl -O http://github.com/windmill/windmill/tarball/master
However, the Windmill project doesn't release frequently. Our recommandation is
-to used the last snapshot of the Git repository:
-
-.. sourcecode:: bash
+to used the last snapshot of the Git repository::
- git clone git://github.com/windmill/windmill.git HEAD
- cd windmill
- python setup.py develop
+ $ git clone git://github.com/windmill/windmill.git HEAD
+ $ cd windmill
+ $ python setup.py develop
Install instructions are `available <http://wiki.github.com/windmill/windmill/installing>`_.
Be sure to have the windmill module in your PYTHONPATH afterwards::
- python -c "import windmill"
+ $ python -c "import windmill"
X dummy
-------
@@ -60,9 +58,9 @@
*From: http://www.x.org/wiki/XorgTesting*
-Then, you can run the X server with the following command :
+Then, you can run the X server with the following command ::
- /usr/bin/X11/Xvfb :1 -ac -screen 0 1280x1024x8 -fbdir /tmp
+ $ /usr/bin/X11/Xvfb :1 -ac -screen 0 1280x1024x8 -fbdir /tmp
Windmill usage
@@ -82,13 +80,15 @@
If you are using firefox as client, consider the "firebug" option.
-If you have a running instance, you can refine the test by the *loadtest* windmill option:
+If you have a running instance, you can refine the test by the *loadtest* windmill option::
- windmill -m firebug loadtest=<test_file.py> <instance url>
+ $ windmill -m firebug loadtest=<test_file.py> <instance url>
-Or use the internal windmill shell to explore available commands:
+Or use the internal windmill shell to explore available commands::
- windmill -m firebug shell <instance url>
+ $ windmill -m firebug shell <instance url>
+
+And enter python commands:
.. sourcecode:: python
@@ -125,7 +125,7 @@
To run your test series::
- % pytest test/test_windmill.py
+ $ pytest test/test_windmill.py
By default, CubicWeb will use **firefox** as the default browser and will try
to run test instance server on localhost. In the general case, You've no need
@@ -144,6 +144,8 @@
Examples:
+.. sourcecode:: python
+
browser = 'firefox'
test_dir = osp.join(__file__, 'windmill')
edit_test = False
@@ -162,7 +164,7 @@
For instance, CubicWeb framework windmill tests can be manually run by::
- % pytest web/test/test_windmill.py
+ $ pytest web/test/test_windmill.py
Edit your tests
---------------
@@ -172,7 +174,7 @@
But if you are using `pytest` as test runner, use the `-i` option directly.
The test series will be loaded and you can run assertions step-by-step::
- % pytest -i test/test_windmill.py
+ $ pytest -i test/test_windmill.py
In this case, the `firebug` extension will be loaded automatically for you.
--- a/hooks/syncsources.py Fri Jun 17 18:50:13 2011 +0200
+++ b/hooks/syncsources.py Fri Jun 17 18:53:33 2011 +0200
@@ -136,7 +136,7 @@
if not session.deleted_in_transaction(schemacfg.eid):
source.add_schema_config(schemacfg, checkonly=checkonly)
elif session.deleted_in_transaction(schemacfg.eid):
- source.delete_schema_config(schemacfg, checkonly=checkonly)
+ source.del_schema_config(schemacfg, checkonly=checkonly)
else:
source.update_schema_config(schemacfg, checkonly=checkonly)
@@ -164,5 +164,4 @@
def __call__(self):
SourceMappingChangedOp.get_instance(self._cw).add_data(
(self._cw.entity_from_eid(self.eidfrom),
- self._cw.entity_from_eid(self.eidto)) )
-
+ self._cw.entity_from_eid(self.eidto).repo_source) )
--- a/rqlrewrite.py Fri Jun 17 18:50:13 2011 +0200
+++ b/rqlrewrite.py Fri Jun 17 18:53:33 2011 +0200
@@ -20,12 +20,16 @@
This is used for instance for read security checking in the repository.
"""
+from __future__ import with_statement
__docformat__ = "restructuredtext en"
from rql import nodes as n, stmts, TypeResolverException
from rql.utils import common_parent
+
from yams import BadSchemaDefinition
+
+from logilab.common import tempattr
from logilab.common.graph import has_path
from cubicweb import Unauthorized, typed_eid
@@ -156,7 +160,6 @@
self.exists_snippet = {}
self.pending_keys = []
self.existingvars = existingvars
- self._insert_scope = None
# we have to annotate the rqlst before inserting snippets, even though
# we'll have to redo it latter
self.annotate(select)
@@ -193,6 +196,7 @@
self.varmap = varmap
self.revvarmap = {}
self.varinfos = []
+ self._insert_scope = None
for i, (selectvar, snippetvar) in enumerate(varmap):
assert snippetvar in 'SOX'
self.revvarmap[snippetvar] = (selectvar, i)
@@ -229,7 +233,7 @@
except Unsupported:
continue
inserted = True
- if new is not None:
+ if new is not None and self._insert_scope is None:
self.exists_snippet[rqlexpr] = new
parent = parent or new
else:
@@ -263,11 +267,12 @@
insert_scope = common_parent(scope, insert_scope)
else:
insert_scope = self._insert_scope
- if any(vi.get('stinfo', {}).get('optrelations') for vi in self.varinfos):
+ if self._insert_scope is None and any(vi.get('stinfo', {}).get('optrelations')
+ for vi in self.varinfos):
assert parent is None
self._insert_scope = self.snippet_subquery(varmap, new)
self.insert_pending()
- self._insert_scope = None
+ #self._insert_scope = None
return
if not isinstance(new, (n.Exists, n.Not)):
new = n.Exists(new)
@@ -283,15 +288,14 @@
except Unsupported:
# some solutions have been lost, can't apply this rql expr
if parent is None:
- self.select.remove_node(new, undefine=True)
+ self.current_statement().remove_node(new, undefine=True)
else:
parent.parent.replace(or_, or_.children[0])
self._cleanup_inserted(new)
raise
else:
- self._insert_scope = new
- self.insert_pending()
- self._insert_scope = None
+ with tempattr(self, '_insert_scope', new):
+ self.insert_pending()
return new
self.insert_pending()
@@ -303,6 +307,7 @@
recomputed, we have to insert snippet defined for <action> of entity
types taken by X
"""
+ stmt = self.current_statement()
while self.pending_keys:
key, action = self.pending_keys.pop()
try:
@@ -313,12 +318,12 @@
except KeyError:
# variable isn't used anywhere else, we can't insert security
raise Unauthorized()
- ptypes = self.select.defined_vars[varname].stinfo['possibletypes']
+ ptypes = stmt.defined_vars[varname].stinfo['possibletypes']
if len(ptypes) > 1:
# XXX dunno how to handle this
self.session.error(
'cant check security of %s, ambigous type for %s in %s',
- self.select, varname, key[0]) # key[0] == the rql expression
+ stmt, varname, key[0]) # key[0] == the rql expression
raise Unauthorized()
etype = iter(ptypes).next()
eschema = self.schema.eschema(etype)
@@ -499,17 +504,18 @@
self.rewritten[key] = term.name
def _get_varname_or_term(self, vname):
+ stmt = self.current_statement()
if vname == 'U':
+ stmt = self.select
if self.u_varname is None:
- select = self.select
- self.u_varname = select.allocate_varname()
+ self.u_varname = stmt.allocate_varname()
# generate an identifier for the substitution
- argname = select.allocate_varname()
+ argname = stmt.allocate_varname()
while argname in self.kwargs:
- argname = select.allocate_varname()
+ argname = stmt.allocate_varname()
# insert "U eid %(u)s"
- select.add_constant_restriction(
- select.get_variable(self.u_varname),
+ stmt.add_constant_restriction(
+ stmt.get_variable(self.u_varname),
'eid', unicode(argname), 'Substitute')
self.kwargs[argname] = self.session.user.eid
return self.u_varname
@@ -517,7 +523,7 @@
try:
return self.rewritten[key]
except KeyError:
- self.rewritten[key] = newvname = self.select.allocate_varname()
+ self.rewritten[key] = newvname = stmt.allocate_varname()
return newvname
# visitor methods ##########################################################
@@ -625,14 +631,20 @@
def visit_variableref(self, node):
"""get the sql name for a variable reference"""
+ stmt = self.current_statement()
if node.name in self.revvarmap:
selectvar, index = self.revvarmap[node.name]
vi = self.varinfos[index]
if vi.get('const') is not None:
return n.Constant(vi['const'], 'Int') # XXX gae
- return n.VariableRef(self.select.get_variable(selectvar))
+ return n.VariableRef(stmt.get_variable(selectvar))
vname_or_term = self._get_varname_or_term(node.name)
if isinstance(vname_or_term, basestring):
- return n.VariableRef(self.select.get_variable(vname_or_term))
+ return n.VariableRef(stmt.get_variable(vname_or_term))
# shared term
- return vname_or_term.copy(self.select)
+ return vname_or_term.copy(stmt)
+
+ def current_statement(self):
+ if self._insert_scope is None:
+ return self.select
+ return self._insert_scope.stmt
--- a/selectors.py Fri Jun 17 18:50:13 2011 +0200
+++ b/selectors.py Fri Jun 17 18:53:33 2011 +0200
@@ -1252,15 +1252,18 @@
def on_fire_transition(etype, tr_name, from_state_name=None):
"""Return 1 when entity of the type `etype` is going through transition of
- the name `tr_name`. If `from_state_name` is specified, this selector will
- also check the incoming state.
+ the name `tr_name`.
+
+ If `from_state_name` is specified, this selector will also check the
+ incoming state.
You should use this selector on 'after_add_entity' hook, since it's actually
looking for addition of `TrInfo` entities. Hence in the hook, `self.entity`
will reference the matching `TrInfo` entity, allowing to get all the
transition details (including the entity to which is applied the transition
- but also its original state, transition, destination state, user...). See
- :class:`cubicweb.entities.wfobjs.TrInfo` for more information.
+ but also its original state, transition, destination state, user...).
+
+ See :class:`cubicweb.entities.wfobjs.TrInfo` for more information.
"""
def match_etype_and_transition(trinfo):
# take care trinfo.transition is None when calling change_state
--- a/server/__init__.py Fri Jun 17 18:50:13 2011 +0200
+++ b/server/__init__.py Fri Jun 17 18:53:33 2011 +0200
@@ -18,7 +18,7 @@
"""Server subcube of cubicweb : defines objects used only on the server
(repository) side
-This module contains functions to initialize a new repository.
+The server module contains functions to initialize a new repository.
"""
from __future__ import with_statement
@@ -39,14 +39,23 @@
# server-side debugging #########################################################
# server debugging flags. They may be combined using binary operators.
-DBG_NONE = 0 # no debug information
-DBG_RQL = 1 # rql execution information
-DBG_SQL = 2 # executed sql
-DBG_REPO = 4 # repository events
-DBG_MS = 8 # multi-sources
-DBG_MORE = 16 # more verbosity
-DBG_ALL = 1 + 2 + 4 + 8 + 16
-# current debug mode
+
+#:no debug information
+DBG_NONE = 0 #: no debug information
+#: rql execution information
+DBG_RQL = 1
+#: executed sql
+DBG_SQL = 2
+#: repository events
+DBG_REPO = 4
+#: multi-sources
+DBG_MS = 8
+#: more verbosity
+DBG_MORE = 16
+#: all level enabled
+DBG_ALL = DBG_RQL + DBG_SQL + DBG_REPO + DBG_MS + DBG_MORE
+
+#: current debug mode
DEBUG = 0
def set_debug(debugmode):
--- a/server/mssteps.py Fri Jun 17 18:50:13 2011 +0200
+++ b/server/mssteps.py Fri Jun 17 18:53:33 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -22,6 +22,7 @@
* each step has is own members (this is not necessarily bad, but a bit messy
for now)
"""
+from __future__ import with_statement
__docformat__ = "restructuredtext en"
@@ -32,25 +33,30 @@
AGGR_TRANSFORMS = {'COUNT':'SUM', 'MIN':'MIN', 'MAX':'MAX', 'SUM': 'SUM'}
-def remove_clauses(union, keepgroup):
- clauses = []
- for select in union.children:
- if keepgroup:
- having, orderby = select.having, select.orderby
- select.having, select.orderby = (), ()
- clauses.append( (having, orderby) )
- else:
- groupby, having, orderby = select.groupby, select.having, select.orderby
- select.groupby, select.having, select.orderby = (), (), ()
- clauses.append( (groupby, having, orderby) )
- return clauses
+class remove_and_restore_clauses(object):
+ def __init__(self, union, keepgroup):
+ self.union = union
+ self.keepgroup = keepgroup
+ self.clauses = None
-def restore_clauses(union, keepgroup, clauses):
- for i, select in enumerate(union.children):
- if keepgroup:
- select.having, select.orderby = clauses[i]
- else:
- select.groupby, select.having, select.orderby = clauses[i]
+ def __enter__(self):
+ self.clauses = clauses = []
+ for select in self.union.children:
+ if self.keepgroup:
+ having, orderby = select.having, select.orderby
+ select.having, select.orderby = (), ()
+ clauses.append( (having, orderby) )
+ else:
+ groupby, having, orderby = select.groupby, select.having, select.orderby
+ select.groupby, select.having, select.orderby = (), (), ()
+ clauses.append( (groupby, having, orderby) )
+
+ def __exit__(self, exctype, exc, traceback):
+ for i, select in enumerate(self.union.children):
+ if self.keepgroup:
+ select.having, select.orderby = self.clauses[i]
+ else:
+ select.groupby, select.having, select.orderby = self.clauses[i]
class FetchStep(OneFetchStep):
@@ -94,29 +100,24 @@
plan = self.plan
plan.create_temp_table(self.table)
union = self.union
- # XXX 2.5 use "with"
- clauses = remove_clauses(union, self.keepgroup)
- for source in self.sources:
- source.flying_insert(self.table, plan.session, union, plan.args,
- self.inputmap)
- restore_clauses(union, self.keepgroup, clauses)
+ with remove_and_restore_clauses(union, self.keepgroup):
+ for source in self.sources:
+ source.flying_insert(self.table, plan.session, union, plan.args,
+ self.inputmap)
def mytest_repr(self):
"""return a representation of this step suitable for test"""
- clauses = remove_clauses(self.union, self.keepgroup)
- try:
- inputmap = varmap_test_repr(self.inputmap, self.plan.tablesinorder)
- outputmap = varmap_test_repr(self.outputmap, self.plan.tablesinorder)
- except AttributeError:
- inputmap = self.inputmap
- outputmap = self.outputmap
- try:
+ with remove_and_restore_clauses(self.union, self.keepgroup):
+ try:
+ inputmap = varmap_test_repr(self.inputmap, self.plan.tablesinorder)
+ outputmap = varmap_test_repr(self.outputmap, self.plan.tablesinorder)
+ except AttributeError:
+ inputmap = self.inputmap
+ outputmap = self.outputmap
return (self.__class__.__name__,
- sorted((r.as_string(kwargs=self.plan.args), r.solutions)
- for r in self.union.children),
- sorted(self.sources), inputmap, outputmap)
- finally:
- restore_clauses(self.union, self.keepgroup, clauses)
+ sorted((r.as_string(kwargs=self.plan.args), r.solutions)
+ for r in self.union.children),
+ sorted(self.sources), inputmap, outputmap)
class AggrStep(LimitOffsetMixIn, Step):
--- a/server/rqlannotation.py Fri Jun 17 18:50:13 2011 +0200
+++ b/server/rqlannotation.py Fri Jun 17 18:53:33 2011 +0200
@@ -271,9 +271,17 @@
return has_text_query
def is_ambiguous(self, var):
- # ignore has_text relation
- if len([rel for rel in var.stinfo['relations']
- if rel.scope is var.scope and rel.r_type == 'has_text']) == 1:
+ # ignore has_text relation when we know it will be used as principal.
+ # This is expected by the rql2sql generator which will use the `entities`
+ # table to filter out by type if necessary, This optimisation is very
+ # interesting in multi-sources cases, as it may avoid a costly query
+ # on sources to get all entities of a given type to achieve this, while
+ # we have all the necessary information.
+ root = var.stmt.root # Union node
+ # rel.scope -> Select or Exists node, so add .parent to get Union from
+ # Select node
+ rels = [rel for rel in var.stinfo['relations'] if rel.scope.parent is root]
+ if len(rels) == 1 and rels[0].r_type == 'has_text':
return False
try:
data = var.stmt._deamb_data
--- a/server/sources/native.py Fri Jun 17 18:50:13 2011 +0200
+++ b/server/sources/native.py Fri Jun 17 18:53:33 2011 +0200
@@ -165,6 +165,15 @@
class UndoException(Exception):
"""something went wrong during undoing"""
+ def __unicode__(self):
+ """Called by the unicode builtin; should return a Unicode object
+
+ Type of UndoException message must be `unicode` by design in CubicWeb.
+
+ .. warning::
+ This method is not available in python2.5"""
+ assert isinstance(self.message, unicode)
+ return self.message
def _undo_check_relation_target(tentity, rdef, role):
"""check linked entity has not been redirected for this relation"""
--- a/server/sources/pyrorql.py Fri Jun 17 18:50:13 2011 +0200
+++ b/server/sources/pyrorql.py Fri Jun 17 18:53:33 2011 +0200
@@ -234,6 +234,7 @@
etype, dexturi, dextid = cnx.describe(extid)
if dexturi == 'system' or not (
dexturi in self.repo.sources_by_uri or self._skip_externals):
+ assert etype in self.support_entities, etype
eid = self.repo.extid2eid(self, str(extid), etype, session)
if eid > 0:
return eid, True
--- a/server/test/unittest_querier.py Fri Jun 17 18:50:13 2011 +0200
+++ b/server/test/unittest_querier.py Fri Jun 17 18:53:33 2011 +0200
@@ -1460,5 +1460,14 @@
rset = self.execute('Any X,Y WHERE X nom XD, Y nom XD, X eid Z, Y eid > Z')
self.assertEqual(rset.rows, [[peid1, peid2]])
+ def test_nonregr_has_text_ambiguity_1(self):
+ peid = self.execute("INSERT CWUser X: X login 'bidule', X upassword 'bidule', X in_group G WHERE G name 'users'")[0][0]
+ aeid = self.execute("INSERT Affaire X: X ref 'bidule'")[0][0]
+ self.commit()
+ rset = self.execute('Any X WHERE X is CWUser, X has_text "bidule"')
+ self.assertEqual(rset.rows, [[peid]])
+ rset = self.execute('Any X WHERE X is CWUser, X has_text "bidule", X in_state S, S name SN')
+ self.assertEqual(rset.rows, [[peid]])
+
if __name__ == '__main__':
unittest_main()
--- a/server/test/unittest_rqlannotation.py Fri Jun 17 18:50:13 2011 +0200
+++ b/server/test/unittest_rqlannotation.py Fri Jun 17 18:53:33 2011 +0200
@@ -18,13 +18,16 @@
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
"""unit tests for modules cubicweb.server.rqlannotation"""
-from cubicweb.devtools import init_test_database
+from cubicweb.devtools import TestServerConfiguration, get_test_db_handler
from cubicweb.devtools.repotest import BaseQuerierTC
def setUpModule(*args):
+ handler = get_test_db_handler(TestServerConfiguration(
+ 'data2', apphome=SQLGenAnnotatorTC.datadir))
+ handler.build_db_cache()
global repo, cnx
- repo, cnx = init_test_database(apphome=SQLGenAnnotatorTC.datadir)
+ repo, cnx = handler.get_repo_and_cnx()
def tearDownModule(*args):
global repo, cnx
@@ -330,6 +333,13 @@
self.assertEqual(rqlst.defined_vars['N']._q_invariant, False)
self.assertEqual(rqlst.defined_vars['F']._q_invariant, True)
+ def test_nonregr_ambiguity_2(self):
+ rqlst = self._prepare('Any S,SN WHERE X has_text "tot", X in_state S, S name SN, X is CWUser')
+ # X use has_text but should not be invariant as ambiguous, and has_text
+ # may not be its principal
+ self.assertEqual(rqlst.defined_vars['X']._q_invariant, False)
+ self.assertEqual(rqlst.defined_vars['S']._q_invariant, False)
+
if __name__ == '__main__':
from logilab.common.testlib import unittest_main
unittest_main()
--- a/server/test/unittest_undo.py Fri Jun 17 18:50:13 2011 +0200
+++ b/server/test/unittest_undo.py Fri Jun 17 18:53:33 2011 +0200
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
@@ -21,9 +22,11 @@
from cubicweb.devtools.testlib import CubicWebTC
from cubicweb.transaction import *
+from cubicweb.server.sources.native import UndoException
+
+
class UndoableTransactionTC(CubicWebTC):
-
def setup_database(self):
req = self.request()
self.session.undo_actions = set('CUDAR')
@@ -285,6 +288,15 @@
# test implicit 'replacement' of an inlined relation
+
+class UndoExceptionInUnicode(CubicWebTC):
+
+ # problem occurs in string manipulation for python < 2.6
+ def test___unicode__method(self):
+ u = UndoException(u"voilĂ ")
+ self.assertIsInstance(unicode(u), unicode)
+
+
if __name__ == '__main__':
from logilab.common.testlib import unittest_main
unittest_main()
--- a/test/unittest_rqlrewrite.py Fri Jun 17 18:50:13 2011 +0200
+++ b/test/unittest_rqlrewrite.py Fri Jun 17 18:53:33 2011 +0200
@@ -82,8 +82,9 @@
for vref in node.iget_nodes(nodes.VariableRef):
vrefmap.setdefault(vref.name, set()).add(vref)
for var in node.defined_vars.itervalues():
- assert not (var.stinfo['references'] ^ vrefmap[var.name])
- assert (var.stinfo['references'])
+ assert var.stinfo['references']
+ assert not (var.stinfo['references'] ^ vrefmap[var.name]), (node.as_string(), var.stinfo['references'], vrefmap[var.name])
+
class RQLRewriteTC(TestCase):
"""a faire:
@@ -95,10 +96,10 @@
"""
def test_base_var(self):
- card_constraint = ('X in_state S, U in_group G, P require_state S,'
+ constraint = ('X in_state S, U in_group G, P require_state S,'
'P name "read", P require_group G')
rqlst = parse('Card C')
- rewrite(rqlst, {('C', 'X'): (card_constraint,)}, {})
+ rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
self.failUnlessEqual(rqlst.as_string(),
u"Any C WHERE C is Card, B eid %(D)s, "
"EXISTS(C in_state A, B in_group E, F require_state A, "
@@ -130,27 +131,31 @@
"E in_state D, D name 'subscribed'), D is State, E is CWUser)")
def test_simplified_rqlst(self):
- card_constraint = ('X in_state S, U in_group G, P require_state S,'
+ constraint = ('X in_state S, U in_group G, P require_state S,'
'P name "read", P require_group G')
rqlst = parse('Any 2') # this is the simplified rql st for Any X WHERE X eid 12
- rewrite(rqlst, {('2', 'X'): (card_constraint,)}, {})
+ rewrite(rqlst, {('2', 'X'): (constraint,)}, {})
self.failUnlessEqual(rqlst.as_string(),
u"Any 2 WHERE B eid %(C)s, "
"EXISTS(2 in_state A, B in_group D, E require_state A, "
"E name 'read', E require_group D, A is State, D is CWGroup, E is CWPermission)")
- def test_optional_var_base(self):
- card_constraint = ('X in_state S, U in_group G, P require_state S,'
+ def test_optional_var_base_1(self):
+ constraint = ('X in_state S, U in_group G, P require_state S,'
'P name "read", P require_group G')
rqlst = parse('Any A,C WHERE A documented_by C?')
- rewrite(rqlst, {('C', 'X'): (card_constraint,)}, {})
+ rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
self.failUnlessEqual(rqlst.as_string(),
"Any A,C WHERE A documented_by C?, A is Affaire "
"WITH C BEING "
"(Any C WHERE EXISTS(C in_state B, D in_group F, G require_state B, G name 'read', "
"G require_group F), D eid %(A)s, C is Card)")
+
+ def test_optional_var_base_2(self):
+ constraint = ('X in_state S, U in_group G, P require_state S,'
+ 'P name "read", P require_group G')
rqlst = parse('Any A,C,T WHERE A documented_by C?, C title T')
- rewrite(rqlst, {('C', 'X'): (card_constraint,)}, {})
+ rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
self.failUnlessEqual(rqlst.as_string(),
"Any A,C,T WHERE A documented_by C?, A is Affaire "
"WITH C,T BEING "
@@ -158,6 +163,19 @@
"G require_state B, G name 'read', G require_group F), "
"D eid %(A)s, C is Card)")
+ def test_optional_var_base_3(self):
+ constraint1 = ('X in_state S, U in_group G, P require_state S,'
+ 'P name "read", P require_group G')
+ constraint2 = 'X in_state S, S name "public"'
+ rqlst = parse('Any A,C,T WHERE A documented_by C?, C title T')
+ rewrite(rqlst, {('C', 'X'): (constraint1, constraint2)}, {})
+ self.failUnlessEqual(rqlst.as_string(),
+ "Any A,C,T WHERE A documented_by C?, A is Affaire "
+ "WITH C,T BEING (Any C,T WHERE C title T, "
+ "EXISTS(C in_state B, D in_group F, G require_state B, G name 'read', G require_group F), "
+ "D eid %(A)s, C is Card, "
+ "EXISTS(C in_state E, E name 'public'))")
+
def test_optional_var_inlined(self):
c1 = ('X require_permission P')
c2 = ('X inlined_card O, O require_permission P')
--- a/web/component.py Fri Jun 17 18:50:13 2011 +0200
+++ b/web/component.py Fri Jun 17 18:53:33 2011 +0200
@@ -440,7 +440,6 @@
params.pop('view', None)
params.pop('entity', None)
form = params.pop('formparams', {})
- form['pageid'] = self._cw.pageid
if entity.has_eid():
eid = entity.eid
else:
--- a/web/data/cubicweb.calendar.js Fri Jun 17 18:50:13 2011 +0200
+++ b/web/data/cubicweb.calendar.js Fri Jun 17 18:53:33 2011 +0200
@@ -15,9 +15,9 @@
/**
* .. class:: Calendar
*
- * Calendar (graphical) widget
+ * Calendar (graphical) widget
*
- * public methods are :
+ * public methods are :
*
* __init__ :
* :attr:`containerId`: the DOM node's ID where the calendar will be displayed
@@ -74,7 +74,7 @@
/**
* .. function:: Calendar._uppercaseFirst(s)
*
- * utility function (the only use for now is inside the calendar)
+ * utility function (the only use for now is inside the calendar)
*/
this._uppercaseFirst = function(s) {
return s.charAt(0).toUpperCase();
@@ -83,7 +83,7 @@
/**
* .. function:: Calendar._domForRows(rows)
*
- * accepts the cells data and builds the corresponding TR nodes
+ * accepts the cells data and builds the corresponding TR nodes
*
* * `rows`, a list of list of couples (daynum, cssprops)
*/
@@ -98,7 +98,7 @@
/**
* .. function:: Calendar._headdisplay(row)
*
- * builds the calendar headers
+ * builds the calendar headers
*/
this._headdisplay = function(row) {
if (_CAL_HEADER) {
@@ -224,13 +224,17 @@
this.hide); // connect(inputId, 'onfocus', this, 'hide');
};
-// keep track of each calendar created
+/**
+ * .. data:: Calendar.REGISTRY
+ *
+ * keep track of each calendar created
+ */
Calendar.REGISTRY = {};
/**
* .. function:: toggleCalendar(containerId, inputId, year, month)
*
- * popup / hide calendar associated to `containerId`
+ * popup / hide calendar associated to `containerId`
*/
function toggleCalendar(containerId, inputId, year, month) {
var cal = Calendar.REGISTRY[containerId];
@@ -251,7 +255,7 @@
/**
* .. function:: toggleNextMonth(containerId)
*
- * ask for next month to calendar displayed in `containerId`
+ * ask for next month to calendar displayed in `containerId`
*/
function toggleNextMonth(containerId) {
var cal = Calendar.REGISTRY[containerId];
@@ -261,7 +265,7 @@
/**
* .. function:: togglePreviousMonth(containerId)
*
- * ask for previous month to calendar displayed in `containerId`
+ * ask for previous month to calendar displayed in `containerId`
*/
function togglePreviousMonth(containerId) {
var cal = Calendar.REGISTRY[containerId];
@@ -271,7 +275,7 @@
/**
* .. function:: dateSelected(cell, containerId)
*
- * Callback called when the user clicked on a cell in the popup calendar
+ * callback called when the user clicked on a cell in the popup calendar
*/
function dateSelected(cell, containerId) {
var cal = Calendar.REGISTRY[containerId];
--- a/web/data/cubicweb.edition.js Fri Jun 17 18:50:13 2011 +0200
+++ b/web/data/cubicweb.edition.js Fri Jun 17 18:53:33 2011 +0200
@@ -583,6 +583,7 @@
* around the corresponding input fields.
*/
function validateForm(formid, action, onsuccess, onfailure) {
+ freezeFormButtons(formid);
try {
var zipped = cw.utils.formContents(formid);
var args = ajaxFuncArgs('validate_form', null, action, zipped[0], zipped[1]);
--- a/web/data/cubicweb.timeline-bundle.js Fri Jun 17 18:50:13 2011 +0200
+++ b/web/data/cubicweb.timeline-bundle.js Fri Jun 17 18:53:33 2011 +0200
@@ -1,10 +1,15 @@
+/**
+ * This file contains timeline utilities
+ * :organization: Logilab
+ */
+
var SimileAjax_urlPrefix = baseuri() + 'data/';
var Timeline_urlPrefix = baseuri() + 'data/';
/*
* Simile Ajax API
*
- * Include this file in your HTML file as follows:
+ * Include this file in your HTML file as follows::
*
* <script src="http://simile.mit.edu/ajax/api/simile-ajax-api.js" type="text/javascript"></script>
*
--- a/web/request.py Fri Jun 17 18:50:13 2011 +0200
+++ b/web/request.py Fri Jun 17 18:53:33 2011 +0200
@@ -624,8 +624,10 @@
extraparams.setdefault('fname', 'view')
url = self.build_url('json', **extraparams)
cbname = build_cb_uid(url[:50])
+ # think to propagate pageid. XXX see https://www.cubicweb.org/ticket/1753121
jscode = 'function %s() { $("#%s").%s; }' % (
- cbname, nodeid, js.loadxhtml(url, None, 'get', replacemode))
+ cbname, nodeid, js.loadxhtml(url, {'pageid': self.pageid},
+ 'get', replacemode))
self.html_headers.add_post_inline_script(jscode)
return "javascript: %s()" % cbname
--- a/web/test/unittest_web.py Fri Jun 17 18:50:13 2011 +0200
+++ b/web/test/unittest_web.py Fri Jun 17 18:53:33 2011 +0200
@@ -38,7 +38,7 @@
self.failUnless(url.endswith('()'))
cbname = url.split()[1][:-2]
self.assertMultiLineEqual(
- 'function %s() { $("#foo").loadxhtml("http://testing.fr/cubicweb/json?%s",null,"get","replace"); }' % (cbname, qs),
+ 'function %s() { $("#foo").loadxhtml("http://testing.fr/cubicweb/json?%s",{"pageid": "%s"},"get","replace"); }' % (cbname, qs, req.pageid),
req.html_headers.post_inlined_scripts[0])
if __name__ == '__main__':
--- a/web/views/basecontrollers.py Fri Jun 17 18:50:13 2011 +0200
+++ b/web/views/basecontrollers.py Fri Jun 17 18:53:33 2011 +0200
@@ -604,7 +604,7 @@
if not errors:
self.redirect()
return self._cw._('some errors occurred:') + self._cw.view(
- 'pyvalist', pyvalue=errors)
+ 'pyvallist', pyvalue=errors)
def redirect(self):
req = self._cw
--- a/web/views/cwsources.py Fri Jun 17 18:50:13 2011 +0200
+++ b/web/views/cwsources.py Fri Jun 17 18:53:33 2011 +0200
@@ -27,7 +27,7 @@
from cubicweb.selectors import is_instance, score_entity, match_user_groups
from cubicweb.view import EntityView, StartupView
from cubicweb.schema import META_RTYPES, VIRTUAL_RTYPES, display_name
-from cubicweb.web import uicfg
+from cubicweb.web import uicfg, formwidgets as wdgs
from cubicweb.web.views import tabs, actions
@@ -35,6 +35,12 @@
_abaa.tag_object_of(('CWSourceSchemaConfig', 'cw_schema', '*'), False)
_abaa.tag_object_of(('CWSourceSchemaConfig', 'cw_for_source', '*'), False)
+_afs = uicfg.autoform_section
+_afs.tag_attribute(('CWSource', 'synchronizing'), 'main', 'hidden')
+_afs.tag_object_of(('*', 'cw_for_source', 'CWSource'), 'main', 'hidden')
+_affk = uicfg.autoform_field_kwargs
+_affk.tag_attribute(('CWSource', 'parser'), {'widget': wdgs.TextInput})
+
# source primary views #########################################################
_pvs = uicfg.primaryview_section
--- a/web/views/idownloadable.py Fri Jun 17 18:50:13 2011 +0200
+++ b/web/views/idownloadable.py Fri Jun 17 18:53:33 2011 +0200
@@ -15,8 +15,10 @@
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
-"""Specific views for entities adapting to IDownloadable"""
-
+"""
+Specific views for entities adapting to IDownloadable
+=====================================================
+"""
__docformat__ = "restructuredtext en"
_ = unicode
@@ -50,6 +52,7 @@
class DownloadBox(component.EntityCtxComponent):
+ """add download box"""
__regid__ = 'download_box' # no download box for images
__select__ = (component.EntityCtxComponent.__select__ &
adaptable('IDownloadable') & ~has_mimetype('image/'))
@@ -71,7 +74,9 @@
class DownloadView(EntityView):
- """this view is replacing the deprecated 'download' controller and allow
+ """download view
+
+ this view is replacing the deprecated 'download' controller and allow
downloading of entities providing the necessary interface
"""
__regid__ = 'download'
@@ -199,6 +204,7 @@
class ImageView(AbstractEmbeddedView):
+ """image embedded view"""
__regid__ = 'image'
__select__ = has_mimetype('image/')
@@ -207,6 +213,7 @@
class EHTMLView(AbstractEmbeddedView):
+ """html embedded view"""
__regid__ = 'ehtml'
__select__ = has_mimetype('text/html')
--- a/web/views/rdf.py Fri Jun 17 18:50:13 2011 +0200
+++ b/web/views/rdf.py Fri Jun 17 18:53:33 2011 +0200
@@ -59,6 +59,9 @@
self.entity2graph(graph, entity)
self.w(graph.serialize().decode('utf-8'))
+ def entity_call(self, entity):
+ self.call()
+
def entity2graph(self, graph, entity):
cwuri = URIRef(entity.cwuri)
add = graph.add