# HG changeset patch # User Sylvain Thénault # Date 1261567210 -3600 # Node ID 60256056bda6c339bc9bf48ceb1c87ccce061f9b # Parent 8e2eaa6b373308a149f86df7bf8441bb6bb143d1# Parent e972fc3067193c93895ed48f52d17bde051b8dbb backport stable branch diff -r 8e2eaa6b3733 -r 60256056bda6 .hgtags --- a/.hgtags Tue Dec 22 19:27:51 2009 +0100 +++ b/.hgtags Wed Dec 23 12:20:10 2009 +0100 @@ -94,3 +94,5 @@ d7f2d32340fb59753548ef29cbc1958ef3a55fc6 cubicweb-debian-version-3.5.9-1 9b52725d8c534ba40877457b413077a10173bf88 cubicweb-version-3.5.10 dfe2f245248c97bea3a29c8ecc6d293e25ff708e cubicweb-debian-version-3.5.10-1 +f48b2f193961803cf42147272671a335a2daeceb cubicweb-version-3.5.11 +4920121d41f28c8075a4f00461911677396fc566 cubicweb-debian-version-3.5.11-1 diff -r 8e2eaa6b3733 -r 60256056bda6 __pkginfo__.py diff -r 8e2eaa6b3733 -r 60256056bda6 debian/changelog --- a/debian/changelog Tue Dec 22 19:27:51 2009 +0100 +++ b/debian/changelog Wed Dec 23 12:20:10 2009 +0100 @@ -1,3 +1,9 @@ +cubicweb (3.5.11-1) unstable; urgency=low + + * new upstream release + + -- Sylvain Thénault Wed, 23 Dec 2009 10:27:12 +0100 + cubicweb (3.5.10-1) unstable; urgency=low * new upstream release diff -r 8e2eaa6b3733 -r 60256056bda6 debian/control --- a/debian/control Tue Dec 22 19:27:51 2009 +0100 +++ b/debian/control Wed Dec 23 12:20:10 2009 +0100 @@ -77,7 +77,7 @@ Package: cubicweb-common Architecture: all XB-Python-Version: ${python:Versions} -Depends: ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.6.0), python-logilab-common (>= 0.45.2), python-yams (>= 0.25.0), python-rql (>= 0.22.3), python-lxml +Depends: ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.6.0), python-logilab-common (>= 0.46.0), python-yams (>= 0.25.0), python-rql (>= 0.22.3), python-lxml Recommends: python-simpletal (>= 4.0) Conflicts: cubicweb-core Replaces: cubicweb-core diff -r 8e2eaa6b3733 -r 60256056bda6 etwist/server.py --- a/etwist/server.py Tue Dec 22 19:27:51 2009 +0100 +++ b/etwist/server.py Wed Dec 23 12:20:10 2009 +0100 @@ -100,9 +100,11 @@ self.debugmode = debug self.config = config self.base_url = config['base-url'] or config.default_base_url() - assert self.base_url[-1] == '/' + if self.base_url[-1] != '/': + self.base_url += '/' self.https_url = config['https-url'] - assert not self.https_url or self.https_url[-1] == '/' + if self.https_url and self.https_url[-1] != '/': + self.https_url += '/' # instantiate publisher here and not in init_publisher to get some # checks done before daemonization (eg versions consistency) self.appli = CubicWebPublisher(config, debug=self.debugmode) diff -r 8e2eaa6b3733 -r 60256056bda6 i18n/en.po --- a/i18n/en.po Tue Dec 22 19:27:51 2009 +0100 +++ b/i18n/en.po Wed Dec 23 12:20:10 2009 +0100 @@ -3617,7 +3617,7 @@ msgstr "" #, python-format -msgid "transition %s isn't allowed from %s" +msgid "transition %(tr)s isn't allowed from %(st)s" msgstr "" msgid "transition doesn't belong to entity's workflow" diff -r 8e2eaa6b3733 -r 60256056bda6 i18n/es.po --- a/i18n/es.po Tue Dec 22 19:27:51 2009 +0100 +++ b/i18n/es.po Wed Dec 23 12:20:10 2009 +0100 @@ -3716,7 +3716,7 @@ msgstr "cambiar valor" #, python-format -msgid "transition %s isn't allowed from %s" +msgid "transition %(tr)s isn't allowed from %(st)s" msgstr "" msgid "transition doesn't belong to entity's workflow" diff -r 8e2eaa6b3733 -r 60256056bda6 i18n/fr.po --- a/i18n/fr.po Tue Dec 22 19:27:51 2009 +0100 +++ b/i18n/fr.po Wed Dec 23 12:20:10 2009 +0100 @@ -3741,8 +3741,8 @@ msgstr "inverser les cases à cocher" #, python-format -msgid "transition %s isn't allowed from %s" -msgstr "la transition %s n'est pas autorisée depuis l'état %s" +msgid "transition %(tr)s isn't allowed from %(st)s" +msgstr "la transition %(tr)s n'est pas autorisée depuis l'état %(st)s" msgid "transition doesn't belong to entity's workflow" msgstr "la transition n'appartient pas au workflow de l'entité" diff -r 8e2eaa6b3733 -r 60256056bda6 server/migractions.py --- a/server/migractions.py Tue Dec 22 19:27:51 2009 +0100 +++ b/server/migractions.py Wed Dec 23 12:20:10 2009 +0100 @@ -196,7 +196,7 @@ if systemonly and source.uri != 'system': continue try: - source.restore(osp.join(tmpdir, source.uri), drop=drop) + source.restore(osp.join(tmpdir, source.uri), self.confirm, drop) except Exception, exc: print '-> error trying to restore [%s]' % exc if not self.confirm('Continue anyway?', default='n'): diff -r 8e2eaa6b3733 -r 60256056bda6 server/serverctl.py --- a/server/serverctl.py Tue Dec 22 19:27:51 2009 +0100 +++ b/server/serverctl.py Wed Dec 23 12:20:10 2009 +0100 @@ -55,8 +55,11 @@ password = source['db-password'] else: password = getpass('password: ') + extra_args = source.get('db-extra-arguments') + extra = extra_args and {'extra_args': extra_args} or {} return get_connection(driver, dbhost, dbname, user, password=password, - port=source.get('db-port')) + port=source.get('db-port'), + **extra) def system_source_cnx(source, dbms_system_base=False, special_privs='CREATE/DROP DATABASE', verbose=True): @@ -281,11 +284,16 @@ automatic = self.get('automatic') appid = pop_arg(args, msg='No instance specified !') config = ServerConfiguration.config_for(appid) - create_db = self.config.create_db source = config.sources()['system'] + dbname = source['db-name'] driver = source['db-driver'] + create_db = self.config.create_db helper = get_adv_func_helper(driver) - if create_db: + if driver == 'sqlite': + if os.path.exists(dbname) and automatic or \ + ASK.confirm('Database %s already exists -- do you want to drop it ?' % dbname): + os.unlink(dbname) + elif create_db: print '\n'+underline_title('Creating the system database') # connect on the dbms system base to create our base dbcnx = _db_sys_cnx(source, 'CREATE DATABASE and / or USER', verbose=verbose) @@ -297,7 +305,6 @@ ASK.confirm('Create db user %s ?' % user, default_is_yes=False)): helper.create_user(source['db-user'], source['db-password']) print '-> user %s created.' % user - dbname = source['db-name'] if dbname in helper.list_databases(cursor): if automatic or ASK.confirm('Database %s already exists -- do you want to drop it ?' % dbname): cursor.execute('DROP DATABASE %s' % dbname) @@ -310,7 +317,7 @@ helper.create_database(cursor, dbname, encoding=source['db-encoding']) dbcnx.commit() - print '-> database %s created.' % source['db-name'] + print '-> database %s created.' % dbname except: dbcnx.rollback() raise @@ -364,10 +371,13 @@ config = ServerConfiguration.config_for(appid) try: system = config.sources()['system'] + extra_args=system.get('db-extra-arguments') + extra = extra_args and {'extra_args': extra_args} or {} get_connection( system['db-driver'], database=system['db-name'], host=system.get('db-host'), port=system.get('db-port'), - user=system.get('db-user'), password=system.get('db-password')) + user=system.get('db-user'), password=system.get('db-password'), + **extra) except Exception, ex: raise ConfigurationError( 'You seem to have provided wrong connection information in '\ diff -r 8e2eaa6b3733 -r 60256056bda6 server/sources/__init__.py --- a/server/sources/__init__.py Tue Dec 22 19:27:51 2009 +0100 +++ b/server/sources/__init__.py Wed Dec 23 12:20:10 2009 +0100 @@ -104,7 +104,7 @@ """method called to create a backup of source's data""" pass - def restore(self, backupfile): + def restore(self, backupfile, confirm, drop): """method called to restore a backup of source's data""" pass diff -r 8e2eaa6b3733 -r 60256056bda6 server/sources/extlite.py --- a/server/sources/extlite.py Tue Dec 22 19:27:51 2009 +0100 +++ b/server/sources/extlite.py Wed Dec 23 12:20:10 2009 +0100 @@ -101,11 +101,11 @@ finally: self.open_pool_connections() - def restore(self, backupfile, drop): + def restore(self, backupfile, confirm, drop): """method called to restore a backup of source's data""" self.close_pool_connections() try: - self.sqladapter.restore_from_file(backupfile, drop) + self.sqladapter.restore_from_file(backupfile, confirm, drop) finally: self.open_pool_connections() diff -r 8e2eaa6b3733 -r 60256056bda6 server/sources/native.py --- a/server/sources/native.py Tue Dec 22 19:27:51 2009 +0100 +++ b/server/sources/native.py Wed Dec 23 12:20:10 2009 +0100 @@ -215,12 +215,12 @@ finally: self.open_pool_connections() - def restore(self, backupfile, drop): + def restore(self, backupfile, confirm, drop): """method called to restore a backup of source's data""" if self.repo.config.open_connections_pools: self.close_pool_connections() try: - self.restore_from_file(backupfile, drop) + self.restore_from_file(backupfile, confirm, drop=drop) finally: if self.repo.config.open_connections_pools: self.open_pool_connections() @@ -579,7 +579,7 @@ def sql_schema(driver): helper = get_adv_func_helper(driver) - tstamp_col_type = helper.TYPE_MAPPING.get('TIMESTAMP', 'TIMESTAMP') + tstamp_col_type = helper.TYPE_MAPPING['Datetime'] schema = """ /* Create the repository's system database */ @@ -668,7 +668,7 @@ raise AuthenticationError('bad login') # passwords are stored using the Bytes type, so we get a StringIO if pwd is not None: - args['pwd'] = crypt_password(password, pwd.getvalue()[:2]) + args['pwd'] = Binary(crypt_password(password, pwd.getvalue()[:2])) # get eid from login and (crypted) password rset = self.source.syntax_tree_search(session, self._auth_rqlst, args) try: diff -r 8e2eaa6b3733 -r 60256056bda6 server/sqlutils.py --- a/server/sqlutils.py Tue Dec 22 19:27:51 2009 +0100 +++ b/server/sqlutils.py Wed Dec 23 12:20:10 2009 +0100 @@ -144,6 +144,7 @@ self.dbpasswd = source_config.get('db-password') self.encoding = source_config.get('db-encoding', 'UTF-8') self.dbapi_module = db.get_dbapi_compliant_module(self.dbdriver) + self.dbdriver_extra_args = source_config.get('db-extra-arguments') self.binary = self.dbapi_module.Binary self.dbhelper = self.dbapi_module.adv_func_helper self.sqlgen = SQLGenerator() @@ -156,10 +157,14 @@ else: self.info('connecting to %s@%s', self.dbname, self.dbhost or 'localhost') + extra = {} + if self.dbdriver_extra_args: + extra = {'extra_args': self.dbdriver_extra_args} cnx = self.dbapi_module.connect(self.dbhost, self.dbname, user or self.dbuser, password or self.dbpasswd, - port=self.dbport) + port=self.dbport, + **extra) init_cnx(self.dbdriver, cnx) #self.dbapi_module.type_code_test(cnx.cursor()) return cnx @@ -178,7 +183,9 @@ keepownership=False, drop=drop): if os.system(cmd): - raise Exception('Failed command: %s' % cmd) + print '-> Failed command: %s' % cmd + if not confirm('Continue anyway?', default='n'): + raise Exception('Failed command: %s' % cmd) def merge_args(self, args, query_args): if args is not None: @@ -241,6 +248,7 @@ value = value.getvalue() else: value = crypt_password(value) + value = self.binary(value) # XXX needed for sqlite but I don't think it is for other backends elif atype == 'Datetime' and isinstance(value, date): value = todatetime(value) diff -r 8e2eaa6b3733 -r 60256056bda6 server/test/unittest_querier.py --- a/server/test/unittest_querier.py Tue Dec 22 19:27:51 2009 +0100 +++ b/server/test/unittest_querier.py Wed Dec 23 12:20:10 2009 +0100 @@ -1080,7 +1080,8 @@ % (SQL_PREFIX, SQL_PREFIX, SQL_PREFIX)) passwd = str(cursor.fetchone()[0]) self.assertEquals(passwd, crypt_password('toto', passwd[:2])) - rset = self.execute("Any X WHERE X is CWUser, X login 'bob', X upassword '%s'" % passwd) + rset = self.execute("Any X WHERE X is CWUser, X login 'bob', X upassword %(pwd)s", + {'pwd': Binary(passwd)}) self.assertEquals(len(rset.rows), 1) self.assertEquals(rset.description, [('CWUser',)]) @@ -1094,7 +1095,8 @@ % (SQL_PREFIX, SQL_PREFIX, SQL_PREFIX)) passwd = str(cursor.fetchone()[0]) self.assertEquals(passwd, crypt_password('tutu', passwd[:2])) - rset = self.execute("Any X WHERE X is CWUser, X login 'bob', X upassword '%s'" % passwd) + rset = self.execute("Any X WHERE X is CWUser, X login 'bob', X upassword %(pwd)s", + {'pwd': Binary(passwd)}) self.assertEquals(len(rset.rows), 1) self.assertEquals(rset.description, [('CWUser',)]) diff -r 8e2eaa6b3733 -r 60256056bda6 web/data/cubicweb.ajax.js --- a/web/data/cubicweb.ajax.js Tue Dec 22 19:27:51 2009 +0100 +++ b/web/data/cubicweb.ajax.js Wed Dec 23 12:20:10 2009 +0100 @@ -13,54 +13,80 @@ var loaded = []; var jqtagfilter = tag + '[' + srcattr + ']'; jQuery('head ' + jqtagfilter).each(function(i) { - loaded.push(this.getAttribute(srcattr)); + loaded.push(this.getAttribute(srcattr)); }); node.find(tag).each(function(i) { - if (this.getAttribute(srcattr)) { - if (!loaded.contains(this.getAttribute(srcattr))) { - jQuery(this).appendTo(head); - } - } else { - jQuery(this).appendTo(head); - } + if (this.getAttribute(srcattr)) { + if (!loaded.contains(this.getAttribute(srcattr))) { + jQuery(this).appendTo(head); + } + } else { + jQuery(this).appendTo(head); + } }); node.find(jqtagfilter).remove(); } /* - * inspect dom response, search for a
node and - * put its content into the real document's head. + * inspect dom response (as returned by getDomFromResponse), search for + * a
node and put its content into the real + * document's head. * This enables dynamic css and js loading and is used by replacePageChunk */ -function loadAjaxHtmlHead(node) { - var head = jQuery('head'); - node = jQuery(node).find('div.ajaxHtmlHead'); - _loadAjaxHtmlHead(node, head, 'script', 'src'); - _loadAjaxHtmlHead(node, head, 'link', 'href'); - node.find('*').appendTo(head); +function loadAjaxHtmlHead(response) { + var $head = jQuery('head'); + var $responseHead = jQuery(response).find('div.ajaxHtmlHead'); + // no ajaxHtmlHead found, no processing required + if (!$responseHead.length) { + return response; + } + _loadAjaxHtmlHead($responseHead, $head, 'script', 'src'); + _loadAjaxHtmlHead($responseHead, $head, 'link', 'href'); + // add any remaining children (e.g. meta) + $responseHead.children().appendTo($head); + // remove original container, which is now empty + $responseHead.remove(); + // if there was only one actual node in the reponse besides + // the ajaxHtmlHead, then remove the wrapper created by + // getDomFromResponse() and return this single element + // For instance : + // 1/ CW returned the following content : + //
the-actual-content
...
+ // 2/ getDomFromReponse() wrapped this into a single DIV to hold everything + // in one, unique, dom element + // 3/ now that we've removed the ajaxHtmlHead div, the only + // node left in the wrapper if the 'real' node built by the view, + // we can safely return this node. Otherwise, the view itself + // returned several 'root' nodes and we need to keep the wrapper + // created by getDomFromResponse() + if (response.childNodes.length == 1 && + response.getAttribute('cubicweb:type') == 'cwResponseWrapper') { + return response.firstChild; + } + return response; } function preprocessAjaxLoad(node, newdomnode) { - loadAjaxHtmlHead(newdomnode); + return loadAjaxHtmlHead(newdomnode); } function postAjaxLoad(node) { // find sortable tables if there are some if (typeof(Sortable) != 'undefined') { - Sortable.sortTables(node); + Sortable.sortTables(node); } // find textareas and wrap them if there are some if (typeof(FCKeditor) != 'undefined') { - buildWysiwygEditors(); + buildWysiwygEditors(); } if (typeof initFacetBoxEvents != 'undefined') { - initFacetBoxEvents(node); + initFacetBoxEvents(node); } if (typeof buildWidgets != 'undefined') { - buildWidgets(node); + buildWidgets(node); } if (typeof roundedCorners != 'undefined') { - roundedCorners(node); + roundedCorners(node); } if (typeof setFormsTarget != 'undefined') { setFormsTarget(node); @@ -71,7 +97,7 @@ // we probably need to unbind the fired events // When this is done, jquery.treeview.js (for instance) // can be unpatched. - jQuery(CubicWeb).trigger('ajax-loaded'); + jQuery(CubicWeb).trigger('ajax-loaded'); } /* cubicweb loadxhtml plugin to make jquery handle xhtml response @@ -87,38 +113,38 @@ jQuery.fn.loadxhtml = function(url, data, reqtype, mode) { var ajax = null; if (reqtype == 'post') { - ajax = jQuery.post; + ajax = jQuery.post; } else { - ajax = jQuery.get; + ajax = jQuery.get; } if (this.size() > 1) { - log('loadxhtml was called with more than one element'); + log('loadxhtml was called with more than one element'); } var node = this.get(0); // only consider the first element mode = mode || 'replace'; var callback = null; if (data && data.callback) { - callback = data.callback; - delete data.callback; + callback = data.callback; + delete data.callback; } ajax(url, data, function(response) { - var domnode = getDomFromResponse(response); - preprocessAjaxLoad(node, domnode); - if (mode == 'swap') { - var origId = node.id; - node = swapDOM(node, domnode); - if (!node.id) { - node.id = origId; - } - } else if (mode == 'replace') { - jQuery(node).empty().append(domnode); - } else if (mode == 'append') { - jQuery(node).append(domnode); - } - postAjaxLoad(node); - while (jQuery.isFunction(callback)) { - callback = callback.apply(this, [domnode]); - } + var domnode = getDomFromResponse(response); + domnode = preprocessAjaxLoad(node, domnode); + if (mode == 'swap') { + var origId = node.id; + node = swapDOM(node, domnode); + if (!node.id) { + node.id = origId; + } + } else if (mode == 'replace') { + jQuery(node).empty().append(domnode); + } else if (mode == 'append') { + jQuery(node).append(domnode); + } + postAjaxLoad(node); + while (jQuery.isFunction(callback)) { + callback = callback.apply(this, [domnode]); + } }); }; @@ -129,43 +155,43 @@ */ function loadDynamicFragments(node) { if (node) { - var fragments = jQuery(node).find('div.dynamicFragment'); + var fragments = jQuery(node).find('div.dynamicFragment'); } else { - var fragments = jQuery('div.dynamicFragment'); + var fragments = jQuery('div.dynamicFragment'); } if (fragments.length == 0) { - return; + return; } if (typeof LOADING_MSG == 'undefined') { - LOADING_MSG = 'loading'; // this is only a safety belt, it should not happen + LOADING_MSG = 'loading'; // this is only a safety belt, it should not happen } for(var i=0; i'; - // if cubicweb:loadurl is set, just pick the url et send it to loadxhtml - var url = getNodeAttribute(fragment, 'cubicweb:loadurl'); - if (url) { - jQuery(fragment).loadxhtml(url); - continue; - } - // else: rebuild full url by fetching cubicweb:rql, cubicweb:vid, etc. - var rql = getNodeAttribute(fragment, 'cubicweb:rql'); - var items = getNodeAttribute(fragment, 'cubicweb:vid').split('&'); - var vid = items[0]; + var fragment = fragments[i]; + fragment.innerHTML = '

' + LOADING_MSG + ' ...

'; + // if cubicweb:loadurl is set, just pick the url et send it to loadxhtml + var url = getNodeAttribute(fragment, 'cubicweb:loadurl'); + if (url) { + jQuery(fragment).loadxhtml(url); + continue; + } + // else: rebuild full url by fetching cubicweb:rql, cubicweb:vid, etc. + var rql = getNodeAttribute(fragment, 'cubicweb:rql'); + var items = getNodeAttribute(fragment, 'cubicweb:vid').split('&'); + var vid = items[0]; var extraparams = {}; - // case where vid='myvid¶m1=val1¶m2=val2': this is a deprecated abuse-case - if (items.length > 1) { - console.log("[3.5] you're using extraargs in cubicweb:vid attribute, this is deprecated, consider using loadurl instead"); - for (var j=1; j 1) { + console.log("[3.5] you're using extraargs in cubicweb:vid attribute, this is deprecated, consider using loadurl instead"); + for (var j=1; j return the whole document - return jQuery(doc).clone().context; + // no child (error cases) => return the whole document + return jQuery(doc).clone().context; } children = stripEmptyTextNodes(children); if (children.length == 1) { - // only one child => return it - return jQuery(children[0]).clone().context; + // only one child => return it + return jQuery(children[0]).clone().context; } // several children => wrap them in a single node and return the wrap - return DIV(null, map(function(node) { - return jQuery(node).clone().context; - }, - children)); + return DIV({'cubicweb:type': "cwResponseWrapper"}, + map(function(node) { + return jQuery(node).clone().context; + }, children)); } function postJSON(url, data, callback) {