# HG changeset patch # User Sylvain Thénault # Date 1270644175 -7200 # Node ID 395e1ff018ae311edd3f4b05a4f64fdb6c94de5f # Parent ddd5219d7eef5aeec0ce9b7be3448348c31e7338# Parent 6efb7a7ae5708dc36d805d57a1a8ce34ac1cb87e backport stable diff -r ddd5219d7eef -r 395e1ff018ae debian/control --- a/debian/control Wed Apr 07 14:42:44 2010 +0200 +++ b/debian/control Wed Apr 07 14:42:55 2010 +0200 @@ -33,7 +33,7 @@ Conflicts: cubicweb-multisources Replaces: cubicweb-multisources Provides: cubicweb-multisources -Depends: ${python:Depends}, cubicweb-common (= ${source:Version}), cubicweb-ctl (= ${source:Version}), python-logilab-database, cubicweb-postgresql-support | cubicweb-mysql-support | python-pysqlite2 +Depends: ${python:Depends}, cubicweb-common (= ${source:Version}), cubicweb-ctl (= ${source:Version}), python-logilab-database (=> 1.0.2), cubicweb-postgresql-support | cubicweb-mysql-support | python-pysqlite2 Recommends: pyro, cubicweb-documentation (= ${source:Version}) Description: server part of the CubicWeb framework CubicWeb is a semantic web application framework. diff -r ddd5219d7eef -r 395e1ff018ae devtools/__init__.py --- a/devtools/__init__.py Wed Apr 07 14:42:44 2010 +0200 +++ b/devtools/__init__.py Wed Apr 07 14:42:55 2010 +0200 @@ -197,6 +197,8 @@ init_test_database_sqlite(config) elif driver == 'postgres': init_test_database_postgres(config) + elif driver == 'sqlserver2005': + init_test_database_sqlserver2005(config, source) else: raise ValueError('no initialization function for driver %r' % driver) config._cubes = None # avoid assertion error @@ -219,11 +221,19 @@ ### postgres test database handling ############################################ def init_test_database_postgres(config): - """initialize a fresh sqlite databse used for testing purpose""" + """initialize a fresh postgresql databse used for testing purpose""" if config.init_repository: from cubicweb.server import init_repository init_repository(config, interactive=False, drop=True) +### sqlserver2005 test database handling ############################################ + +def init_test_database_sqlserver2005(config): + """initialize a fresh sqlserver databse used for testing purpose""" + if config.init_repository: + from cubicweb.server import init_repository + init_repository(config, interactive=False, drop=True, vreg=vreg) + ### sqlite test database handling ############################################## diff -r ddd5219d7eef -r 395e1ff018ae devtools/testlib.py --- a/devtools/testlib.py Wed Apr 07 14:42:44 2010 +0200 +++ b/devtools/testlib.py Wed Apr 07 14:42:55 2010 +0200 @@ -265,6 +265,12 @@ self.setup_database() self.commit() MAILBOX[:] = [] # reset mailbox + self._cnxs = [] + + def tearDown(self): + for cnx in self._cnxs: + if not cnx._closed: + cnx.close() def setup_database(self): """add your database setup code by overriding this method""" @@ -306,6 +312,7 @@ self.cnx = repo_connect(self.repo, unicode(login), cnxprops=ConnectionProperties('inmemory'), **kwargs) + self._cnxs.append(self.cnx) if login == self.vreg.config.anonymous_user()[0]: self.cnx.anonymous_connection = True return self.cnx @@ -314,6 +321,7 @@ if not self.cnx is self._orig_cnx: try: self.cnx.close() + self._cnxs.remove(self.cnx) except ProgrammingError: pass # already closed self.cnx = self._orig_cnx diff -r ddd5219d7eef -r 395e1ff018ae doc/book/en/annexes/faq.rst --- a/doc/book/en/annexes/faq.rst Wed Apr 07 14:42:44 2010 +0200 +++ b/doc/book/en/annexes/faq.rst Wed Apr 07 14:42:55 2010 +0200 @@ -6,8 +6,6 @@ [XXX 'copy answer from forum' means reusing text from http://groups.google.com/group/google-appengine/browse_frm/thread/c9476925f5f66ec6 and -http://groups.google.com/group/google-appengine/browse_frm/thread/d791ce17e2716147/eb078f8cfe8426e0 -and http://groups.google.com/group/google-appengine/browse_frm/thread/f48cf6099973aef5/c28cd6934dd72457 ] @@ -26,8 +24,13 @@ Python is the templating language that we use in *CubicWeb*, but again, it does not prevent you from using a templating language. -The reason template languages are not used in this book is that -experience has proved us that using pure python was less cumbersome. +Moreover, CubicWeb currently supports `simpletal`_ out of the box and +it is also possible to use the `cwtags`_ library to build html trees +using the `with statement`_ with more comfort than raw strings. + +.. _`simpletal`: http://www.owlfish.com/software/simpleTAL/ +.. _`cwtags`: http://www.cubicweb.org/project/cwtags +.. _`with statement`: http://www.python.org/dev/peps/pep-0343/ Why do you think using pure python is better than using a template language ? ----------------------------------------------------------------------------- @@ -36,31 +39,24 @@ already provides a consistent and strong architecture and syntax a templating language would not reach. -When doing development, you need a real language and template -languages are not real languages. - Using Python instead of a template langage for describing the user interface makes it to maintain with real functions/classes/contexts without the need of learning a new dialect. By using Python, we use standard OOP techniques and this is a key factor in a robust application. -The `cwtags` (http://www.cubicweb.org/project/cwtags) package can be -used in cubes to help generate html from Python with more comfort than -raw strings. - Why do you use the LGPL license to prevent me from doing X ? ------------------------------------------------------------ LGPL means that *if* you redistribute your application, you need to redistribute the changes you made to CubicWeb under the LGPL licence. -Publishing a web site has nothing to do with redistributing -source code. A fair amount of companies use modified LGPL code -for internal use. And someone could publish a *CubicWeb* component -under a BSD licence for others to plug into a LGPL framework without -any problem. The only thing we are trying to prevent here is someone -taking the framework and packaging it as closed source to his own -clients. +Publishing a web site has nothing to do with redistributing source +code according to the terms of the LGPL. A fair amount of companies +use modified LGPL code for internal use. And someone could publish a +*CubicWeb* component under a BSD licence for others to plug into a +LGPL framework without any problem. The only thing we are trying to +prevent here is someone taking the framework and packaging it as +closed source to his own clients. CubicWeb looks pretty recent. Is it stable ? @@ -77,7 +73,7 @@ SPARQL. Except that SPARQL did not exist when we started the project. With version 3.4, CubicWeb has support for SPARQL. -That RQL language is what is going to make a difference with django- +The RQL language is what is going to make a difference with django- like frameworks for several reasons. 1. accessing data is *much* easier with it. One can write complex @@ -192,23 +188,17 @@ well defined context (request, result set). It enables much more dynamic views. -What is the difference between `AppRsetObject` and `AppObject` ? ----------------------------------------------------------------- - -`AppRsetObject` instances are selected on a request and a result -set. `AppObject` instances are directly selected by id. - How to update a database after a schema modification ? ------------------------------------------------------ It depends on what has been modified in the schema. -* Update the permissions and properties of an entity or a relation: +* update the permissions and properties of an entity or a relation: ``sync_schema_props_perms('MyEntityOrRelation')``. -* Add an attribute: ``add_attribute('MyEntityType', 'myattr')``. +* add an attribute: ``add_attribute('MyEntityType', 'myattr')``. -* Add a relation: ``add_relation_definition('SubjRelation', 'MyRelation', 'ObjRelation')``. +* add a relation: ``add_relation_definition('SubjRelation', 'MyRelation', 'ObjRelation')``. How to create an anonymous user ? @@ -401,3 +391,9 @@ remove {'PR': 'Project', 'C': 'CWUser'} from solutions since your_user has no read access to cost This is because you have to put your user in the "users" group. The user has to be in both groups. + +How 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. diff -r ddd5219d7eef -r 395e1ff018ae doc/book/en/development/devweb/js.rst --- a/doc/book/en/development/devweb/js.rst Wed Apr 07 14:42:44 2010 +0200 +++ b/doc/book/en/development/devweb/js.rst Wed Apr 07 14:42:55 2010 +0200 @@ -4,9 +4,8 @@ ---------- *CubicWeb* uses quite a bit of javascript in its user interface and -ships with jquery (1.3.x) and parts of the jquery UI -library, plus a number of homegrown files and also other thirparty -libraries. +ships with jquery (1.3.x) and parts of the jquery UI library, plus a +number of homegrown files and also other thir party libraries. All javascript files are stored in cubicweb/web/data/. There are around thirty js files there. In a cube it goes to data/. @@ -23,7 +22,7 @@ XXX external_resources variable (which needs love) -CubicWeb javascript api +CubicWeb javascript API ~~~~~~~~~~~~~~~~~~~~~~~ Javascript resources are typically loaded on demand, from views. The @@ -57,13 +56,18 @@ Important AJAX APIS ~~~~~~~~~~~~~~~~~~~ +* `asyncRemoteExec` and `remoteExec` are the base building blocks for + doing arbitrary async (resp. sync) communications with the server + +* `reloadComponent` is a convenience function to replace a DOM node + with server supplied content coming from a specific registry (this + is quite handy to refresh the content of some boxes for instances) + * `jQuery.fn.loadxhtml` is an important extension to jQuery which - allow proper loading and in-place DOM update of xhtml views. It is + allows proper loading and in-place DOM update of xhtml views. It is suitably augmented to trigger necessary events, and process CubicWeb specific elements such as the facet system, fckeditor, etc. -* `asyncRemoteExec` and `remoteExec` are the base building blocks for - doing arbitrary async (resp. sync) communications with the server A simple example with asyncRemoteExec ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -89,20 +93,108 @@ .. sourcecode: javascript - function async_hello(name) { + function asyncHello(name) { var deferred = asyncRemoteExec('say_hello', name); deferred.addCallback(function (response) { alert(response); }); - deferred.addErrback(function () { + deferred.addErrback(function (error) { alert('something fishy happened'); }); } - function sync_hello(name) { + function syncHello(name) { alert( remoteExec('say_hello', name) ); } +Anatomy of a reloadComponent call +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`reloadComponent` allows to dynamically replace some DOM node with new +elements. It has the following signature: + +* `compid` (mandatory) is the name of the component to be reloaded + +* `rql` (optional) will be used to generate a result set given as + argument to the selected component + +* `registry` (optional) defaults to 'components' but can be any other + valid registry name + +* `nodeid` (optional) defaults to compid + 'Component' but can be any + explicitly specified DOM node id + +* `extraargs` (optional) should be a dictionary of values that will be + given to the cell_call method of the component + +A simple reloadComponent example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The server side implementation of `reloadComponent` is the +js_component method of the JSonController. + +The following function implements a two-steps method to delete a +standard bookmark and refresh the UI, while keeping the UI responsive. + +.. sourcecode:: javascript + + function removeBookmark(beid) { + d = asyncRemoteExec('delete_bookmark', beid); + d.addCallback(function(boxcontent) { + reloadComponent('bookmarks_box', '', 'boxes', 'bookmarks_box'); + document.location.hash = '#header'; + updateMessage(_("bookmark has been removed")); + }); + } + +`reloadComponent` is called with the id of the bookmark box as +argument, no rql expression (because the bookmarks display is actually +independant of any dataset context), a reference to the 'boxes' +registry (which hosts all left, right and contextual boxes) and +finally an explicit 'bookmarks_box' nodeid argument that stipulates +the target DOM node. + +Anatomy of a loadxhtml call +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`jQuery.fn.loadxhtml` is an important extension to jQuery which allows +proper loading and in-place DOM update of xhtml views. The existing +`jQuery.load`_ function does not handle xhtml, hence the addition. The +API of loadxhtml is roughly similar to that of `jQuery.load`_. + +.. _`jQuery.load`: http://api.jquery.com/load/ + + +* `url` (mandatory) should be a complete url (typically referencing + the JSonController, but this is not strictly mandatory) + +* `data` (optional) is a dictionary of values given to the + controller specified through an `url` argument; some keys may have a + special meaning depending on the choosen controller (such as `fname` + for the JSonController); the `callback` key, if present, must refer + to a function to be called at the end of loadxhtml (more on this + below) + +* `reqtype` (optional) specifies the request method to be used (get or + post); if the argument is 'post', then the post method is used, + otherwise the get method is used + +* `mode` (optional) is one of `replace` (the default) which means the + loaded node will replace the current node content, `swap` to replace + the current node with the loaded node, and `append` which will + append the loaded node to the current node content + +About the `callback` option: + +* it is called with two parameters: the current node, and a list + containing the loaded (and post-processed node) + +* whenever is returns another function, this function is called in + turn with the same parameters as above + +This mechanism allows callback chaining. + + A simple example with loadxhtml ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -110,7 +202,7 @@ injected in the live DOM. The view will be of course selected server-side using an entity eid provided by the client side. -.. sourcecode: python +.. sourcecode:: python from cubicweb import typed_eid from cubicweb.web.views.basecontrollers import JSonController, xhtmlize @@ -121,7 +213,7 @@ entity = self._cw.entity_from_eid(typed_eid(eid)) return entity.view('frob', name=frobname) -.. sourcecode: javascript +.. sourcecode:: javascript function update_some_div(divid, eid, frobname) { var params = {fname:'frob_status', eid: eid, frobname:frobname}; @@ -131,7 +223,7 @@ In this example, the url argument is the base json url of a cube instance (it should contain something like `http://myinstance/json?`). The actual JSonController method name is -encoded in the `params` dictionnary using the `fname` key. +encoded in the `params` dictionary using the `fname` key. A more real-life example from CubicWeb ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -144,7 +236,7 @@ We present here a skeletal version of the mecanism used in CubicWeb and available in web/views/tabs.py, in the `LazyViewMixin` class. -.. sourcecode: python +.. sourcecode:: python def lazyview(self, vid, rql=None): """ a lazy version of wview """ @@ -161,7 +253,7 @@ load_now('#lazy-%(vid)s');});""" % {'event': 'load_%s' % vid, 'vid': vid}) -This creates a `div` with an specific event associated to it. +This creates a `div` with a specific event associated to it. The full version deals with: @@ -177,7 +269,7 @@ The javascript side is quite simple, due to loadxhtml awesomeness. -.. sourcecode: javascript +.. sourcecode:: javascript function load_now(eltsel) { var lazydiv = jQuery(eltsel); @@ -205,10 +297,10 @@ computation-intensive web page could be scinded into one fast-loading part and a delayed part). -In the server side, a simple call to a javascript function is +On the server side, a simple call to a javascript function is sufficient. -.. sourcecode: python +.. sourcecode:: python def forceview(self, vid): """trigger an event that will force immediate loading of the view @@ -218,49 +310,17 @@ The browser-side definition follows. -.. sourcecode: javascript +.. sourcecode:: javascript function trigger_load(divid) { jQuery('#lazy-' + divd).trigger('load_' + divid); } -Anatomy of a lodxhtml call -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The loadxhtml extension to jQuery accept many parameters with rich -semantics. Let us detail these. - -* `url` (mandatory) should be a complete url, typically based on the - JSonController, but this is not strictly mandatory - -* `data` (optional) is a dictionnary of values given to the - controller specified through an `url` argument; some keys may have a - special meaning depending on the choosen controller (such as `fname` - for the JSonController); the `callback` key, if present, must refer - to a function to be called at the end of loadxhtml (more on this - below) - -* `reqtype` (optional) specifies the request method to be used (get or - post); if the argument is 'post', then the post method is used, - otherwise the get method is used - -* `mode` (optional) is one of `replace` (the default) which means the - loaded node will replace the current node content, `swap` to replace - the current node with the loaded node, and `append` which will - append the loaded node to the current node content -About the `callback` option: - -* it is called with two parameters: the current node, and a list - containing the loaded (and post-processed node) - -* whenever is returns another function, this function is called in - turn with the same parameters as above - -This mecanism allows callback chaining. - +XXX reloadComponent +XXX userCallback / user_callback Javascript library: overview ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff -r ddd5219d7eef -r 395e1ff018ae doc/book/en/development/entityclasses/application-logic.rst --- a/doc/book/en/development/entityclasses/application-logic.rst Wed Apr 07 14:42:44 2010 +0200 +++ b/doc/book/en/development/entityclasses/application-logic.rst Wed Apr 07 14:42:55 2010 +0200 @@ -8,7 +8,7 @@ .. _`ORM`: http://en.wikipedia.org/wiki/Object-relational_mapping Entities objects are used in the repository and web sides of -CubicWeb. In the repository side of things, one should manipulate them +CubicWeb. On the repository side of things, one should manipulate them in Hooks and Operations. Hooks and Operations provide support for the implementation of rules @@ -19,7 +19,7 @@ So a lot of an application's business rules will be written in Hooks (or Operations). -In the web side, views also typically operate using entity +On the web side, views also typically operate using entity objects. Obvious entity methods for use in views are the dublin code method like dc_title, etc. For separation of concerns reasons, one should ensure no ui logic pervades the entities level, and also no diff -r ddd5219d7eef -r 395e1ff018ae hooks/workflow.py --- a/hooks/workflow.py Wed Apr 07 14:42:44 2010 +0200 +++ b/hooks/workflow.py Wed Apr 07 14:42:55 2010 +0200 @@ -193,7 +193,7 @@ raise ValidationError(entity.eid, {None: msg}) # True if we are coming back from subworkflow swtr = session.transaction_data.pop((forentity.eid, 'subwfentrytr'), None) - cowpowers = ('managers' in session.user.groups + cowpowers = (session.user.is_in_group('managers') or not session.write_security) # no investigate the requested state change... try: diff -r ddd5219d7eef -r 395e1ff018ae server/msplanner.py --- a/server/msplanner.py Wed Apr 07 14:42:44 2010 +0200 +++ b/server/msplanner.py Wed Apr 07 14:42:55 2010 +0200 @@ -1044,7 +1044,7 @@ for select in subquery.query.children] for sppi in sppis: if sppi.needsplit or sppi.part_sources != ppi.part_sources: - temptable = 'T%s' % make_uid(id(subquery)) + temptable = plan.make_temp_table_name('T%s' % make_uid(id(subquery))) sstep = self._union_plan(plan, sppis, temptable)[0] break else: @@ -1077,7 +1077,7 @@ inputmap = self._ppi_subqueries(ppi) aggrstep = need_aggr_step(select, sources) if aggrstep: - atemptable = 'T%s' % make_uid(id(select)) + atemptable = plan.make_temp_table_name('T%s' % make_uid(id(select))) sunion = Union() sunion.append(select) selected = select.selection[:] @@ -1121,7 +1121,7 @@ subinputmap = self._ppi_subqueries(ppi) stepdefs = ppi.part_steps() if need_aggr_step(select, ppi.part_sources, stepdefs): - atemptable = 'T%s' % make_uid(id(select)) + atemptable = plan.make_temp_table_name('T%s' % make_uid(id(select))) selection = select.selection[:] select_group_sort(select) else: @@ -1171,6 +1171,7 @@ else: table = '_T%s%s' % (''.join(sorted(v._ms_table_key() for v in terms)), ''.join(sorted(str(i) for i in solindices))) + table = plan.make_temp_table_name(table) ppi.build_non_final_part(minrqlst, solindices, sources, insertedvars, table) # finally: join parts, deal with aggregat/group/sorts if necessary diff -r ddd5219d7eef -r 395e1ff018ae server/querier.py --- a/server/querier.py Wed Apr 07 14:42:44 2010 +0200 +++ b/server/querier.py Wed Apr 07 14:42:55 2010 +0200 @@ -164,6 +164,13 @@ finally: self.clean() + def make_temp_table_name(self, table): + """ + return a temp table name according to db backend + """ + return self.syssource.make_temp_table_name(table) + + def init_temp_table(self, table, selected, sol): """initialize sql schema and variable map for a temporary table which will be used to store result for the given rqlst diff -r ddd5219d7eef -r 395e1ff018ae server/repository.py --- a/server/repository.py Wed Apr 07 14:42:44 2010 +0200 +++ b/server/repository.py Wed Apr 07 14:42:55 2010 +0200 @@ -593,7 +593,7 @@ raise except: # FIXME: check error to catch internal errors - self.exception('unexpected error') + self.exception('unexpected error while executing %s with %s', rqlstring, args) raise finally: session.reset_pool() diff -r ddd5219d7eef -r 395e1ff018ae server/sources/native.py --- a/server/sources/native.py Wed Apr 07 14:42:44 2010 +0200 +++ b/server/sources/native.py Wed Apr 07 14:42:55 2010 +0200 @@ -669,6 +669,15 @@ pass return None + def make_temp_table_name(self, table): + try: # XXX remove this once + return self.dbhelper.temporary_table_name(table) + except AttributeError: + import warnings + warnings.warn('Please hg up logilab.database') + return table + + def temp_table_def(self, selected, sol, table): return make_schema(selected, sol, table, self.dbhelper.TYPE_MAPPING) diff -r ddd5219d7eef -r 395e1ff018ae skeleton/test/pytestconf.py --- a/skeleton/test/pytestconf.py Wed Apr 07 14:42:44 2010 +0200 +++ b/skeleton/test/pytestconf.py Wed Apr 07 14:42:55 2010 +0200 @@ -26,11 +26,11 @@ parser.add_option('-u', '--dbuser', dest='dbuser', action='store', default=login, help="database user") parser.add_option('-w', '--dbpassword', dest='dbpassword', action='store', - default=login, help="database name") + default=login, help="database user's password") parser.add_option('-n', '--dbname', dest='dbname', action='store', default=None, help="database name") parser.add_option('--euser', dest='euser', action='store', - default=login, help="esuer name") + default=login, help="euser name") parser.add_option('--epassword', dest='epassword', action='store', default=login, help="euser's password' name") return parser diff -r ddd5219d7eef -r 395e1ff018ae web/data/cubicweb.ajax.js --- a/web/data/cubicweb.ajax.js Wed Apr 07 14:42:44 2010 +0200 +++ b/web/data/cubicweb.ajax.js Wed Apr 07 14:42:55 2010 +0200 @@ -239,7 +239,7 @@ function asyncRemoteExec(fname /* ... */) { setProgressCursor(); var props = {'fname' : fname, 'pageid' : pageid, - 'arg': map(jQuery.toJSON, sliceList(arguments, 1))}; + 'arg': map(jQuery.toJSON, sliceList(arguments, 1))}; var deferred = loadRemote(JSON_BASE_URL, props, 'POST'); deferred = deferred.addErrback(remoteCallFailed); deferred = deferred.addErrback(resetCursor); diff -r ddd5219d7eef -r 395e1ff018ae web/views/basecontrollers.py --- a/web/views/basecontrollers.py Wed Apr 07 14:42:44 2010 +0200 +++ b/web/views/basecontrollers.py Wed Apr 07 14:42:55 2010 +0200 @@ -410,6 +410,7 @@ extraargs = extraargs or {} stream = comp.set_stream() comp.render(**extraargs) + # XXX why not _call_view ? extresources = self._cw.html_headers.getvalue(skiphead=True) if extresources: stream.write(u'
\n')