web/views/basecontrollers.py
changeset 3451 6b46d73823f5
parent 3377 dd9d292b6a6d
child 3460 e4843535db25
equal deleted inserted replaced
3448:495862266785 3451:6b46d73823f5
    34 def jsonize(func):
    34 def jsonize(func):
    35     """decorator to sets correct content_type and calls `simplejson.dumps` on
    35     """decorator to sets correct content_type and calls `simplejson.dumps` on
    36     results
    36     results
    37     """
    37     """
    38     def wrapper(self, *args, **kwargs):
    38     def wrapper(self, *args, **kwargs):
    39         self.req.set_content_type('application/json')
    39         self._cw.set_content_type('application/json')
    40         return json_dumps(func(self, *args, **kwargs))
    40         return json_dumps(func(self, *args, **kwargs))
    41     wrapper.__name__ = func.__name__
    41     wrapper.__name__ = func.__name__
    42     return wrapper
    42     return wrapper
    43 
    43 
    44 def xhtmlize(func):
    44 def xhtmlize(func):
    45     """decorator to sets correct content_type and calls `xmlize` on results"""
    45     """decorator to sets correct content_type and calls `xmlize` on results"""
    46     def wrapper(self, *args, **kwargs):
    46     def wrapper(self, *args, **kwargs):
    47         self.req.set_content_type(self.req.html_content_type())
    47         self._cw.set_content_type(self._cw.html_content_type())
    48         result = func(self, *args, **kwargs)
    48         result = func(self, *args, **kwargs)
    49         return ''.join((self.req.document_surrounding_div(), result.strip(),
    49         return ''.join((self._cw.document_surrounding_div(), result.strip(),
    50                         u'</div>'))
    50                         u'</div>'))
    51     wrapper.__name__ = func.__name__
    51     wrapper.__name__ = func.__name__
    52     return wrapper
    52     return wrapper
    53 
    53 
    54 def check_pageid(func):
    54 def check_pageid(func):
    55     """decorator which checks the given pageid is found in the
    55     """decorator which checks the given pageid is found in the
    56     user's session data
    56     user's session data
    57     """
    57     """
    58     def wrapper(self, *args, **kwargs):
    58     def wrapper(self, *args, **kwargs):
    59         data = self.req.get_session_data(self.req.pageid)
    59         data = self._cw.get_session_data(self._cw.pageid)
    60         if data is None:
    60         if data is None:
    61             raise RemoteCallFailed(self.req._('pageid-not-found'))
    61             raise RemoteCallFailed(self._cw._('pageid-not-found'))
    62         return func(self, *args, **kwargs)
    62         return func(self, *args, **kwargs)
    63     return wrapper
    63     return wrapper
    64 
    64 
    65 
    65 
    66 class LoginController(Controller):
    66 class LoginController(Controller):
    67     __regid__ = 'login'
    67     __regid__ = 'login'
    68 
    68 
    69     def publish(self, rset=None):
    69     def publish(self, rset=None):
    70         """log in the instance"""
    70         """log in the instance"""
    71         if self.config['auth-mode'] == 'http':
    71         if self._cw.config['auth-mode'] == 'http':
    72             # HTTP authentication
    72             # HTTP authentication
    73             raise ExplicitLogin()
    73             raise ExplicitLogin()
    74         else:
    74         else:
    75             # Cookie authentication
    75             # Cookie authentication
    76             return self.appli.need_login_content(self.req)
    76             return self.appli.need_login_content(self._cw)
    77 
    77 
    78 
    78 
    79 class LogoutController(Controller):
    79 class LogoutController(Controller):
    80     __regid__ = 'logout'
    80     __regid__ = 'logout'
    81 
    81 
    82     def publish(self, rset=None):
    82     def publish(self, rset=None):
    83         """logout from the instance"""
    83         """logout from the instance"""
    84         return self.appli.session_handler.logout(self.req)
    84         return self.appli.session_handler.logout(self._cw)
    85 
    85 
    86 
    86 
    87 class ViewController(Controller):
    87 class ViewController(Controller):
    88     """standard entry point :
    88     """standard entry point :
    89     - build result set
    89     - build result set
    95     def publish(self, rset=None):
    95     def publish(self, rset=None):
    96         """publish a request, returning an encoded string"""
    96         """publish a request, returning an encoded string"""
    97         view, rset = self._select_view_and_rset(rset)
    97         view, rset = self._select_view_and_rset(rset)
    98         self.add_to_breadcrumbs(view)
    98         self.add_to_breadcrumbs(view)
    99         self.validate_cache(view)
    99         self.validate_cache(view)
   100         template = self.appli.main_template_id(self.req)
   100         template = self.appli.main_template_id(self._cw)
   101         return self.vreg['views'].main_template(self.req, template,
   101         return self._cw.vreg['views'].main_template(self._cw, template,
   102                                                 rset=rset, view=view)
   102                                                 rset=rset, view=view)
   103 
   103 
   104     def _select_view_and_rset(self, rset):
   104     def _select_view_and_rset(self, rset):
   105         req = self.req
   105         req = self._cw
   106         if rset is None and not hasattr(req, '_rql_processed'):
   106         if rset is None and not hasattr(req, '_rql_processed'):
   107             req._rql_processed = True
   107             req._rql_processed = True
   108             rset = self.process_rql(req.form.get('rql'))
   108             rset = self.process_rql(req.form.get('rql'))
   109         if rset and rset.rowcount == 1 and '__method' in req.form:
   109         if rset and rset.rowcount == 1 and '__method' in req.form:
   110             entity = rset.get_entity(0, 0)
   110             entity = rset.get_entity(0, 0)
   114             except Redirect: # propagate redirect that might occur in method()
   114             except Redirect: # propagate redirect that might occur in method()
   115                 raise
   115                 raise
   116             except Exception, ex:
   116             except Exception, ex:
   117                 self.exception('while handling __method')
   117                 self.exception('while handling __method')
   118                 req.set_message(req._("error while handling __method: %s") % req._(ex))
   118                 req.set_message(req._("error while handling __method: %s") % req._(ex))
   119         vid = req.form.get('vid') or vid_from_rset(req, rset, self.schema)
   119         vid = req.form.get('vid') or vid_from_rset(req, rset, self._cw.schema)
   120         try:
   120         try:
   121             view = self.vreg['views'].select(vid, req, rset=rset)
   121             view = self._cw.vreg['views'].select(vid, req, rset=rset)
   122         except ObjectNotFound:
   122         except ObjectNotFound:
   123             self.warning("the view %s could not be found", vid)
   123             self.warning("the view %s could not be found", vid)
   124             req.set_message(req._("The view %s could not be found") % vid)
   124             req.set_message(req._("The view %s could not be found") % vid)
   125             vid = vid_from_rset(req, rset, self.schema)
   125             vid = vid_from_rset(req, rset, self._cw.schema)
   126             view = self.vreg['views'].select(vid, req, rset=rset)
   126             view = self._cw.vreg['views'].select(vid, req, rset=rset)
   127         except NoSelectableObject:
   127         except NoSelectableObject:
   128             if rset:
   128             if rset:
   129                 req.set_message(req._("The view %s can not be applied to this query") % vid)
   129                 req.set_message(req._("The view %s can not be applied to this query") % vid)
   130             else:
   130             else:
   131                 req.set_message(req._("You have no access to this view or it can not "
   131                 req.set_message(req._("You have no access to this view or it can not "
   132                                       "be used to display the current data."))
   132                                       "be used to display the current data."))
   133             self.warning("the view %s can not be applied to this query", vid)
   133             self.warning("the view %s can not be applied to this query", vid)
   134             vid = vid_from_rset(req, rset, self.schema)
   134             vid = vid_from_rset(req, rset, self._cw.schema)
   135             view = self.vreg['views'].select(vid, req, rset=rset)
   135             view = self._cw.vreg['views'].select(vid, req, rset=rset)
   136         return view, rset
   136         return view, rset
   137 
   137 
   138     def add_to_breadcrumbs(self, view):
   138     def add_to_breadcrumbs(self, view):
   139         # update breadcrumps **before** validating cache, unless the view
   139         # update breadcrumps **before** validating cache, unless the view
   140         # specifies explicitly it should not be added to breadcrumb or the
   140         # specifies explicitly it should not be added to breadcrumb or the
   141         # view is a binary view
   141         # view is a binary view
   142         if view.add_to_breadcrumbs and not view.binary:
   142         if view.add_to_breadcrumbs and not view.binary:
   143             self.req.update_breadcrumbs()
   143             self._cw.update_breadcrumbs()
   144 
   144 
   145     def validate_cache(self, view):
   145     def validate_cache(self, view):
   146         view.set_http_cache_headers()
   146         view.set_http_cache_headers()
   147         self.req.validate_cache()
   147         self._cw.validate_cache()
   148 
   148 
   149     def execute_linkto(self, eid=None):
   149     def execute_linkto(self, eid=None):
   150         """XXX __linkto parameter may cause security issue
   150         """XXX __linkto parameter may cause security issue
   151 
   151 
   152         defined here since custom application controller inheriting from this
   152         defined here since custom application controller inheriting from this
   153         one use this method?
   153         one use this method?
   154         """
   154         """
   155         req = self.req
   155         req = self._cw
   156         if not '__linkto' in req.form:
   156         if not '__linkto' in req.form:
   157             return
   157             return
   158         if eid is None:
   158         if eid is None:
   159             eid = typed_eid(req.form['eid'])
   159             eid = typed_eid(req.form['eid'])
   160         for linkto in req.list_form_param('__linkto', pop=True):
   160         for linkto in req.list_form_param('__linkto', pop=True):
   209 
   209 
   210 class FormValidatorController(Controller):
   210 class FormValidatorController(Controller):
   211     __regid__ = 'validateform'
   211     __regid__ = 'validateform'
   212 
   212 
   213     def response(self, domid, status, args, entity):
   213     def response(self, domid, status, args, entity):
   214         callback = str(self.req.form.get('__onsuccess', 'null'))
   214         callback = str(self._cw.form.get('__onsuccess', 'null'))
   215         errback = str(self.req.form.get('__onfailure', 'null'))
   215         errback = str(self._cw.form.get('__onfailure', 'null'))
   216         cbargs = str(self.req.form.get('__cbargs', 'null'))
   216         cbargs = str(self._cw.form.get('__cbargs', 'null'))
   217         self.req.set_content_type('text/html')
   217         self._cw.set_content_type('text/html')
   218         jsargs = simplejson.dumps((status, args, entity), cls=CubicWebJsonEncoder)
   218         jsargs = simplejson.dumps((status, args, entity), cls=CubicWebJsonEncoder)
   219         return """<script type="text/javascript">
   219         return """<script type="text/javascript">
   220  wp = window.parent;
   220  wp = window.parent;
   221  window.parent.handleFormValidationResponse('%s', %s, %s, %s, %s);
   221  window.parent.handleFormValidationResponse('%s', %s, %s, %s, %s);
   222 </script>""" %  (domid, callback, errback, jsargs, cbargs)
   222 </script>""" %  (domid, callback, errback, jsargs, cbargs)
   223 
   223 
   224     def publish(self, rset=None):
   224     def publish(self, rset=None):
   225         self.req.json_request = True
   225         self._cw.json_request = True
   226         # XXX unclear why we have a separated controller here vs
   226         # XXX unclear why we have a separated controller here vs
   227         # js_validate_form on the json controller
   227         # js_validate_form on the json controller
   228         status, args, entity = _validate_form(self.req, self.vreg)
   228         status, args, entity = _validate_form(self._cw, self._cw.vreg)
   229         domid = self.req.form.get('__domid', 'entityForm').encode(
   229         domid = self._cw.form.get('__domid', 'entityForm').encode(
   230             self.req.encoding)
   230             self._cw.encoding)
   231         return self.response(domid, status, args, entity)
   231         return self.response(domid, status, args, entity)
   232 
   232 
   233 
   233 
   234 class JSonController(Controller):
   234 class JSonController(Controller):
   235     __regid__ = 'json'
   235     __regid__ = 'json'
   241         :args: arguments list (json)
   241         :args: arguments list (json)
   242 
   242 
   243         note: it's the responsability of js_* methods to set the correct
   243         note: it's the responsability of js_* methods to set the correct
   244         response content type
   244         response content type
   245         """
   245         """
   246         self.req.json_request = True
   246         self._cw.json_request = True
   247         self.req.pageid = self.req.form.get('pageid')
   247         self._cw.pageid = self._cw.form.get('pageid')
   248         try:
   248         try:
   249             fname = self.req.form['fname']
   249             fname = self._cw.form['fname']
   250             func = getattr(self, 'js_%s' % fname)
   250             func = getattr(self, 'js_%s' % fname)
   251         except KeyError:
   251         except KeyError:
   252             raise RemoteCallFailed('no method specified')
   252             raise RemoteCallFailed('no method specified')
   253         except AttributeError:
   253         except AttributeError:
   254             raise RemoteCallFailed('no %s method' % fname)
   254             raise RemoteCallFailed('no %s method' % fname)
   255         # no <arg> attribute means the callback takes no argument
   255         # no <arg> attribute means the callback takes no argument
   256         args = self.req.form.get('arg', ())
   256         args = self._cw.form.get('arg', ())
   257         if not isinstance(args, (list, tuple)):
   257         if not isinstance(args, (list, tuple)):
   258             args = (args,)
   258             args = (args,)
   259         args = [simplejson.loads(arg) for arg in args]
   259         args = [simplejson.loads(arg) for arg in args]
   260         try:
   260         try:
   261             result = func(*args)
   261             result = func(*args)
   269             raise RemoteCallFailed(repr(ex))
   269             raise RemoteCallFailed(repr(ex))
   270         if result is None:
   270         if result is None:
   271             return ''
   271             return ''
   272         # get unicode on @htmlize methods, encoded string on @jsonize methods
   272         # get unicode on @htmlize methods, encoded string on @jsonize methods
   273         elif isinstance(result, unicode):
   273         elif isinstance(result, unicode):
   274             return result.encode(self.req.encoding)
   274             return result.encode(self._cw.encoding)
   275         return result
   275         return result
   276 
   276 
   277     def _rebuild_posted_form(self, names, values, action=None):
   277     def _rebuild_posted_form(self, names, values, action=None):
   278         form = {}
   278         form = {}
   279         for name, value in zip(names, values):
   279         for name, value in zip(names, values):
   295         return form
   295         return form
   296 
   296 
   297     def _exec(self, rql, args=None, eidkey=None, rocheck=True):
   297     def _exec(self, rql, args=None, eidkey=None, rocheck=True):
   298         """json mode: execute RQL and return resultset as json"""
   298         """json mode: execute RQL and return resultset as json"""
   299         if rocheck:
   299         if rocheck:
   300             self.req.ensure_ro_rql(rql)
   300             self._cw.ensure_ro_rql(rql)
   301         try:
   301         try:
   302             return self.req.execute(rql, args, eidkey)
   302             return self._cw.execute(rql, args, eidkey)
   303         except Exception, ex:
   303         except Exception, ex:
   304             self.exception("error in _exec(rql=%s): %s", rql, ex)
   304             self.exception("error in _exec(rql=%s): %s", rql, ex)
   305             return None
   305             return None
   306         return None
   306         return None
   307 
   307 
   308     def _call_view(self, view, **kwargs):
   308     def _call_view(self, view, **kwargs):
   309         req = self.req
   309         req = self._cw
   310         divid = req.form.get('divid', 'pageContent')
   310         divid = req.form.get('divid', 'pageContent')
   311         # we need to call pagination before with the stream set
   311         # we need to call pagination before with the stream set
   312         stream = view.set_stream()
   312         stream = view.set_stream()
   313         if req.form.get('paginate'):
   313         if req.form.get('paginate'):
   314             if divid == 'pageContent':
   314             if divid == 'pageContent':
   315                 # mimick main template behaviour
   315                 # mimick main template behaviour
   316                 stream.write(u'<div id="pageContent">')
   316                 stream.write(u'<div id="pageContent">')
   317                 vtitle = self.req.form.get('vtitle')
   317                 vtitle = self._cw.form.get('vtitle')
   318                 if vtitle:
   318                 if vtitle:
   319                     stream.write(u'<h1 class="vtitle">%s</h1>\n' % vtitle)
   319                     stream.write(u'<h1 class="vtitle">%s</h1>\n' % vtitle)
   320             view.paginate()
   320             view.paginate()
   321             if divid == 'pageContent':
   321             if divid == 'pageContent':
   322                 stream.write(u'<div id="contentmain">')
   322                 stream.write(u'<div id="contentmain">')
   331         return stream.getvalue()
   331         return stream.getvalue()
   332 
   332 
   333     @xhtmlize
   333     @xhtmlize
   334     def js_view(self):
   334     def js_view(self):
   335         # XXX try to use the page-content template
   335         # XXX try to use the page-content template
   336         req = self.req
   336         req = self._cw
   337         rql = req.form.get('rql')
   337         rql = req.form.get('rql')
   338         if rql:
   338         if rql:
   339             rset = self._exec(rql)
   339             rset = self._exec(rql)
   340         else:
   340         else:
   341             rset = None
   341             rset = None
   342         vid = req.form.get('vid') or vid_from_rset(req, rset, self.schema)
   342         vid = req.form.get('vid') or vid_from_rset(req, rset, self._cw.schema)
   343         try:
   343         try:
   344             view = self.vreg['views'].select(vid, req, rset=rset)
   344             view = self._cw.vreg['views'].select(vid, req, rset=rset)
   345         except NoSelectableObject:
   345         except NoSelectableObject:
   346             vid = req.form.get('fallbackvid', 'noresult')
   346             vid = req.form.get('fallbackvid', 'noresult')
   347             view = self.vreg['views'].select(vid, req, rset=rset)
   347             view = self._cw.vreg['views'].select(vid, req, rset=rset)
   348         return self._call_view(view)
   348         return self._call_view(view)
   349 
   349 
   350     @xhtmlize
   350     @xhtmlize
   351     def js_prop_widget(self, propkey, varname, tabindex=None):
   351     def js_prop_widget(self, propkey, varname, tabindex=None):
   352         """specific method for CWProperty handling"""
   352         """specific method for CWProperty handling"""
   353         entity = self.vreg['etypes'].etype_class('CWProperty')(self.req)
   353         entity = self._cw.vreg['etypes'].etype_class('CWProperty')(self._cw)
   354         entity.eid = varname
   354         entity.eid = varname
   355         entity['pkey'] = propkey
   355         entity['pkey'] = propkey
   356         form = self.vreg['forms'].select('edition', self.req, entity=entity)
   356         form = self._cw.vreg['forms'].select('edition', self._cw, entity=entity)
   357         form.form_build_context()
   357         form.form_build_context()
   358         vfield = form.field_by_name('value')
   358         vfield = form.field_by_name('value')
   359         renderer = FormRenderer(self.req)
   359         renderer = FormRenderer(self._cw)
   360         return vfield.render(form, renderer, tabindex=tabindex) \
   360         return vfield.render(form, renderer, tabindex=tabindex) \
   361                + renderer.render_help(form, vfield)
   361                + renderer.render_help(form, vfield)
   362 
   362 
   363     @xhtmlize
   363     @xhtmlize
   364     def js_component(self, compid, rql, registry='components', extraargs=None):
   364     def js_component(self, compid, rql, registry='components', extraargs=None):
   365         if rql:
   365         if rql:
   366             rset = self._exec(rql)
   366             rset = self._exec(rql)
   367         else:
   367         else:
   368             rset = None
   368             rset = None
   369         comp = self.vreg[registry].select(compid, self.req, rset=rset)
   369         comp = self._cw.vreg[registry].select(compid, self._cw, rset=rset)
   370         if extraargs is None:
   370         if extraargs is None:
   371             extraargs = {}
   371             extraargs = {}
   372         else: # we receive unicode keys which is not supported by the **syntax
   372         else: # we receive unicode keys which is not supported by the **syntax
   373             extraargs = dict((str(key), value)
   373             extraargs = dict((str(key), value)
   374                              for key, value in extraargs.items())
   374                              for key, value in extraargs.items())
   376         return comp.render(**extraargs)
   376         return comp.render(**extraargs)
   377 
   377 
   378     @check_pageid
   378     @check_pageid
   379     @xhtmlize
   379     @xhtmlize
   380     def js_inline_creation_form(self, peid, ttype, rtype, role, i18nctx):
   380     def js_inline_creation_form(self, peid, ttype, rtype, role, i18nctx):
   381         view = self.vreg['views'].select('inline-creation', self.req,
   381         view = self._cw.vreg['views'].select('inline-creation', self._cw,
   382                                          etype=ttype, peid=peid, rtype=rtype,
   382                                          etype=ttype, peid=peid, rtype=rtype,
   383                                          role=role)
   383                                          role=role)
   384         return self._call_view(view, etype=ttype, peid=peid,
   384         return self._call_view(view, etype=ttype, peid=peid,
   385                                rtype=rtype, role=role, i18nctx=i18nctx)
   385                                rtype=rtype, role=role, i18nctx=i18nctx)
   386 
   386 
   387     @jsonize
   387     @jsonize
   388     def js_validate_form(self, action, names, values):
   388     def js_validate_form(self, action, names, values):
   389         return self.validate_form(action, names, values)
   389         return self.validate_form(action, names, values)
   390 
   390 
   391     def validate_form(self, action, names, values):
   391     def validate_form(self, action, names, values):
   392         self.req.form = self._rebuild_posted_form(names, values, action)
   392         self._cw.form = self._rebuild_posted_form(names, values, action)
   393         return _validate_form(self.req, self.vreg)
   393         return _validate_form(self._cw, self._cw.vreg)
   394 
   394 
   395     @jsonize
   395     @jsonize
   396     def js_edit_field(self, action, names, values, rtype, eid, default):
   396     def js_edit_field(self, action, names, values, rtype, eid, default):
   397         success, args, _ = self.validate_form(action, names, values)
   397         success, args, _ = self.validate_form(action, names, values)
   398         if success:
   398         if success:
   399             # Any X,N where we don't seem to use N is an optimisation
   399             # Any X,N where we don't seem to use N is an optimisation
   400             # printable_value won't need to query N again
   400             # printable_value won't need to query N again
   401             rset = self.req.execute('Any X,N WHERE X eid %%(x)s, X %s N' % rtype,
   401             rset = self._cw.execute('Any X,N WHERE X eid %%(x)s, X %s N' % rtype,
   402                                     {'x': eid}, 'x')
   402                                     {'x': eid}, 'x')
   403             entity = rset.get_entity(0, 0)
   403             entity = rset.get_entity(0, 0)
   404             value = entity.printable_value(rtype) or default
   404             value = entity.printable_value(rtype) or default
   405             return (success, args, value)
   405             return (success, args, value)
   406         else:
   406         else:
   407             return (success, args, None)
   407             return (success, args, None)
   408 
   408 
   409     @jsonize
   409     @jsonize
   410     def js_reledit_form(self, eid, rtype, role, default, lzone):
   410     def js_reledit_form(self, eid, rtype, role, default, lzone):
   411         """XXX we should get rid of this and use loadxhtml"""
   411         """XXX we should get rid of this and use loadxhtml"""
   412         entity = self.req.entity_from_eid(eid)
   412         entity = self._cw.entity_from_eid(eid)
   413         return entity.view('reledit', rtype=rtype, role=role,
   413         return entity.view('reledit', rtype=rtype, role=role,
   414                            default=default, landing_zone=lzone)
   414                            default=default, landing_zone=lzone)
   415 
   415 
   416     @jsonize
   416     @jsonize
   417     def js_i18n(self, msgids):
   417     def js_i18n(self, msgids):
   418         """returns the translation of `msgid`"""
   418         """returns the translation of `msgid`"""
   419         return [self.req._(msgid) for msgid in msgids]
   419         return [self._cw._(msgid) for msgid in msgids]
   420 
   420 
   421     @jsonize
   421     @jsonize
   422     def js_format_date(self, strdate):
   422     def js_format_date(self, strdate):
   423         """returns the formatted date for `msgid`"""
   423         """returns the formatted date for `msgid`"""
   424         date = strptime(strdate, '%Y-%m-%d %H:%M:%S')
   424         date = strptime(strdate, '%Y-%m-%d %H:%M:%S')
   425         return self.format_date(date)
   425         return self.format_date(date)
   426 
   426 
   427     @jsonize
   427     @jsonize
   428     def js_external_resource(self, resource):
   428     def js_external_resource(self, resource):
   429         """returns the URL of the external resource named `resource`"""
   429         """returns the URL of the external resource named `resource`"""
   430         return self.req.external_resource(resource)
   430         return self._cw.external_resource(resource)
   431 
   431 
   432     @check_pageid
   432     @check_pageid
   433     @jsonize
   433     @jsonize
   434     def js_user_callback(self, cbname):
   434     def js_user_callback(self, cbname):
   435         page_data = self.req.get_session_data(self.req.pageid, {})
   435         page_data = self._cw.get_session_data(self._cw.pageid, {})
   436         try:
   436         try:
   437             cb = page_data[cbname]
   437             cb = page_data[cbname]
   438         except KeyError:
   438         except KeyError:
   439             return None
   439             return None
   440         return cb(self.req)
   440         return cb(self._cw)
   441 
   441 
   442     if HAS_SEARCH_RESTRICTION:
   442     if HAS_SEARCH_RESTRICTION:
   443         @jsonize
   443         @jsonize
   444         def js_filter_build_rql(self, names, values):
   444         def js_filter_build_rql(self, names, values):
   445             form = self._rebuild_posted_form(names, values)
   445             form = self._rebuild_posted_form(names, values)
   446             self.req.form = form
   446             self._cw.form = form
   447             builder = FilterRQLBuilder(self.req)
   447             builder = FilterRQLBuilder(self._cw)
   448             return builder.build_rql()
   448             return builder.build_rql()
   449 
   449 
   450         @jsonize
   450         @jsonize
   451         def js_filter_select_content(self, facetids, rql):
   451         def js_filter_select_content(self, facetids, rql):
   452             rqlst = self.vreg.parse(self.req, rql) # XXX Union unsupported yet
   452             rqlst = self._cw.vreg.parse(self._cw, rql) # XXX Union unsupported yet
   453             mainvar = prepare_facets_rqlst(rqlst)[0]
   453             mainvar = prepare_facets_rqlst(rqlst)[0]
   454             update_map = {}
   454             update_map = {}
   455             for facetid in facetids:
   455             for facetid in facetids:
   456                 facet = get_facet(self.req, facetid, rqlst.children[0], mainvar)
   456                 facet = get_facet(self._cw, facetid, rqlst.children[0], mainvar)
   457                 update_map[facetid] = facet.possible_values()
   457                 update_map[facetid] = facet.possible_values()
   458             return update_map
   458             return update_map
   459 
   459 
   460     def js_unregister_user_callback(self, cbname):
   460     def js_unregister_user_callback(self, cbname):
   461         self.req.unregister_callback(self.req.pageid, cbname)
   461         self._cw.unregister_callback(self._cw.pageid, cbname)
   462 
   462 
   463     def js_unload_page_data(self):
   463     def js_unload_page_data(self):
   464         self.req.del_session_data(self.req.pageid)
   464         self._cw.del_session_data(self._cw.pageid)
   465 
   465 
   466     def js_cancel_edition(self, errorurl):
   466     def js_cancel_edition(self, errorurl):
   467         """cancelling edition from javascript
   467         """cancelling edition from javascript
   468 
   468 
   469         We need to clear associated req's data :
   469         We need to clear associated req's data :
   470           - errorurl
   470           - errorurl
   471           - pending insertions / deletions
   471           - pending insertions / deletions
   472         """
   472         """
   473         self.req.cancel_edition(errorurl)
   473         self._cw.cancel_edition(errorurl)
   474 
   474 
   475     def js_delete_bookmark(self, beid):
   475     def js_delete_bookmark(self, beid):
   476         rql = 'DELETE B bookmarked_by U WHERE B eid %(b)s, U eid %(u)s'
   476         rql = 'DELETE B bookmarked_by U WHERE B eid %(b)s, U eid %(u)s'
   477         self.req.execute(rql, {'b': typed_eid(beid), 'u' : self.req.user.eid})
   477         self._cw.execute(rql, {'b': typed_eid(beid), 'u' : self._cw.user.eid})
   478 
   478 
   479     def js_node_clicked(self, treeid, nodeeid):
   479     def js_node_clicked(self, treeid, nodeeid):
   480         """add/remove eid in treestate cookie"""
   480         """add/remove eid in treestate cookie"""
   481         from cubicweb.web.views.treeview import treecookiename
   481         from cubicweb.web.views.treeview import treecookiename
   482         cookies = self.req.get_cookie()
   482         cookies = self._cw.get_cookie()
   483         statename = treecookiename(treeid)
   483         statename = treecookiename(treeid)
   484         treestate = cookies.get(statename)
   484         treestate = cookies.get(statename)
   485         if treestate is None:
   485         if treestate is None:
   486             cookies[statename] = nodeeid
   486             cookies[statename] = nodeeid
   487             self.req.set_cookie(cookies, statename)
   487             self._cw.set_cookie(cookies, statename)
   488         else:
   488         else:
   489             marked = set(filter(None, treestate.value.split(';')))
   489             marked = set(filter(None, treestate.value.split(';')))
   490             if nodeeid in marked:
   490             if nodeeid in marked:
   491                 marked.remove(nodeeid)
   491                 marked.remove(nodeeid)
   492             else:
   492             else:
   493                 marked.add(nodeeid)
   493                 marked.add(nodeeid)
   494             cookies[statename] = ';'.join(marked)
   494             cookies[statename] = ';'.join(marked)
   495             self.req.set_cookie(cookies, statename)
   495             self._cw.set_cookie(cookies, statename)
   496 
   496 
   497     def js_set_cookie(self, cookiename, cookievalue):
   497     def js_set_cookie(self, cookiename, cookievalue):
   498         # XXX we should consider jQuery.Cookie
   498         # XXX we should consider jQuery.Cookie
   499         cookiename, cookievalue = str(cookiename), str(cookievalue)
   499         cookiename, cookievalue = str(cookiename), str(cookievalue)
   500         cookies = self.req.get_cookie()
   500         cookies = self._cw.get_cookie()
   501         cookies[cookiename] = cookievalue
   501         cookies[cookiename] = cookievalue
   502         self.req.set_cookie(cookies, cookiename)
   502         self._cw.set_cookie(cookies, cookiename)
   503 
   503 
   504     # relations edition stuff ##################################################
   504     # relations edition stuff ##################################################
   505 
   505 
   506     def _add_pending(self, eidfrom, rel, eidto, kind):
   506     def _add_pending(self, eidfrom, rel, eidto, kind):
   507         key = 'pending_%s' % kind
   507         key = 'pending_%s' % kind
   508         pendings = self.req.get_session_data(key, set())
   508         pendings = self._cw.get_session_data(key, set())
   509         pendings.add( (typed_eid(eidfrom), rel, typed_eid(eidto)) )
   509         pendings.add( (typed_eid(eidfrom), rel, typed_eid(eidto)) )
   510         self.req.set_session_data(key, pendings)
   510         self._cw.set_session_data(key, pendings)
   511 
   511 
   512     def _remove_pending(self, eidfrom, rel, eidto, kind):
   512     def _remove_pending(self, eidfrom, rel, eidto, kind):
   513         key = 'pending_%s' % kind
   513         key = 'pending_%s' % kind
   514         pendings = self.req.get_session_data(key)
   514         pendings = self._cw.get_session_data(key)
   515         pendings.remove( (typed_eid(eidfrom), rel, typed_eid(eidto)) )
   515         pendings.remove( (typed_eid(eidfrom), rel, typed_eid(eidto)) )
   516         self.req.set_session_data(key, pendings)
   516         self._cw.set_session_data(key, pendings)
   517 
   517 
   518     def js_remove_pending_insert(self, (eidfrom, rel, eidto)):
   518     def js_remove_pending_insert(self, (eidfrom, rel, eidto)):
   519         self._remove_pending(eidfrom, rel, eidto, 'insert')
   519         self._remove_pending(eidfrom, rel, eidto, 'insert')
   520 
   520 
   521     def js_add_pending_inserts(self, tripletlist):
   521     def js_add_pending_inserts(self, tripletlist):
   530 
   530 
   531     # XXX specific code. Kill me and my AddComboBox friend
   531     # XXX specific code. Kill me and my AddComboBox friend
   532     @jsonize
   532     @jsonize
   533     def js_add_and_link_new_entity(self, etype_to, rel, eid_to, etype_from, value_from):
   533     def js_add_and_link_new_entity(self, etype_to, rel, eid_to, etype_from, value_from):
   534         # create a new entity
   534         # create a new entity
   535         eid_from = self.req.execute('INSERT %s T : T name "%s"' % ( etype_from, value_from ))[0][0]
   535         eid_from = self._cw.execute('INSERT %s T : T name "%s"' % ( etype_from, value_from ))[0][0]
   536         # link the new entity to the main entity
   536         # link the new entity to the main entity
   537         rql = 'SET F %(rel)s T WHERE F eid %(eid_to)s, T eid %(eid_from)s' % {'rel' : rel, 'eid_to' : eid_to, 'eid_from' : eid_from}
   537         rql = 'SET F %(rel)s T WHERE F eid %(eid_to)s, T eid %(eid_from)s' % {'rel' : rel, 'eid_to' : eid_to, 'eid_from' : eid_from}
   538         return eid_from
   538         return eid_from
   539 
   539 
   540 
   540 
   542     __regid__ = 'sendmail'
   542     __regid__ = 'sendmail'
   543     __select__ = match_user_groups('managers', 'users')
   543     __select__ = match_user_groups('managers', 'users')
   544 
   544 
   545     def recipients(self):
   545     def recipients(self):
   546         """returns an iterator on email's recipients as entities"""
   546         """returns an iterator on email's recipients as entities"""
   547         eids = self.req.form['recipient']
   547         eids = self._cw.form['recipient']
   548         # make sure we have a list even though only one recipient was specified
   548         # make sure we have a list even though only one recipient was specified
   549         if isinstance(eids, basestring):
   549         if isinstance(eids, basestring):
   550             eids = (eids,)
   550             eids = (eids,)
   551         rql = 'Any X WHERE X eid in (%s)' % (','.join(eids))
   551         rql = 'Any X WHERE X eid in (%s)' % (','.join(eids))
   552         rset = self.req.execute(rql)
   552         rset = self._cw.execute(rql)
   553         for entity in rset.entities():
   553         for entity in rset.entities():
   554             entity.complete() # XXX really?
   554             entity.complete() # XXX really?
   555             yield entity
   555             yield entity
   556 
   556 
   557     @property
   557     @property
   558     @cached
   558     @cached
   559     def smtp(self):
   559     def smtp(self):
   560         mailhost, port = self.config['smtp-host'], self.config['smtp-port']
   560         mailhost, port = self._cw.config['smtp-host'], self._cw.config['smtp-port']
   561         try:
   561         try:
   562             return SMTP(mailhost, port)
   562             return SMTP(mailhost, port)
   563         except Exception, ex:
   563         except Exception, ex:
   564             self.exception("can't connect to smtp server %s:%s (%s)",
   564             self.exception("can't connect to smtp server %s:%s (%s)",
   565                              mailhost, port, ex)
   565                              mailhost, port, ex)
   566             url = self.build_url(__message=self.req._('could not connect to the SMTP server'))
   566             url = self.build_url(__message=self._cw._('could not connect to the SMTP server'))
   567             raise Redirect(url)
   567             raise Redirect(url)
   568 
   568 
   569     def sendmail(self, recipient, subject, body):
   569     def sendmail(self, recipient, subject, body):
   570         helo_addr = '%s <%s>' % (self.config['sender-name'],
   570         helo_addr = '%s <%s>' % (self._cw.config['sender-name'],
   571                                  self.config['sender-addr'])
   571                                  self._cw.config['sender-addr'])
   572         msg = format_mail({'email' : self.req.user.get_email(),
   572         msg = format_mail({'email' : self._cw.user.get_email(),
   573                            'name' : self.req.user.dc_title(),},
   573                            'name' : self._cw.user.dc_title(),},
   574                           [recipient], body, subject)
   574                           [recipient], body, subject)
   575         self.smtp.sendmail(helo_addr, [recipient], msg.as_string())
   575         self.smtp.sendmail(helo_addr, [recipient], msg.as_string())
   576 
   576 
   577     def publish(self, rset=None):
   577     def publish(self, rset=None):
   578         # XXX this allows users with access to an cubicweb instance to use it as
   578         # XXX this allows users with access to an cubicweb instance to use it as
   579         # a mail relay
   579         # a mail relay
   580         body = self.req.form['mailbody']
   580         body = self._cw.form['mailbody']
   581         subject = self.req.form['subject']
   581         subject = self._cw.form['subject']
   582         for recipient in self.recipients():
   582         for recipient in self.recipients():
   583             text = body % recipient.as_email_context()
   583             text = body % recipient.as_email_context()
   584             self.sendmail(recipient.get_email(), subject, text)
   584             self.sendmail(recipient.get_email(), subject, text)
   585         # breadcrumbs = self.req.get_session_data('breadcrumbs', None)
   585         # breadcrumbs = self._cw.get_session_data('breadcrumbs', None)
   586         url = self.build_url(__message=self.req._('emails successfully sent'))
   586         url = self.build_url(__message=self._cw._('emails successfully sent'))
   587         raise Redirect(url)
   587         raise Redirect(url)
   588 
   588 
   589 
   589 
   590 class MailBugReportController(SendMailController):
   590 class MailBugReportController(SendMailController):
   591     __regid__ = 'reportbug'
   591     __regid__ = 'reportbug'
   592     __select__ = yes()
   592     __select__ = yes()
   593 
   593 
   594     def publish(self, rset=None):
   594     def publish(self, rset=None):
   595         body = self.req.form['description']
   595         body = self._cw.form['description']
   596         self.sendmail(self.config['submit-mail'], _('%s error report') % self.config.appid, body)
   596         self.sendmail(self._cw.config['submit-mail'], _('%s error report') % self._cw.config.appid, body)
   597         url = self.build_url(__message=self.req._('bug report sent'))
   597         url = self.build_url(__message=self._cw._('bug report sent'))
   598         raise Redirect(url)
   598         raise Redirect(url)
   599 
   599