diff -r 7070250bf50d -r 0a927fe4541b web/views/basecontrollers.py --- a/web/views/basecontrollers.py Fri Dec 09 12:14:11 2011 +0100 +++ b/web/views/basecontrollers.py Fri Dec 16 12:30:12 2011 +0100 @@ -22,20 +22,21 @@ __docformat__ = "restructuredtext en" _ = unicode -from logilab.common.date import strptime +from warnings import warn + from logilab.common.deprecation import deprecated from cubicweb import (NoSelectableObject, ObjectNotFound, ValidationError, AuthenticationError, typed_eid) -from cubicweb.utils import UStringIO, json, json_dumps -from cubicweb.uilib import exc_message -from cubicweb.selectors import authenticated_user, anonymous_user, match_form_params -from cubicweb.mail import format_mail -from cubicweb.web import Redirect, RemoteCallFailed, DirectResponse, facet +from cubicweb.utils import json_dumps +from cubicweb.selectors import (authenticated_user, anonymous_user, + match_form_params) +from cubicweb.web import Redirect, RemoteCallFailed from cubicweb.web.controller import Controller -from cubicweb.web.views import vid_from_rset, formrenderers +from cubicweb.web.views import vid_from_rset +@deprecated('jsonize is deprecated, use AjaxFunction appobjects instead') def jsonize(func): """decorator to sets correct content_type and calls `json_dumps` on results @@ -46,6 +47,7 @@ wrapper.__name__ = func.__name__ return wrapper +@deprecated('xhtmlize is deprecated, use AjaxFunction appobjects instead') def xhtmlize(func): """decorator to sets correct content_type and calls `xmlize` on results""" def wrapper(self, *args, **kwargs): @@ -56,6 +58,7 @@ wrapper.__name__ = func.__name__ return wrapper +@deprecated('check_pageid is deprecated, use AjaxFunction appobjects instead') def check_pageid(func): """decorator which checks the given pageid is found in the user's session data @@ -234,7 +237,7 @@ """ % (domid, callback, errback, jsargs, cbargs) def publish(self, rset=None): - self._cw.json_request = True + self._cw.ajax_request = True # XXX unclear why we have a separated controller here vs # js_validate_form on the json controller status, args, entity = _validate_form(self._cw, self._cw.vreg) @@ -242,339 +245,18 @@ self._cw.encoding) return self.response(domid, status, args, entity) -def optional_kwargs(extraargs): - if extraargs is None: - return {} - # we receive unicode keys which is not supported by the **syntax - return dict((str(key), value) for key, value in extraargs.iteritems()) - class JSonController(Controller): __regid__ = 'json' def publish(self, rset=None): - """call js_* methods. Expected form keys: - - :fname: the method name without the js_ prefix - :args: arguments list (json) - - note: it's the responsability of js_* methods to set the correct - response content type - """ - self._cw.json_request = True - try: - fname = self._cw.form['fname'] - func = getattr(self, 'js_%s' % fname) - except KeyError: - raise RemoteCallFailed('no method specified') - except AttributeError: - raise RemoteCallFailed('no %s method' % fname) - # no attribute means the callback takes no argument - args = self._cw.form.get('arg', ()) - if not isinstance(args, (list, tuple)): - args = (args,) - try: - args = [json.loads(arg) for arg in args] - except ValueError, exc: - self.exception('error while decoding json arguments for js_%s: %s (err: %s)', - fname, args, exc) - raise RemoteCallFailed(exc_message(exc, self._cw.encoding)) - try: - result = func(*args) - except (RemoteCallFailed, DirectResponse): - raise - except Exception, exc: - self.exception('an exception occurred while calling js_%s(%s): %s', - fname, args, exc) - raise RemoteCallFailed(exc_message(exc, self._cw.encoding)) - if result is None: - return '' - # get unicode on @htmlize methods, encoded string on @jsonize methods - elif isinstance(result, unicode): - return result.encode(self._cw.encoding) - return result - - def _rebuild_posted_form(self, names, values, action=None): - form = {} - for name, value in zip(names, values): - # remove possible __action_xxx inputs - if name.startswith('__action'): - if action is None: - # strip '__action_' to get the actual action name - action = name[9:] - continue - # form.setdefault(name, []).append(value) - if name in form: - curvalue = form[name] - if isinstance(curvalue, list): - curvalue.append(value) - else: - form[name] = [curvalue, value] - else: - form[name] = value - # simulate click on __action_%s button to help the controller - if action: - form['__action_%s' % action] = u'whatever' - return form - - def _exec(self, rql, args=None, rocheck=True): - """json mode: execute RQL and return resultset as json""" - rql = rql.strip() - if rql.startswith('rql:'): - rql = rql[4:] - if rocheck: - self._cw.ensure_ro_rql(rql) - try: - return self._cw.execute(rql, args) - except Exception, ex: - self.exception("error in _exec(rql=%s): %s", rql, ex) - return None - return None - - def _call_view(self, view, paginate=False, **kwargs): - divid = self._cw.form.get('divid') - # we need to call pagination before with the stream set - try: - stream = view.set_stream() - except AttributeError: - stream = UStringIO() - kwargs['w'] = stream.write - assert not paginate - if divid == 'pageContent': - # ensure divid isn't reused by the view (e.g. table view) - del self._cw.form['divid'] - # mimick main template behaviour - stream.write(u'
') - vtitle = self._cw.form.get('vtitle') - if vtitle: - stream.write(u'

%s

\n' % vtitle) - paginate = True - nav_html = UStringIO() - if paginate and not view.handle_pagination: - view.paginate(w=nav_html.write) - stream.write(nav_html.getvalue()) - if divid == 'pageContent': - stream.write(u'
') - view.render(**kwargs) - extresources = self._cw.html_headers.getvalue(skiphead=True) - if extresources: - stream.write(u'
\n') # XXX use a widget ? - stream.write(extresources) - stream.write(u'
\n') - if divid == 'pageContent': - stream.write(u'
%s
' % nav_html.getvalue()) - return stream.getvalue() - - @xhtmlize - def js_view(self): - # XXX try to use the page-content template - req = self._cw - rql = req.form.get('rql') - if rql: - rset = self._exec(rql) - elif 'eid' in req.form: - rset = self._cw.eid_rset(req.form['eid']) - else: - rset = None - vid = req.form.get('vid') or vid_from_rset(req, rset, self._cw.vreg.schema) - try: - view = self._cw.vreg['views'].select(vid, req, rset=rset) - except NoSelectableObject: - vid = req.form.get('fallbackvid', 'noresult') - view = self._cw.vreg['views'].select(vid, req, rset=rset) - self.validate_cache(view) - return self._call_view(view, paginate=req.form.pop('paginate', False)) - - @xhtmlize - def js_prop_widget(self, propkey, varname, tabindex=None): - """specific method for CWProperty handling""" - entity = self._cw.vreg['etypes'].etype_class('CWProperty')(self._cw) - entity.eid = varname - entity['pkey'] = propkey - form = self._cw.vreg['forms'].select('edition', self._cw, entity=entity) - form.build_context() - vfield = form.field_by_name('value') - renderer = formrenderers.FormRenderer(self._cw) - return vfield.render(form, renderer, tabindex=tabindex) \ - + renderer.render_help(form, vfield) - - @xhtmlize - def js_component(self, compid, rql, registry='components', extraargs=None): - if rql: - rset = self._exec(rql) - else: - rset = None - # XXX while it sounds good, addition of the try/except below cause pb: - # when filtering using facets return an empty rset, the edition box - # isn't anymore selectable, as expected. The pb is that with the - # try/except below, we see a "an error occurred" message in the ui, while - # we don't see it without it. Proper fix would probably be to deal with - # this by allowing facet handling code to tell to js_component that such - # error is expected and should'nt be reported. - #try: - comp = self._cw.vreg[registry].select(compid, self._cw, rset=rset, - **optional_kwargs(extraargs)) - #except NoSelectableObject: - # raise RemoteCallFailed('unselectable') - return self._call_view(comp, **optional_kwargs(extraargs)) - - @xhtmlize - def js_render(self, registry, oid, eid=None, - selectargs=None, renderargs=None): - if eid is not None: - rset = self._cw.eid_rset(eid) - # XXX set row=0 - elif self._cw.form.get('rql'): - rset = self._cw.execute(self._cw.form['rql']) - else: - rset = None - view = self._cw.vreg[registry].select(oid, self._cw, rset=rset, - **optional_kwargs(selectargs)) - return self._call_view(view, **optional_kwargs(renderargs)) - - @check_pageid - @xhtmlize - def js_inline_creation_form(self, peid, petype, ttype, rtype, role, i18nctx): - view = self._cw.vreg['views'].select('inline-creation', self._cw, - etype=ttype, rtype=rtype, role=role, - peid=peid, petype=petype) - return self._call_view(view, i18nctx=i18nctx) - - @jsonize - def js_validate_form(self, action, names, values): - return self.validate_form(action, names, values) - - def validate_form(self, action, names, values): - self._cw.form = self._rebuild_posted_form(names, values, action) - return _validate_form(self._cw, self._cw.vreg) - - @xhtmlize - def js_reledit_form(self): - req = self._cw - args = dict((x, req.form[x]) - for x in ('formid', 'rtype', 'role', 'reload', 'action')) - rset = req.eid_rset(typed_eid(self._cw.form['eid'])) - try: - args['reload'] = json.loads(args['reload']) - except ValueError: # not true/false, an absolute url - assert args['reload'].startswith('http') - view = req.vreg['views'].select('reledit', req, rset=rset, rtype=args['rtype']) - return self._call_view(view, **args) - - @jsonize - def js_i18n(self, msgids): - """returns the translation of `msgid`""" - return [self._cw._(msgid) for msgid in msgids] - - @jsonize - def js_format_date(self, strdate): - """returns the formatted date for `msgid`""" - date = strptime(strdate, '%Y-%m-%d %H:%M:%S') - return self._cw.format_date(date) - - @jsonize - def js_external_resource(self, resource): - """returns the URL of the external resource named `resource`""" - return self._cw.uiprops[resource] - - @check_pageid - @jsonize - def js_user_callback(self, cbname): - page_data = self._cw.session.data.get(self._cw.pageid, {}) - try: - cb = page_data[cbname] - except KeyError: - return None - return cb(self._cw) - - @jsonize - def js_filter_build_rql(self, names, values): - form = self._rebuild_posted_form(names, values) - self._cw.form = form - builder = facet.FilterRQLBuilder(self._cw) - return builder.build_rql() - - @jsonize - def js_filter_select_content(self, facetids, rql, mainvar): - # Union unsupported yet - select = self._cw.vreg.parse(self._cw, rql).children[0] - filtered_variable = facet.get_filtered_variable(select, mainvar) - facet.prepare_select(select, filtered_variable) - update_map = {} - for fid in facetids: - fobj = facet.get_facet(self._cw, fid, select, filtered_variable) - update_map[fid] = fobj.possible_values() - return update_map - - def js_unregister_user_callback(self, cbname): - self._cw.unregister_callback(self._cw.pageid, cbname) - - def js_unload_page_data(self): - self._cw.session.data.pop(self._cw.pageid, None) - - def js_cancel_edition(self, errorurl): - """cancelling edition from javascript - - We need to clear associated req's data : - - errorurl - - pending insertions / deletions - """ - self._cw.cancel_edition(errorurl) - - def js_delete_bookmark(self, beid): - rql = 'DELETE B bookmarked_by U WHERE B eid %(b)s, U eid %(u)s' - self._cw.execute(rql, {'b': typed_eid(beid), 'u' : self._cw.user.eid}) - - def js_node_clicked(self, treeid, nodeeid): - """add/remove eid in treestate cookie""" - from cubicweb.web.views.treeview import treecookiename - cookies = self._cw.get_cookie() - statename = treecookiename(treeid) - treestate = cookies.get(statename) - if treestate is None: - self._cw.set_cookie(statename, nodeeid) - else: - marked = set(filter(None, treestate.value.split(':'))) - if nodeeid in marked: - marked.remove(nodeeid) - else: - marked.add(nodeeid) - self._cw.set_cookie(statename, ':'.join(marked)) - - @jsonize - @deprecated("[3.13] use jQuery.cookie(cookiename, cookievalue, {path: '/'}) in js land instead") - def js_set_cookie(self, cookiename, cookievalue): - cookiename, cookievalue = str(cookiename), str(cookievalue) - self._cw.set_cookie(cookiename, cookievalue) - - # relations edition stuff ################################################## - - def _add_pending(self, eidfrom, rel, eidto, kind): - key = 'pending_%s' % kind - pendings = self._cw.session.data.setdefault(key, set()) - pendings.add( (typed_eid(eidfrom), rel, typed_eid(eidto)) ) - - def _remove_pending(self, eidfrom, rel, eidto, kind): - key = 'pending_%s' % kind - pendings = self._cw.session.data[key] - pendings.remove( (typed_eid(eidfrom), rel, typed_eid(eidto)) ) - - def js_remove_pending_insert(self, (eidfrom, rel, eidto)): - self._remove_pending(eidfrom, rel, eidto, 'insert') - - def js_add_pending_inserts(self, tripletlist): - for eidfrom, rel, eidto in tripletlist: - self._add_pending(eidfrom, rel, eidto, 'insert') - - def js_remove_pending_delete(self, (eidfrom, rel, eidto)): - self._remove_pending(eidfrom, rel, eidto, 'delete') - - def js_add_pending_delete(self, (eidfrom, rel, eidto)): - self._add_pending(eidfrom, rel, eidto, 'delete') + warn('[3.15] JSONController is deprecated, use AjaxController instead', + DeprecationWarning) + ajax_controller = self._cw.vreg['controllers'].select('ajax', self._cw, appli=self.appli) + return ajax_controller.publish(rset) # XXX move to massmailing - class MailBugReportController(Controller): __regid__ = 'reportbug' __select__ = match_form_params('description')