# HG changeset patch # User Aurelien Campeas # Date 1278599419 -7200 # Node ID 9cbde75fefe8ae93d4fbe2da53b9ed90d5da56a0 # Parent b962dff47c3629be5747f4d4f0be07b46aa9f07a# Parent 846d1fb32aa8a13aed907f6de17542d8ff01f1b4 merge diff -r 846d1fb32aa8 -r 9cbde75fefe8 .hgtags --- a/.hgtags Thu Jul 08 16:29:51 2010 +0200 +++ b/.hgtags Thu Jul 08 16:30:19 2010 +0200 @@ -135,3 +135,5 @@ 5d05b08adeab1ea301e49ed8537e35ede6db92f6 cubicweb-debian-version-3.8.5-1 1a24c62aefc5e57f61be3d04affd415288e81904 cubicweb-version-3.8.6 607a90073911b6bb941a49b5ec0b0d2a9cd479af cubicweb-debian-version-3.8.6-1 +d9936c39d478b6701a4adef17bc28888ffa011c6 cubicweb-version-3.9.0 +eda4940ffef8b7d36127e68de63a52388374a489 cubicweb-debian-version-3.9.0-1 diff -r 846d1fb32aa8 -r 9cbde75fefe8 cwconfig.py --- a/cwconfig.py Thu Jul 08 16:29:51 2010 +0200 +++ b/cwconfig.py Thu Jul 08 16:30:19 2010 +0200 @@ -991,6 +991,29 @@ """write down current configuration""" self.generate_config(open(self.main_config_file(), 'w')) + def check_writeable_uid_directory(self, path): + """check given directory path exists, belongs to the user running the + server process and is writeable. + + If not, try to fix this, leting exception propagate when not possible. + """ + if not exists(path): + os.makedirs(path) + if self['uid']: + try: + uid = int(self['uid']) + except ValueError: + from pwd import getpwnam + uid = getpwnam(self['uid']).pw_uid + else: + uid = os.getuid() + fstat = os.stat(path) + if fstat.st_uid != uid: + os.chown(path, uid, os.getgid()) + import stat + if not (fstat.st_mode & stat.S_IWUSR): + os.chmod(path, fstat.st_mode | stat.S_IWUSR) + @cached def instance_md5_version(self): import hashlib diff -r 846d1fb32aa8 -r 9cbde75fefe8 etwist/server.py --- a/etwist/server.py Thu Jul 08 16:29:51 2010 +0200 +++ b/etwist/server.py Thu Jul 08 16:30:19 2010 +0200 @@ -38,11 +38,11 @@ from twisted.web import static, resource from twisted.web.server import NOT_DONE_YET -from cubicweb.web import dumps from logilab.common.decorators import monkeypatch from cubicweb import AuthenticationError, ConfigurationError, CW_EVENT_MANAGER +from cubicweb.utils import json_dumps from cubicweb.web import Redirect, DirectResponse, StatusResponse, LogOut from cubicweb.web.application import CubicWebPublisher from cubicweb.web.http_headers import generateDateTime @@ -317,12 +317,12 @@ self.setResponseCode(http.BAD_REQUEST) if path in JSON_PATHS: # XXX better json path detection self.setHeader('content-type',"application/json") - body = dumps({'reason': 'request max size exceeded'}) + body = json_dumps({'reason': 'request max size exceeded'}) elif path in FRAME_POST_PATHS: # XXX better frame post path detection self.setHeader('content-type',"text/html") body = ('' % dumps( (False, 'request max size exceeded', None) )) + '' % json_dumps( (False, 'request max size exceeded', None) )) else: self.setHeader('content-type',"text/html") body = ("Processing Failed" @@ -402,6 +402,7 @@ def run(config, vreg=None, debug=None): if debug is not None: config.debugmode = debug + config.check_writeable_uid_directory(config.appdatahome) # create the site root_resource = CubicWebRootResource(config, vreg=vreg) website = server.Site(root_resource) diff -r 846d1fb32aa8 -r 9cbde75fefe8 selectors.py --- a/selectors.py Thu Jul 08 16:29:51 2010 +0200 +++ b/selectors.py Thu Jul 08 16:30:19 2010 +0200 @@ -202,6 +202,7 @@ from logilab.common.interface import implements as implements_iface from yams import BASE_TYPES +from rql.nodes import Function from cubicweb import (Unauthorized, NoSelectableObject, NotAnEntity, CW_EVENT_MANAGER, role) @@ -588,12 +589,17 @@ @lltrace def sorted_rset(cls, req, rset=None, **kwargs): """Return 1 for sorted result set (e.g. from an RQL query containing an - :ref:ORDERBY clause. + :ref:ORDERBY clause), with exception that it will return 0 if the rset is + 'ORDERBY FTIRANK(VAR)' (eg sorted by rank value of the has_text index). """ if rset is None: return 0 - rqlst = rset.syntax_tree() - if len(rqlst.children) > 1 or not rqlst.children[0].orderby: + selects = rset.syntax_tree().children + if (len(selects) > 1 or + not selects[0].orderby or + (isinstance(selects[0].orderby[0].term, Function) and + selects[0].orderby[0].term.name == 'FTIRANK') + ): return 0 return 2 diff -r 846d1fb32aa8 -r 9cbde75fefe8 server/msplanner.py --- a/server/msplanner.py Thu Jul 08 16:29:51 2010 +0200 +++ b/server/msplanner.py Thu Jul 08 16:30:19 2010 +0200 @@ -1155,6 +1155,7 @@ sunion = Union() for select in queries: sunion.append(select) + self.rqlhelper.annotate(select) if temptable: steps.append(FetchStep(plan, sunion, sources, temptable, True, inputmap)) else: diff -r 846d1fb32aa8 -r 9cbde75fefe8 server/serverconfig.py --- a/server/serverconfig.py Thu Jul 08 16:29:51 2010 +0200 +++ b/server/serverconfig.py Thu Jul 08 16:30:19 2010 +0200 @@ -15,9 +15,8 @@ # # You should have received a copy of the GNU Lesser General Public License along # with CubicWeb. If not, see . -"""server.serverconfig definition +"""server.serverconfig definition""" -""" __docformat__ = "restructuredtext en" from os.path import join, exists diff -r 846d1fb32aa8 -r 9cbde75fefe8 utils.py --- a/utils.py Thu Jul 08 16:29:51 2010 +0200 +++ b/utils.py Thu Jul 08 16:30:19 2010 +0200 @@ -327,12 +327,13 @@ try: # may not be there if cubicweb-web not installed - if sys.version_info < (2,6): + if sys.version_info < (2, 6): import simplejson as json else: import json except ImportError: - pass + json_dumps = None + else: class CubicWebJsonEncoder(json.JSONEncoder): @@ -360,6 +361,9 @@ # just return None in those cases. return None + def json_dumps(value): + return json.dumps(value, cls=CubicWebJsonEncoder) + @deprecated('[3.7] merge_dicts is deprecated') def merge_dicts(dict1, dict2): diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/__init__.py --- a/web/__init__.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/__init__.py Thu Jul 08 16:30:19 2010 +0200 @@ -22,20 +22,14 @@ __docformat__ = "restructuredtext en" _ = unicode -import sys -if sys.version_info < (2,6): - import simplejson as json -else: - import json - -dumps = json.dumps - from urllib import quote as urlquote from logilab.common.deprecation import deprecated from cubicweb.web._exceptions import * -from cubicweb.utils import CubicWebJsonEncoder +from cubicweb.utils import json_dumps + +dumps = deprecated('[3.9] use cubicweb.utils.json_dumps instead of dumps')(json_dumps) INTERNAL_FIELD_VALUE = '__cubicweb_internal_field__' @@ -64,9 +58,6 @@ FACETTES = set() -def json_dumps(value): - return dumps(value, cls=CubicWebJsonEncoder) - def jsonize(function): def newfunc(*args, **kwargs): value = function(*args, **kwargs) diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/_exceptions.py --- a/web/_exceptions.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/_exceptions.py Thu Jul 08 16:30:19 2010 +0200 @@ -16,12 +16,12 @@ # # You should have received a copy of the GNU Lesser General Public License along # with CubicWeb. If not, see . -"""exceptions used in the core of the CubicWeb web application +"""exceptions used in the core of the CubicWeb web application""" -""" __docformat__ = "restructuredtext en" from cubicweb._exceptions import * +from cubicweb.utils import json_dumps class PublishException(CubicWebException): """base class for publishing related exception""" @@ -66,8 +66,7 @@ self.reason = reason def dumps(self): - from cubicweb.web import json - return json.dumps({'reason': self.reason}) + return json_dumps({'reason': self.reason}) class LogOut(PublishException): """raised to ask for deauthentication of a logged in user""" diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/component.py --- a/web/component.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/component.py Thu Jul 08 16:30:19 2010 +0200 @@ -26,7 +26,7 @@ from logilab.mtconverter import xml_escape from cubicweb import role -from cubicweb.web import json +from cubicweb.utils import json_dumps from cubicweb.view import Component from cubicweb.selectors import ( paginated_rset, one_line_rset, primary_view, match_context_prop, @@ -146,9 +146,9 @@ rql = params.pop('rql', self.cw_rset.printable_rql()) # latest 'true' used for 'swap' mode url = 'javascript: replacePageChunk(%s, %s, %s, %s, true)' % ( - json.dumps(params.get('divid', 'pageContent')), - json.dumps(rql), json.dumps(params.pop('vid', None)), - json.dumps(params)) + json_dumps(params.get('divid', 'pageContent')), + json_dumps(rql), json_dumps(params.pop('vid', None)), + json_dumps(params)) else: url = self._cw.build_url(path, **params) return url diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/data/boxHeader.png Binary file web/data/boxHeader.png has changed diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/data/cubicweb.ajax.js --- a/web/data/cubicweb.ajax.js Thu Jul 08 16:29:51 2010 +0200 +++ b/web/data/cubicweb.ajax.js Thu Jul 08 16:30:19 2010 +0200 @@ -241,7 +241,7 @@ $(node).removeClass("hidden"); if (mode == 'swap') { var origId = node.id; - node = swapDOM(node, domnode); + node = cw.swapDOM(node, domnode); if (!node.id) { node.id = origId; } @@ -255,16 +255,16 @@ callback = callback.apply(this, [domnode]); } }); + d.addErrback(remoteCallFailed); if (cursor) { d.addCallback(resetCursor); d.addErrback(resetCursor); - d.addErrback(remoteCallFailed); } return d; } /** - * .. function:: loadRemote(url, form, reqtype='GET', async=true) + * .. function:: loadRemote(url, form, reqtype='GET', sync=false) * * Asynchronously (unless `async` argument is set to false) load an url or path * and return a deferred whose callbacks args are decoded according to the diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/data/cubicweb.css --- a/web/data/cubicweb.css Thu Jul 08 16:29:51 2010 +0200 +++ b/web/data/cubicweb.css Thu Jul 08 16:30:19 2010 +0200 @@ -322,7 +322,7 @@ overflow: hidden; font-weight: bold; color: #fff; - background: %(boxTitleBgColor)s url("header.png") repeat-x 50% 50%; + background: %(boxTitleBg)s; } div.boxTitle span, @@ -333,7 +333,7 @@ div.searchBoxFrame div.boxTitle, div.greyBoxFrame div.boxTitle { - background: %(actionBoxTitleBgColor)s url("actionBoxHeader.png") repeat-x 50% 50% ; + background: %(actionBoxTitleBg)s; } div.sideBoxTitle span, @@ -379,7 +379,7 @@ } div.sideBoxTitle { - background: %(actionBoxTitleBgColor)s; + background: %(actionBoxTitleBg)s; display: block; font-weight: bold; } @@ -400,11 +400,11 @@ div.sideBoxBody { padding: 0.2em 5px; - background: %(sideBoxBodyBgColor)s; + background: %(sideBoxBodyBg)s; } div.sideBoxBody a { - color: %(sideBoxColor)s; + color: %(sideBoxBodyColor)s; } div.sideBoxBody a:hover { @@ -804,7 +804,7 @@ ul.boxListing a:hover, ul.boxListing ul li a:hover { text-decoration: none; - background: %(sideBoxBodyBgColor)s; + background: %(sideBoxBodyBg)s; } ul.boxListing ul li a:hover{ diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/data/cubicweb.edition.js --- a/web/data/cubicweb.edition.js Thu Jul 08 16:29:51 2010 +0200 +++ b/web/data/cubicweb.edition.js Thu Jul 08 16:30:19 2010 +0200 @@ -48,7 +48,7 @@ if ($.inArray(tagName, inputTypes)) { if (jQuery(elem).attr('tabindex') != null) { tabindex += 1; - jQuery(elem).attr('tabindex', tabindex); + jQuery(elem).attr('tabindex', tabindex); } return null; } @@ -170,7 +170,8 @@ // add hidden parameter var entityForm = jQuery('#entityForm'); var oid = optionNode.id.substring(2); // option id is prefixed by "id" - remoteExec('add_pending_inserts', [oid.split(':')]); + loadRemote('json', ajaxFuncArgs('add_pending_inserts', null, + [oid.split(':')]), 'GET', true); var selectNode = optionNode.parentNode; // remove option node selectNode.removeChild(optionNode); @@ -208,7 +209,8 @@ } } elementId = elementId.substring(2, elementId.length); - remoteExec('remove_pending_insert', elementId.split(':')); + loadRemote('json', ajaxFuncArgs('remove_pending_inserts', null, + elementId.split(':')), 'GET', true); } /** @@ -272,7 +274,7 @@ function selectForAssociation(tripletIdsString, originalEid) { var tripletlist = $.map(tripletIdsString.split('-'), - function(x) { return x.split(':');}); + function(x) { return [x.split(':')] ;}); var d = loadRemote('json', ajaxFuncArgs('add_pending_inserts', null, tripletlist)); d.addCallback(function() { var args = { @@ -337,8 +339,8 @@ */ function removeInlineForm(peid, rtype, role, eid, showaddnewlink) { cw.jqNode(['div', peid, rtype, eid].join('-')).slideUp('fast', function() { - $(this).remove(); - updateInlinedEntitiesCounters(rtype, role); + $(this).remove(); + updateInlinedEntitiesCounters(rtype, role); }); if (showaddnewlink) { toggleVisibility(showaddnewlink); @@ -469,10 +471,10 @@ var errmsg; // Unknown structure if ( !cw.utils.isArrayLike(descr) || descr.length != 2 ) { - errmsg = descr; + errmsg = descr; } else { - _displayValidationerrors(formid, descr[0], descr[1]); - errmsg = _('please correct errors below'); + _displayValidationerrors(formid, descr[0], descr[1]); + errmsg = _('please correct errors below'); } updateMessage(errmsg); // ensure the browser does not scroll down diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/data/cubicweb.facets.js --- a/web/data/cubicweb.facets.js Thu Jul 08 16:29:51 2010 +0200 +++ b/web/data/cubicweb.facets.js Thu Jul 08 16:30:19 2010 +0200 @@ -1,10 +1,15 @@ -/** +/** filter form, aka facets, javascript functions + * * :organization: Logilab * :copyright: 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. * :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr */ -//============= filter form functions ========================================// +var SELECTED_IMG = baseuri() + "data/black-check.png"; +var UNSELECTED_IMG = baseuri() + "data/no-check-no-border.png"; +var UNSELECTED_BORDER_IMG = baseuri() + "data/black-uncheck.png"; + + function copyParam(origparams, newparams, param) { var index = jQuery.inArray(param, origparams[0]); if (index > - 1) { @@ -12,31 +17,33 @@ } } -function facetFormContent(form) { + +function facetFormContent($form) { var names = []; var values = []; - jQuery(form).find('.facet').each(function() { + $form.find('.facet').each(function() { var facetName = jQuery(this).find('.facetTitle').attr('cubicweb:facetName'); var facetValues = jQuery(this).find('.facetValueSelected').each(function(x) { names.push(facetName); values.push(this.getAttribute('cubicweb:value')); }); }); - jQuery(form).find('input').each(function() { + $form.find('input').each(function() { names.push(this.name); values.push(this.value); }); - jQuery(form).find('select option[selected]').each(function() { + $form.find('select option[selected]').each(function() { names.push(this.parentNode.name); values.push(this.value); }); return [names, values]; } + function buildRQL(divid, vid, paginate, vidargs) { jQuery(CubicWeb).trigger('facets-content-loading', [divid, vid, paginate, vidargs]); - var form = getNode(divid + 'Form'); - var zipped = facetFormContent(form); + var $form = $('#' + divid + 'Form'); + var zipped = facetFormContent($form); zipped[0].push('facetargs'); zipped[1].push(vidargs); var d = loadRemote('json', ajaxFuncArgs('filter_build_rql', null, zipped[0], zipped[1])); @@ -78,8 +85,8 @@ }); if (paginate) { // FIXME the edit box might not be displayed in which case we don't - // know where to put the potential new one, just skip this case - // for now + // know where to put the potential new one, just skip this case for + // now var $node = jQuery('#edit_box'); if ($node.length) { $node.loadxhtml('json', ajaxFuncArgs('render', { @@ -116,9 +123,6 @@ }); } -var SELECTED_IMG = baseuri() + "data/black-check.png"; -var UNSELECTED_IMG = baseuri() + "data/no-check-no-border.png"; -var UNSELECTED_BORDER_IMG = baseuri() + "data/black-uncheck.png"; function initFacetBoxEvents(root) { // facetargs : (divid, vid, paginate, extraargs) @@ -206,6 +210,7 @@ }); } + // trigger this function on document ready event if you provide some kind of // persistent search (eg crih) function reorderFacetsItems(root) { @@ -233,10 +238,11 @@ }); } -// we need to differenciate cases where initFacetBoxEvents is called -// with one argument or without any argument. If we use `initFacetBoxEvents` -// as the direct callback on the jQuery.ready event, jQuery will pass some argument -// of his, so we use this small anonymous function instead. + +// we need to differenciate cases where initFacetBoxEvents is called with one +// argument or without any argument. If we use `initFacetBoxEvents` as the +// direct callback on the jQuery.ready event, jQuery will pass some argument of +// his, so we use this small anonymous function instead. jQuery(document).ready(function() { initFacetBoxEvents(); }); diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/data/uiprops.py --- a/web/data/uiprops.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/data/uiprops.py Thu Jul 08 16:30:19 2010 +0200 @@ -2,7 +2,6 @@ # CSS stylesheets to include systematically in HTML headers # use the following line if you *need* to keep the old stylesheet -#STYLESHEETS = [data('cubicweb.old.css')] STYLESHEETS = [data('cubicweb.reset.css'), data('cubicweb.css'), ] STYLESHEETS_IE = [data('cubicweb.ie.css')] @@ -66,7 +65,7 @@ defaultFontFamily = "'Bitstream Vera Sans','Lucida Grande','Lucida Sans Unicode','Geneva','Verdana',sans-serif" defaultSize = '12px' defaultLineHeight = '1.5' -defaultLineHeightEm = defaultLineHeight + 'em' +defaultLineHeightEm = lazystr('%(defaultLineHeight)sem') baseRhythmBg = 'rhythm18.png' inputHeight = '1.3em' @@ -82,7 +81,7 @@ h1Padding = '0 0 0.14em 0 ' h1Margin = '0.8em 0 0.5em' h1Color = '#000' -h1BorderBottomStyle = '0.06em solid %s' % h1Color +h1BorderBottomStyle = lazystr('0.06em solid %(h1Color)s') h2FontSize = '1.33333em' h2Padding = '0.4em 0 0.35em 0' @@ -94,7 +93,7 @@ # links aColor = '#e6820e' -aActiveColor = aVisitedColor = aLinkColor = aColor +aActiveColor = aVisitedColor = aLinkColor = lazystr('%(aColor)s') # page frame @@ -105,13 +104,15 @@ pageMinHeight = '800px' # boxes -boxTitleBgColor = headerBgColor +boxTitleBg = lazystr('%(headerBgColor)s url("boxHeader.png") repeat-x 50%% 50%%') boxBodyBgColor = '#efefde' # action, search, sideBoxes actionBoxTitleBgColor = '#cfceb7' +actionBoxTitleBg = lazystr('%(actionBoxTitleBgColor)s url("actionBoxHeader.png") repeat-x 50%% 50%%') sideBoxBodyBgColor = '#f8f8ee' -sideBoxColor = '#555544' +sideBoxBodyBg = lazystr('%(sideBoxBodyBgColor)s') +sideBoxBodyColor = '#555544' # table listing & co listingBorderColor = '#ccc' @@ -122,7 +123,7 @@ bulletDownImg = 'url("puce_down.png") 98% 6px no-repeat' #forms -formHeaderBgColor = listingHeaderBgColor +formHeaderBgColor = lazystr('%(listingHeaderBgColor)s') helperColor = '#555' # button diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/propertysheet.py --- a/web/propertysheet.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/propertysheet.py Thu Jul 08 16:30:19 2010 +0200 @@ -23,6 +23,13 @@ import os import os.path as osp +class lazystr(object): + def __init__(self, string, context): + self.string = string + self.context = context + def __str__(self): + return self.string % self.context + class PropertySheet(dict): def __init__(self, cache_directory, **context): @@ -30,8 +37,12 @@ self.context = context self.reset() context['sheet'] = self + context['lazystr'] = self.lazystr self._percent_rgx = re.compile('%(?!\()') + def lazystr(self, str): + return lazystr(str, self) + def reset(self): self.clear() self._ordered_propfiles = [] diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/request.py --- a/web/request.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/request.py Thu Jul 08 16:30:19 2010 +0200 @@ -37,14 +37,12 @@ from cubicweb.dbapi import DBAPIRequest from cubicweb.mail import header from cubicweb.uilib import remove_html_tags -from cubicweb.utils import SizeConstrainedList, HTMLHead, make_uid +from cubicweb.utils import SizeConstrainedList, HTMLHead, make_uid, json_dumps from cubicweb.view import STRICT_DOCTYPE, TRANSITIONAL_DOCTYPE_NOEXT from cubicweb.web import (INTERNAL_FIELD_VALUE, LOGGER, NothingToEdit, - RequestError, StatusResponse, json) + RequestError, StatusResponse) from cubicweb.web.http_headers import Headers -dumps = json.dumps - _MARKER = object() @@ -358,7 +356,7 @@ """ self.add_js('cubicweb.ajax.js') cbname = self.register_onetime_callback(cb, *args) - msg = dumps(msg or '') + msg = json_dumps(msg or '') return "javascript:userCallbackThenReloadPage('%s', %s)" % ( cbname, msg) @@ -592,7 +590,7 @@ extraparams.setdefault('fname', 'view') url = self.build_url('json', **extraparams) return "javascript: $('#%s').loadxhtml(%s, null, 'get', '%s'); noop()" % ( - nodeid, dumps(url), replacemode) + nodeid, json_dumps(url), replacemode) # urls/path management #################################################### diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/test/data/sheet1.py --- a/web/test/data/sheet1.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/test/data/sheet1.py Thu Jul 08 16:30:19 2010 +0200 @@ -1,3 +1,4 @@ bgcolor = '#000000' stylesheets = ['%s/cubicweb.css' % datadir_url] logo = '%s/logo.png' % datadir_url +lazy = lazystr('%(bgcolor)s') diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/test/unittest_propertysheet.py --- a/web/test/unittest_propertysheet.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/test/unittest_propertysheet.py Thu Jul 08 16:30:19 2010 +0200 @@ -27,6 +27,10 @@ # defined by sheet1, extended by sheet2 self.assertEquals(ps['stylesheets'], ['http://cwtest.com/cubicweb.css', 'http://cwtest.com/mycube.css']) + # lazy string defined by sheet1 + self.assertIsInstance(ps['lazy'], lazystr) + self.assertEquals(str(ps['lazy']), '#FFFFFF') + # test compilation self.assertEquals(ps.compile('a {bgcolor: %(bgcolor)s; size: 1%;}'), 'a {bgcolor: #FFFFFF; size: 1%;}') self.assertEquals(ps.process_resource(DATADIR, 'pouet.css'), diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/test/unittest_views_basecontrollers.py --- a/web/test/unittest_views_basecontrollers.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/test/unittest_views_basecontrollers.py Thu Jul 08 16:30:19 2010 +0200 @@ -15,17 +15,16 @@ # # You should have received a copy of the GNU Lesser General Public License along # with CubicWeb. If not, see . -"""cubicweb.web.views.basecontrollers unit tests - -""" +"""cubicweb.web.views.basecontrollers unit tests""" from logilab.common.testlib import unittest_main, mock_object from cubicweb import Binary, NoSelectableObject, ValidationError from cubicweb.view import STRICT_DOCTYPE from cubicweb.devtools.testlib import CubicWebTC +from cubicweb.utils import json_dumps from cubicweb.uilib import rql_for_eid -from cubicweb.web import INTERNAL_FIELD_VALUE, Redirect, RequestError, json +from cubicweb.web import INTERNAL_FIELD_VALUE, Redirect, RequestError from cubicweb.entities.authobjs import CWUser from cubicweb.web.views.autoform import get_pending_inserts, get_pending_deletes u = unicode @@ -562,7 +561,7 @@ # rql = 'Any T,N WHERE T is Tag, T name N' # ctrl = self.ctrl(self.request(mode='json', rql=rql, pageid='123')) # self.assertEquals(ctrl.publish(), -# json.dumps(self.execute(rql).rows)) +# json_dumps(self.execute(rql).rows)) def test_remote_add_existing_tag(self): self.remote_call('tag_entity', self.john.eid, ['python']) @@ -643,14 +642,14 @@ # silly tests def test_external_resource(self): self.assertEquals(self.remote_call('external_resource', 'RSS_LOGO')[0], - json.dumps(self.config.uiprops['RSS_LOGO'])) + json_dumps(self.config.uiprops['RSS_LOGO'])) def test_i18n(self): self.assertEquals(self.remote_call('i18n', ['bimboom'])[0], - json.dumps(['bimboom'])) + json_dumps(['bimboom'])) def test_format_date(self): self.assertEquals(self.remote_call('format_date', '2007-01-01 12:00:00')[0], - json.dumps('2007/01/01')) + json_dumps('2007/01/01')) diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/test/unittest_views_baseviews.py --- a/web/test/unittest_views_baseviews.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/test/unittest_views_baseviews.py Thu Jul 08 16:30:19 2010 +0200 @@ -15,21 +15,17 @@ # # You should have received a copy of the GNU Lesser General Public License along # with CubicWeb. If not, see . -""" -""" from logilab.common.testlib import unittest_main from logilab.mtconverter import html_unescape from cubicweb.devtools.testlib import CubicWebTC - +from cubicweb.utils import json from cubicweb.web.htmlwidgets import TableWidget from cubicweb.web.views import vid_from_rset -from cubicweb.web import json -loads = json.loads def loadjson(value): - return loads(html_unescape(value)) + return json.loads(html_unescape(value)) class VidFromRsetTC(CubicWebTC): diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/views/autoform.py --- a/web/views/autoform.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/views/autoform.py Thu Jul 08 16:30:19 2010 +0200 @@ -134,8 +134,9 @@ from cubicweb.selectors import ( match_kwargs, match_form_params, non_final_entity, specified_etype_implements) -from cubicweb.web import stdmsgs, uicfg, eid_param, dumps, \ - form as f, formwidgets as fw, formfields as ff +from cubicweb.utils import json_dumps +from cubicweb.web import (stdmsgs, uicfg, eid_param, + form as f, formwidgets as fw, formfields as ff) from cubicweb.web.views import forms _AFS = uicfg.autoform_section @@ -374,7 +375,7 @@ entities """ js = u"javascript: togglePendingDelete('%s', %s);" % ( - nodeid, xml_escape(dumps(eid))) + nodeid, xml_escape(json_dumps(eid))) return u'[%s]' % ( js, nodeid, label) @@ -475,7 +476,7 @@ w(u'') w(u' """ % (hidden and 'hidden' or '', divid, selectid, - xml_escape(dumps(entity.eid)), is_cell and 'true' or 'null', relname, + xml_escape(json_dumps(entity.eid)), is_cell and 'true' or 'null', relname, '\n'.join(options)) def _get_select_options(self, entity, rschema, role): diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/views/basecontrollers.py --- a/web/views/basecontrollers.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/views/basecontrollers.py Thu Jul 08 16:30:19 2010 +0200 @@ -26,11 +26,10 @@ from cubicweb import (NoSelectableObject, ObjectNotFound, ValidationError, AuthenticationError, typed_eid) -from cubicweb.utils import CubicWebJsonEncoder +from cubicweb.utils import json, json_dumps from cubicweb.selectors import authenticated_user, anonymous_user, match_form_params from cubicweb.mail import format_mail -from cubicweb.web import (Redirect, RemoteCallFailed, DirectResponse, - json, json_dumps) +from cubicweb.web import Redirect, RemoteCallFailed, DirectResponse from cubicweb.web.controller import Controller from cubicweb.web.views import vid_from_rset, formrenderers @@ -42,7 +41,7 @@ HAS_SEARCH_RESTRICTION = False def jsonize(func): - """decorator to sets correct content_type and calls `json.dumps` on + """decorator to sets correct content_type and calls `json_dumps` on results """ def wrapper(self, *args, **kwargs): diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/views/editforms.py --- a/web/views/editforms.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/views/editforms.py Thu Jul 08 16:30:19 2010 +0200 @@ -33,7 +33,7 @@ specified_etype_implements, is_instance, yes) from cubicweb.view import EntityView from cubicweb.schema import display_name -from cubicweb.web import uicfg, stdmsgs, eid_param, dumps, \ +from cubicweb.web import uicfg, stdmsgs, eid_param, \ formfields as ff, formwidgets as fw from cubicweb.web.form import FormViewMixIn, FieldNotFound from cubicweb.web.views import forms, reledit diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/views/facets.py --- a/web/views/facets.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/views/facets.py Thu Jul 08 16:30:19 2010 +0200 @@ -15,9 +15,8 @@ # # You should have received a copy of the GNU Lesser General Public License along # with CubicWeb. If not, see . -"""the facets box and some basic facets +"""the facets box and some basic facets""" -""" __docformat__ = "restructuredtext en" from logilab.mtconverter import xml_escape @@ -25,7 +24,7 @@ from cubicweb.appobject import objectify_selector from cubicweb.selectors import (non_final_entity, multi_lines_rset, match_context_prop, yes, relation_possible) -from cubicweb.web import dumps +from cubicweb.utils import json_dumps from cubicweb.web.box import BoxTemplate from cubicweb.web.facet import (AbstractFacet, FacetStringWidget, RelationFacet, prepare_facets_rqlst, filter_hiddens, _cleanup_rqlst, @@ -102,7 +101,7 @@ self.display_bookmark_link(rset) w = self.w w(u'
' % ( - divid, xml_escape(dumps([divid, vid, paginate, self.facetargs()])))) + divid, xml_escape(json_dumps([divid, vid, paginate, self.facetargs()])))) w(u'
') hiddens = {'facets': ','.join(wdg.facet.__regid__ for wdg in widgets), 'baserql': baserql} diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/views/formrenderers.py --- a/web/views/formrenderers.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/views/formrenderers.py Thu Jul 08 16:30:19 2010 +0200 @@ -41,7 +41,8 @@ from cubicweb import tags from cubicweb.appobject import AppObject from cubicweb.selectors import is_instance, yes -from cubicweb.web import dumps, eid_param, formwidgets as fwdgs +from cubicweb.utils import json_dumps +from cubicweb.web import eid_param, formwidgets as fwdgs def checkbox(name, value, attrs='', checked=None): @@ -359,7 +360,7 @@ values = form.form_previous_values qeid = eid_param('eid', entity.eid) cbsetstate = "setCheckboxesState('eid', %s, 'checked')" % \ - xml_escape(dumps(entity.eid)) + xml_escape(json_dumps(entity.eid)) w(u'' % (entity.cw_row % 2 and u'even' or u'odd')) # XXX turn this into a widget used on the eid field w(u'%s' % checkbox('eid', entity.eid, diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/views/igeocodable.py --- a/web/views/igeocodable.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/views/igeocodable.py Thu Jul 08 16:30:19 2010 +0200 @@ -22,7 +22,7 @@ from cubicweb.interfaces import IGeocodable from cubicweb.view import EntityView, EntityAdapter, implements_adapter_compat from cubicweb.selectors import implements, adaptable -from cubicweb.web import json +from cubicweb.utils import json_dumps class IGeocodableAdapter(EntityAdapter): """interface required by geocoding views such as gmap-view""" @@ -82,7 +82,7 @@ 'center': center, 'markers': markers, } - self.w(json.dumps(geodata)) + self.w(json_dumps(geodata)) def build_marker_data(self, entity, igeocodable, extraparams): return {'latitude': igeocodable.latitude, diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/views/navigation.py --- a/web/views/navigation.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/views/navigation.py Thu Jul 08 16:30:19 2010 +0200 @@ -132,7 +132,7 @@ if rel is None: continue attrname = rel.r_type - if attrname == 'is': + if attrname in ('is', 'has_text'): continue if not rschema(attrname).final: col = var.selected_index() diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/views/plots.py --- a/web/views/plots.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/views/plots.py Thu Jul 08 16:30:19 2010 +0200 @@ -23,10 +23,9 @@ from logilab.common.date import datetime2ticks from logilab.mtconverter import xml_escape -from cubicweb.utils import UStringIO +from cubicweb.utils import UStringIO, json_dumps from cubicweb.appobject import objectify_selector from cubicweb.selectors import multi_columns_rset -from cubicweb.web import dumps from cubicweb.web.views import baseviews @objectify_selector @@ -107,7 +106,7 @@ # cf. function onPlotHover in cubicweb.flot.js if self.timemode: plot = [(datetime2ticks(x), y, datetime2ticks(x)) for x, y in plot] - return dumps(plot) + return json_dumps(plot) def _render(self, req, width=500, height=400): if req.ie_browser(): diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/views/reledit.py --- a/web/views/reledit.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/views/reledit.py Thu Jul 08 16:30:19 2010 +0200 @@ -1,10 +1,30 @@ +# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of CubicWeb. +# +# CubicWeb is free software: you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 2.1 of the License, or (at your option) +# any later version. +# +# CubicWeb is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with CubicWeb. If not, see . +"""the 'reedit' feature (eg edit attribute/relation from primary view) +""" + import copy from logilab.mtconverter import xml_escape from cubicweb import neg_role from cubicweb.schema import display_name -from cubicweb.utils import json +from cubicweb.utils import json_dumps from cubicweb.selectors import non_final_entity, match_kwargs from cubicweb.view import EntityView from cubicweb.web import uicfg, stdmsgs @@ -196,7 +216,7 @@ extradata=None): divid = self._build_divid(rtype, role, entity.eid) event_args = {'divid' : divid, 'eid' : entity.eid, 'rtype' : rtype, 'formid': formid, - 'reload' : json.dumps(reload), 'default_value' : default_value, + 'reload' : json_dumps(reload), 'default_value' : default_value, 'role' : role, 'vid' : u''} if extradata: event_args.update(extradata) diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/views/tableview.py --- a/web/views/tableview.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/views/tableview.py Thu Jul 08 16:30:19 2010 +0200 @@ -16,17 +16,13 @@ # You should have received a copy of the GNU Lesser General Public License along # with CubicWeb. If not, see . """generic table view, including filtering abilities""" + __docformat__ = "restructuredtext en" -try: - from json import dumps -except ImportError: - from simplejson import dumps - from logilab.mtconverter import xml_escape from cubicweb.selectors import nonempty_rset, match_form_params -from cubicweb.utils import make_uid +from cubicweb.utils import make_uid, json_dumps from cubicweb.view import EntityView, AnyRsetView from cubicweb import tags from cubicweb.uilib import toggle_action, limitsize, htmlescape @@ -77,7 +73,7 @@ # drop False / None values from vidargs vidargs = dict((k, v) for k, v in vidargs.iteritems() if v) w(u'' % - xml_escape(dumps([divid, self.__regid__, False, vidargs]))) + xml_escape(json_dumps([divid, self.__regid__, False, vidargs]))) w(u'
' % (divid, hidden and 'hidden' or '')) w(u'' % divid) w(u'') @@ -197,7 +193,8 @@ rql = params.pop('rql', self.cw_rset.printable_rql()) # latest 'true' used for 'swap' mode return 'javascript: replacePageChunk(%s, %s, %s, %s, true)' % ( - dumps(divid), dumps(rql), dumps(self.__regid__), dumps(params)) + json_dumps(divid), json_dumps(rql), json_dumps(self.__regid__), + json_dumps(params)) def show_hide_actions(self, divid, currentlydisplayed=False): showhide = u';'.join(toggle_action('%s%s' % (divid, what))[11:] diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/views/timeline.py --- a/web/views/timeline.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/views/timeline.py Thu Jul 08 16:30:19 2010 +0200 @@ -26,7 +26,7 @@ from cubicweb.selectors import adaptable from cubicweb.view import EntityView, StartupView -from cubicweb.web import json +from cubicweb.utils import json_dumps _ = unicode @@ -52,7 +52,7 @@ events.append(event) timeline_data = {'dateTimeFormat': self.date_fmt, 'events': events} - self.w(json.dumps(timeline_data)) + self.w(json_dumps(timeline_data)) # FIXME: those properties should be defined by the entity class def onclick_url(self, entity): diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/views/treeview.py --- a/web/views/treeview.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/views/treeview.py Thu Jul 08 16:30:19 2010 +0200 @@ -25,11 +25,10 @@ from logilab.mtconverter import xml_escape -from cubicweb.utils import make_uid +from cubicweb.utils import make_uid, json from cubicweb.selectors import adaptable from cubicweb.view import EntityView from cubicweb.mixins import _done_init -from cubicweb.web import json from cubicweb.web.views import baseviews def treecookiename(treeid): diff -r 846d1fb32aa8 -r 9cbde75fefe8 web/webconfig.py --- a/web/webconfig.py Thu Jul 08 16:29:51 2010 +0200 +++ b/web/webconfig.py Thu Jul 08 16:30:19 2010 +0200 @@ -15,9 +15,8 @@ # # You should have received a copy of the GNU Lesser General Public License along # with CubicWeb. If not, see . -"""common web configuration for twisted/modpython instances +"""web ui configuration for cubicweb instances""" -""" __docformat__ = "restructuredtext en" _ = unicode @@ -335,14 +334,18 @@ def _build_ui_properties(self): # self.datadir_url[:-1] to remove trailing / from cubicweb.web.propertysheet import PropertySheet + cachedir = join(self.appdatahome, 'uicache') + self.check_writeable_uid_directory(cachedir) self.uiprops = PropertySheet( - join(self.appdatahome, 'uicache'), + cachedir, data=lambda x: self.datadir_url + x, datadir_url=self.datadir_url[:-1]) self._init_uiprops(self.uiprops) if self['https-url']: + cachedir = join(self.appdatahome, 'uicachehttps') + self.check_writeable_uid_directory(cachedir) self.https_uiprops = PropertySheet( - join(self.appdatahome, 'uicache'), + cachedir, data=lambda x: self.https_datadir_url + x, datadir_url=self.https_datadir_url[:-1]) self._init_uiprops(self.https_uiprops)