# HG changeset patch # User Sylvain Thénault # Date 1273075833 -7200 # Node ID ee87c5352e636f37982d2689cfb592ed07376cfa # Parent b7bf1c6751fd95875e5015f7279e973314a41563# Parent fd6b136a840432765e178fb8501939fa8d621726 backport stable diff -r b7bf1c6751fd -r ee87c5352e63 .hgtags --- a/.hgtags Wed May 05 18:08:34 2010 +0200 +++ b/.hgtags Wed May 05 18:10:33 2010 +0200 @@ -119,6 +119,8 @@ 44c7bf90df71dd562e5a7be5ced3019da603d24f cubicweb-debian-version-3.7.3-1 ec23f3ebcd34a92b9898b312f44d56cca748d0d6 cubicweb-version-3.7.4 fefeda65bb83dcc2d775255fe69fdee0e793d135 cubicweb-debian-version-3.7.4-1 +c476d106705ebdd9205d97e64cafa72707acabe7 cubicweb-version-3.7.5 +2d0982252e8d780ba964f293a0e691d48070db6d cubicweb-debian-version-3.7.5-1 3c703f3245dc7696341ae1d66525554d9fa2d11d cubicweb-version-3.8.0 24cc65ab2eca05729d66cef3de6f69bb7f9dfa35 cubicweb-debian-version-3.8.0-1 1e074c6150fe00844160986852db364cc5992848 cubicweb-version-3.8.1 diff -r b7bf1c6751fd -r ee87c5352e63 __init__.py --- a/__init__.py Wed May 05 18:08:34 2010 +0200 +++ b/__init__.py Wed May 05 18:10:33 2010 +0200 @@ -21,6 +21,13 @@ """ __docformat__ = "restructuredtext en" +# ignore the pygments UserWarnings +import warnings +warnings.filterwarnings('ignore', category=UserWarning, + message='.*was already imported', + module='.*pygments') + + import __builtin__ # '_' is available in builtins to mark internationalized string but should # not be used to do the actual translation diff -r b7bf1c6751fd -r ee87c5352e63 __pkginfo__.py --- a/__pkginfo__.py Wed May 05 18:08:34 2010 +0200 +++ b/__pkginfo__.py Wed May 05 18:10:33 2010 +0200 @@ -78,7 +78,7 @@ join('hooks', 'test', 'data'), join('web', 'test', 'data'), join('devtools', 'test', 'data'), - 'skeleton'] + 'schemas', 'skeleton'] _server_migration_dir = join('misc', 'migration') diff -r b7bf1c6751fd -r ee87c5352e63 cwconfig.py --- a/cwconfig.py Wed May 05 18:08:34 2010 +0200 +++ b/cwconfig.py Wed May 05 18:10:33 2010 +0200 @@ -361,7 +361,7 @@ 'default': False, 'help': "don't display actual email addresses but mangle them if \ this option is set to yes", - 'group': 'email', 'level': 2, + 'group': 'email', 'level': 3, }), ) # static and class methods used to get instance independant resources ## diff -r b7bf1c6751fd -r ee87c5352e63 dbapi.py --- a/dbapi.py Wed May 05 18:08:34 2010 +0200 +++ b/dbapi.py Wed May 05 18:10:33 2010 +0200 @@ -674,10 +674,11 @@ self._repo.rollback(self.sessionid) def cursor(self, req=None): - """Return a new Cursor Object using the connection. If the database - does not provide a direct cursor concept, the module will have to - emulate cursors using other means to the extent needed by this - specification. + """Return a new Cursor Object using the connection. + + On pyro connection, you should get cursor after calling if + load_appobjects method if desired (which you should call if you intend + to use ORM abilities). """ if self._closed is not None: raise ProgrammingError('Can\'t get cursor on closed connection') diff -r b7bf1c6751fd -r ee87c5352e63 debian/changelog --- a/debian/changelog Wed May 05 18:08:34 2010 +0200 +++ b/debian/changelog Wed May 05 18:10:33 2010 +0200 @@ -10,6 +10,12 @@ -- Sylvain Thénault Tue, 20 Apr 2010 16:31:44 +0200 +cubicweb (3.7.5-1) unstable; urgency=low + + * new upstream release on the 3.7 branch + + -- Alexandre Fayolle Thu, 29 Apr 2010 13:51:52 +0200 + cubicweb (3.7.4-1) unstable; urgency=low * new upstream release diff -r b7bf1c6751fd -r ee87c5352e63 devtools/devctl.py --- a/devtools/devctl.py Wed May 05 18:08:34 2010 +0200 +++ b/devtools/devctl.py Wed May 05 18:10:33 2010 +0200 @@ -176,9 +176,10 @@ 'inlined:%s.%s.%s' % (etype, rschema, role)) add_msg(w, str(tschema), 'inlined:%s.%s.%s' % (etype, rschema, role)) - if appearsin_addmenu.etype_get(eschema, rschema, role, tschema) and \ - (libconfig is None or not - libappearsin_addmenu.etype_get(eschema, rschema, role, tschema)): + if appearsin_addmenu.etype_get(eschema, rschema, role, tschema): + if libconfig is not None and libappearsin_addmenu.etype_get(eschema, rschema, role, tschema): + if eschema in libschema and tschema in libschema: + continue if role == 'subject': label = 'add %s %s %s %s' % (eschema, rschema, tschema, role) diff -r b7bf1c6751fd -r ee87c5352e63 doc/book/en/devweb/controllers.rst --- a/doc/book/en/devweb/controllers.rst Wed May 05 18:08:34 2010 +0200 +++ b/doc/book/en/devweb/controllers.rst Wed May 05 18:10:33 2010 +0200 @@ -36,16 +36,16 @@ operations in response to a form being submitted; it works in close association with the Forms, to which it delegates some of the work -* the Form validator controller provides form validation from Ajax +* the ``Form validator controller`` provides form validation from Ajax context, using the Edit controller, to implement the classic form - handling loop (user edits, hits 'submit/apply', validation occurs + handling loop (user edits, hits `submit/apply`, validation occurs server-side by way of the Form validator controller, and the UI is decorated with failure information, either global or per-field , until it is valid) `Other`: -* the SendMail controller (web/views/basecontrollers.py) is reponsible +* the ``SendMail controller`` (web/views/basecontrollers.py) is reponsible for outgoing email notifications * the MailBugReport controller (web/views/basecontrollers.py) allows @@ -57,14 +57,16 @@ All controllers (should) live in the 'controllers' namespace within the global registry. -API -+++ +Concrete controllers +++++++++++++++++++++ Most API details should be resolved by source code inspection, as the -various controllers have differing goals. +various controllers have differing goals. See for instance the +:ref:`edit_controller` chapter. -`web/controller.py` contains the top-level abstract Controller class and -its (NotImplemented) entry point `publish(rset=None)` method. +:mod:`cubicweb.web.controller` contains the top-level abstract +Controller class and its unimplemented entry point +`publish(rset=None)` method. A handful of helpers are also provided there: @@ -77,125 +79,3 @@ implementations dealing with HTTP (thus, for instance, not the SendMail controller) may very well call this in their publication process. - - -.. _edit_controller: - -The `edit controller` -+++++++++++++++++++++ - -It can be found in (:mod:`cubicweb.web.views.editcontroller`). - -Editing control -~~~~~~~~~~~~~~~~ - -.. XXX this look obsolete - -The parameters related to entities to edit are specified as follows :: - - : - -where entity eid could be a letter in case of an entity to create. We -name those parameters as *qualified*. - -1. Retrieval of entities to edit by looking for the forms parameters - starting by `eid:` and also having a parameter `__type` associated - (also *qualified* by eid) - -2. For all the attributes and the relations of an entity to edit: - - 1. search for a parameter `edits-` or `edito-` - qualified in the case of a relation where the entity is object - 2. if found, the value returned is considered as the initial value - for this relaiton and we then look for the new value(s) in the parameter - (qualified) - 3. if the value returned is different from the initial value, an database update - request is done - -3. For each entity to edit: - - 1. if a qualified parameter `__linkto` is specified, its value has to be - a string (or a list of string) such as: :: - - :: - - where is either `subject` or `object` and each eid could be - separated from the others by a `_`. Target specifies if the *edited entity* - is subject or object of the relation and each relation specified will - be inserted. - - 2. if a qualified parameter `__clone_eid` is specified for an entity, the - relations of the specified entity passed as value of this parameter are - copied on the edited entity. - - 3. if a qualified parameter `__delete` is specified, its value must be - a string or a list of string such as follows: :: - - :: - - where each eid subject or object can be seperated from the other - by `_`. Each relation specified will be deleted. - - 4. if a qualified parameter `__insert` is specified, its value should - follow the same pattern as `__delete`, but each relation specified is - inserted. - -4. If the parameters `__insert` and/or `__delete` are found not qualified, - they are interpreted as explained above (independantly from the number - of entities edited). - -5. If no entity is edited but the form contains the parameters `__linkto` - and `eid`, this one is interpreted by using the value specified for `eid` - to designate the entity on which to add the relations. - - -.. note:: - - * If the parameter `__action_delete` is found, all the entities specified - as to be edited will be deleted. - - * If the parameter `__action_cancel` is found, no action is completed. - - * If the parameter `__action_apply` is found, the editing is - applied normally but the redirection is done on the form (see - :ref:`RedirectionControl`). - - * The parameter `__method` is also supported as for the main template - - * If no entity is found to be edited and if there is no parameter - `__action_delete`, `__action_cancel`, `__linkto`, `__delete` or - `__insert`, an error is raised. - - * Using the parameter `__message` in the form will allow to use its value - as a message to provide the user once the editing is completed. - - -.. _RedirectionControl: - -Redirection control -~~~~~~~~~~~~~~~~~~~ -Once editing is completed, there is still an issue left: where should we go -now? If nothing is specified, the controller will do his job but it does not -mean we will be happy with the result. We can control that by using the -following parameters: - -* `__redirectpath`: path of the URL (relative to the root URL of the site, - no form parameters - -* `__redirectparams`: forms parameters to add to the path - -* `__redirectrql`: redirection RQL request - -* `__redirectvid`: redirection view identifier - -* `__errorurl`: initial form URL, used for redirecting in case a validation - error is raised during editing. If this one is not specified, an error page - is displayed instead of going back to the form (which is, if necessary, - responsible for displaying the errors) - -* `__form_id`: initial view form identifier, used if `__action_apply` is - found - -In general we use either `__redirectpath` and `__redirectparams` or -`__redirectrql` and `__redirectvid`. - diff -r b7bf1c6751fd -r ee87c5352e63 doc/book/en/devweb/edition/dissection.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/edition/dissection.rst Wed May 05 18:10:33 2010 +0200 @@ -0,0 +1,276 @@ + +Dissection of a Form +-------------------- + +This is done (again) with a vanilla instance of the `tracker`_ +cube. We will populate the database with a bunch of entities and see +what kind of job the automatic entity form does. + +.. _`tracker`: http://www.cubicweb.org/project/cubicweb-tracker + +Patching the session object +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to play interactively with web side application objects, we +have to cheat a bit: we will decorate the session object with some +missing artifacts that should belong to a web request object. With +that we can instantiate and render forms interactively. + +The function below does the minimum to allow going through this +exercice. Some attributes or methods may be missing for other +purposes. It is nevertheless not complicated to enhance it if need +arises. + +.. sourcecode:: python + + def monkey_patch_session(session): + """ useful to use the cw shell session object + with web appobjects, which expect more than a plain + data repository session + """ + # for autoform selection + session.json_request = False + session.url = lambda: u'http://perdu.com' + session.session = session + session.form = {} + session.list_form_param = lambda *args: [] + # for render + session.use_fckeditor = lambda: False + session._ressources = [] + session.add_js = session.add_css = lambda *args: session._ressources.append(args) + session.external_resource = lambda x:{} + session._tabcount = 0 + def next_tabindex(): + session._tabcount += 1 + return session._tabcount + session.next_tabindex = next_tabindex + return session + +Populating the database +~~~~~~~~~~~~~~~~~~~~~~~ + +We should start by setting up a bit of context: a project with two +unpublished versions, and a ticket linked to the project and the first +version. + +.. sourcecode:: python + + >>> p = rql('INSERT Project P: P name "cubicweb"') + >>> for num in ('0.1.0', '0.2.0'): + ... rql('INSERT Version V: V num "%s", V version_of P WHERE P eid %%(p)s' % num, {'p': p[0][0]}) + ... + + + >>> t = rql('INSERT Ticket T: T title "let us write more doc", T done_in V, ' + 'T concerns P WHERE V num "0.1.0"', P eid %(p)s', {'p': p[0][0]}) + >>> commit() + +Now let's see what the edition form builds for us. + +.. sourcecode:: python + + >>> monkey_patch_session(session) + >>> form = session.vreg['forms'].select('edition', session, rset=rql('Ticket T')) + >>> html = form.render() + +This creates an automatic entity form. The ``.render()`` call yields +an html (unicode) string. The html output is shown below (with +internal fieldset omitted). + +Looking at the html output +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The form enveloppe +'''''''''''''''''' + +.. sourcecode:: html + +
main informations
+
+
+
validating...
+
+ + + + + + + + ... +
+
+
+ +The main fieldset encloses a set of hidden fields containing various +metadata, that will be used by the `edit controller` to process it +back correctly. + +The `freezeFormButtons(...)` javascript callback defined on the +``conlick`` event of the form element prevents accidental multiple +clicks in a row. + +The ``action`` of the form is mapped to the `validateform` controller +(situated in :mod:`cubicweb.web.views.basecontrollers`). + +The attributes section +'''''''''''''''''''''' + +We can have a look at some of the inner nodes of the form. Some fields +are omitted as they are redundant for our purposes. + +.. sourcecode:: html + +
+ + + + + + ... (description field omitted) ... + + + + + ... (type field omitted) ... + + + + + + + + +
+ +
+ +
importance
+
+ +
+ +
version in which this ticket will be / has been done
+
+
+ + +Note that the whole form layout has been computed by the form +renderer. It is the renderer which produces the table +structure. Otherwise, the fields html structure is emitted by their +associated widget. + +While it is called the `attributes` section of the form, it actually +contains attributes and *mandatory relations*. For each field, we +observe: + +* a dedicated row with a specific class, such as ``title_subject_row`` + (responsability of the form renderer) + +* an html widget (input, select, ...) with: + + * an id built from the ``rtype-role:eid`` pattern + + * a name built from the same pattern + + * possible values or preselected options + +The relations section +''''''''''''''''''''' + +.. sourcecode:: html + +
+ This ticket : + + + + +
+ + + + + + +
  
+ +
+
+
+ +The optional relations are grouped into a drop-down combo +box. Selection of an item triggers a javascript function which will: + +* show already related entities in the div of id `relatedentities` + using a two-colown layout, with an action to allow deletion of + individual relations (there are none in this example) + +* provide a relation selector in the div of id `relationSelector_EID` + to allow the user to set up relations and trigger dynamic action on + the last div + +* fill the div of id `unrelatedDivs_EID` with a dynamically computed + selection widget allowing direct selection of an unrelated (but + relatable) entity or a switch towards the `search mode` of + |cubicweb| which allows full browsing and selection of an entity + using a dedicated action situated in the left column boxes. + + +The buttons zone +'''''''''''''''' + +Finally comes the buttons zone. + +.. sourcecode:: html + + + + + + + + +
+ + + + +
+ +The most notable artifacts here are the ``postForm(...)`` calls +defined on click events on these buttons. This function basically +submits the form. XXX validateform vs validateForm, ajax or not ? diff -r b7bf1c6751fd -r ee87c5352e63 doc/book/en/devweb/edition/editcontroller.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/edition/editcontroller.rst Wed May 05 18:10:33 2010 +0200 @@ -0,0 +1,117 @@ +.. _edit_controller: + +The `edit controller` ++++++++++++++++++++++ + +It can be found in (:mod:`cubicweb.web.views.editcontroller`). + +Editing control +~~~~~~~~~~~~~~~~ + +.. XXX this look obsolete + +The parameters related to entities to edit are specified as follows :: + + : + +where entity eid could be a letter in case of an entity to create. We +name those parameters as *qualified*. + +1. Retrieval of entities to edit by looking for the forms parameters + starting by `eid:` and also having a parameter `__type` associated + (also *qualified* by eid) + +2. For all the attributes and the relations of an entity to edit: + + 1. search for a parameter `edits-` or `edito-` + qualified in the case of a relation where the entity is object + 2. if found, the value returned is considered as the initial value + for this relaiton and we then look for the new value(s) in the parameter + (qualified) + 3. if the value returned is different from the initial value, an database update + request is done + +3. For each entity to edit: + + 1. if a qualified parameter `__linkto` is specified, its value has to be + a string (or a list of string) such as: :: + + :: + + where is either `subject` or `object` and each eid could be + separated from the others by a `_`. Target specifies if the *edited entity* + is subject or object of the relation and each relation specified will + be inserted. + + 2. if a qualified parameter `__clone_eid` is specified for an entity, the + relations of the specified entity passed as value of this parameter are + copied on the edited entity. + + 3. if a qualified parameter `__delete` is specified, its value must be + a string or a list of string such as follows: :: + + :: + + where each eid subject or object can be seperated from the other + by `_`. Each relation specified will be deleted. + + 4. if a qualified parameter `__insert` is specified, its value should + follow the same pattern as `__delete`, but each relation specified is + inserted. + +4. If the parameters `__insert` and/or `__delete` are found not qualified, + they are interpreted as explained above (independantly from the number + of entities edited). + +5. If no entity is edited but the form contains the parameters `__linkto` + and `eid`, this one is interpreted by using the value specified for `eid` + to designate the entity on which to add the relations. + + +.. note:: + + * if the parameter `__action_delete` is found, all the entities specified + as to be edited will be deleted. + + * if the parameter `__action_cancel` is found, no action is completed. + + * if the parameter `__action_apply` is found, the editing is + applied normally but the redirection is done on the form (see + :ref:`RedirectionControl`). + + * if no entity is found to be edited and if there is no parameter + `__action_delete`, `__action_cancel`, `__linkto`, `__delete` or + `__insert`, an error is raised. + + * using the parameter `__message` in the form will allow to use its value + as a message to provide the user once the editing is completed. + + +.. _RedirectionControl: + +Redirection control +~~~~~~~~~~~~~~~~~~~ +Once editing is completed, there is still an issue left: where should we go +now? If nothing is specified, the controller will do his job but it does not +mean we will be happy with the result. We can control that by using the +following parameters: + +* `__redirectpath`: path of the URL (relative to the root URL of the site, + no form parameters + +* `__redirectparams`: forms parameters to add to the path + +* `__redirectrql`: redirection RQL request + +* `__redirectvid`: redirection view identifier + +* `__errorurl`: initial form URL, used for redirecting in case a validation + error is raised during editing. If this one is not specified, an error page + is displayed instead of going back to the form (which is, if necessary, + responsible for displaying the errors) + +* `__form_id`: initial view form identifier, used if `__action_apply` is + found + +In general we use either `__redirectpath` and `__redirectparams` or +`__redirectrql` and `__redirectvid`. diff -r b7bf1c6751fd -r ee87c5352e63 doc/book/en/devweb/edition/examples.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/edition/examples.rst Wed May 05 18:10:33 2010 +0200 @@ -0,0 +1,189 @@ +Examples +-------- + +Example of ad-hoc fields form +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We want to define a form doing something else than editing an entity. The idea is +to propose a form to send an email to entities in a resultset which implements +:class:`IEmailable`. Let's take a simplified version of what you'll find in +:mod:`cubicweb.web.views.massmailing`. + +Here is the source code: + +.. sourcecode:: python + + def sender_value(form): + return '%s <%s>' % (form._cw.user.dc_title(), form._cw.user.get_email()) + + def recipient_choices(form, field): + return [(e.get_email(), e.eid) + for e in form.cw_rset.entities() + if e.get_email()] + + def recipient_value(form): + return [e.eid for e in form.cw_rset.entities() + if e.get_email()] + + class MassMailingForm(forms.FieldsForm): + __regid__ = 'massmailing' + + needs_js = ('cubicweb.widgets.js',) + domid = 'sendmail' + action = 'sendmail' + + sender = ff.StringField(widget=TextInput({'disabled': 'disabled'}), + label=_('From:'), + value=sender_value) + + recipient = ff.StringField(widget=CheckBox(), + label=_('Recipients:'), + choices=recipient_choices, + value=recipients_value) + + subject = ff.StringField(label=_('Subject:'), max_length=256) + + mailbody = ff.StringField(widget=AjaxWidget(wdgtype='TemplateTextField', + inputid='mailbody')) + + form_buttons = [ImgButton('sendbutton', "javascript: $('#sendmail').submit()", + _('send email'), 'SEND_EMAIL_ICON'), + ImgButton('cancelbutton', "javascript: history.back()", + stdmsgs.BUTTON_CANCEL, 'CANCEL_EMAIL_ICON')] + +Let's detail what's going on up there. Our form will hold four fields: + +* a sender field, which is disabled and will simply contains the user's name and + email + +* a recipients field, which will be displayed as a list of users in the context + result set with checkboxes so user can still choose who will receive his mailing + by checking or not the checkboxes. By default all of them will be checked since + field's value return a list containing same eids as those returned by the + vocabulary function. + +* a subject field, limited to 256 characters (hence we know a + :class:`~cubicweb.web.formwidgets.TextInput` will be used, as explained in + :class:`~cubicweb.web.formfields.StringField`) + +* a mailbody field. This field use an ajax widget, defined in `cubicweb.widgets.js`, + and whose definition won't be shown here. Notice though that we tell this form + need this javascript file by using `needs_js` + +Last but not least, we add two buttons control: one to post the form using +javascript (`$('#sendmail')` being the jQuery call to get the element with DOM id +set to 'sendmail', which is our form DOM id as specified by its `domid` +attribute), another to cancel the form which will go back to the previous page +using another javascript call. Also we specify an image to use as button icon as a +resource identifier (see :ref:`external_resources`) given as last argument to +:class:`cubicweb.web.formwidgets.ImgButton`. + +To see this form, we still have to wrap it in a view. This is pretty simple: + +.. sourcecode:: python + + class MassMailingFormView(form.FormViewMixIn, EntityView): + __regid__ = 'massmailing' + __select__ = implements(IEmailable) & authenticated_user() + + def call(self): + form = self._cw.vreg['forms'].select('massmailing', self._cw, + rset=self.cw_rset) + self.w(form.render()) + +As you see, we simply define a view with proper selector so it only apply to a +result set containing :class:`IEmailable` entities, and so that only users in the +managers or users group can use it. Then in the `call()` method for this view we +simply select the above form and write what its `.render()` method returns. + +When this form is submitted, a controller with id 'sendmail' will be called (as +specified using `action`). This controller will be responsible to actually send +the mail to specified recipients. + +Here is what it looks like: + +.. sourcecode:: python + + class SendMailController(Controller): + __regid__ = 'sendmail' + __select__ = (authenticated_user() & + match_form_params('recipient', 'mailbody', 'subject')) + + def publish(self, rset=None): + body = self._cw.form['mailbody'] + subject = self._cw.form['subject'] + eids = self._cw.form['recipient'] + # eids may be a string if only one recipient was specified + if isinstance(eids, basestring): + rset = self._cw.execute('Any X WHERE X eid %(x)s', {'x': eids}) + else: + rset = self._cw.execute('Any X WHERE X eid in (%s)' % (','.join(eids))) + recipients = list(rset.entities()) + msg = format_mail({'email' : self._cw.user.get_email(), + 'name' : self._cw.user.dc_title()}, + recipients, body, subject) + if not self._cw.vreg.config.sendmails([(msg, recipients]): + msg = self._cw._('could not connect to the SMTP server') + else: + msg = self._cw._('emails successfully sent') + raise Redirect(self._cw.build_url(__message=msg)) + + +The entry point of a controller is the publish method. In that case we simply get +back post values in request's `form` attribute, get user instances according +to eids found in the 'recipient' form value, and send email after calling +:func:`format_mail` to get a proper email message. If we can't send email or +if we successfully sent email, we redirect to the index page with proper message +to inform the user. + +Also notice that our controller has a selector that deny access to it +to anonymous users (we don't want our instance to be used as a spam +relay), but also checks if the expected parameters are specified in +forms. That avoids later defensive programming (though it's not enough +to handle all possible error cases). + +To conclude our example, suppose we wish a different form layout and that existent +renderers are not satisfying (we would check that first of course :). We would then +have to define our own renderer: + +.. sourcecode:: python + + class MassMailingFormRenderer(formrenderers.FormRenderer): + __regid__ = 'massmailing' + + def _render_fields(self, fields, w, form): + w(u'') + for field in fields: + if field.name == 'mailbody': + w(u'
') + w(u'
') + w(u'
    ') + for button in form.form_buttons: + w(u'
  • %s
  • ' % button.render(form)) + w(u'
') + w(u'
') + w(u'
') + w(field.render(form, self)) + w(u'
') + else: + w(u'') + w(u'%s' % + self.render_label(form, field)) + w(u'') + w(field.render(form, self)) + w(u'') + + def render_buttons(self, w, form): + pass + +We simply override the `_render_fields` and `render_buttons` method of the base form renderer +to arrange fields as we desire it: here we'll have first a two columns table with label and +value of the sender, recipients and subject field (form order respected), then form controls, +then a div containing the textarea for the email's content. + +To bind this renderer to our form, we should add to our form definition above: + +.. sourcecode:: python + + form_renderer_id = 'massmailing' + diff -r b7bf1c6751fd -r ee87c5352e63 doc/book/en/devweb/edition/form.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/edition/form.rst Wed May 05 18:10:33 2010 +0200 @@ -0,0 +1,223 @@ +HTML form construction +---------------------- + +CubicWeb provides the somewhat usual form / field / widget / renderer abstraction +to provide generic building blocks which will greatly help you in building forms +properly integrated with CubicWeb (coherent display, error handling, etc...), +while keeping things as flexible as possible. + +A ``form`` basically only holds a set of ``fields``, and has te be bound to a +``renderer`` which is responsible to layout them. Each field is bound to a +``widget`` that will be used to fill in value(s) for that field (at form +generation time) and 'decode' (fetch and give a proper Python type to) values +sent back by the browser. + +The ``field`` should be used according to the type of what you want to edit. +E.g. if you want to edit some date, you'll have to use the +:class:`cubicweb.web.formfields.DateField`. Then you can choose among multiple +widgets to edit it, for instance :class:`cubicweb.web.formwidgets.TextInput` (a +bare text field), :class:`~cubicweb.web.formwidgets.DateTimePicker` (a simple +calendar) or even :class:`~cubicweb.web.formwidgets.JQueryDatePicker` (the JQuery +calendar). You can of course also write your own widget. + +Exploring the available forms +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A small excursion into a |cubicweb| shell is the quickest way to +discover available forms (or application objects in general). + +.. sourcecode:: python + + >>> from pprint import pprint + >>> pprint( session.vreg['forms'] ) + {'base': [, + ], + 'changestate': [, + ], + 'composite': [, + ], + 'deleteconf': [], + 'edition': [, + , + ], + 'logform': [], + 'massmailing': [], + 'muledit': [], + 'sparql': []} + + +The two most important form families here (for all pracitcal purposes) +are `base` and `edition`. Most of the time one wants alterations of +the AutomaticEntityForm (from the `edition` category). + +The Automatic Entity Form +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: cubicweb.web.views.autoform + +Anatomy of a choices function +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Let's have a look at the `ticket_done_in_choices` function given to +the `choices` parameter of the relation tag that is applied to the +('Ticket', 'done_in', '*') relation definition, as it is both typical +and sophisticated enough. This is a code snippet from the `tracker`_ +cube. + +.. _`tracker`: http://www.cubicweb.org/project/cubicweb-tracker + +The ``Ticket`` entity type can be related to a ``Project`` and a +``Version``, respectively through the ``concerns`` and ``done_in`` +relations. When a user is about to edit a ticket, we want to fill the +combo box for the ``done_in`` relation with values pertinent with +respect to the context. The important context here is: + +* creation or modification (we cannot fetch values the same way in + either case) + +* ``__linkto`` url parameter given in a creation context + +.. sourcecode:: python + + from cubicweb.web import formfields + + def ticket_done_in_choices(form, field): + entity = form.edited_entity + # first see if its specified by __linkto form parameters + linkedto = formfields.relvoc_linkedto(entity, 'done_in', 'subject') + if linkedto: + return linkedto + # it isn't, get initial values + vocab = formfields.relvoc_init(entity, 'done_in', 'subject') + veid = None + # try to fetch the (already or pending) related version and project + if not entity.has_eid(): + peids = entity.linked_to('concerns', 'subject') + peid = peids and peids[0] + else: + peid = entity.project.eid + veid = entity.done_in and entity.done_in[0].eid + if peid: + # we can complete the vocabulary with relevant values + rschema = form._cw.vreg.schema['done_in'].rdef('Ticket', 'Version') + rset = form._cw.execute( + 'Any V, VN ORDERBY version_sort_value(VN) ' + 'WHERE V version_of P, P eid %(p)s, V num VN, ' + 'V in_state ST, NOT ST name "published"', {'p': peid}, 'p') + vocab += [(v.view('combobox'), v.eid) for v in rset.entities() + if rschema.has_perm(form._cw, 'add', toeid=v.eid) + and v.eid != veid] + return vocab + +The first thing we have to do is fetch potential values from the +``__linkto`` url parameter that is often found in entity creation +contexts (the creation action provides such a parameter with a +predetermined value; for instance in this case, ticket creation could +occur in the context of a `Version` entity). The +:mod:`cubicweb.web.formfields` module provides a ``relvoc_linkedto`` +utility function that gets a list suitably filled with vocabulary +values. + +.. sourcecode:: python + + linkedto = formfields.relvoc_linkedto(entity, 'done_in', 'subject') + if linkedto: + return linkedto + +Then, if no ``__linkto`` argument was given, we must prepare the +vocabulary with an initial empty value (because `done_in` is not +mandatory, we must allow the user to not select a verson) and already +linked values. This is done with the ``relvoc_init`` function. + +.. sourcecode:: python + + vocab = formfields.relvoc_init(entity, 'done_in', 'subject') + +But then, we have to give more: if the ticket is related to a project, +we should provide all the non published versions of this project +(`Version` and `Project` can be related through the `version_of` +relation). Conversely, if we do not know yet the project, it would not +make sense to propose all existing versions as it could potentially +lead to incoherences. Even if these will be caught by some +RQLConstraint, it is wise not to tempt the user with error-inducing +candidate values. + +The "ticket is related to a project" part must be decomposed as: + +* this is a new ticket which is created is the context of a project + +* this is an already existing ticket, linked to a project (through the + `concerns` relation) + +* there is no related project (quite unlikely given the cardinality of + the `concerns` relation, so it can only mean that we are creating a + new ticket, and a project is about to be selected but there is no + ``__linkto`` argument) + +.. note:: + + the last situation could happen in several ways, but of course in a + polished application, the paths to ticket creation should be + controlled so as to avoid a suboptimal end-user experience + +Hence, we try to fetch the related project. + +.. sourcecode:: python + + veid = None + if not entity.has_eid(): + peids = entity.linked_to('concerns', 'subject') + peid = peids and peids[0] + else: + peid = entity.project.eid + veid = entity.done_in and entity.done_in[0].eid + +We distinguish between entity creation and entity modification using +the ``Entity.has_eid()`` method, which returns `False` on creation. At +creation time the only way to get a project is through the +``__linkto`` parameter. Notice that we fetch the version in which the +ticket is `done_in` if any, for later. + +.. note:: + + the implementation above assumes that if there is a ``__linkto`` + parameter, it is only about a project. While it makes sense most of + the time, it is not an absolute. Depending on how an entity creation + action action url is built, several outcomes could be possible + there + +If the ticket is already linked to a project, fetching it is +trivial. Then we add the relevant version to the initial vocabulary. + +.. sourcecode:: python + + if peid: + rschema = form._cw.vreg.schema['done_in'].rdef('Ticket', 'Version') + rset = form._cw.execute( + 'Any V, VN ORDERBY version_sort_value(VN) ' + 'WHERE V version_of P, P eid %(p)s, V num VN, ' + 'V in_state ST, NOT ST name "published"', {'p': peid}) + vocab += [(v.view('combobox'), v.eid) for v in rset.entities() + if rschema.has_perm(form._cw, 'add', toeid=v.eid) + and v.eid != veid] + +.. warning:: + + we have to defend ourselves against lack of a project eid. Given + the cardinality of the `concerns` relation, there *must* be a + project, but this rule can only be enforced at validation time, + which will happen of course only after form subsmission + +Here, given a project eid, we complete the vocabulary with all +unpublished versions defined in the project (sorted by number) for +which the current user is allowed to establish the relation. + +APIs +~~~~ + +.. automodule:: cubicweb.web.formfields +.. automodule:: cubicweb.web.formwidgets +.. automodule:: cubicweb.web.views.forms +.. automodule:: cubicweb.web.views.formrenderers + + diff -r b7bf1c6751fd -r ee87c5352e63 doc/book/en/devweb/edition/index.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/edition/index.rst Wed May 05 18:10:33 2010 +0200 @@ -0,0 +1,15 @@ +Edition control +=============== + +This chapter covers the editing capabilities of |cubicweb|. It +explains html Form construction, the Edit Controller and their +interactions. + + +.. toctree:: + :maxdepth: 2 + + form + dissection + editcontroller + examples diff -r b7bf1c6751fd -r ee87c5352e63 doc/book/en/devweb/form.rst --- a/doc/book/en/devweb/form.rst Wed May 05 18:08:34 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,216 +0,0 @@ -HTML form construction ----------------------- - -CubicWeb provides the somewhat usual form / field / widget / renderer abstraction -to provide generic building blocks which will greatly help you in building forms -properly integrated with CubicWeb (coherent display, error handling, etc...), -while keeping things as flexible as possible. - -A **form** basically only holds a set of **fields**, and has te be bound to a -**renderer** which is responsible to layout them. Each field is bound to a -**widget** that will be used to fill in value(s) for that field (at form -generation time) and 'decode' (fetch and give a proper Python type to) values -sent back by the browser. - -The **field** should be used according to the type of what you want to edit. -E.g. if you want to edit some date, you'll have to use the -:class:`cubicweb.web.formfields.DateField`. Then you can choose among multiple -widgets to edit it, for instance :class:`cubicweb.web.formwidgets.TextInput` (a -bare text field), :class:`~cubicweb.web.formwidgets.DateTimePicker` (a simple -calendar) or even :class:`~cubicweb.web.formwidgets.JQueryDatePicker` (the JQuery -calendar). You can of course also write your own widget. - - -.. automodule:: cubicweb.web.views.autoform - - -Example of bare fields form -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -We want to define a form doing something else than editing an entity. The idea is -to propose a form to send an email to entities in a resultset which implements -:class:`IEmailable`. Let's take a simplified version of what you'll find in -:mod:`cubicweb.web.views.massmailing`. - -Here is the source code: - -.. sourcecode:: python - - def sender_value(form): - return '%s <%s>' % (form._cw.user.dc_title(), form._cw.user.get_email()) - - def recipient_choices(form, field): - return [(e.get_email(), e.eid) for e in form.cw_rset.entities() - if e.get_email()] - - def recipient_value(form): - return [e.eid for e in form.cw_rset.entities() if e.get_email()] - - class MassMailingForm(forms.FieldsForm): - __regid__ = 'massmailing' - - needs_js = ('cubicweb.widgets.js',) - domid = 'sendmail' - action = 'sendmail' - - sender = ff.StringField(widget=TextInput({'disabled': 'disabled'}), - label=_('From:'), - value=sender_value) - - recipient = ff.StringField(widget=CheckBox(), - label=_('Recipients:'), - choices=recipient_choices, - value=recipients_value) - - subject = ff.StringField(label=_('Subject:'), max_length=256) - - mailbody = ff.StringField(widget=AjaxWidget(wdgtype='TemplateTextField', - inputid='mailbody')) - - form_buttons = [ImgButton('sendbutton', "javascript: $('#sendmail').submit()", - _('send email'), 'SEND_EMAIL_ICON'), - ImgButton('cancelbutton', "javascript: history.back()", - stdmsgs.BUTTON_CANCEL, 'CANCEL_EMAIL_ICON')] - -Let's detail what's going on up there. Our form will hold four fields: - -* a sender field, which is disabled and will simply contains the user's name and - email - -* a recipients field, which will be displayed as a list of users in the context - result set with checkboxes so user can still choose who will receive his mailing - by checking or not the checkboxes. By default all of them will be checked since - field's value return a list containing same eids as those returned by the - vocabulary function. - -* a subject field, limited to 256 characters (hence we know a - :class:`~cubicweb.web.formwidgets.TextInput` will be used, as explained in - :class:`~cubicweb.web.formfields.StringField`) - -* a mailbody field. This field use an ajax widget, defined in `cubicweb.widgets.js`, - and whose definition won't be shown here. Notice though that we tell this form - need this javascript file by using `needs_js` - -Last but not least, we add two buttons control: one to post the form using -javascript (`$('#sendmail')` being the jQuery call to get the element with DOM id -set to 'sendmail', which is our form DOM id as specified by its `domid` -attribute), another to cancel the form which will go back to the previous page -using another javascript call. Also we specify an image to use as button icon as a -resource identifier (see :ref:`external_resources`) given as last argument to -:class:`cubicweb.web.formwidgets.ImgButton`. - -To see this form, we still have to wrap it in a view. This is pretty simple: - -.. sourcecode:: python - - class MassMailingFormView(form.FormViewMixIn, EntityView): - __regid__ = 'massmailing' - __select__ = implements(IEmailable) & authenticated_user() - - def call(self): - form = self._cw.vreg['forms'].select('massmailing', self._cw, - rset=self.cw_rset) - self.w(form.render()) - -As you see, we simply define a view with proper selector so it only apply to a -result set containing :class:`IEmailable` entities, and so that only users in the -managers or users group can use it. Then in the `call()` method for this view we -simply select the above form and write what its `.render()` method returns. - -When this form is submitted, a controller with id 'sendmail' will be called (as -specified using `action`). This controller will be responsible to actually send -the mail to specified recipients. - -Here is what it looks like: - -.. sourcecode:: python - - class SendMailController(Controller): - __regid__ = 'sendmail' - __select__ = authenticated_user() & match_form_params('recipient', 'mailbody', 'subject') - - def publish(self, rset=None): - body = self._cw.form['mailbody'] - subject = self._cw.form['subject'] - eids = self._cw.form['recipient'] - # eids may be a string if only one recipient was specified - if isinstance(eids, basestring): - rset = self._cw.execute('Any X WHERE X eid %(x)s', {'x': eids}) - else: - rset = self._cw.execute('Any X WHERE X eid in (%s)' % (','.join(eids))) - recipients = list(rset.entities()) - msg = format_mail({'email' : self._cw.user.get_email(), - 'name' : self._cw.user.dc_title()}, - recipients, body, subject) - if not self._cw.vreg.config.sendmails([(msg, recipients]): - msg = self._cw._('could not connect to the SMTP server') - else: - msg = self._cw._('emails successfully sent') - raise Redirect(self._cw.build_url(__message=msg)) - - -The entry point of a controller is the publish method. In that case we simply get -back post values in request's `form` attribute, get user instances according -to eids found in the 'recipient' form value, and send email after calling -:func:`format_mail` to get a proper email message. If we can't send email or -if we successfully sent email, we redirect to the index page with proper message -to inform the user. - -Also notice that our controller has a selector that deny access to it to -anonymous users (we don't want our instance to be used as a spam relay), but also -check expected parameters are specified in forms. That avoids later defensive -programming (though it's not enough to handle all possible error cases). - -To conclude our example, suppose we wish a different form layout and that existent -renderers are not satisfying (we would check that first of course :). We would then -have to define our own renderer: - -.. sourcecode:: python - - class MassMailingFormRenderer(formrenderers.FormRenderer): - __regid__ = 'massmailing' - - def _render_fields(self, fields, w, form): - w(u'') - for field in fields: - if field.name == 'mailbody': - w(u'
') - w(u'
') - w(u'
    ') - for button in form.form_buttons: - w(u'
  • %s
  • ' % button.render(form)) - w(u'
') - w(u'
') - w(u'
') - w(field.render(form, self)) - w(u'
') - else: - w(u'') - w(u'%s' % self.render_label(form, field)) - w(u'') - w(field.render(form, self)) - w(u'') - - def render_buttons(self, w, form): - pass - -We simply override the `_render_fields` and `render_buttons` method of the base form renderer -to arrange fields as we desire it: here we'll have first a two columns table with label and -value of the sender, recipients and subject field (form order respected), then form controls, -then a div containing the textarea for the email's content. - -To bind this renderer to our form, we should add to our form definition above: - -.. sourcecode:: python - - form_renderer_id = 'massmailing' - -API -~~~ - -.. automodule:: cubicweb.web.formfields -.. automodule:: cubicweb.web.formwidgets -.. automodule:: cubicweb.web.views.forms -.. automodule:: cubicweb.web.views.formrenderers - -.. Example of entity fields form diff -r b7bf1c6751fd -r ee87c5352e63 doc/book/en/devweb/index.rst --- a/doc/book/en/devweb/index.rst Wed May 05 18:08:34 2010 +0200 +++ b/doc/book/en/devweb/index.rst Wed May 05 18:10:33 2010 +0200 @@ -14,7 +14,7 @@ rtags js css - form + edition/index facets internationalization .. property diff -r b7bf1c6751fd -r ee87c5352e63 doc/book/en/devweb/request.rst --- a/doc/book/en/devweb/request.rst Wed May 05 18:08:34 2010 +0200 +++ b/doc/book/en/devweb/request.rst Wed May 05 18:10:33 2010 +0200 @@ -4,20 +4,28 @@ Overview ```````` -A request instance is created when an HTTP request is sent to the web server. -It contains informations such as form parameters, user authenticated, etc. +A request instance is created when an HTTP request is sent to the web +server. It contains informations such as form parameters, +authenticated user, etc. It is a very prevalent object and is used +throughout all of the framework and applications. -**Globally, a request represents a user query, either through HTTP or not -(we also talk about RQL queries on the server side for example).** +**A request represents a user query, either through HTTP or not (we +also talk about RQL queries on the server side for example).** + +Here is a non-exhaustive list of attributes and methods available on +request objects (grouped by category): -An instance of `Request` has the following attributes: +* `Browser control`: -* `user`, instance of `cubicweb.common.utils.User` corresponding to the authenticated - user -* `form`, dictionary containing the values of a web form -* `encoding`, character encoding to use in the response + * `ie_browser`: tells if the browser belong to the Internet Explorer + family + * `xhtml_browser`: tells if the browser is able to properly handle + XHTML (at the HTTP content_type level) -But also: +* `User and identification`: + + * `user`, instance of `cubicweb.common.utils.User` corresponding to + the authenticated user * `Session data handling` @@ -27,6 +35,36 @@ * `set_session_data(key, value)`, assign a value to a key * `del_session_data(key)`, suppress the value associated to a key +* `Edition` (utilities for edition control): + + * `cancel_edition`: resets error url and cleans up pending operations + * `create_entity`: utility to create an entity (from an etype, + attributes and relation values) + * `datadir_url`: returns the url to the merged external resources + (|cubicweb|'s `web/data` directory plus all `data` directories of + used cubes) + * `edited_eids`: returns the list of eids of entities that are + edited under the current http request + * `eid_rset(eid)`: utility which returns a result set from an eid + * `entity_from_eid(eid)`: returns an entity instance from the given eid + * `encoding`: returns the encoding of the current HTTP request + * `ensure_ro_rql(rql)`: ensure some rql query is a data request + * etype_rset + * `form`, dictionary containing the values of a web form + * `encoding`, character encoding to use in the response + * `next_tabindex()`: returns a monotonically growing integer used to + build the html tab index of forms + +* `HTTP` + + * `authmode`: returns a string describing the authentication mode + (http, cookie, ...) + * `lang`: returns the user agents/browser's language as carried by + the http request + * `demote_to_html()`: in the context of an XHTML compliant browser, + this will force emission of the response as an HTML document + (using the http content negociation) + * `Cookies handling` * `get_cookie()`, returns a dictionary containing the value of the header @@ -39,10 +77,28 @@ * `URL handling` + * `build_url(__vid, *args, **kwargs)`: return an absolute URL using + params dictionary key/values as URL parameters. Values are + automatically URL quoted, and the publishing method to use may be + specified or will be guessed. + * `build_url_params(**kwargs)`: returns a properly prepared (quoted, + separators, ...) string from the given parameters * `url()`, returns the full URL of the HTTP request * `base_url()`, returns the root URL of the web application * `relative_path()`, returns the relative path of the request +* `Web resource (.css, .js files, etc.) handling`: + + * `add_css(cssfiles)`: adds the given list of css resources to the current + html headers + * `add_js(jsfiles)`: adds the given list of javascript resources to the + current html headers + * `add_onload(jscode)`: inject the given jscode fragment (an unicode + string) into the current html headers, wrapped inside a + document.ready(...) or another ajax-friendly one-time trigger event + * `add_header(header, values)`: adds the header/value pair to the + current html headers + * `And more...` * `set_content_type(content_type, filename=None)`, adds the header HTTP diff -r b7bf1c6751fd -r ee87c5352e63 doc/book/en/devweb/views/editforms.rst --- a/doc/book/en/devweb/views/editforms.rst Wed May 05 18:08:34 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -Standard forms --------------- - - (:mod:`cubicweb.web.views.editforms`) - -XXX feed me diff -r b7bf1c6751fd -r ee87c5352e63 doc/book/en/tutorials/advanced/index.rst --- a/doc/book/en/tutorials/advanced/index.rst Wed May 05 18:08:34 2010 +0200 +++ b/doc/book/en/tutorials/advanced/index.rst Wed May 05 18:10:33 2010 +0200 @@ -508,9 +508,11 @@ It's not complete, but show most things you'll want to do in tests: adding some content, creating users and connecting as them in the test, etc... -To run it type: :: +To run it type: - [syt@scorpius test]$ pytest unittest_sytweb.py +.. sourcecode:: bash + + $ pytest unittest_sytweb.py ======================== unittest_sytweb.py ======================== -> creating tables [....................] -> inserting default user and default groups. @@ -524,9 +526,11 @@ The first execution is taking time, since it creates a sqlite database for the -test instance. The second one will be much quicker: :: +test instance. The second one will be much quicker: - [syt@scorpius test]$ pytest unittest_sytweb.py +.. sourcecode:: bash + + $ pytest unittest_sytweb.py ======================== unittest_sytweb.py ======================== . ---------------------------------------------------------------------- @@ -537,12 +541,11 @@ If you do some changes in your schema, you'll have to force regeneration of that database. You do that by removing the tmpdb files before running the test: :: - [syt@scorpius test]$ rm tmpdb* + $ rm tmpdb* .. Note:: - pytest is a very convenient utilities to control test execution, from the `logilab-common`_ - package + pytest is a very convenient utility used to control test execution. It is available from the `logilab-common`_ package. .. _`logilab-common`: http://www.logilab.org/project/logilab-common @@ -578,7 +581,7 @@ To migrate my instance I simply type:: - [syt@scorpius ~]$ cubicweb-ctl upgrade sytweb + cubicweb-ctl upgrade sytweb I'll then be asked some questions to do the migration step by step. You should say YES when it asks if a backup of your database should be done, so you can get back diff -r b7bf1c6751fd -r ee87c5352e63 doc/book/en/tutorials/base/blog-in-five-minutes.rst --- a/doc/book/en/tutorials/base/blog-in-five-minutes.rst Wed May 05 18:08:34 2010 +0200 +++ b/doc/book/en/tutorials/base/blog-in-five-minutes.rst Wed May 05 18:10:33 2010 +0200 @@ -29,9 +29,9 @@ Instance parameters ~~~~~~~~~~~~~~~~~~~ -If the database installation failed, you'd like to change some instance parameters, for example, the database host or the user name. These informations can be edited in the `source` file located in the /etc/cubicweb.d/myblog directory. +If you would like to change some instance parameters, for example, the database host or the user name, edit the `source` file located in the /etc/cubicweb.d/myblog directory. -Then relaunch the database creation: +Then relaunch the database creation:: cubicweb-ctl db-create myblog diff -r b7bf1c6751fd -r ee87c5352e63 doc/book/en/tutorials/base/conclusion.rst --- a/doc/book/en/tutorials/base/conclusion.rst Wed May 05 18:08:34 2010 +0200 +++ b/doc/book/en/tutorials/base/conclusion.rst Wed May 05 18:10:33 2010 +0200 @@ -3,13 +3,11 @@ What's next? ------------ -We demonstrated how from a straight out of the box *CubicWeb* installation, you -can build your web application based on a data model. It's all already there: -views, templates, permissions, etc. The step forward is now for you to customize -according to your needs. +In this chapter, we have seen have you can, right after the installation of *CubicWeb*, build a web application in five minutes by defining a data model. Everything is there already: views, templates, permissions, etc. -Many features are available to extend your application, for example: RSS channel -integration (:ref:`XmlAndRss`), hooks (:ref:`hooks`), support of sources such as -Google App Engine (:ref:`GoogleAppEngineSource`) and lots of others to discover -through our book. +The next step is to change the design and learn about the many features available to customize and extend your application: RSS channels (:ref:`XmlAndRss`), events (:ref:`hooks`), support of sources such as +Google App Engine (:ref:`GoogleAppEngineSource`), etc. +You will find more `tutorials and howtos`_ in the blog published on the CubicWeb.org website. + +.. _`tutorials and howtos`: http://www.cubicweb.org/view?rql=Any+X+ORDERBY+D+DESC+WHERE+X+is+BlogEntry%2C+T+tags+X%2C+T+name+IN+%28%22tutorial%22%2C+%22howto%22%29%2C+X+creation_date+D diff -r b7bf1c6751fd -r ee87c5352e63 doc/book/en/tutorials/base/create-cube.rst --- a/doc/book/en/tutorials/base/create-cube.rst Wed May 05 18:08:34 2010 +0200 +++ b/doc/book/en/tutorials/base/create-cube.rst Wed May 05 18:10:33 2010 +0200 @@ -30,7 +30,7 @@ Customize the views of your data: how and which part of your data are showed. -Note: views don't concern the look'n'feel or design of the site. For that, you should use CSS instead, and default CSS or your new cube are located in 'blog/data/'. +.. note:: views do not define the look'n'feel and the design of your application. For that, you will use CSS and the files located 'blog/data/'. 5. :ref:`DefineEntities` @@ -396,6 +396,7 @@ want to add a ``category`` attribute in the ``Blog`` data type. This is called a migration. The required steps are: + 1. modify the file ``schema.py``. The ``Blog`` class looks now like this: .. sourcecode:: python @@ -405,7 +406,11 @@ description = String() category = String(required=True, vocabulary=(_('Professional'), _('Personal')), default='Personal') -2. stop your ``blogdemo`` instance +2. stop your ``blogdemo`` instance: + +.. sourcecode:: bash + + cubicweb-ctl stop blogdemo 3. start the cubicweb shell for your instance by running the following command: @@ -413,15 +418,21 @@ cubicweb-ctl shell blogdemo -4. in the shell, execute: +4. at the cubicweb shell prompt, execute: .. sourcecode:: python add_attribute('Blog', 'category') -5. you can restart your instance, modify a blog entity and check that the new attribute +5. restart your instance: + +.. sourcecode:: bash + + cubicweb-ctl start blogdemo + +6. modify a blog entity and check that the new attribute ``category`` has been added. -Of course, you may also want to add relations, entity types, ... See :ref:`migration` +Of course, you may also want to add relations, entity types, etc. See :ref:`migration` for a list of all available migration commands. diff -r b7bf1c6751fd -r ee87c5352e63 entities/test/unittest_wfobjs.py --- a/entities/test/unittest_wfobjs.py Wed May 05 18:08:34 2010 +0200 +++ b/entities/test/unittest_wfobjs.py Wed May 05 18:10:33 2010 +0200 @@ -56,7 +56,7 @@ self.commit() wf.add_state(u'foo') ex = self.assertRaises(ValidationError, self.commit) - self.assertEquals(ex.errors, {'name-subject': 'workflow already have a state of that name'}) + self.assertEquals(ex.errors, {'state_of-subject': 'workflow already have a state of that name'}) # no pb if not in the same workflow wf2 = add_wf(self, 'Company') foo = wf2.add_state(u'foo', initial=True) @@ -432,7 +432,7 @@ self.execute('SET X custom_workflow WF WHERE X eid %(x)s, WF eid %(wf)s', {'wf': wf.eid, 'x': self.member.eid}) ex = self.assertRaises(ValidationError, self.commit) - self.assertEquals(ex.errors, {'custom_workflow-subject': 'workflow isn\'t a workflow for this type'}) + self.assertEquals(ex.errors, {'custom_workflow-subject': u"workflow isn't a workflow for this type"}) def test_del_custom_wf(self): """member in some state shared by the new workflow, nothing has to be diff -r b7bf1c6751fd -r ee87c5352e63 etwist/twconfig.py --- a/etwist/twconfig.py Wed May 05 18:08:34 2010 +0200 +++ b/etwist/twconfig.py Wed May 05 18:10:33 2010 +0200 @@ -80,7 +80,7 @@ {'type' : 'string', 'default': None, 'help': 'profile code and use the specified file to store stats if this option is set', - 'group': 'main', 'level': 2, + 'group': 'main', 'level': 3, }), ('pyro-server', {'type' : 'yn', diff -r b7bf1c6751fd -r ee87c5352e63 hooks/integrity.py --- a/hooks/integrity.py Wed May 05 18:08:34 2010 +0200 +++ b/hooks/integrity.py Wed May 05 18:10:33 2010 +0200 @@ -160,28 +160,30 @@ class _CheckConstraintsOp(hook.LateOperation): - """check a new relation satisfy its constraints - """ + """ check a new relation satisfy its constraints """ + def precommit_event(self): - eidfrom, rtype, eidto = self.rdef - # first check related entities have not been deleted in the same - # transaction - if self.session.deleted_in_transaction(eidfrom): - return - if self.session.deleted_in_transaction(eidto): - return - for constraint in self.constraints: - # XXX - # * lock RQLConstraint as well? - # * use a constraint id to use per constraint lock and avoid - # unnecessary commit serialization ? - if isinstance(constraint, RQLUniqueConstraint): - _acquire_unique_cstr_lock(self.session) - try: - constraint.repo_check(self.session, eidfrom, rtype, eidto) - except NotImplementedError: - self.critical('can\'t check constraint %s, not supported', - constraint) + session = self.session + for values in session.transaction_data.pop('check_constraints_op'): + eidfrom, rtype, eidto, constraints = values + # first check related entities have not been deleted in the same + # transaction + if session.deleted_in_transaction(eidfrom): + return + if session.deleted_in_transaction(eidto): + return + for constraint in constraints: + # XXX + # * lock RQLConstraint as well? + # * use a constraint id to use per constraint lock and avoid + # unnecessary commit serialization ? + if isinstance(constraint, RQLUniqueConstraint): + _acquire_unique_cstr_lock(session) + try: + constraint.repo_check(session, eidfrom, rtype, eidto) + except NotImplementedError: + self.critical('can\'t check constraint %s, not supported', + constraint) def commit_event(self): pass @@ -201,8 +203,9 @@ constraints = self._cw.schema_rproperty(self.rtype, self.eidfrom, self.eidto, 'constraints') if constraints: - _CheckConstraintsOp(self._cw, constraints=constraints, - rdef=(self.eidfrom, self.rtype, self.eidto)) + hook.set_operation(self._cw, 'check_constraints_op', + (self.eidfrom, self.rtype, self.eidto, tuple(constraints)), + _CheckConstraintsOp) class CheckAttributeConstraintHook(IntegrityHook): @@ -221,8 +224,9 @@ constraints = [c for c in eschema.rdef(attr).constraints if isinstance(c, (RQLUniqueConstraint, RQLConstraint))] if constraints: - _CheckConstraintsOp(self._cw, constraints=constraints, - rdef=(self.entity.eid, attr, None)) + hook.set_operation(self._cw, 'check_constraints_op', + (self.entity.eid, attr, None, tuple(constraints)), + _CheckConstraintsOp) class CheckUniqueHook(IntegrityHook): diff -r b7bf1c6751fd -r ee87c5352e63 hooks/metadata.py --- a/hooks/metadata.py Wed May 05 18:08:34 2010 +0200 +++ b/hooks/metadata.py Wed May 05 18:10:33 2010 +0200 @@ -69,11 +69,13 @@ def precommit_event(self): session = self.session - if session.deleted_in_transaction(self.entity.eid): - # entity have been created and deleted in the same transaction - return - if not self.entity.created_by: - session.add_relation(self.entity.eid, 'created_by', session.user.eid) + for eid in session.transaction_data.pop('set_creator_op'): + if session.deleted_in_transaction(eid): + # entity have been created and deleted in the same transaction + continue + entity = session.entity_from_eid(eid) + if not entity.created_by: + session.add_relation(eid, 'created_by', session.user.eid) class SetIsHook(MetaDataHook): @@ -108,14 +110,14 @@ def __call__(self): if not self._cw.is_internal_session: self._cw.add_relation(self.entity.eid, 'owned_by', self._cw.user.eid) - _SetCreatorOp(self._cw, entity=self.entity) - + hook.set_operation(self._cw, 'set_creator_op', self.entity.eid, _SetCreatorOp) class _SyncOwnersOp(hook.Operation): def precommit_event(self): - self.session.execute('SET X owned_by U WHERE C owned_by U, C eid %(c)s,' - 'NOT EXISTS(X owned_by U, X eid %(x)s)', - {'c': self.compositeeid, 'x': self.composedeid}) + for compositeeid, composedeid in self.session.transaction_data.pop('sync_owners_op'): + self.session.execute('SET X owned_by U WHERE C owned_by U, C eid %(c)s,' + 'NOT EXISTS(X owned_by U, X eid %(x)s)', + {'c': compositeeid, 'x': composedeid}) class SyncCompositeOwner(MetaDataHook): @@ -132,9 +134,9 @@ eidfrom, eidto = self.eidfrom, self.eidto composite = self._cw.schema_rproperty(self.rtype, eidfrom, eidto, 'composite') if composite == 'subject': - _SyncOwnersOp(self._cw, compositeeid=eidfrom, composedeid=eidto) + hook.set_operation(self._cw, 'sync_owners_op', (eidfrom, eidto), _SyncOwnersOp) elif composite == 'object': - _SyncOwnersOp(self._cw, compositeeid=eidto, composedeid=eidfrom) + hook.set_operation(self._cw, 'sync_owners_op', (eidto, eidfrom), _SyncOwnersOp) class FixUserOwnershipHook(MetaDataHook): diff -r b7bf1c6751fd -r ee87c5352e63 hooks/security.py --- a/hooks/security.py Wed May 05 18:08:34 2010 +0200 +++ b/hooks/security.py Wed May 05 18:10:33 2010 +0200 @@ -53,8 +53,12 @@ class _CheckEntityPermissionOp(hook.LateOperation): def precommit_event(self): #print 'CheckEntityPermissionOp', self.session.user, self.entity, self.action - self.entity.check_perm(self.action) - check_entity_attributes(self.session, self.entity, self.editedattrs) + session = self.session + for values in session.transaction_data.pop('check_entity_perm_op'): + entity = session.entity_from_eid(values[0]) + action = values[1] + entity.check_perm(action) + check_entity_attributes(session, entity, values[2:]) def commit_event(self): pass @@ -89,9 +93,9 @@ events = ('after_add_entity',) def __call__(self): - _CheckEntityPermissionOp(self._cw, entity=self.entity, - editedattrs=tuple(self.entity.edited_attributes), - action='add') + hook.set_operation(self._cw, 'check_entity_perm_op', + (self.entity.eid, 'add') + tuple(self.entity.edited_attributes), + _CheckEntityPermissionOp) class AfterUpdateEntitySecurityHook(SecurityHook): @@ -108,9 +112,9 @@ # save back editedattrs in case the entity is reedited later in the # same transaction, which will lead to edited_attributes being # overwritten - _CheckEntityPermissionOp(self._cw, entity=self.entity, - editedattrs=tuple(self.entity.edited_attributes), - action='update') + hook.set_operation(self._cw, 'check_entity_perm_op', + (self.entity.eid, 'update') + tuple(self.entity.edited_attributes), + _CheckEntityPermissionOp) class BeforeDelEntitySecurityHook(SecurityHook): diff -r b7bf1c6751fd -r ee87c5352e63 req.py --- a/req.py Wed May 05 18:08:34 2010 +0200 +++ b/req.py Wed May 05 18:10:33 2010 +0200 @@ -106,10 +106,7 @@ return rset def empty_rset(self): - """return a result set for the given eid without doing actual query - (we have the eid, we can suppose it exists and user has access to the - entity) - """ + """ return a guaranteed empty result """ rset = ResultSet([], 'Any X WHERE X eid -1') rset.req = self return rset diff -r b7bf1c6751fd -r ee87c5352e63 server/serverconfig.py --- a/server/serverconfig.py Wed May 05 18:08:34 2010 +0200 +++ b/server/serverconfig.py Wed May 05 18:10:33 2010 +0200 @@ -125,20 +125,20 @@ {'type' : 'time', 'default': '30min', 'help': 'session expiration time, default to 30 minutes', - 'group': 'main', 'level': 1, + 'group': 'main', 'level': 3, }), ('connections-pool-size', {'type' : 'int', 'default': 4, 'help': 'size of the connections pools. Each source supporting multiple \ connections will have this number of opened connections.', - 'group': 'main', 'level': 1, + 'group': 'main', 'level': 3, }), ('rql-cache-size', {'type' : 'int', 'default': 300, 'help': 'size of the parsed rql cache size.', - 'group': 'main', 'level': 1, + 'group': 'main', 'level': 3, }), ('undo-support', {'type' : 'string', 'default': '', @@ -146,20 +146,20 @@ [C]reate [U]pdate [D]elete entities / [A]dd [R]emove relation. Leave it empty \ for no undo support, set it to CUDAR for full undo support, or to DR for \ support undoing of deletion only.', - 'group': 'main', 'level': 1, + 'group': 'main', 'level': 3, }), ('keep-transaction-lifetime', {'type' : 'int', 'default': 7, 'help': 'number of days during which transaction records should be \ kept (hence undoable).', - 'group': 'main', 'level': 1, + 'group': 'main', 'level': 3, }), ('multi-sources-etypes', {'type' : 'csv', 'default': (), 'help': 'defines which entity types from this repository are used \ by some other instances. You should set this properly so those instances to \ detect updates / deletions.', - 'group': 'main', 'level': 1, + 'group': 'main', 'level': 3, }), ('delay-full-text-indexation', @@ -168,7 +168,7 @@ ' to be done when entity are added/modified by users, activate this ' 'option and setup a job using cubicweb-ctl db-rebuild-fti on your ' 'system (using cron for instance).', - 'group': 'main', 'level': 1, + 'group': 'main', 'level': 3, }), # email configuration @@ -181,7 +181,7 @@ modes are "default-dest-addrs" (emails specified in the configuration \ variable with the same name), "users" (every users which has activated \ account with an email set), "none" (no notification).', - 'group': 'email', 'level': 1, + 'group': 'email', 'level': 2, }), ('default-dest-addrs', {'type' : 'csv', @@ -189,7 +189,7 @@ 'help': 'comma separated list of email addresses that will be used \ as default recipient when an email is sent and the notification has no \ specific recipient rules.', - 'group': 'email', 'level': 1, + 'group': 'email', 'level': 2, }), ('supervising-addrs', {'type' : 'csv', @@ -205,7 +205,7 @@ 'help': 'Pyro server host, if not detectable correctly through \ gethostname(). It may contains port information using : notation, \ and if not set, it will be choosen randomly', - 'group': 'pyro', 'level': 2, + 'group': 'pyro', 'level': 3, }), ) + CubicWebConfiguration.options) diff -r b7bf1c6751fd -r ee87c5352e63 server/sources/ldapuser.py --- a/server/sources/ldapuser.py Wed May 05 18:08:34 2010 +0200 +++ b/server/sources/ldapuser.py Wed May 05 18:10:33 2010 +0200 @@ -44,6 +44,7 @@ from ldap.filter import filter_format, escape_filter_chars from ldapurl import LDAPUrl +from logilab.common.configuration import time_validator from cubicweb import AuthenticationError, UnknownEid, RepositoryError from cubicweb.server.utils import cartesian_product from cubicweb.server.sources import (AbstractSource, TrFunc, GlobTrFunc, @@ -85,13 +86,13 @@ 'default': 'simple', 'choices': ('simple', 'cram_md5', 'digest_md5', 'gssapi'), 'help': 'authentication mode used to authenticate user to the ldap.', - 'group': 'ldap-source', 'level': 1, + 'group': 'ldap-source', 'level': 3, }), ('auth-realm', {'type' : 'string', 'default': None, 'help': 'realm to use when using gssapi/kerberos authentication.', - 'group': 'ldap-source', 'level': 1, + 'group': 'ldap-source', 'level': 3, }), ('data-cnx-dn', @@ -152,13 +153,13 @@ 'default': '1d', 'help': 'interval between synchronization with the ldap \ directory (default to once a day).', - 'group': 'ldap-source', 'level': 2, + 'group': 'ldap-source', 'level': 3, }), ('cache-life-time', {'type' : 'time', 'default': '2h', 'help': 'life time of query cache in minutes (default to two hours).', - 'group': 'ldap-source', 'level': 2, + 'group': 'ldap-source', 'level': 3, }), ) @@ -186,9 +187,11 @@ for o in self.user_classes] self._conn = None self._cache = {} - ttlm = int(source_config.get('cache-life-type', 2*60)) + ttlm = time_validator(None, None, + source_config.get('cache-life-time', 2*60)) self._query_cache = TimedCache(ttlm) - self._interval = int(source_config.get('synchronization-interval', + self._interval = time_validator(None, None, + source_config.get('synchronization-interval', 24*60*60)) def reset_caches(self): diff -r b7bf1c6751fd -r ee87c5352e63 server/test/unittest_repository.py --- a/server/test/unittest_repository.py Wed May 05 18:08:34 2010 +0200 +++ b/server/test/unittest_repository.py Wed May 05 18:10:33 2010 +0200 @@ -295,11 +295,19 @@ cnx = connect(self.repo.config.appid, u'admin', password='gingkow', initlog=False) # don't reset logging configuration try: + cnx.load_appobjects(subpath=('entities',)) # check we can get the schema schema = cnx.get_schema() + self.failUnless(cnx.vreg) + self.failUnless('etypes'in cnx.vreg) self.assertEquals(schema.__hashmode__, None) cu = cnx.cursor() rset = cu.execute('Any U,G WHERE U in_group G') + user = iter(rset.entities()).next() + self.failUnless(user._cw) + self.failUnless(user._cw.vreg) + from cubicweb.entities import authobjs + self.assertIsInstance(user._cw.user, authobjs.CWUser) cnx.close() done.append(True) finally: diff -r b7bf1c6751fd -r ee87c5352e63 web/data/cubicweb.schema.css --- a/web/data/cubicweb.schema.css Wed May 05 18:08:34 2010 +0200 +++ b/web/data/cubicweb.schema.css Wed May 05 18:10:33 2010 +0200 @@ -15,21 +15,22 @@ } -div.relationDefinition { - float: left; +div.relationDefinition { + float: left; position: relative; width: 60%; padding: 0; } -div.acl{ +div.acl{ position: relative; /* right: 20%;*/ width: 25%; padding:0px 0px 0px 2em; } -div.acl table tr,td{ +div.acl table td, +div.acl table tr { padding: 2px 2px 2px 2px; } @@ -37,8 +38,8 @@ width : 100%; } -div.entityAttributes{ - margin: 3em 0 5em; +div.entityAttributes{ + margin: 3em 0 5em; font: normal 9pt Arial; } @@ -58,12 +59,12 @@ div.body{ padding : 0.2em; - padding-bottom : 0.4em; + padding-bottom : 0.4em; overflow: auto; } div.body table td{ - padding:0.4em; + padding:0.4em; } div.box{ @@ -73,27 +74,27 @@ div.vl{ float:left; - position:relative; - margin-top:1em; - border-top:1px solid black; - line-height : 1px; - width: 1em; + position:relative; + margin-top:1em; + border-top:1px solid black; + line-height : 1px; + width: 1em; height : 0px} div.hl{ float:left; - position:relative; - margin-top:1em; - border-left:1px solid black; - width: 1px; + position:relative; + margin-top:1em; + border-left:1px solid black; + width: 1px; height : 10px } div.rels{ - float:left; - position:relative; - margin-top:1em; - border-left:1px solid black; + float:left; + position:relative; + margin-top:1em; + border-left:1px solid black; margin-left:-2px;} div.firstrel, div.rel, div.lastrel{ @@ -104,7 +105,7 @@ } /* FIXME set to 9em or an image*/ -div.rel, div.lastrel{ +div.rel, div.lastrel{ margin-top:0.7em} div.vars{ @@ -113,24 +114,24 @@ div.firstvar, div.var, div.lastvar{ line-height:1em; - border:1px solid black; + border:1px solid black; padding:0.2em} div.firstvar{ margin-top:1em;} div.var{ - margin-top:0.5em; + margin-top:0.5em; } div.lastvar{ border:none; } -div.firstvar a, +div.firstvar a, div.var a, div.rel a, -div.firstrel a{ +div.firstrel a{ padding:0px ! important; - margin : 0px ! important; + margin : 0px ! important; } diff -r b7bf1c6751fd -r ee87c5352e63 web/schemaviewer.py --- a/web/schemaviewer.py Wed May 05 18:08:34 2010 +0200 +++ b/web/schemaviewer.py Wed May 05 18:10:33 2010 +0200 @@ -48,13 +48,13 @@ # no self.req managements - def may_read(self, rdef, action): + def may_read(self, rdef, action='read'): """Return true if request user may read the given schema. Always return True when no request is provided. """ if self.req is None: return True - return sch.may_have_permission('read', self.req) + return rdef.may_have_permission(action, self.req) def format_eschema(self, eschema): text = eschema.type diff -r b7bf1c6751fd -r ee87c5352e63 web/views/autoform.py --- a/web/views/autoform.py Wed May 05 18:08:34 2010 +0200 +++ b/web/views/autoform.py Wed May 05 18:10:33 2010 +0200 @@ -16,16 +16,13 @@ # You should have received a copy of the GNU Lesser General Public License along # with CubicWeb. If not, see . """ -The automatic entity form -------------------------- - .. autodocstring:: cubicweb.web.views.autoform::AutomaticEntityForm Configuration through uicfg ``````````````````````````` It is possible to manage which and how an entity's attributes and relations -will be edited in the various context where the automatic entity form is used +will be edited in the various contexts where the automatic entity form is used by using proper uicfg tags. The details of the uicfg syntax can be found in the :ref:`uicfg` chapter. @@ -53,7 +50,7 @@ section may be one of: -* 'hidden', don't display (not even in an hidden input, right?) +* 'hidden', don't display (not even in a hidden input) * 'attributes', display in the attributes section @@ -104,7 +101,11 @@ autoform_field_kwargs.tag_attribute(('RQLExpression', 'expression'), {'widget': fw.TextInput}) +.. note:: + the widget argument can be either a class or an instance (the later + case being convenient to pass the Widget specific initialisation + options) Overriding permissions ^^^^^^^^^^^^^^^^^^^^^^ @@ -621,13 +622,13 @@ # The automatic entity form #################################################### class AutomaticEntityForm(forms.EntityFieldsForm): - """AutomaticEntityForm is an automagic form to edit any entity. It is - designed to be fully generated from schema but highly configurable through - :ref:`uicfg`. + """AutomaticEntityForm is an automagic form to edit any entity. It + is designed to be fully generated from schema but highly + configurable through uicfg. Of course, as for other forms, you can also customise it by specifying various standard form parameters on selection, overriding, or - adding/removing fields in a selected instances. + adding/removing fields in selected instances. """ __regid__ = 'edition' diff -r b7bf1c6751fd -r ee87c5352e63 web/views/forms.py --- a/web/views/forms.py Wed May 05 18:08:34 2010 +0200 +++ b/web/views/forms.py Wed May 05 18:10:33 2010 +0200 @@ -25,8 +25,8 @@ using a form renderer. No display is actually done here, though you'll find some attributes of form that are used to control the rendering process. -Besides the automagic form we'll see later, they are barely two form -classes in |cubicweb|: +Besides the automagic form we'll see later, there are roughly two main +form classes in |cubicweb|: .. autoclass:: cubicweb.web.views.forms.FieldsForm .. autoclass:: cubicweb.web.views.forms.EntityFieldsForm diff -r b7bf1c6751fd -r ee87c5352e63 web/views/sparql.py --- a/web/views/sparql.py Wed May 05 18:08:34 2010 +0200 +++ b/web/views/sparql.py Wed May 05 18:10:33 2010 +0200 @@ -64,11 +64,13 @@ self.w(self._cw._('we are not yet ready to handle this query')) except xy.UnsupportedVocabulary, ex: self.w(self._cw._('unknown vocabulary:') + u' ' + unicode('ex')) - if vid == 'sparqlxml': - url = self._cw.build_url('view', rql=qinfo.finalize(), vid=vid) - raise Redirect(url) - rset = self._cw.execute(qinfo.finalize()) - self.wview(vid, rset, 'null') + else: + if vid == 'sparqlxml': + url = self._cw.build_url('view', rql=qinfo.finalize(), vid=vid) + raise Redirect(url) + print qinfo.finalize() + rset = self._cw.execute(*qinfo.finalize()) + self.wview(vid, rset, 'null') ## sparql resultset views ##################################################### diff -r b7bf1c6751fd -r ee87c5352e63 web/webconfig.py --- a/web/webconfig.py Wed May 05 18:08:34 2010 +0200 +++ b/web/webconfig.py Wed May 05 18:10:33 2010 +0200 @@ -98,7 +98,7 @@ {'type' : 'string', 'default': None, 'help': 'web instance query log file', - 'group': 'main', 'level': 2, + 'group': 'main', 'level': 3, }), # web configuration ('https-url', @@ -112,20 +112,20 @@ 'differentiate between http vs https access. For instance: \n'\ 'RewriteRule ^/demo/(.*) http://127.0.0.1:8080/https/$1 [L,P]\n'\ 'where the cubicweb web server is listening on port 8080.', - 'group': 'main', 'level': 2, + 'group': 'main', 'level': 3, }), ('auth-mode', {'type' : 'choice', 'choices' : ('cookie', 'http'), 'default': 'cookie', 'help': 'authentication mode (cookie / http)', - 'group': 'web', 'level': 1, + 'group': 'web', 'level': 3, }), ('realm', {'type' : 'string', 'default': 'cubicweb', 'help': 'realm to use on HTTP authentication mode', - 'group': 'web', 'level': 2, + 'group': 'web', 'level': 3, }), ('http-session-time', {'type' : 'time', @@ -144,7 +144,7 @@ 'So even if http-session-time is 0 and the user don\'t close his ' 'browser, he will have to reauthenticate after this time of ' 'inactivity. Default to 24h.', - 'group': 'web', 'level': 2, + 'group': 'web', 'level': 3, }), ('cleanup-anonymous-session-time', {'type' : 'time', @@ -152,14 +152,14 @@ 'help': 'Same as cleanup-session-time but specific to anonymous ' 'sessions. You can have a much smaller timeout here since it will be ' 'transparent to the user. Default to 5min.', - 'group': 'web', 'level': 2, + 'group': 'web', 'level': 3, }), ('force-html-content-type', {'type' : 'yn', 'default': False, 'help': 'force text/html content type for your html pages instead of cubicweb user-agent based'\ 'deduction of an appropriate content type', - 'group': 'web', 'level': 2, + 'group': 'web', 'level': 3, }), ('embed-allowed', {'type' : 'regexp', @@ -167,7 +167,7 @@ 'help': 'regular expression matching URLs that may be embeded. \ leave it blank if you don\'t want the embedding feature, or set it to ".*" \ if you want to allow everything', - 'group': 'web', 'level': 1, + 'group': 'web', 'level': 3, }), ('submit-mail', {'type' : 'string', @@ -197,14 +197,14 @@ 'default': join(CubicWebConfiguration.shared_dir(), 'data', 'porkys.ttf'), 'help': 'True type font to use for captcha image generation (you \ must have the python imaging library installed to use captcha)', - 'group': 'web', 'level': 2, + 'group': 'web', 'level': 3, }), ('captcha-font-size', {'type' : 'int', 'default': 25, 'help': 'Font size to use for captcha image generation (you must \ have the python imaging library installed to use captcha)', - 'group': 'web', 'level': 2, + 'group': 'web', 'level': 3, }), )) diff -r b7bf1c6751fd -r ee87c5352e63 xy.py --- a/xy.py Wed May 05 18:08:34 2010 +0200 +++ b/xy.py Wed May 05 18:10:33 2010 +0200 @@ -22,8 +22,11 @@ from yams import xy xy.register_prefix('http://purl.org/dc/elements/1.1/', 'dc') -xy.register_prefix('http://xmlns.com/foaf/0.1/', 'foaf') -xy.register_prefix('http://usefulinc.com/ns/doap#', 'doap') +xy.register_prefix('http://xmlns.com/foaf/0.1/', 'foaf') +xy.register_prefix('http://usefulinc.com/ns/doap#', 'doap') +xy.register_prefix('http://rdfs.org/sioc/ns#', 'sioc') +xy.register_prefix('http://www.w3.org/2002/07/owl#', 'owl') +xy.register_prefix('http://purl.org/dc/terms/', 'dcterms') xy.add_equivalence('creation_date', 'dc:date') xy.add_equivalence('created_by', 'dc:creator')