# HG changeset patch # User Aurelien Campeas # Date 1272036706 -7200 # Node ID 105011657405e63ae33c7cc9a0565a5a4458e0f3 # Parent 875bdc0fe8ce89f6355216a9f3872cd29066cc6e [doc/book] move devweb up from development, turn development into devrepo (much better structure) diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/cubes/available-cubes.rst --- a/doc/book/en/development/cubes/available-cubes.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ - -Available cubes ---------------- - -An instance is based on several basic cubes. In the set of available -basic cubes we can find for example : - -Base entity types -~~~~~~~~~~~~~~~~~ -* addressbook_: PhoneNumber and PostalAddress -* card_: Card, generic documenting card -* event_: Event (define events, display them in calendars) -* file_: File (to allow users to upload and store binary or text files) -* link_: Link (to collect links to web resources) -* mailinglist_: MailingList (to reference a mailing-list and the URLs - for its archives and its admin interface) -* person_: Person (easily mixed with addressbook) -* task_: Task (something to be done between start and stop date) -* zone_: Zone (to define places within larger places, for example a - city in a state in a country) - - -Classification -~~~~~~~~~~~~~~ -* folder_: Folder (to organize things but grouping them in folders) -* keyword_: Keyword (to define classification schemes) -* tag_: Tag (to tag anything) - -Other features -~~~~~~~~~~~~~~ -* basket_: Basket (like a shopping cart) -* blog_: a blogging system uxing Blog and BlogEntry entity types -* comment_: system to attach comment threads to entities) -* email_: archiving management for emails (`Email`, `Emailpart`, - `Emailthread`), trigger action in cubicweb through email - - - - - -.. _addressbook: http://www.cubicweb.org/project/cubicweb-addressbook -.. _basket: http://www.cubicweb.org/project/cubicweb-basket -.. _card: http://www.cubicweb.org/project/cubicweb-card -.. _blog: http://www.cubicweb.org/project/cubicweb-blog -.. _comment: http://www.cubicweb.org/project/cubicweb-comment -.. _email: http://www.cubicweb.org/project/cubicweb-email -.. _event: http://www.cubicweb.org/project/cubicweb-event -.. _file: http://www.cubicweb.org/project/cubicweb-file -.. _folder: http://www.cubicweb.org/project/cubicweb-folder -.. _keyword: http://www.cubicweb.org/project/cubicweb-keyword -.. _link: http://www.cubicweb.org/project/cubicweb-link -.. _mailinglist: http://www.cubicweb.org/project/cubicweb-mailinglist -.. _person: http://www.cubicweb.org/project/cubicweb-person -.. _tag: http://www.cubicweb.org/project/cubicweb-tag -.. _task: http://www.cubicweb.org/project/cubicweb-task -.. _zone: http://www.cubicweb.org/project/cubicweb-zone - -To declare the use of a component, once installed, add the name of the component -to the variable `__use__` in the file `__pkginfo__.py` of your own component. - -.. note:: - The listed cubes above are available as debian-packages on `CubicWeb's forge`_. - -.. _`CubicWeb's forge`: http://www.cubicweb.org/project?vtitle=All%20cubicweb%20projects diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/cubes/cc-newcube.rst --- a/doc/book/en/development/cubes/cc-newcube.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -Creating a new cube from scratch using :command:`cubicweb-ctl newcube` ----------------------------------------------------------------------- - -Let's start by creating the cube environment in which we will develop :: - - cd ~/hg - # use cubicweb-ctl to generate a template for the cube - cubicweb-ctl newcube mycube # will ask some questions, most with nice default - # makes the cube source code managed by mercurial - cd mycube - hg init - hg add . - hg ci - -If all went well, you should see the cube you just created in the list -returned by ``cubicweb-ctl list`` in the section *Available cubes*, -and if it is not the case please refer to :ref:`ConfigurationEnv`. - -To reuse an existing cube, add it to the list named ``__use__`` and defined in -:file:`__pkginfo__.py`. This variable is used for the instance packaging -(dependencies handled by system utility tools such as APT) and the usable cubes -at the time the base is created (import_erschema('MyCube') will not properly -work otherwise). - -.. note:: - - Please note that if you do not wish to use default directory for your cubes - library, you should set the :envvar:`CW_CUBES_PATH` environment variable to - add extra directories where cubes will be search, and you'll then have to use - the option `--directory` to specify where you would like to place the source - code of your cube: - - ``cubicweb-ctl newcube --directory=/path/to/cubes/library mycube`` - - -.. XXX resurrect once live-server is back -.. Usage of :command:`cubicweb-ctl liveserver` -.. ------------------------------------------- - -.. To quickly test a new cube, you can also use the `liveserver` command for cubicweb-ctl -.. which allows to create an instance in memory (using an SQLite database by -.. default) and make it accessible through a web server :: - -.. cubicweb-ctl live-server mycube - -.. or by using an existing database (SQLite or Postgres):: - -.. cubicweb-ctl live-server -s myfile_sources mycube diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/cubes/index.rst --- a/doc/book/en/development/cubes/index.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -Cubes -===== - -This chapter describes how to define your own cubes and reuse already available cubes. - -.. toctree:: - :maxdepth: 1 - - layout.rst - cc-newcube.rst - available-cubes.rst diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/cubes/layout.rst --- a/doc/book/en/development/cubes/layout.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,134 +0,0 @@ - -.. _foundationsCube: - -.. _cubelayout: - -Standard structure for a cube ------------------------------ - -A cube is structured as follows: - -:: - - mycube/ - | - |-- data/ - | |-- cubes.mycube.css - | |-- cubes.mycube.js - | `-- external_resources - | - |-- debian/ - | |-- changelog - | |-- compat - | |-- control - | |-- copyright - | |-- cubicweb-mycube.prerm - | `-- rules - | - |-- entities.py - | - |-- i18n/ - | |-- en.po - | |-- es.po - | `-- fr.po - | - |-- __init__.py - | - |-- MANIFEST.in - | - |-- migration/ - | |-- postcreate.py - | `-- precreate.py - | - |-- __pkginfo__.py - | - |-- schema.py - | - |-- setup.py - | - |-- site_cubicweb.py - | - |-- hooks.py - | - |-- test/ - | |-- data/ - | | `-- bootstrap_cubes - | |-- pytestconf.py - | |-- realdb_test_mycube.py - | `-- test_mycube.py - | - `-- views.py - - -We can use subpackages instead of python modules for ``views.py``, ``entities.py``, -``schema.py`` or ``hooks.py``. For example, we could have: - -:: - - mycube/ - | - |-- entities.py - |-- hooks.py - `-- views/ - |-- forms.py - |-- primary.py - `-- widgets.py - - -where : - -* ``schema`` contains the schema definition (server side only) -* ``entities`` contains the entities definition (server side and web interface) -* ``hooks`` contains hooks and/or views notifications (server side only) -* ``views`` contains the web interface components (web interface only) -* ``test`` contains tests related to the cube (not installed) -* ``i18n`` contains message catalogs for supported languages (server side and - web interface) -* ``data`` contains data files for static content (images, css, javascripts) - ...(web interface only) -* ``migration`` contains initialization files for new instances (``postcreate.py``) - and a file containing dependencies of the component depending on the version - (``depends.map``) -* ``debian`` contains all the files managing debian packaging (you will find - the usual files ``control``, ``rules``, ``changelog``... not installed) -* file ``__pkginfo__.py`` provides component meta-data, especially the distribution - and the current version (server side and web interface) or sub-cubes used by - the cube. - - -At least you should have the file ``__pkginfo__.py``. - - -The :file:`__init__.py` and :file:`site_cubicweb.py` files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The :file:`__pkginfo__.py` file -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -It contains metadata describing your cubes, mostly useful for -packaging. - - -:file:`migration/precreate.py` and :file:`migration/postcreate.py` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. XXX detail steps of instance creation - - -External resources such as image, javascript and css files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. XXX naming convention external_resources file - - -Out-of the box testing -~~~~~~~~~~~~~~~~~~~~~~ - -.. XXX MANIFEST.in, __pkginfo__.include_dirs, debian - - -Packaging and distribution -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. XXX MANIFEST.in, __pkginfo__.include_dirs, debian - diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/datamodel/baseschema.rst --- a/doc/book/en/development/datamodel/baseschema.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -.. _pre_defined_entity_types: - -Pre-defined entities in the library ------------------------------------ - -The library defines a set of entity schemas that are required by the system -or commonly used in *CubicWeb* instances. - - -Entity types used to store the schema -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* _`CWEType`, entity type -* _`CWRType`, relation type -* _`CWRelation`, relation definition -* _`CWAttribute`, attribute relation definition -* _`CWConstraint`, `CWConstraintType`, `RQLExpression` - -Entity types used to manage users and permissions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* _`CWUser`, system users -* _`CWGroup`, users groups -* _`CWPermission`, used to configure the security of the instance - -Entity types used to manage workflows -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* _`Workflow`, workflow entity, linked to some entity types which may use this workflow -* _`State`, workflow state -* _`Transition`, workflow transition -* _`TrInfo`, record of a transition trafic for an entity - -Other entity types -~~~~~~~~~~~~~~~~~~ -* _`CWCache`, cache entities used to improve performances -* _`CWProperty`, used to configure the instance - -* _`EmailAddress`, email address, used by the system to send notifications - to the users and also used by others optionnals schemas - -* _`Bookmark`, an entity type used to allow a user to customize his links within - the instance - -* _`ExternalUri`, used for semantic web site to indicate that an entity is the - same as another from an external site diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/datamodel/define-workflows.rst --- a/doc/book/en/development/datamodel/define-workflows.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,159 +0,0 @@ -.. -*- coding: utf-8 -*- - -.. _Workflow: - -Define a Workflow -================= - -General -------- - -A workflow describes how certain entities have to evolve between -different states. Hence we have a set of states, and a "transition graph", -i.e. a set of possible transitions from one state to another state. - -We will define a simple workflow for a blog, with only the following -two states: `submitted` and `published`. So first, we create a simple -*CubicWeb* instance in ten minutes (see :ref:`BlogFiveMinutes`). - -Set-up a workflow ------------------ - -We want to create a workflow to control the quality of the BlogEntry -submitted on the instance. When a BlogEntry is created by a user -its state should be `submitted`. To be visible to all, it has to -be in the state `published`. To move it from `submitted` to `published`, -we need a transition that we can call `approve_blogentry`. - -A BlogEntry state should not be modifiable by every user. -So we have to define a group of users, `moderators`, and -this group will have appropriate permissions to publish a BlogEntry. - -There are two ways to create a workflow: from the user interface, or -by defining it in ``migration/postcreate.py``. This script is executed -each time a new ``cubicweb-ctl db-init`` is done. We strongly -recommend to create the workflow in ``migration/postcreate.py`` and we -will now show you how. Read `Two bits of warning`_ to understand why. - -The state of an entity is managed by the `in_state` attribute which -can be added to your entity schema by inheriting from -`cubicweb.schema.WorkflowableEntityType`. - - -About our example of BlogEntry, we must have: - -.. sourcecode:: python - - from cubicweb.schema import WorkflowableEntityType - - class BlogEntry(WorkflowableEntityType): - ... - - -Create states, transitions and group permissions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``postcreate.py`` script is executed in a special environment, adding -several *CubicWeb* primitives that can be used. - -They are all defined in the ``class ServerMigrationHelper``. -We will only discuss the methods we use to create a workflow in this example. - -A workflow is a collection of entities of type ``State`` and of type -``Transition`` which are standard *CubicWeb* entity types. - -To define a workflow for BlogDemo, please add the following lines -to ``migration/postcreate.py``: - -.. sourcecode:: python - - _ = unicode - - moderators = add_entity('CWGroup', name=u"moderators") - -This adds the `moderators` user group. - -.. sourcecode:: python - - wf = add_workflow(u'blog publication workflow', 'BlogEntry') - -At first, instanciate a new workflow object with a gentle description -and the concerned entity types (this one can be a tuple for multiple -value). - -.. sourcecode:: python - - submitted = wf.add_state(_('submitted'), initial=True) - published = wf.add_state(_('published')) - -This will create two entities of type ``State``, one with name -'submitted', and the other with name 'published'. - -``add_state`` expects as first argument the name of the state you want -to create and an optional argument to say if it is supposed to be the -initial state of the entity type. - -.. sourcecode:: python - - wf.add_transition(_('approve_blogentry'), (submitted,), published, ('moderators', 'managers'),) - -This will create an entity of type ``Transition`` with name -`approve_blogentry` which will be linked to the ``State`` entities -created before. - -``add_transition`` expects - - * as the first argument: the name of the transition - * then the list of states on which the transition can be triggered, - * the target state of the transition, - * and the permissions - (e.g. a list of user groups who can apply the transition; the user - has to belong to at least one of the listed group to perform the action). - -.. sourcecode:: python - - checkpoint() - -.. note:: - Do not forget to add the `_()` in front of all states and transitions names while creating - a workflow so that they will be identified by the i18n catalog scripts. - -In addition to the user groups (one of which the user needs to belong -to), we could have added a RQL condition. In this case, the user can -only perform the action if the two conditions are satisfied. - -If we use an RQL condition on a transition, we can use the following variables: - -* `X`, the entity on which we may pass the transition -* `U`, the user executing that may pass the transition - - -.. image:: ../../images/03-transitions-view_en.png - -You can notice that in the action box of a BlogEntry, the state is now -listed as well as the possible transitions for the current state -defined by the workflow. - -The transitions will only be displayed for users having the right permissions. -In our example, the transition `approve_blogentry` will only be displayed -for the users belonging to the group `moderators` or `managers`. - - -Two bits of warning -~~~~~~~~~~~~~~~~~~~ - -We could perfectly use the administration interface to do these -operations. It is a convenient thing to do at times (when doing -development, to quick-check things). But it is not recommended beyond -that because it is a bit complicated to do it right and it will be -only local to your instance (or, said a bit differently, such a -workflow only exists in an instance database). Furthermore, you cannot -write unit tests against deployed instances, and experience shows it -is mandatory to have tests for any mildly complicated workflow -setup. - -Indeed, if you create the states and transitions through the user -interface, next time you initialize the database you will have to -re-create all the workflow entities. The user interface should only be -a reference for you to view the states and transitions, but is not the -appropriate interface to define your application workflow. diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/datamodel/definition.rst --- a/doc/book/en/development/datamodel/definition.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,630 +0,0 @@ - .. -*- coding: utf-8 -*- - -Yams *schema* -------------- - -The **schema** is the core piece of a *CubicWeb* instance as it -defines and handles the data model. It is based on entity types that -are either already defined in `Yams`_ and the *CubicWeb* standard -library; or more specific types defined in cubes. The schema for a -cube is defined in a `schema` python module or package. - -.. _`Yams`: http://www.logilab.org/project/yams - -Overview -~~~~~~~~ - -The core idea of the yams schema is not far from the classical -`Entity-relationship`_ model. But while an E/R model (or `logical -model`) traditionally has to be manually translated to a lower-level -data description language (such as the SQL `create table` -sublanguage), also often described as the `physical model`, no such -step is required with |yams| and |cubicweb|. - -.. _`Entity-relationship`: http://en.wikipedia.org/wiki/Entity-relationship_model - -This is because in addition to high-level, logical |yams| models, one -uses the |rql| data manipulation language to query, insert, update and -delete data. |rql| abstracts as much of the underlying SQL database as -a |yams| schema abstracts from the physical layout. The vagaries of -SQL are avoided. - -As a bonus point, such abstraction make it quite comfortable to build -or use different backends to which |rql| queries apply. - -So, as in the E/R formalism, the building blocks are ``entities`` -(:ref:`EntityType`), ``relationships`` (:ref:`RelationType`, -:ref:`RelationDefinition`) and ``attributes`` (handled like relation -with |yams|). - -Let us detail a little the divergences between E/R and |yams|: - -* all relationship are binary which means that to represent a - non-binary relationship, one has to use an entity, -* relationships do not support attributes (yet, see: - http://www.cubicweb.org/ticket/341318), hence the need to reify it - as an entity if need arises, -* all entities have an `eid` attribute (an integer) that is its - primary key (but it is possible to declare uniqueness on other - attributes) - -Also |yams| supports the notions of: - -* entity inheritance, -* relation type: that is, relationships can be established over a set - of couple of entity types (henre the distinction made between - `RelationType` and `RelationDefinition` below) - -Finally |yams| has a few concepts of its own: - -* relationships being oriented and binary, we call the left hand - entity type the `subject` and the right hand entity type the - `object` - -.. note:: - - The |yams| schema is available at run time through the .schema - attribute of the `vregistry`. It's an instance of - :class:`cubicweb.schema.Schema`, which extends - :class:`yams.schema.Schema`. - -.. _EntityType: - -Entity type -~~~~~~~~~~~ - -An entity type is an instance of :class:`yams.schema.EntitySchema`. Each entity type has -a set of attributes and relations, and some permissions which define who can add, read, -update or delete entities of this type. - -The following built-in types are available: ``String``, ``Int``, -``Float``, ``Decimal``, ``Boolean``, ``Date``, ``Datetime``, ``Time``, -``Interval``, ``Byte`` and ``Password``. They can only be used as -attributes of an other entity type. - -You can find more base entity types in -:ref:`pre_defined_entity_types`. - -.. XXX yams inheritance - -.. _RelationType: - -Relation type -~~~~~~~~~~~~~ - -A relation type is an instance of -:class:`yams.schema.RelationSchema`. A relation type is simply a -semantic definition of a kind of relationship that may occur in an -application. - -It may be referenced by zero, one or more relation definitions. - -It is important to choose a good name, at least to avoid conflicts -with some semantically different relation defined in other cubes -(since there's only a shared name space for these names). - -A relation type holds the following properties (which are hence shared -between all relation definitions of that type): - -* `inlined`: boolean handling the physical optimization for archiving - the relation in the subject entity table, instead of creating a specific - table for the relation. This applies to relations where cardinality - of subject->relation->object is 0..1 (`?`) or 1..1 (`1`) for *all* its relation - definitions. - -* `symmetric`: boolean indicating that the relation is symmetrical, which - means that `X relation Y` implies `Y relation X`. - -.. _RelationDefinition: - -Relation definition -~~~~~~~~~~~~~~~~~~~ - -A relation definition is an instance of -:class:`yams.schema.RelationDefinition`. It is a complete triplet -" ". - -When creating a new instance of that class, the corresponding -:class:`RelationType` instance is created on the fly if necessary. - -Properties -`````````` - -The available properties for relation definitions are enumerated -here. There are several kind of properties, as some relation -definitions are actually attribute definitions, and other are not. - -Some properties may be completely optional, other may have a default -value. - -Common properties for attributes and relations: - -* `description`: an unicode string describing an attribute or a - relation. By default this string will be used in the editing form of - the entity, which means that it is supposed to help the end-user and - should be flagged by the function `_` to be properly - internationalized. - -* `constraints`: a list of conditions/constraints that the relation has to - satisfy (c.f. `Constraints`_) - -* `cardinality`: a two character string specifying the cardinality of - the relation. The first character defines the cardinality of the - relation on the subject, and the second on the object. When a - relation can have multiple subjects or objects, the cardinality - applies to all, not on a one-to-one basis (so it must be - consistent...). Default value is '**'. The possible values are - inspired from regular expression syntax: - - * `1`: 1..1 - * `?`: 0..1 - * `+`: 1..n - * `*`: 0..n - -Attributes properties: - -* `unique`: boolean indicating if the value of the attribute has to be - unique or not within all entities of the same type (false by - default) - -* `indexed`: boolean indicating if an index needs to be created for - this attribute in the database (false by default). This is useful - only if you know that you will have to run numerous searches on the - value of this attribute. - -* `default`: default value of the attribute. In case of date types, the values - which could be used correspond to the RQL keywords `TODAY` and `NOW`. - -Properties for `String` attributes: - -* `fulltextindexed`: boolean indicating if the attribute is part of - the full text index (false by default) (*applicable on the type - `Byte` as well*) - -* `internationalizable`: boolean indicating if the value of the - attribute is internationalizable (false by default) - -Relation properties: - -* `composite`: string indicating that the subject (composite == - 'subject') is composed of the objects of the relations. For the - opposite case (when the object is composed of the subjects of the - relation), we just set 'object' as value. The composition implies - that when the relation is deleted (so when the composite is deleted, - at least), the composed are also deleted. - -* `fulltext_container`: string indicating if the value if the full - text indexation of the entity on one end of the relation should be - used to find the entity on the other end. The possible values are - 'subject' or 'object'. For instance the use_email relation has that - property set to 'subject', since when performing a full text search - people want to find the entity using an email address, and not the - entity representing the email address. - -Constraints -``````````` - -By default, the available constraint types are: - -General Constraints -...................... - -* `SizeConstraint`: allows to specify a minimum and/or maximum size on - string (generic case of `maxsize`) - -* `BoundConstraint`: allows to specify a minimum and/or maximum value - on numeric types and date - -.. sourcecode:: python - - from yams.constraints import BoundConstraint, TODAY - BoundConstraint('<=', TODAY()) - -* `IntervalBoundConstraint`: allows to specify an interval with - included values - -.. sourcecode:: python - - class Node(EntityType): - latitude = Float(constraints=[IntervalBoundConstraint(-90, +90)]) - -* `UniqueConstraint`: identical to "unique=True" - -* `StaticVocabularyConstraint`: identical to "vocabulary=(...)" - -.. XXX Attribute, NOW - -RQL Based Constraints -...................... - -RQL based constraints may take three arguments. The first one is the ``WHERE`` -clause of a RQL query used by the constraint. The second argument ``mainvars`` -is the ``Any`` clause of the query. By default this include `S` reserved for the -subject of the relation and `O` for the object. Additional variables could be -specified using ``mainvars``. The argument expects a single string with all -variable's name separated by spaces. The last one, ``msg``, is the error message -displayed when the constraint fails. As RQLVocabularyConstraint never fails the -third argument is not available. - -* `RQLConstraint`: allows to specify a RQL query that has to be satisfied - by the subject and/or the object of relation. In this query the variables - `S` and `O` are reserved for the relation subject and object entities. - -* `RQLVocabularyConstraint`: similar to the previous type of constraint except - that it does not express a "strong" constraint, which means it is only used to - restrict the values listed in the drop-down menu of editing form, but it does - not prevent another entity to be selected. - -* `RQLUniqueConstraint`: allows to the specify a RQL query that ensure that an - attribute is unique in a specific context. The Query must **never** return more - than a single result to be satisfied. In this query the variables `S` is - reserved for the relation subject entity. The other variables should be - specified with the second constructor argument (mainvars). This constraints - should be used when UniqueConstraint doesn't fit. Here is a simple example. - -.. sourcecode:: python - - # Check that in the same Workflow each state's name is unique. Using - # UniqueConstraint (or unique=True) here would prevent states in different - # workflows to have the same name. - - # With: State S, Workflow W, String N ; S state_of W, S name N - - RQLUniqueConstraint('S name N, S state_of WF, Y state_of WF, Y name N', - mainvars='Y', - msg=_('workflow already has a state of that name')) - -.. XXX note about how to add new constraint - -.. _securitymodel: - -The security model -~~~~~~~~~~~~~~~~~~ - -The security model of `CubicWeb` is based on `Access Control List`. -The main principles are: - -* users and groups of users -* a user belongs to at least one group of user -* permissions (read, update, create, delete) -* permissions are assigned to groups (and not to users) - -For *CubicWeb* in particular: - -* we associate rights at the entities/relations schema level -* for each entity, we distinguish four kinds of permissions: `read`, - `add`, `update` and `delete` -* for each relation, we distinguish three kinds of permissions: `read`, - `add` and `delete` (it is not possible to `modify` a relation) -* the default groups are: `administrators`, `users` and `guests` -* by default, users belong to the `users` group -* there is a virtual group called `owners` to which we - can associate only `delete` and `update` permissions - - * we can not add users to the `Owners` group, they are - implicitly added to it according to the context of the objects - they own - * the permissions of this group are only checked on `update`/`delete` - actions if all the other groups the user belongs to do not provide - those permissions - -Setting permissions is done with the attribute `__permissions__` of entities and -relation types. The value of this attribute is a dictionary where the keys are the access types -(action), and the values are the authorized groups or expressions. - -For an entity type, the possible actions are `read`, `add`, `update` and -`delete`. - -For a relation type, the possible actions are `read`, `add`, and `delete`. - -For each access type, a tuple indicates the name of the authorized groups and/or -one or multiple RQL expressions to satisfy to grant access. The access is -provided if the user is in one of the listed groups or if one of the RQL condition -is satisfied. - -The standard user groups -```````````````````````` - -* `guests` - -* `users` - -* `managers` - -* `owners`: virtual group corresponding to the entity's owner. - This can only be used for the actions `update` and `delete` of an entity - type. - -It is also possible to use specific groups if they are defined in the -precreate script of the cube (``migration/precreate.py``). Defining groups in -postcreate script or later makes them unavailable for security -purposes (in this case, an `sync_schema_props_perms` command has to -be issued in a CubicWeb shell). - - -Use of RQL expression for write permissions -``````````````````````````````````````````` -It is possible to define RQL expression to provide update permission -(`add`, `delete` and `update`) on relation and entity types. - -RQL expression for entity type permission: - -* you have to use the class `ERQLExpression` - -* the used expression corresponds to the WHERE statement of an RQL query - -* in this expression, the variables `X` and `U` are pre-defined references - respectively on the current entity (on which the action is verified) and - on the user who send the request - -* it is possible to use, in this expression, a special relation - "has__permission" where the subject is the user and the - object is any variable, meaning that the user needs to have - permission to execute the action on the entities related - to this variable - -For RQL expressions on a relation type, the principles are the same except -for the following: - -* you have to use the class `RRQLExpression` in the case of a non-final relation - -* in the expression, the variables `S`, `O` and `U` are pre-defined references - to respectively the subject and the object of the current relation (on - which the action is being verified) and the user who executed the query - -* we can also define rights over attributes of an entity (non-final relation), - knowing that: - - - to define RQL expression, we have to use the class `ERQLExpression` - in which `X` represents the entity the attribute belongs to - - - the permissions `add` and `delete` are equivalent. Only `add`/`read` - are actually taken in consideration. - -.. note:: - - Potentially, the `use of an RQL expression to add an entity or a - relation` can cause problems for the user interface, because if the - expression uses the entity or the relation to create, then we are - not able to verify the permissions before we actually add the entity - (please note that this is not a problem for the RQL server at all, - because the permissions checks are done after the creation). In such - case, the permission check methods (CubicWebEntitySchema.check_perm - and has_perm) can indicate that the user is not allowed to create - this entity but can obtain the permission. To compensate this - problem, it is usually necessary, for such case, to use an action - that reflects the schema permissions but which enables to check - properly the permissions so that it would show up if necessary. - - -Use of RQL expression for reading rights -```````````````````````````````````````` - -The principles are the same but with the following restrictions: - -* we can not use `RRQLExpression` on relation types for reading - -* special relations "has__permission" can not be used - - - - -Defining your schema using yams -------------------------------- - -Entity type definition -~~~~~~~~~~~~~~~~~~~~~~ - -An entity type is defined by a Python class which inherits from -:class:`yams.buildobjs.EntityType`. The class definition contains the -description of attributes and relations for the defined entity type. -The class name corresponds to the entity type name. It is expected to -be defined in the module ``mycube.schema``. - -:Note on schema definition: - - The code in ``mycube.schema`` is not meant to be executed. The class - EntityType mentioned above is different from the EntitySchema class - described in the previous chapter. EntityType is a helper class to - make Entity definition easier. Yams will process EntityType classes - and create EntitySchema instances from these class definitions. Similar - manipulation happen for relations. - -When defining a schema using python files, you may use the following shortcuts: - -- `required`: boolean indicating if the attribute is required, ed subject cardinality is '1' - -- `vocabulary`: specify static possible values of an attribute - -- `maxsize`: integer providing the maximum size of a string (no limit by default) - -For example: - -.. sourcecode:: python - - class Person(EntityType): - """A person with the properties and the relations necessary for my - application""" - - last_name = String(required=True, fulltextindexed=True) - first_name = String(required=True, fulltextindexed=True) - title = String(vocabulary=('Mr', 'Mrs', 'Miss')) - date_of_birth = Date() - works_for = SubjectRelation('Company', cardinality='?*') - - -The entity described above defines three attributes of type String, -last_name, first_name and title, an attribute of type Date for the date of -birth and a relation that connects a `Person` to another entity of type -`Company` through the semantic `works_for`. - -:Naming convention: - - Entity class names must start with an uppercase letter. The common - usage is to use ``CamelCase`` names. - - Attribute and relation names must start with a lowercase letter. The - common usage is to use ``underscore_separated_words``. Attribute and - relation names starting with a single underscore are permitted, to - denote a somewhat "protected" or "private" attribute. - - In any case, identifiers starting with "CW" or "cw" are reserved for - internal use by the framework. - - -The name of the Python attribute corresponds to the name of the attribute -or the relation in *CubicWeb* application. - -An attribute is defined in the schema as follows:: - - attr_name = attr_type(properties) - -where `attr_type` is one of the type listed above and `properties` is -a list of the attribute needs to satisfy (see `Properties`_ -for more details). - -* it is possible to use the attribute `meta` to flag an entity type as a `meta` - (e.g. used to describe/categorize other entities) - -.. XXX the paragraph below needs clarification and / or moving out in -.. another place - -*Note*: if you end up with an `if` in the definition of your entity, this probably -means that you need two separate entities that implement the `ITree` interface and -get the result from `.children()` which ever entity is concerned. - -Inheritance -``````````` -XXX feed me - - -Definition of relations -~~~~~~~~~~~~~~~~~~~~~~~ - -XXX add note about defining relation type / definition - -A relation is defined by a Python class heriting `RelationType`. The name -of the class corresponds to the name of the type. The class then contains -a description of the properties of this type of relation, and could as well -contain a string for the subject and a string for the object. This allows to create -new definition of associated relations, (so that the class can have the -definition properties from the relation) for example :: - - class locked_by(RelationType): - """relation on all entities indicating that they are locked""" - inlined = True - cardinality = '?*' - subject = '*' - object = 'CWUser' - -If provided, the `subject` and `object` attributes denote the subject -and object of the various relation definitions related to the relation -type. Allowed values for these attributes are: - -* a string corresponding to an entity type -* a tuple of string corresponding to multiple entity types -* special string such as follows: - - - "**": all types of entities - - "*": all types of non-meta entities - - "@": all types of meta entities but not system entities (e.g. used for - the basic schema description) - -When a relation is not inlined and not symmetrical, and it does not require -specific permissions, it can be defined using a `SubjectRelation` -attribute in the EntityType class. The first argument of `SubjectRelation` gives -the entity type for the object of the relation. - -:Naming convention: - - Although this way of defining relations uses a Python class, the - naming convention defined earlier prevails over the PEP8 conventions - used in the framework: relation type class names use - ``underscore_separated_words``. - -:Historical note: - - It has been historically possible to use `ObjectRelation` which - defines a relation in the opposite direction. This feature is soon to be - deprecated and therefore should not be used in newly written code. - -:Future deprecation note: - - In an even more remote future, it is quite possible that the - SubjectRelation shortcut will become deprecated, in favor of the - RelationType declaration which offers some advantages in the context - of reusable cubes. - -Definition of permissions -~~~~~~~~~~~~~~~~~~~~~~~~~~ -The entity type `CWPermission` from the standard library -allows to build very complex and dynamic security architectures. The schema of -this entity type is as follow: - -.. sourcecode:: python - - class CWPermission(EntityType): - """entity type that may be used to construct some advanced security configuration - """ - name = String(required=True, indexed=True, internationalizable=True, maxsize=100) - require_group = SubjectRelation('CWGroup', cardinality='+*', - description=_('groups to which the permission is granted')) - require_state = SubjectRelation('State', - description=_("entity's state in which the permission is applicable")) - # can be used on any entity - require_permission = ObjectRelation('**', cardinality='*1', composite='subject', - description=_("link a permission to the entity. This " - "permission should be used in the security " - "definition of the entity's type to be useful.")) - - -Example of configuration: - -.. sourcecode:: python - - class Version(EntityType): - """a version is defining the content of a particular project's release""" - - __permissions__ = {'read': ('managers', 'users', 'guests',), - 'update': ('managers', 'logilab', 'owners',), - 'delete': ('managers', ), - 'add': ('managers', 'logilab', - ERQLExpression('X version_of PROJ, U in_group G,' - 'PROJ require_permission P, P name "add_version",' - 'P require_group G'),)} - - - class version_of(RelationType): - """link a version to its project. A version is necessarily linked to one and only one project. - """ - __permissions__ = {'read': ('managers', 'users', 'guests',), - 'delete': ('managers', ), - 'add': ('managers', 'logilab', - RRQLExpression('O require_permission P, P name "add_version",' - 'U in_group G, P require_group G'),) - } - inlined = True - - -This configuration indicates that an entity `CWPermission` named -"add_version" can be associated to a project and provides rights to create -new versions on this project to specific groups. It is important to notice that: - -* in such case, we have to protect both the entity type "Version" and the relation - associating a version to a project ("version_of") - -* because of the genericity of the entity type `CWPermission`, we have to execute - a unification with the groups and/or the states if necessary in the expression - ("U in_group G, P require_group G" in the above example) - - - -Handling schema changes -~~~~~~~~~~~~~~~~~~~~~~~ - -Also, it should be clear that to properly handle data migration, an -instance's schema is stored in the database, so the python schema file -used to defined it is only read when the instance is created or -upgraded. - -.. XXX complete me diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/datamodel/index.rst --- a/doc/book/en/development/datamodel/index.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -Data model -========== - -This chapter describes how you define a schema and how to make it evolves as the time goes. - -.. toctree:: - :maxdepth: 1 - - definition - metadata - baseschema - define-workflows diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/datamodel/metadata.rst --- a/doc/book/en/development/datamodel/metadata.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ - -Metadata --------- - -.. index:: - schema: meta-data; - schema: eid; creation_date; modification_data; cwuri - schema: created_by; owned_by; is; is_instance; - -Each entity type in |cubicweb| has at least the following meta-data attributes and relations: - -`eid` - entity's identifier which is unique in an instance. We usually call this identifier `eid` for historical reason. - -`creation_date` - Date and time of the creation of the entity. - -`modification_date` - Date and time of the latest modification of an entity. - -`cwuri` - Reference URL of the entity, which is not expected to change. - -`created_by` - Relation to the :ref:`users ` who has created the entity - -`owned_by` - Relation to :ref:`users ` whom the entity belongs; usually the creator but not - necessary, and it could have multiple owners notably for permission control - -`is` - Relation to the :ref:`entity type ` of which type the entity is. - -`is_instance` - Relation to the :ref:`entity types ` of which type the - entity is an instance of. - diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devcore/cwconfig.rst --- a/doc/book/en/development/devcore/cwconfig.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -Configuration -------------- - -.. automodule:: cubicweb.cwconfig - :members: diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devcore/dbapi.rst --- a/doc/book/en/development/devcore/dbapi.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,119 +0,0 @@ -.. _dbapi: - -Python/RQL API -~~~~~~~~~~~~~~ - -The Python API developped to interface with RQL is inspired from the standard db-api, -with a Connection object having the methods cursor, rollback and commit essentially. -The most important method is the `execute` method of a cursor. - -.. sourcecode:: python - - execute(rqlstring, args=None, build_descr=True) - -:rqlstring: the RQL query to execute (unicode) -:args: if the query contains substitutions, a dictionary containing the values to use - -The `Connection` object owns the methods `commit` and `rollback`. You -*should never need to use them* during the development of the web -interface based on the *CubicWeb* framework as it determines the end -of the transaction depending on the query execution success. They are -however useful in other contexts such as tests or custom controllers. - -.. note:: - - While executing update queries (SET, INSERT, DELETE), if a query generates - an error related to security, a rollback is automatically done on the current - transaction. - -Executing RQL queries from a view or a hook -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When you're within code of the web interface, the db-api like connexion is -handled by the request object. You should not have to access it directly, but -use the `execute` method directly available on the request, eg: - - rset = self._cw.execute(rqlstring, kwargs) - -Similarly, on the server side (eg in hooks), there is no db-api connexion (since -you're directly inside the data-server), so you'll have to use the execute method -of the session object. - - -Important note about proper usage of .execute -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Let's say you want to get T which is in configuration C, this translates to: - -.. sourcecode:: python - - self._cw.execute('Any T WHERE T in_conf C, C eid %s' % entity.eid) - -But it must be written in a syntax that will benefit from the use -of a cache on the RQL server side: - -.. sourcecode:: python - - self._cw.execute('Any T WHERE T in_conf C, C eid %(x)s', {'x': entity.eid}) - -The syntax tree is built once for the "generic" RQL and can be re-used -with a number of different eids. There rql IN operator is an exception -to this rule. - -.. sourcecode:: python - - self._cw.execute('Any T WHERE T in_conf C, C name IN (%s)' - % ','.join(['foo', 'bar'])) - -Alternativelly, some of the common data related to an entity can be -obtained from the `entity.related()` method (which is used under the -hood by the orm when you use attribute access notation on an entity to -get a relation. The initial request would then be translated to: - -.. sourcecode:: python - - entity.related('in_conf', 'object') - -Additionnaly this benefits from the fetch_attrs policy (see -:ref:`FetchAttrs`) eventually defined on the class element, which says -which attributes must be also loaded when the entity is loaded through -the orm. - - -.. _resultset: - -The `ResultSet` API -~~~~~~~~~~~~~~~~~~~ - -ResultSet instances are a very commonly manipulated object. They have -a rich API as seen below, but we would like to highlight a bunch of -methods that are quite useful in day-to-day practice: - -* `__str__()` (applied by `print`) gives a very useful overview of both - the underlying RQL expression and the data inside; unavoidable for - debugging purposes - -* `printable_rql()` produces back a well formed RQL expression as a - string; it is very useful to build views - -* `entities()` returns a generator on all entities of the result set - -* `get_entity(row, col)` gets the entity at row, col coordinates; one - of the most used result set method - -.. autoclass:: cubicweb.rset.ResultSet - :members: - - -The `Cursor` API -~~~~~~~~~~~~~~~~ - -The whole cursor API is developped below. - -.. note: - - In practice we use the `.execute` method on the _cw object of - appobjects. Usage of other methods is quite rare. - -.. autoclass:: cubicweb.dbapi.Cursor - :members: diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devcore/index.rst --- a/doc/book/en/development/devcore/index.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -Core APIs -========= - -.. toctree:: - :maxdepth: 1 - - dbapi.rst - reqbase.rst - cwconfig.rst - diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devcore/reqbase.rst --- a/doc/book/en/development/devcore/reqbase.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -Request and ResultSet methods ------------------------------ - -Those are methods you'll find on both request objects and on repository session: - -:URL handling: - * `build_url(*args, **kwargs)`, returns an absolute URL based on the - given arguments. The *controller* supposed to handle the response, - can be specified through the first positional parameter (the - connection is theoretically done automatically :). -:Data formatting: - * `format_date(date, date_format=None, time=False)` returns a string for a - date time according to instance's configuration - - * `format_time(time)` returns a string for a date time according to - instance's configuration - -:And more...: - - * `tal_render(template, variables)`, renders a precompiled page template with - variables in the given dictionary as context - - -Result set methods: - - * `get_entity(row, col)`, returns the entity corresponding to the data position - in the *result set* - - * `complete_entity(row, col, skip_bytes=True)`, is equivalent to `get_entity` but - also call the method `complete()` on the entity before returning it - - diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devrepo/hooks.rst --- a/doc/book/en/development/devrepo/hooks.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,436 +0,0 @@ -.. -*- coding: utf-8 -*- - -.. _hooks: - -Hooks and Operations -==================== - -Generalities ------------- - -Paraphrasing the `emacs`_ documentation, let us say that hooks are an -important mechanism for customizing an application. A hook is -basically a list of functions to be called on some well-defined -occasion (this is called `running the hook`). - -.. _`emacs`: http://www.gnu.org/software/emacs/manual/html_node/emacs/Hooks.html - -In CubicWeb, hooks are subclasses of the Hook class in -`server/hook.py`, implementing their own `call` method, and selected -over a set of pre-defined `events` (and possibly more conditions, -hooks being selectable AppObjects like views and components). - -There are two families of events: data events and server events. In a -typical application, most of the Hooks are defined over data -events. - -The purpose of data hooks is to complement the data model as defined -in the schema.py, which is static by nature, with dynamic or value -driven behaviours. It is functionally equivalent to a `database -trigger`_, except that database triggers definition languages are not -standardized, hence not portable (for instance, PL/SQL works with -Oracle and PostgreSQL but not SqlServer nor Sqlite). - -.. _`database trigger`: http://en.wikipedia.org/wiki/Database_trigger - -Data hooks can serve the following purposes: - -* enforcing constraints that the static schema cannot express - (spanning several entities/relations, exotic value ranges and - cardinalities, etc.) - -* implement computed attributes - -Operations are Hook-like objects that may be created by Hooks and -scheduled to happen just before (or after) the `commit` event. Hooks -being fired immediately on data operations, it is sometime necessary -to delay the actual work down to a time where all other Hooks have -run, for instance a validation check which needs that all relations be -already set on an entity. Also while the order of execution of Hooks -is data dependant (and thus hard to predict), it is possible to force -an order on Operations. - -Operations also may be used to process various side effects associated -with a transaction such as filesystem udpates, mail notifications, -etc. - -Operations are subclasses of the Operation class in `server/hook.py`, -implementing `precommit_event` and other standard methods (wholly -described in :ref:`operations_api`). - -Events ------- - -Hooks are mostly defined and used to handle `dataflow`_ operations. It -means as data gets in (entities added, updated, relations set or -unset), specific events are issued and the Hooks matching these events -are called. - -.. _`dataflow`: http://en.wikipedia.org/wiki/Dataflow - -Below comes a list of the dataflow events related to entities operations: - -* before_add_entity - -* before_update_entity - -* before_delete_entity - -* after_add_entity - -* after_update_entity - -* after_delete_entity - -These define ENTTIES HOOKS. RELATIONS HOOKS are defined -over the following events: - -* after_add_relation - -* after_delete_relation - -* before_add_relation - -* before_delete_relation - -This is an occasion to remind us that relations support the add/delete -operation, but no update. - -Non data events also exist. These are called SYSTEM HOOKS. - -* server_startup - -* server_shutdown - -* server_maintenance - -* server_backup - -* server_restore - -* session_open - -* session_close - - -Using dataflow Hooks --------------------- - -Dataflow hooks either automate data operations or maintain the -consistency of the data model. In the later case, we must use a -specific exception named ValidationError - -Validation Errors -~~~~~~~~~~~~~~~~~ - -When a condition is not met in a Hook/Operation, it must raise a -`ValidationError`. Raising anything but a (subclass of) -ValidationError is a programming error. Raising a ValidationError -entails aborting the current transaction. - -The ValidationError exception is used to convey enough information up -to the user interface. Hence its constructor is different from the -default Exception constructor. It accepts, positionally: - -* an entity eid, - -* a dict whose keys represent attribute (or relation) names and values - an end-user facing message (hence properly translated) relating the - problem. - -An entity hook -~~~~~~~~~~~~~~ - -We will use a very simple example to show hooks usage. Let us start -with the following schema. - -.. sourcecode:: python - - class Person(EntityType): - age = Int(required=True) - -We would like to add a range constraint over a person's age. Let's -write an hook. It shall be placed into mycube/hooks.py. If this file -were to grow too much, we can easily have a mycube/hooks/... package -containing hooks in various modules. - -.. sourcecode:: python - - from cubicweb import ValidationError - from cubicweb.selectors import implements - from cubicweb.server.hook import Hook - - class PersonAgeRange(Hook): - __regid__ = 'person_age_range' - events = ('before_add_entity', 'before_update_entity') - __select__ = Hook.__select__ & implements('Person') - - def __call__(self): - if 0 >= self.entity.age <= 120: - return - msg = self._cw._('age must be between 0 and 120') - raise ValidationError(self.entity.eid, {'age': msg}) - -Hooks being AppObjects like views, they have a __regid__ and a -__select__ class attribute. The base __select__ is augmented with an -`implements` selector matching the desired entity type. The `events` -tuple is used by the Hook.__select__ base selector to dispatch the -hook on the right events. In an entity hook, it is possible to -dispatch on any entity event (e.g. 'before_add_entity', -'before_update_entity') at once if needed. - -Like all appobjects, hooks have the `self._cw` attribute which -represents the current session. In entity hooks, a `self.entity` -attribute is also present. - - -A relation hook -~~~~~~~~~~~~~~~ - -Let us add another entity type with a relation to person (in -mycube/schema.py). - -.. sourcecode:: python - - class Company(EntityType): - name = String(required=True) - boss = SubjectRelation('Person', cardinality='1*') - -We would like to constrain the company's bosses to have a minimum -(legal) age. Let's write an hook for this, which will be fired when -the `boss` relation is established. - -.. sourcecode:: python - - class CompanyBossLegalAge(Hook): - __regid__ = 'company_boss_legal_age' - events = ('before_add_relation',) - __select__ = Hook.__select__ & match_rtype('boss') - - def __call__(self): - boss = self._cw.entity_from_eid(self.eidto) - if boss.age < 18: - msg = self._cw._('the minimum age for a boss is 18') - raise ValidationError(self.eidfrom, {'boss': msg}) - -We use the `match_rtype` selector to select the proper relation type. - -The essential difference with respect to an entity hook is that there -is no self.entity, but `self.eidfrom` and `self.eidto` hook attributes -which represent the subject and object eid of the relation. - - -Using Operations ----------------- - -Let's augment our example with a new `subsidiary_of` relation on Company. - -.. sourcecode:: python - - class Company(EntityType): - name = String(required=True) - boss = SubjectRelation('Person', cardinality='1*') - subsidiary_of = SubjectRelation('Company', cardinality='*?') - -Base example -~~~~~~~~~~~~ - -We would like to check that there is no cycle by the `subsidiary_of` -relation. This is best achieved in an Operation since all relations -are likely to be set at commit time. - -.. sourcecode:: python - - def check_cycle(self, session, eid, rtype, role='subject'): - parents = set([eid]) - parent = session.entity_from_eid(eid) - while parent.related(rtype, role): - parent = parent.related(rtype, role)[0] - if parent.eid in parents: - msg = session._('detected %s cycle' % rtype) - raise ValidationError(eid, {rtype: msg}) - parents.add(parent.eid) - - class CheckSubsidiaryCycleOp(Operation): - - def precommit_event(self): - check_cycle(self.session, self.eidto, 'subsidiary_of') - - - class CheckSubsidiaryCycleHook(Hook): - __regid__ = 'check_no_subsidiary_cycle' - events = ('after_add_relation',) - __select__ = Hook.__select__ & match_rtype('subsidiary_of') - - def __call__(self): - CheckSubsidiaryCycleOp(self._cw, eidto=self.eidto) - -The operation is instantiated in the Hook.__call__ method. - -An operation always takes a session object as first argument -(accessible as `.session` from the operation instance), and optionally -all keyword arguments needed by the operation. These keyword arguments -will be accessible as attributes from the operation instance. - -Like in Hooks, ValidationError can be raised in Operations. Other -exceptions are programming errors. - -Notice how our hook will instantiate an operation each time the Hook -is called, i.e. each time the `subsidiary_of` relation is set. - -Using set_operation -~~~~~~~~~~~~~~~~~~~ - -There is an alternative method to schedule an Operation from a Hook, -using the `set_operation` function. - -.. sourcecode:: python - - from cubicweb.server.hook import set_operation - - class CheckSubsidiaryCycleHook(Hook): - __regid__ = 'check_no_subsidiary_cycle' - events = ('after_add_relation',) - __select__ = Hook.__select__ & match_rtype('subsidiary_of') - - def __call__(self): - set_operation(self._cw, 'subsidiary_cycle_detection', self.eidto, - CheckSubsidiaryCycleOp, rtype=self.rtype) - - class CheckSubsidiaryCycleOp(Operation): - - def precommit_event(self): - for eid in self._cw.transaction_data['subsidiary_cycle_detection']: - check_cycle(self.session, eid, self.rtype) - -Here, we call set_operation with a session object, a specially forged -key, a value that is the actual payload of an individual operation (in -our case, the object of the subsidiary_of relation) , the class of the -Operation, and more optional parameters to give to the operation (here -the rtype which do not vary accross operations). - -The body of the operation must then iterate over the values that have -been mapped in the transaction_data dictionary to the forged key. - -This mechanism is especially useful on two occasions (not shown in our -example): - -* massive data import (reduced memory consumption within a large - transaction) - -* when several hooks need to instantiate the same operation (e.g. an - entity and a relation hook). - -.. note:: - - A more realistic example can be found in the advanced tutorial - chapter :ref:`adv_tuto_security_propagation`. - -.. _operations_api: - -Operation: a small API overview -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. autoclass:: cubicweb.server.hook.Operation -.. autoclass:: cubicweb.server.hook.LateOperation -.. autofunction:: cubicweb.server.hook.set_operation - -Hooks writing rules -------------------- - -Remainder -~~~~~~~~~ - -Never, ever use the `entity.foo = 42` notation to update an entity. It -will not work. - -How to choose between a before and an after event ? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Before hooks give you access to the old attribute (or relation) -values. By definition the database is not yet updated in a before -hook. - -To access old and new values in an before_update_entity hook, one can -use the `server.hook.entity_oldnewvalue` function which returns a -tuple of the old and new values. This function takes an entity and an -attribute name as parameters. - -In a 'before_add|update_entity' hook the self.entity contains the new -values. One is allowed to further modify them before database -operations, using the dictionary notation. - -.. sourcecode:: python - - self.entity['age'] = 42 - -This is because using self.entity.set_attributes(age=42) will -immediately update the database (which does not make sense in a -pre-database hook), and will trigger any existing -before_add|update_entity hook, thus leading to infinite hook loops or -such awkward situations. - -Beyond these specific cases, updating an entity attribute or relation -must *always* be done using `set_attributes` and `set_relations` -methods. - -(Of course, ValidationError will always abort the current transaction, -whetever the event). - -Peculiarities of inlined relations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Some relations are defined in the schema as `inlined` (see -:ref:`RelationType` for details). In this case, they are inserted in -the database at the same time as entity attributes. - -Hence in the case of before_add_relation, such relations already exist -in the database. - -Edited attributes -~~~~~~~~~~~~~~~~~ - -On udpates, it is possible to ask the `entity.edited_attributes` -variable whether one attribute has been updated. - -.. sourcecode:: python - - if 'age' not in entity.edited_attribute: - return - -Deleted in transaction -~~~~~~~~~~~~~~~~~~~~~~ - -The session object has a deleted_in_transaction method, which can help -writing deletion Hooks. - -.. sourcecode:: python - - if self._cw.deleted_in_transaction(self.eidto): - return - -Given this predicate, we can avoid scheduling an operation. - -Disabling hooks -~~~~~~~~~~~~~~~ - -It is sometimes convenient to disable some hooks. For instance to -avoid infinite Hook loops. One uses the `hooks_control` context -manager. - -This can be controlled more finely through the `category` Hook class -attribute, which is a string. - -.. sourcecode:: python - - with hooks_control(self.session, self.session.HOOKS_ALLOW_ALL, ): - # ... do stuff - -.. autoclass:: cubicweb.server.session.hooks_control - -The existing categories are: ``email``, ``syncsession``, -``syncschema``, ``bookmark``, ``security``, ``worfklow``, -``metadata``, ``notification``, ``integrity``, ``activeintegrity``. - -Nothing precludes one to invent new categories and use the -hooks_control context manager to filter them (in or out). diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devrepo/index.rst --- a/doc/book/en/development/devrepo/index.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -.. -*- coding: utf-8 -*- - -Repository customization -++++++++++++++++++++++++ -.. toctree:: - :maxdepth: 1 - - sessions - hooks - notifications - tasks - - diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devrepo/notifications.rst --- a/doc/book/en/development/devrepo/notifications.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -.. -*- coding: utf-8 -*- - -Notifications management -======================== - -.. XXX FILLME diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devrepo/sessions.rst --- a/doc/book/en/development/devrepo/sessions.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -.. -*- coding: utf-8 -*- - -Sessions -======== - -There are three kinds of sessions. - -* `user sessions` are the most common: they are related to users and - carry security checks coming with user credentials - -* `super sessions` are children of ordinary user sessions and allow to - bypass security checks (they are created by calling unsafe_execute - on a user session); this is often convenient in hooks which may - touch data that is not directly updatable by users - -* `internal sessions` have all the powers; they are also used in only a - few situations where you don't already have an adequate session at - hand, like: user authentication, data synchronisation in - multi-source contexts - -.. note:: - Do not confuse the session type with their connection mode, for - instance : 'in memory' or 'pyro'. - -[WRITE ME] - -* authentication and management of sessions diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devrepo/tasks.rst --- a/doc/book/en/development/devrepo/tasks.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -.. -*- coding: utf-8 -*- - -Tasks -========= - -[WRITE ME] - -* repository tasks - diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/controllers.rst --- a/doc/book/en/development/devweb/controllers.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,199 +0,0 @@ -.. _controllers: - -Controllers ------------ - -Overview -++++++++ - -Controllers are responsible for taking action upon user requests -(loosely following the terminology of the MVC meta pattern). - -The following controllers are provided out-of-the box in CubicWeb. We -list them by category. - -`Browsing`: - -* the View controller (web/views/basecontrollers.py) is associated - with most browsing actions within a CubicWeb application: it always - instantiates a `main template` and lets the ResultSet/Views dispatch - system build up the whole content; it handles ObjectNotFound and - NoSelectableObject errors that may bubble up to its entry point, in - an end-user-friendly way (but other programming errors will slip - through) - -* the JSon controller (web/views/basecontrollers.py) provides services - for Ajax calls, typically using JSON as a serialization format for - input, and sometimes using either JSON or XML for output; - -* the Login/Logout controllers (web/views/basecontrollers.py) make - effective user login or logout requests - -`Edition`: - -* the Edit controller (see :ref:`edit_controller`) handles CRUD - 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 (web/views/basecontrollers.py) - provides form validation from Ajax context, using the Edit - controller, to implement the classic form 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 - for outgoing email notifications - -* the MailBugReport controller (web/views/basecontrollers.py) allows - to quickly have a `repotbug` feature in one's application - -Registration -++++++++++++ - -All controllers (should) live in the 'controllers' namespace within -the global registry. - -API -+++ - -Most API details should be resolved by source code inspection, as the -various controllers have differing goals. - -`web/controller.py` contains the top-level abstract Controller class and -its (NotImplemented) entry point `publish(rset=None)` method. - -A handful of helpers are also provided there: - -* process_rql builds a result set from an rql query typically issued - from the browser (and available through _cw.form['rql']) - -* validate_cache will force cache validation handling with respect to - the HTTP Cache directives (that were typically originally issued - from a previous server -> client response); concrete Controller - 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 -~~~~~~~~~~~~~~~~ - -Re-requisites: 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 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/css.rst --- a/doc/book/en/development/devweb/css.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ -.. -*- coding: utf-8 -*- - -CSS Stylesheet ---------------- -Conventions -~~~~~~~~~~~ - -XXX external_resources variable - naming convention - request.add_css - - -Extending / overriding existing styles -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -We cannot modify the order in which the application is reading the CSS. In -the case we want to create new CSS style, the best is to define it a in a new -CSS located under ``myapp/data/`` and use those new styles while writing -customized views and templates. - -If you want to modify an existing CSS styling property, you will have to use -``!important`` declaration to override the existing property. The application -apply a higher priority on the default CSS and you can not change that. -Customized CSS will not be read first. - - -CubicWeb stylesheets -~~~~~~~~~~~~~~~~~~~~ -XXX explain diffenrent files and main classes diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/facets.rst --- a/doc/book/en/development/devweb/facets.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,171 +0,0 @@ -The facets system ------------------ - -Facets allow to restrict searches according to some criteria. CubicWeb has a builtin `facet`_ system to define restrictions -`filters`_ really as easily as possible. A few base classes for facets -are provided in ``cubicweb.web.facet.py``. All classes inherits from -the base class ``AbstractFacet``. - -Here is an overview of the facets rendering pick from the `tracker` cube: - -.. image:: ../../images/facet_overview.png - -Facets will appear on each page presenting more than one entity. - - - -VocabularyFacet -~~~~~~~~~~~~~~~~ -The ``VocabularyFacet`` inherits from the ``AbstractFacet``. -A class which inherits from VocabularyFacets must redefine these methods: - -.. automethod:: cubicweb.web.facet.VocabularyFacet.vocabulary -.. automethod:: cubicweb.web.facet.VocabularyFacet.possible_values - -RelationFacet -~~~~~~~~~~~~~~ - -The ``RelationFacet`` inherits from the ``VocabularyFacet``. It allows to filter entities according to certain relation's values. Generally, you just have to define some class attributes like: - -- rtype: the name of the relation -- role: the default value is set to `subject` -- target_attr: needed if it is not the default attribute of the entity - - -To illustrate this facet, let's take for example an *excerpt* of the schema of an office location search application: - -.. sourcecode:: python - - class Office(WorkflowableEntityType): - price = Int(description='euros / m2 / HC / HT') - surface = Int(description='m2') - description = RichString(fulltextindexed=True) - has_address = SubjectRelation('PostalAddress', - cardinality='1?', - composite='subject') - proposed_by = SubjectRelation('Agency') - comments = ObjectRelation('Comment', - cardinality='1*', - composite='object') - screenshots = SubjectRelation(('File', 'Image'), - cardinality='*1', - composite='subject') - - -We define a facet to filter offices according to the attribute -`postalcode` of their associated `PostalAdress`. - -.. sourcecode:: python - - class PostalCodeFacet(RelationFacet): - __regid__ = 'postalcode-facet' # every registered class must have an id - __select__ = implements('Office') # this facet should only be selected when - # visualizing offices - rtype = 'has_address' # this facet is a filter on the entity linked to - # the office thrhough the relation - # has_address - target_attr = 'postalcode' # the filter's key is the attribute "postal_code" - # of the target PostalAddress entity - - -AttributeFacet -~~~~~~~~~~~~~~ - -The ``AttributeFacet`` inherits from the ``RelationFacet``. It allows to filter entities according to certain attribute's values. - -The example below resumes the former schema. We define now a filter based on the `surface` attribute of the -`Office`. - -.. sourcecode:: python - - class SurfaceFacet(AttributeFacet): - __regid__ = 'surface-facet' # every registered class must have an id - __select__ = implements('Office') # this facet should only be selected when - # visualizing offices - rtype = 'surface' # the filter's key is the attribute "surface" - comparator = '>=' # override the default value of operator since - # we want to filter according to a - # minimal - # value, not an exact one - - def rset_vocabulary(self, ___): - """override the default vocabulary method since we want to hard-code - our threshold values. - Not overriding would generate a filter box with all existing surfaces - defined in the database. - """ - return [('> 200', '200'), ('> 250', '250'), - ('> 275', '275'), ('> 300', '300')] - -RangeFacet -~~~~~~~~~~ -The ``RangeFacet`` inherits from the ``AttributeFacet``. It allows to filter entities according to certain attributes of numerical type. - -The ``RangeFacet`` displays a slider using `jquery`_ to choose a lower bound and an upper bound. - -The example below defines a facet to filter a selection of books according to their number of pages. - -.. sourcecode:: python - - class BookPagesFacet(RangeFacet): - __regid__ = 'priority-facet' - __select__ = RangeFacet.__select__ & implements('Book') - rtype = 'pages' - -The image below display the rendering of the ``RangeFacet``: - -.. image:: ../../images/facet_range.png - -DateRangeFacet -~~~~~~~~~~~~~~ -The ``DateRangeFacet`` inherits from the ``RangeFacet``. It allows to filter entities according to certain attributes of date type. - -Here is an example of code that defines a facet to filter -musical works according to their composition date: - -.. sourcecode:: python - - class CompositionDateFacet(DateRangeFacet): - # 1. make sure this facet is displayed only on Track selection - __select__ = DateRangeFacet.__select__ & implements('Track') - # 2. give the facet an id required by CubicWeb) - __regid__ = 'compdate-facet' - # 3. specify the attribute name that actually stores the date in the DB - rtype = 'composition_date' - -With this facet, on each page displaying tracks, you'll be able to filter them -according to their composition date with a jquery slider. - -The image below display the rendering of the ``DateRangeFacet``: - -.. image:: ../../images/facet_date_range.png - - -HasRelationFacet -~~~~~~~~~~~~~~~~ - -The ``DateRangeFacet`` inherits from the ``AbstractFacet``. It will -display a simple checkbox and lets you refine your selection in order -to get only entities that actually use this relation. - -Here is an example of the rendering of the ``HasRelationFacet`` to -filter entities with image and the corresponding code: - -.. image:: ../../images/facet_has_image.png - -.. sourcecode:: python - - class HasImageFacet(HasRelationFacet): - __regid__ = 'hasimage-facet' - __select__ = HasRelationFacet.__select__ & implements('Book') - rtype = 'has_image' - - - -To use ``HasRelationFacet`` on a reverse relation add ``role = 'object'`` in -it's definitions. - -.. _facet: http://en.wikipedia.org/wiki/Faceted_browser -.. _filters: http://www.cubicweb.org/blogentry/154152 -.. _jquery: http://www.jqueryui.com/ - diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/form.rst --- a/doc/book/en/development/devweb/form.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,213 +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.formfields -.. automodule:: cubicweb.web.formwidgets -.. automodule:: cubicweb.web.views.forms -.. automodule:: cubicweb.web.views.autoform -.. automodule:: cubicweb.web.views.formrenderers - - -Now what ? 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' - - -.. Example of entity fields form diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/httpcaching.rst --- a/doc/book/en/development/devweb/httpcaching.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -HTTP cache management ---------------------- -XXX feedme \ No newline at end of file diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/index.rst --- a/doc/book/en/development/devweb/index.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ -Web development -=============== - -In this chapter, we will describe the core APIs for web development in -the *CubicWeb* framework. - -.. toctree:: - :maxdepth: 2 - - publisher - controllers - request - views/index - rtags - js - css - form - facets - internationalization - property - httpcaching diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/internationalization.rst --- a/doc/book/en/development/devweb/internationalization.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,222 +0,0 @@ -.. -*- coding: utf-8 -*- - -.. _internationalization: - -Internationalization ---------------------- - -Cubicweb fully supports the internalization of its content and interface. - -Cubicweb's interface internationalization is based on the translation project `GNU gettext`_. - -.. _`GNU gettext`: http://www.gnu.org/software/gettext/ - -Cubicweb' internalization involves two steps: - -* in your Python code and cubicweb-tal templates : mark translatable strings - -* in your instance : handle the translation catalog, edit translations - -String internationalization -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -User defined string -``````````````````` - -In the Python code and cubicweb-tal templates translatable strings can be -marked in one of the following ways : - - * by using the *built-in* function `_` :: - - class PrimaryView(EntityView): - """the full view of an non final entity""" - __regid__ = 'primary' - title = _('primary') - - OR - - * by using the equivalent request's method :: - - class NoResultView(View): - """default view when no result has been found""" - __regid__ = 'noresult' - - def call(self, **kwargs): - self.w(u'
%s
\n' - % self._cw._('No result matching query')) - -The goal of the *built-in* function `_` is only **to mark the -translatable strings**, it will only return the string to translate -itself, but not its translation (it's actually another name for the -`unicode` builtin). - -In the other hand the request's method `self._cw._` is also meant to -retrieve the proper translation of translation strings in the -requested language. - -Finally you can also use the `__` attribute of request object to get a -translation for a string *which should not itself added to the catalog*, -usually in case where the actual msgid is created by string interpolation :: - - self._cw.__('This %s' % etype) - -In this example ._cw.__` is used instead of ._cw._` so we don't have 'This %s' in -messages catalogs. - -Translations in cubicweb-tal template can also be done with TAL tags -`i18n:content` and `i18n:replace`. - -If you need to add messages on top of those that can be found in the source, -you can create a file named `i18n/static-messages.pot`. - -You could put there messages not found in the python sources or -overrides for some messages of used cubes. - -Generated string -```````````````` - -We do not need to mark the translation strings of entities/relations used by a -particular instance's schema as they are generated automatically. String for -various actions are also generated. - -For exemple the following schema :: - - Class EntityA(EntityType): - relation_a2b = SubjectRelation('EntityB') - - class EntityB(EntityType): - pass - -May generate the following message :: - - add EntityA relation_a2b EntityB subject - -This message will be used in views of ``EntityA`` for creation of a new -``EntityB`` with a preset relation ``relation_a2b`` between the current -``EntityA`` and the new ``EntityB``. The opposite message :: - - add EntityA relation_a2b EntityB object - -Is used for similar creation of an ``EntityA`` from a view of ``EntityB``. The -title of they respective creation form will be :: - - creating EntityB (EntityA %(linkto)s relation_a2b EntityB) - - creating EntityA (EntityA relation_a2b %(linkto)s EntityA) - -In the translated string you can use ``%(linkto)s`` for reference to the source -``entity``. - -Handling the translation catalog -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Once the internationalization is done in your code, you need to populate and -update the translation catalog. Cubicweb provides the following commands for this -purpose: - - -* `i18ncubicweb` updates Cubicweb framework's translation - catalogs. Unless you actually work on the framework itself, you - don't need to use this command. - -* `i18ncube` updates the translation catalogs of *one particular cube* - (or of all cubes). After this command is executed you must update - the translation files *.po* in the "i18n" directory of your - cube. This command will of course not remove existing translations - still in use. It will mark unused translation but not remove them. - -* `i18ninstance` recompiles the translation catalogs of *one particular - instance* (or of all instances) after the translation catalogs of - its cubes have been updated. This command is automatically - called every time you create or update your instance. The compiled - catalogs (*.mo*) are stored in the i18n//LC_MESSAGES of - instance where `lang` is the language identifier ('en' or 'fr' - for exemple). - - -Example -``````` - -You have added and/or modified some translation strings in your cube -(after creating a new view or modifying the cube's schema for exemple). -To update the translation catalogs you need to do: - -1. `cubicweb-ctl i18ncube ` -2. Edit the /i18n/xxx.po files and add missing translations (empty `msgstr`) -3. `hg ci -m "updated i18n catalogs"` -4. `cubicweb-ctl i18ninstance ` - -Editing po files -~~~~~~~~~~~~~~~~ - -Using a PO aware editor -```````````````````````` - -Many tools exist to help maintain .po (PO) files. Common editors or -development environment provides modes for these. One can also find -dedicated PO files editor, such as `poedit`_. - -.. _`poedit`: http://www.poedit.net/ - -While usage of such a tool is commendable, PO files are perfectly -editable with a (unicode aware) plain text editor. It is also useful -to know their structure for troubleshooting purposes. - -Structure of a PO file -`````````````````````` - -In this section, we selectively quote passages of the `GNU gettext`_ -manual chapter on PO files, available there:: - - http://www.gnu.org/software/hello/manual/gettext/PO-Files.html - -One PO file entry has the following schematic structure:: - - white-space - # translator-comments - #. extracted-comments - #: reference... - #, flag... - #| msgid previous-untranslated-string - msgid untranslated-string - msgstr translated-string - - -A simple entry can look like this:: - - #: lib/error.c:116 - msgid "Unknown system error" - msgstr "Error desconegut del sistema" - -It is also possible to have entries with a context specifier. They -look like this:: - - white-space - # translator-comments - #. extracted-comments - #: reference... - #, flag... - #| msgctxt previous-context - #| msgid previous-untranslated-string - msgctxt context - msgid untranslated-string - msgstr translated-string - - -The context serves to disambiguate messages with the same -untranslated-string. It is possible to have several entries with the -same untranslated-string in a PO file, provided that they each have a -different context. Note that an empty context string and an absent -msgctxt line do not mean the same thing. - -Contexts and CubicWeb -````````````````````` - -CubicWeb PO files have both non-contextual and contextual msgids. - -Contextual entries are automatically used in some cases. For instance, -entity.dc_type(), eschema.display_name(req) or display_name(etype, -req, form, context) methods/function calls will use them. - -It is also possible to explicitly use the with _cw.pgettext(context, -msgid). diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/js.rst --- a/doc/book/en/development/devweb/js.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,355 +0,0 @@ -.. -*- coding: utf-8 -*- - -Javascript ----------- - -*CubicWeb* uses quite a bit of javascript in its user interface and -ships with jquery (1.3.x) and parts of the jquery UI library, plus a -number of homegrown files and also other third party libraries. - -All javascript files are stored in cubicweb/web/data/. There are -around thirty js files there. In a cube it goes to data/. - -Obviously one does not want javascript pieces to be loaded all at -once, hence the framework provides a number of mechanisms and -conventions to deal with javascript resources. - -Conventions -~~~~~~~~~~~ - -It is good practice to name cube specific js files after the name of -the cube, like this : 'cube.mycube.js', so as to avoid name clashes. - -XXX external_resources variable (which needs love) - -CubicWeb javascript API -~~~~~~~~~~~~~~~~~~~~~~~ - -Javascript resources are typically loaded on demand, from views. The -request object (available as self._cw from most application objects, -for instance views and entities objects) has a few methods to do that: - -* `add_js(self, jsfiles, localfile=True)` which takes a sequence of - javascript files and writes proper entries into the HTML header - section. The localfile parameter allows to declare resources which - are not from web/data (for instance, residing on a content delivery - network). - -* `add_onload(self, jscode)` which adds one raw javascript code - snippet inline in the html headers. This is quite useful for setting - up early jQuery(document).ready(...) initialisations. - -CubicWeb javascript events -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* ``server-response``: this event is triggered on HTTP responses (both - standard and ajax). The two following extra parameters are passed - to callbacks : - - - ``ajax``: a boolean that says if the reponse was issued by an - ajax request - - - ``node``: the DOM node returned by the server in case of an - ajax request, otherwise the document itself for standard HTTP - requests. - -Important AJAX APIS -~~~~~~~~~~~~~~~~~~~ - -* `asyncRemoteExec` and `remoteExec` are the base building blocks for - doing arbitrary async (resp. sync) communications with the server - -* `reloadComponent` is a convenience function to replace a DOM node - with server supplied content coming from a specific registry (this - is quite handy to refresh the content of some boxes for instances) - -* `jQuery.fn.loadxhtml` is an important extension to jQuery which - allows proper loading and in-place DOM update of xhtml views. It is - suitably augmented to trigger necessary events, and process CubicWeb - specific elements such as the facet system, fckeditor, etc. - - -A simple example with asyncRemoteExec -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In the python side, we have to extend the BaseController class. The -@jsonize decorator ensures that the `return value` of the method is -encoded as JSON data. By construction, the JSonController inputs -everything in JSON format. - -.. sourcecode: python - - from cubicweb.web.views.basecontrollers import JSonController, jsonize - - @monkeypatch(JSonController) - @jsonize - def js_say_hello(self, name): - return u'hello %s' % name - -In the javascript side, we do the asynchronous call. Notice how it -creates a `deferred` object. Proper treatment of the return value or -error handling has to be done through the addCallback and addErrback -methods. - -.. sourcecode: javascript - - function asyncHello(name) { - var deferred = asyncRemoteExec('say_hello', name); - deferred.addCallback(function (response) { - alert(response); - }); - deferred.addErrback(function (error) { - alert('something fishy happened'); - }); - } - - function syncHello(name) { - alert( remoteExec('say_hello', name) ); - } - -Anatomy of a reloadComponent call -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -`reloadComponent` allows to dynamically replace some DOM node with new -elements. It has the following signature: - -* `compid` (mandatory) is the name of the component to be reloaded - -* `rql` (optional) will be used to generate a result set given as - argument to the selected component - -* `registry` (optional) defaults to 'components' but can be any other - valid registry name - -* `nodeid` (optional) defaults to compid + 'Component' but can be any - explicitly specified DOM node id - -* `extraargs` (optional) should be a dictionary of values that will be - given to the cell_call method of the component - -A simple reloadComponent example -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The server side implementation of `reloadComponent` is the -js_component method of the JSonController. - -The following function implements a two-steps method to delete a -standard bookmark and refresh the UI, while keeping the UI responsive. - -.. sourcecode:: javascript - - function removeBookmark(beid) { - d = asyncRemoteExec('delete_bookmark', beid); - d.addCallback(function(boxcontent) { - reloadComponent('bookmarks_box', '', 'boxes', 'bookmarks_box'); - document.location.hash = '#header'; - updateMessage(_("bookmark has been removed")); - }); - } - -`reloadComponent` is called with the id of the bookmark box as -argument, no rql expression (because the bookmarks display is actually -independant of any dataset context), a reference to the 'boxes' -registry (which hosts all left, right and contextual boxes) and -finally an explicit 'bookmarks_box' nodeid argument that stipulates -the target DOM node. - -Anatomy of a loadxhtml call -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -`jQuery.fn.loadxhtml` is an important extension to jQuery which allows -proper loading and in-place DOM update of xhtml views. The existing -`jQuery.load`_ function does not handle xhtml, hence the addition. The -API of loadxhtml is roughly similar to that of `jQuery.load`_. - -.. _`jQuery.load`: http://api.jquery.com/load/ - - -* `url` (mandatory) should be a complete url (typically referencing - the JSonController, but this is not strictly mandatory) - -* `data` (optional) is a dictionary of values given to the - controller specified through an `url` argument; some keys may have a - special meaning depending on the choosen controller (such as `fname` - for the JSonController); the `callback` key, if present, must refer - to a function to be called at the end of loadxhtml (more on this - below) - -* `reqtype` (optional) specifies the request method to be used (get or - post); if the argument is 'post', then the post method is used, - otherwise the get method is used - -* `mode` (optional) is one of `replace` (the default) which means the - loaded node will replace the current node content, `swap` to replace - the current node with the loaded node, and `append` which will - append the loaded node to the current node content - -About the `callback` option: - -* it is called with two parameters: the current node, and a list - containing the loaded (and post-processed node) - -* whenever is returns another function, this function is called in - turn with the same parameters as above - -This mechanism allows callback chaining. - - -A simple example with loadxhtml -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Here we are concerned with the retrieval of a specific view to be -injected in the live DOM. The view will be of course selected -server-side using an entity eid provided by the client side. - -.. sourcecode:: python - - from cubicweb import typed_eid - from cubicweb.web.views.basecontrollers import JSonController, xhtmlize - - @monkeypatch(JSonController) - @xhtmlize - def js_frob_status(self, eid, frobname): - entity = self._cw.entity_from_eid(typed_eid(eid)) - return entity.view('frob', name=frobname) - -.. sourcecode:: javascript - - function update_some_div(divid, eid, frobname) { - var params = {fname:'frob_status', eid: eid, frobname:frobname}; - jQuery('#'+divid).loadxhtml(JSON_BASE_URL, params, 'post'); - } - -In this example, the url argument is the base json url of a cube -instance (it should contain something like -`http://myinstance/json?`). The actual JSonController method name is -encoded in the `params` dictionary using the `fname` key. - -A more real-life example from CubicWeb -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A frequent use case of Web 2 applications is the delayed (or -on-demand) loading of pieces of the DOM. This is typically achieved -using some preparation of the initial DOM nodes, jQuery event handling -and proper use of loadxhtml. - -We present here a skeletal version of the mecanism used in CubicWeb -and available in web/views/tabs.py, in the `LazyViewMixin` class. - -.. sourcecode:: python - - def lazyview(self, vid, rql=None): - """ a lazy version of wview """ - w = self.w - self._cw.add_js('cubicweb.lazy.js') - urlparams = {'vid' : vid, 'fname' : 'view'} - if rql is not None: - urlparams['rql'] = rql - w(u'
' % ( - vid, xml_escape(self._cw.build_url('json', **urlparams)))) - w(u'
') - self._cw.add_onload(u""" - jQuery('#lazy-%(vid)s').bind('%(event)s', function() { - load_now('#lazy-%(vid)s');});""" - % {'event': 'load_%s' % vid, 'vid': vid}) - -This creates a `div` with a specific event associated to it. - -The full version deals with: - -* optional parameters such as an entity eid, an rset - -* the ability to further reload the fragment - -* the ability to display a spinning wheel while the fragment is still - not loaded - -* handling of browsers that do not support ajax (search engines, - text-based browsers such as lynx, etc.) - -The javascript side is quite simple, due to loadxhtml awesomeness. - -.. sourcecode:: javascript - - function load_now(eltsel) { - var lazydiv = jQuery(eltsel); - lazydiv.loadxhtml(lazydiv.attr('cubicweb:loadurl')); - } - -This is all significantly different of the previous `simple example` -(albeit this example actually comes from real-life code). - -Notice how the `cubicweb:loadurl` is used to convey the url -information. The base of this url is similar to the global javascript -JSON_BASE_URL. According to the pattern described earlier, -the `fname` parameter refers to the standard `js_view` method of the -JSonController. This method renders an arbitrary view provided a view -id (or `vid`) is provided, and most likely an rql expression yielding -a result set against which a proper view instance will be selected. - -The `cubicweb:loadurl` is one of the 29 attributes extensions to XHTML -in a specific cubicweb namespace. It is a means to pass information -without breaking HTML nor XHTML compliance and without resorting to -ungodly hacks. - -Given all this, it is easy to add a small nevertheless useful feature -to force the loading of a lazy view (for instance, a very -computation-intensive web page could be scinded into one fast-loading -part and a delayed part). - -On the server side, a simple call to a javascript function is -sufficient. - -.. sourcecode:: python - - def forceview(self, vid): - """trigger an event that will force immediate loading of the view - on dom readyness - """ - self._cw.add_onload("trigger_load('%s');" % vid) - -The browser-side definition follows. - -.. sourcecode:: javascript - - function trigger_load(divid) { - jQuery('#lazy-' + divd).trigger('load_' + divid); - } - - - - -XXX reloadComponent -XXX userCallback / user_callback - -Javascript library: overview -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* jquery.* : jquery and jquery UI library - -* cubicweb.ajax.js : concentrates all ajax related facilities (it - extends jQuery with the loahxhtml function, provides a handfull of - high-level ajaxy operations like asyncRemoteExec, reloadComponent, - replacePageChunk, getDomFromResponse) - -* cubicweb.python.js : adds a number of practical extension to stdanrd - javascript objects (on Date, Array, String, some list and dictionary - operations), and a pythonesque way to build classes. Defines a - CubicWeb namespace. - -* cubicweb.htmlhelpers.js : a small bag of convenience functions used - in various other cubicweb javascript resources (baseuri, progress - cursor handling, popup login box, html2dom function, etc.) - -* cubicweb.widgets.js : provides a widget namespace and constructors - and helpers for various widgets (mainly facets and timeline) - -* cubicweb.edition.js : used by edition forms - -* cubicweb.preferences.js : used by the preference form - -* cubicweb.facets.js : used by the facets mechanism - -There is also javascript support for massmailing, gmap (google maps), -fckcwconfig (fck editor), timeline, calendar, goa (CubicWeb over -AppEngine), flot (charts drawing), tabs and bookmarks. diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/property.rst --- a/doc/book/en/development/devweb/property.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -The property mecanism ---------------------- -XXX CWProperty and co - - -Property API -~~~~~~~~~~~~ -XXX feed me - -Registering and using your own property -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -XXX feed me diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/publisher.rst --- a/doc/book/en/development/devweb/publisher.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -.. _publisher: - -Publisher ---------- - -What happens when an HTTP request is issued ? - -The story begins with the ``CubicWebPublisher.main_publish`` -method. We do not get upper in the bootstrap process because it is -dependant on the used HTTP library. With `twisted`_ however, -``cubicweb.etwist.server.CubicWebRootResource.render_request`` is the -real entry point. - -What main_publish does: - -* get a controller id and a result set from the path (this is actually - delegated to the `urlpublisher` component) - -* the controller is then selected (if not, this is considered an - authorization failure and signaled as such) and called - -* then either a proper result is returned, in which case the - request/connection object issues a ``commit`` and returns the result - -* or error handling must happen: - - * ``ValidationErrors`` pop up there and may lead to a redirect to a - previously arranged url or standard error handling applies - * an HTTP 500 error (`Internal Server Error`) is issued - - -Now, let's turn to the controller. There are many of them in -:mod:`cubicweb.web.views.basecontrollers`. We can just follow the -default `view` controller that is selected on a `view` path. See the -:ref:`controllers` chapter for more information on controllers. - -The `View` controller's entry point is the `publish` method. It does -the following: - -* compute the `main` view to be applied, using either the given result - set or building one from a user provided rql string (`rql` and `vid` - can be forced from the url GET parameters), that is: - - * compute the `vid` using the result set and the schema (see - `cubicweb.web.views.vid_from_rset`) - * handle all error cases that could happen in this phase - -* do some cache management chores - -* select a main template (typically `TheMainTemplate`, see chapter - :ref:`templates`) - -* call it with the result set and the computed view. - -What happens next actually depends on the template and the view, but -in general this is the rendering phase. - - -CubicWebPublisher API -````````````````````` - -.. autoclass:: cubicweb.web.application.CubicWebPublisher - :members: diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/request.rst --- a/doc/book/en/development/devweb/request.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ -The `Request` class (`cubicweb.web`) ------------------------------------- - -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. - -**Globally, a request represents a user query, either through HTTP or not -(we also talk about RQL queries on the server side for example).** - -An instance of `Request` has the following attributes: - -* `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 - -But also: - -* `Session data handling` - - * `session_data()`, returns a dictionary containing all the session data - * `get_session_data(key, default=None)`, returns a value associated to the given - key or the value `default` if the key is not defined - * `set_session_data(key, value)`, assign a value to a key - * `del_session_data(key)`, suppress the value associated to a key - -* `Cookies handling` - - * `get_cookie()`, returns a dictionary containing the value of the header - HTTP 'Cookie' - * `set_cookie(cookie, key, maxage=300)`, adds a header HTTP `Set-Cookie`, - with a minimal 5 minutes length of duration by default (`maxage` = None - returns a *session* cookie which will expire when the user closes the browser - window) - * `remove_cookie(cookie, key)`, forces a value to expire - -* `URL handling` - - * `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 - -* `And more...` - - * `set_content_type(content_type, filename=None)`, adds the header HTTP - 'Content-Type' - * `get_header(header)`, returns the value associated to an arbitrary header - of the HTTP request - * `set_header(header, value)`, adds an arbitrary header in the response - * `cursor()` returns a RQL cursor on the session - * `execute(*args, **kwargs)`, shortcut to ``.cursor().execute()`` - * `property_value(key)`, properties management (`CWProperty`) - * dictionary `data` to store data to share informations between components - *while a request is executed* - -Please note that this class is abstract and that a concrete implementation -will be provided by the *frontend* web used (in particular *twisted* as of -today). For the views or others that are executed on the server side, -most of the interface of `Request` is defined in the session associated -to the client. - -API -``` - -The elements we gave in overview for above are built in three layers, -from ``cubicweb.req.RequestSessionBase``, ``cubicweb.dbapi.DBAPIRequest`` and -``cubicweb.web.CubicWebRequestBase``. - -.. autoclass:: cubicweb.req.RequestSessionBase - :members: - -.. autoclass:: cubicweb.dbapi.DBAPIRequest - :members: - -.. autoclass:: cubicweb.web.request.CubicWebRequestBase - :members: diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/rtags.rst --- a/doc/book/en/development/devweb/rtags.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ -Configuring the user interface ------------------------------- - -.. _relation_tags: - -Relation tags -~~~~~~~~~~~~~ -.. automodule:: cubicweb.rtags - -.. _uicfg: - -The ``uicfg`` module -~~~~~~~~~~~~~~~~~~~~ - -.. note:: - - The part of uicfg that deals with primary views is in the - :ref:`primary_view_configuration` chapter. - -.. automodule:: cubicweb.web.uicfg - diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/views/basetemplates.rst --- a/doc/book/en/development/devweb/views/basetemplates.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,115 +0,0 @@ -.. -*- coding: utf-8 -*- - -.. |cubicweb| replace:: *CubicWeb* - -.. _templates: - -Templates -========= - -Templates are the entry point for the |cubicweb| view system. As seen -in :ref:`views_base_class`, there are two kinds of views: the -templatable and non-templatable. - -Non-templatable views are standalone. They are responsible for all the -details such as setting a proper content type (or mime type), the -proper document headers, namespaces, etc. Examples are pure xml views -such as RSS or Semantic Web views (`SIOC`_, `DOAP`_, `FOAF`_, `Linked -Data`_, etc.). - -.. _`SIOC`: http://sioc-project.org/ -.. _`DOAP`: http://trac.usefulinc.com/doap -.. _`FOAF`: http://www.foaf-project.org/ -.. _`Linked Data`: http://linkeddata.org/ - -Templatable views are not concerned with such pesky details. They -leave it to the template. Conversely, the template's main job is to: - -* set up the proper document header and content type -* define the general layout of a document -* invoke adequate views in the various sections of the document - - -Look at :mod:`cubicweb.web.views.basetemplates` and you will find the -base templates used to generate (X)HTML for your application. The most -important template there is `TheMainTemplate`. - -.. _the_main_template_layout: - -TheMainTemplate ---------------- - -.. _the_main_template_sections: - -Layout and sections -``````````````````` - -A page is composed as indicated on the schema below : - -.. image:: ../../../images/main_template.png - -The sections dispatches specific views: - -* `header`: the rendering of the header is delegated to the - `htmlheader` view, whose default implementation can be found in - ``basetemplates.py`` and which does the following things: - - * inject the favicon if there is one - * inject the global style sheets and javascript resources - * call and display a link to an rss component if there is one available - - it also sets up the page title, and fills the actual - `header` section with top-level components, using the `header` view, which: - - * tries to display a logo, the name of the application and the `breadcrumbs` - * provides a login status area - * provides a login box (hiden by default) - -* `left column`: this is filled with all selectable boxes matching the - `left` context (there is also a right column but nowadays it is - seldom used due to bad usability) - -* `contentcol`: this is the central column; it is filled with: - - * the `rqlinput` view (hidden by default) - * the `applmessages` component - * the `contentheader` view which in turns dispatches all available - content navigation components having the `navtop` context (this - is used to navigate through entities implementing the IPrevNext - interface) - * the view that was given as input to the template's `call` - method, also dealing with pagination concerns - * the `contentfooter` - -* `footer`: adds all footer actions - -.. note:: - - How and why a view object is given to the main template is explained - in the :ref:`publisher` chapter. - -Class attributes -```````````````` - -We can also control certain aspects of the main template thanks to the following -forms parameters: - -* `__notemplate`, if present (whatever the value assigned), only the content view - is returned -* `__force_display`, if present and its value is not null, no navigation - whatever the number of entities to display -* `__method`, if the result set to render contains only one entity and this - parameter is set, it refers to a method to call on the entity by passing it - the dictionary of the forms parameters, before going the classic way (through - step 1 and 2 described juste above) - -Other templates ---------------- - -Other standard templates include: - -* `login` and `logout` - -* `error-template` specializes TheMainTemplate to do proper end-user - output if an error occurs during the computation of TheMainTemplate - (it is a fallback view). diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/views/baseviews.rst --- a/doc/book/en/development/devweb/views/baseviews.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,84 +0,0 @@ -.. -*- coding: utf-8 -*- - -Base views ----------- - -*CubicWeb* provides a lot of standard views, that can be found in - :mod:`cubicweb.web.views` and :mod:`cubicweb.web.views.baseviews`. - -A certain number of views are used to build the web interface, which -apply to one or more entities. Their identifier is what distinguish -them from each others and the main ones are: - -HTML views -~~~~~~~~~~ - -Special views -````````````` - -*noresult* - This view is the default view used when no result has been found - (e.g. empty result set). - -*final* - Display the value of a cell without trasnformation (in case of a non final - entity, we see the eid). Applicable on any result set. - -.. note:: - - `final` entities are merely attributes. - -*null* - This view is the default view used when nothing needs to be rendered. - It is always applicable. - -Entity views -```````````` - -*incontext, outofcontext* - Those are used to display a link to an entity, depending on the - entity having to be displayed in or out of context - (of another entity). By default it respectively produces the - result of `textincontext` and `textoutofcontext` wrapped in a link - leading to the primary view of the entity. - -*oneline* - This view is used when we can't tell if the entity should be considered as - displayed in or out of context. By default it produces the result of `text` - in a link leading to the primary view of the entity. - -List -````` - -*list* - This view displays a list of entities by creating a HTML list (`
    `) - and call the view `listitem` for each entity of the result set. - -*listitem* - This view redirects by default to the `outofcontext` view. - -*sameetypelist* - This view displays a list of entities of the same type, in HTML section (`
    `) - and call the view `sameetypelistitem` for each entity of the result set. - -*sameetypelistitem* - This view redirects by default to the `listitem` view. - -*csv* - This view applies to entity groups, which are individually - displayed using the `incontext` view. It displays each entity as a - coma separated list. It is NOT related to the well-known text file - format. - -Text entity views -~~~~~~~~~~~~~~~~~ - -*text* - This is the simplest text view for an entity. By default it returns the - result of the `.dc_title` method, which is cut to fit the - `navigation.short-line-size` property if necessary. - -*textincontext, textoutofcontext* - Similar to the `text` view, but called when an entity is considered out or - in context. By default it returns respectively the result of the - methods `.dc_title` and `.dc_long_title` of the entity. diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/views/boxes.rst --- a/doc/book/en/development/devweb/views/boxes.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -Boxes ------ - -(:mod:`cubicweb.web.views.boxes`) - -*sidebox* - This view displays usually a side box of some related entities - in a primary view. - -The action box -~~~~~~~~~~~~~~~ - -The ``add_related`` is an automatic menu in the action box that allows to create -an entity automatically related to the initial entity (context in -which the box is displayed). By default, the links generated in this -box are computed from the schema properties of the displayed entity, -but it is possible to explicitly specify them thanks to the -`cubicweb.web.uicfg.rmode` *relation tag*: - -* `link`, indicates that a relation is in general created pointing - to an existing entity and that we should not to display a link - for this relation - -* `create`, indicates that a relation is in general created pointing - to new entities and that we should display a link to create a new - entity and link to it automatically - - -If necessary, it is possible to overwrite the method -`relation_mode(rtype, targettype, x='subject')` to dynamically -compute a relation creation category. - -Please note that if at least one action belongs to the `addrelated` category, -the automatic behavior is desactivated in favor of an explicit behavior -(e.g. display of `addrelated` category actions only). - diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/views/breadcrumbs.rst --- a/doc/book/en/development/devweb/views/breadcrumbs.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -Breadcrumbs ------------ - -Breadcrumbs are a navigation component to help the user locate himself -along a path of entities. - -Display -~~~~~~~ - -Breadcrumbs are displayed by default in the header section (see -:ref:`the_main_template_sections`). With the default main -template, the header section is composed by the logo, the application -name, breadcrumbs and, at the most right, the login box. Breadcrumbs -are displayed just next to the application name, thus breadcrumbs -begin with a separator. - -Here is the header section of the CubicWeb's forge: - -.. image:: ../../../images/breadcrumbs_header.png - -There are three breadcrumbs components defined in -:mod:`cubicweb.web.views.ibreadcrumbs`: - -- `BreadCrumbEntityVComponent`: displayed for a result set with one line - if the entity implements the ``IBreadCrumbs`` interface. -- `BreadCrumbETypeVComponent`: displayed for a result set with more than - one line, but with all entities of the same type which implement the - ``IBreadCrumbs`` interface. -- `BreadCrumbAnyRSetVComponent`: displayed for any other result set. - -Building breadcrumbs -~~~~~~~~~~~~~~~~~~~~ - -The ``IBreadCrumbs`` interface is defined in the -:mod:`cubicweb.interfaces` module. It specifies that an entity which -implements this interface must have a ``breadcrumbs`` method. - -.. note:: - - Redefining the breadcrumbs is the hammer way to do it. Another way - is to define the `parent` method on an entity (as defined in the - `ITree` interface). If available, it will be used to compute - breadcrumbs. - -Here is the API of the ``breadcrumbs`` method: - -.. automethod:: cubicweb.interfaces.IBreadCrumbs.breadcrumbs - -If the breadcrumbs method return a list of entities, the -``cubicweb.web.views.ibreadcrumbs.BreadCrumbView`` is used to display -the elements. - -By default, for any entity, if recurs=True, breadcrumbs method returns -a list of entities, else a list of a simple string. - -In order to see a hierarchical breadcrumbs, entities must have a -``parent`` method which returns the parent entity. By default this -method doesn't exist on entity, given that it can not be guessed. diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/views/editforms.rst --- a/doc/book/en/development/devweb/views/editforms.rst Fri Apr 23 17:07:55 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 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/views/embedding.rst --- a/doc/book/en/development/devweb/views/embedding.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -.. -*- coding: utf-8 -*- - -Embedding external pages ------------------------- - -(:mod:`cubicweb.web.views.embedding`) - -including external content - diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/views/idownloadable.rst --- a/doc/book/en/development/devweb/views/idownloadable.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -The 'download' view -------------------- - -(:mod:`cubicweb.web.views.idownloadable`) - diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/views/index.rst --- a/doc/book/en/development/devweb/views/index.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -The View system -=============== - -This chapter aims to describe the concept of a `view` used all along -the development of a web application and how it has been implemented -in |cubicweb|. - - -.. toctree:: - :maxdepth: 3 - - views - basetemplates - primary - baseviews - startup - boxes - table - xmlrss -.. editforms - -.. toctree:: - :maxdepth: 3 - - urlpublish - breadcrumbs -.. wdoc -.. embedding -.. idownloadable - diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/views/primary.rst --- a/doc/book/en/development/devweb/views/primary.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,312 +0,0 @@ -.. _primary_view: - -The Primary View ------------------ - -(:mod:`cubicweb.web.views.primary`) - -By default, *CubicWeb* provides a view that fits every available -entity type. This is the first view you might be interested in -modifying. It is also one of the richest and most complex. - -It is automatically selected on a one line result set containing an -entity. - -This view is supposed to render a maximum of informations about the -entity. - -.. _primary_view_layout: - -Layout -`````` - -The primary view has the following layout. - -.. image:: ../../../images/primaryview_template.png - -.. _primary_view_configuration: - -Primary view configuration -`````````````````````````` - -If you want to customize the primary view of an entity, overriding the primary -view class may not be necessary. For simple adjustments (attributes or relations -display locations and styles), a much simpler way is to use uicfg. - -Attributes/relations display location -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -In the primary view, there are 3 sections where attributes and -relations can be displayed (represented in pink in the image above): - -* attributes -* relations -* sideboxes - -**Attributes** can only be displayed in the attributes section (default - behavior). They can also be hidden. - -For instance, to hide the ``title`` attribute of the ``Blog`` entity: - -.. sourcecode:: python - - from cubicweb.web import uicfg - uicfg.primaryview_section.tag_attribute(('Blog', 'title'), 'hidden') - -**Relations** can be either displayed in one of the three sections or hidden. - -For relations, there are two methods: - -* ``tag_object_of`` for modifying the primary view of the object -* ``tag_subject_of`` for modifying the primary view of the subject - -These two methods take two arguments: - -* a triplet ``(subject, relation_name, object)``, where subject or object can be replaced with ``'*'`` -* the section name or ``hidden`` - -.. sourcecode:: python - - pv_section = uicfg.primaryview_section - # hide every relation `entry_of` in the `Blog` primary view - pv_section.tag_object_of(('*', 'entry_of', 'Blog'), 'hidden') - - # display `entry_of` relations in the `relations` - # section in the `BlogEntry` primary view - pv_section.tag_subject_of(('BlogEntry', 'entry_of', '*'), 'relations') - - -Display content -^^^^^^^^^^^^^^^ - -You can use ``primaryview_display_ctrl`` to customize the display of attributes -or relations. Values of ``primaryview_display_ctrl`` are dictionaries. - - -Common keys for attributes and relations are: - -* ``vid``: specifies the regid of the view for displaying the attribute or the relation. - - If ``vid`` is not specified, the default value depends on the section: - * ``attributes`` section: 'reledit' view - * ``relations`` section: 'autolimited' view - * ``sideboxes`` section: 'sidebox' view - -* ``order``: int used to control order within a section. When not specified, - automatically set according to order in which tags are added. - -.. sourcecode:: python - - # let us remind the schema of a blog entry - class BlogEntry(EntityType): - title = String(required=True, fulltextindexed=True, maxsize=256) - publish_date = Date(default='TODAY') - content = String(required=True, fulltextindexed=True) - entry_of = SubjectRelation('Blog', cardinality='?*') - - # now, we want to show attributes - # with an order different from that in the schema definition - view_ctrl = uicfg.primaryview_display_ctrl - for index, attr in enumerate('title', 'content', 'publish_date'): - view_ctrl.tag_attribute(('BlogEntry', attr), {'order': index}) - -Keys for relations only: - -* ``label``: label for the relations section or side box - -* ``showlabel``: boolean telling whether the label is displayed - -* ``limit``: boolean telling if the results should be limited. If so, a link to all results is displayed - -* ``filter``: callback taking the related result set as argument and returning it filtered - -.. sourcecode:: python - - pv_section = uicfg.primaryview_section - # in `CWUser` primary view, display `created_by` - # relations in relations section - pv_section.tag_object_of(('*', 'created_by', 'CWUser'), 'relations') - - # display this relation as a list, sets the label, - # limit the number of results and filters on comments - def filter_comment(rset): - return rset.filtered_rset(lambda x: x.e_schema == 'Comment') - pv_ctrl = uicfg.primaryview_display_ctrl - pv_ctrl.tag_object_of(('*', 'created_by', 'CWUser'), - {'vid': 'list', 'label': _('latest comment(s):'), - 'limit': True, - 'filter': filter_comment}) - -.. warning:: with the ``primaryview_display_ctrl`` rtag, the subject or the - object of the relation is ignored for respectively ``tag_object_of`` or - ``tag_subject_of``. To avoid warnings during execution, they should be set to - ``'*'``. - -Rendering methods and attributes -```````````````````````````````` - -The basic layout of a primary view is as in the -:ref:`primary_view_layout` section. This layout is actually drawn by -the `render_entity` method. - -The methods you may want to modify while customizing a ``PrimaryView`` -are: - -*render_entity_title(self, entity)* - Renders the entity title using the ``def dc_title(self)`` method. - -*render_entity_metadata(self, entity)* - Renders the entity metadata by calling the ``metadata`` view on the - entity. This generic view is in cubicweb.views.baseviews. - -*render_entity_attributes(self, entity)* - Renders all the attribute of an entity with the exception of - attribute of type `Password` and `Bytes`. The skip_none class - attribute controls the display of None valued attributes. - -*render_entity_relations(self, entity)* - Renders all the relations of the entity in the main section of the page. - -*render_side_boxes(self, entity, boxes)* - Renders relations of the entity in a side box. - -The placement of relations in the relations section or in side boxes -can be controlled through the :ref:`primary_view_configuration` mechanism. - -*content_navigation_components(self, context)* - This method is applicable only for entity type implementing the interface - `IPrevNext`. This interface is for entities which can be linked to a previous - and/or next entity. This method will render the navigation links between - entities of this type, either at the top or at the bottom of the page - given the context (navcontent{top|bottom}). - -Also, please note that by setting the following attributes in your -subclass, you can already customize some of the rendering: - -*show_attr_label* - Renders the attribute label next to the attribute value if set to True. - Otherwise, does only display the attribute value. - -*show_rel_label* - Renders the relation label next to the relation value if set to True. - Otherwise, does only display the relation value. - -*skip_none* - Does not render an attribute value that is None if set to True. - -*main_related_section* - Renders the relations of the entity if set to True. - -A good practice is for you to identify the content of your entity type for which -the default rendering does not answer your need so that you can focus on the specific -method (from the list above) that needs to be modified. We do not advise you to -overwrite ``render_entity`` unless you want a completely different layout. - -Example of customization and creation -````````````````````````````````````` - -We'll show you now an example of a ``primary`` view and how to customize it. - -We continue along the basic tutorial :ref:`tuto_blog`. - -If you want to change the way a ``BlogEntry`` is displayed, just override -the method ``cell_call()`` of the view ``primary`` in ``BlogDemo/views.py``. - -.. sourcecode:: python - - from cubicweb.selectors import implements - from cubicweb.web.views.primary import Primaryview - - class BlogEntryPrimaryView(PrimaryView): - __select__ = PrimaryView.__select__ & implements('BlogEntry') - - def render_entity_attributes(self, entity): - self.w(u'

    published on %s

    ' % - entity.publish_date.strftime('%Y-%m-%d')) - super(BlogEntryPrimaryView, self).render_entity_attributes(entity) - -The above source code defines a new primary view for -``BlogEntry``. The `id` class attribute is not repeated there since it -is inherited through the `primary.PrimaryView` class. - -The selector for this view chains the selector of the inherited class -with its own specific criterion. - -The view method ``self.w()`` is used to output data. Here `lines -08-09` output HTML for the publication date of the entry. - -.. image:: ../../../images/lax-book_09-new-view-blogentry_en.png - :alt: blog entries now look much nicer - -Let us now improve the primary view of a blog - -.. sourcecode:: python - - from logilab.mtconverter import xml_escape - from cubicweb.selectors import implements, one_line_rset - from cubicweb.web.views.primary import Primaryview - - class BlogPrimaryView(PrimaryView): - __regid__ = 'primary' - __select__ = PrimaryView.__select__ & implements('Blog') - rql = 'Any BE ORDERBY D DESC WHERE BE entry_of B, BE publish_date D, B eid %(b)s' - - def render_entity_relations(self, entity): - rset = self._cw.execute(self.rql, {'b' : entity.eid}) - for entry in rset.entities(): - self.w(u'

    %s

    ' % entry.view('inblogcontext')) - - class BlogEntryInBlogView(EntityView): - __regid__ = 'inblogcontext' - __select__ = implements('BlogEntry') - - def cell_call(self, row, col): - entity = self.cw_rset.get_entity(row, col) - self.w(u'%s' % - entity.absolute_url(), - xml_escape(entity.content[:50]), - xml_escape(entity.description)) - -This happens in two places. First we override the -render_entity_relations method of a Blog's primary view. Here we want -to display our blog entries in a custom way. - -At `line 10`, a simple request is made to build a result set with all -the entities linked to the current ``Blog`` entity by the relationship -``entry_of``. The part of the framework handling the request knows -about the schema and infers that such entities have to be of the -``BlogEntry`` kind and retrieves them (in the prescribed publish_date -order). - -The request returns a selection of data called a result set. Result -set objects have an .entities() method returning a generator on -requested entities (going transparently through the `ORM` layer). - -At `line 13` the view 'inblogcontext' is applied to each blog entry to -output HTML. (Note that the 'inblogcontext' view is not defined -whatsoever in *CubicWeb*. You are absolutely free to define whole view -families.) We juste arrange to wrap each blogentry output in a 'p' -html element. - -Next, we define the 'inblogcontext' view. This is NOT a primary view, -with its well-defined sections (title, metadata, attribtues, -relations/boxes). All a basic view has to define is cell_call. - -Since views are applied to result sets which can be tables of data, we -have to recover the entity from its (row,col)-coordinates (`line -20`). Then we can spit some HTML. - -.. warning:: - - Be careful: all strings manipulated in *CubicWeb* are actually - unicode strings. While web browsers are usually tolerant to - incoherent encodings they are being served, we should not abuse - it. Hence we have to properly escape our data. The xml_escape() - function has to be used to safely fill (X)HTML elements from Python - unicode strings. - -Assuming we added entries to the blog titled `MyLife`, displaying it -now allows to read its description and all its entries. - -.. image:: ../../../images/lax-book_10-blog-with-two-entries_en.png - :alt: a blog and all its entries diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/views/startup.rst --- a/doc/book/en/development/devweb/views/startup.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -Startup views -------------- - -(:mod:`cubicweb.web.views.startup`) - -The usual selectors are no_rset or yes. These views don't apply to a -result set. - -*index* - This view defines the home page of your application. It does not require - a result set to apply to. - -*schema* - A view dedicated to the display of the schema of the instance - diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/views/table.rst --- a/doc/book/en/development/devweb/views/table.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -Table view ----------- - -(:mod:`cubicweb.web.views.tableview`) - -*table* - Creates a HTML table (``) and call the view `cell` for each cell of - the result set. Applicable on any result set. - -*cell* - By default redirects to the `final` view if this is a final entity or - `outofcontext` view otherwise - - -API -``` - -.. autoclass:: cubicweb.web.views.tableview.TableView - :members: diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/views/urlpublish.rst --- a/doc/book/en/development/devweb/views/urlpublish.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -.. -*- coding: utf-8 -*- - -URL publishing --------------- - -(:mod:`cubicweb.web.views.urlpublishing`) - -.. automodule:: cubicweb.web.views.urlpublishing - -.. autoclass:: cubicweb.web.views.urlpublishing.URLPublisherComponent - :members: - -URL rewriting -------------- - -(:mod:`cubicweb.web.views.urlrewrite`) - -.. autoclass:: cubicweb.web.views.urlrewrite.URLRewriter - :members: - -.. autoclass:: cubicweb.web.views.urlrewrite.SimpleReqRewriter - :members: - -.. autoclass:: cubicweb.web.views.urlrewrite.SchemaBasedRewriter - :members: - - -``SimpleReqRewriter`` is enough for a certain number of simple cases. If it is not sufficient, ``SchemaBasedRewriter`` allows to do more elaborate things. - -Here is an example of ``SimpleReqRewriter`` usage with plain string: - -.. sourcecode:: python - - from cubicweb.web.views.urlrewrite import SimpleReqRewriter - class TrackerSimpleReqRewriter(SimpleReqRewriter): - rules = [ - ('/versions', dict(vid='versionsinfo')), - ] - -When the url is `/versions`, the view with the __regid__ `versionsinfo` is displayed. - -Here is an example of ``SimpleReqRewriter`` usage with regular expressions: - -.. sourcecode:: python - - from cubicweb.web.views.urlrewrite import ( - SimpleReqRewriter, rgx) - - class BlogReqRewriter(SimpleReqRewriter): - rules = [ - (rgx('/blogentry/([a-z_]+)\.rss'), - dict(rql=('Any X ORDERBY CD DESC LIMIT 20 WHERE X is BlogEntry,' - 'X creation_date CD, X created_by U, ' - 'U login "%(user)s"' - % {'user': r'\1'}, vid='rss'))), - ] - -When a url matches the regular expression, the view with the __regid__ -`rss` which match the result set is displayed. - -Here is an example of ``SchemaBasedRewriter`` usage: - -.. sourcecode:: python - - from cubicweb.web.views.urlrewrite import ( - SchemaBasedRewriter, rgx, build_rset) - - class TrackerURLRewriter(SchemaBasedRewriter): - rules = [ - (rgx('/project/([^/]+)/([^/]+)/tests'), - build_rset(rql='Version X WHERE X version_of P, P name %(project)s, X num %(num)s', - rgxgroups=[('project', 1), ('num', 2)], vid='versiontests')), - ] diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/views/views.rst --- a/doc/book/en/development/devweb/views/views.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,142 +0,0 @@ - -.. _Views: - -Principles ----------- - -We'll start with a description of the interface providing a basic -understanding of the available classes and methods, then detail the -view selection principle. - -A `View` is an object responsible for the rendering of data from the -model into an end-user consummable form. They typically churn out an -XHTML stream, but there are views concerned with email other non-html -outputs. - -.. _views_base_class: - -Discovering possible views -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -It is possible to configure the web user interface to have a left box -showing all the views than can be applied to the current result set. - -To enable this, click on your login at the top right corner. Chose -"user preferences", then "boxes", then "possible views box" and check -"visible = yes" before validating your changes. - -The views listed there we either not selected because of a lower -score, or they were deliberately excluded by the main template logic. - - -Basic class for views -~~~~~~~~~~~~~~~~~~~~~ - -Class `View` (`cubicweb.view`) -``````````````````````````````` - -This class is an abstraction of a view class, used as a base class for -every renderable object such as views, templates and other user -interface components. - -A `View` is instantiated to render a result set or part of a result -set. `View` subclasses may be parametrized using the following class -attributes: - -* `templatable` indicates if the view may be embedded in a main - template or if it has to be rendered standalone (i.e. pure XML views - must not be embedded in the main template of HTML pages) - -* if the view is not templatable, it should set the `content_type` - class attribute to the correct MIME type (text/xhtml being the - default) - -* the `category` attribute may be used in the interface to regroup - related view kinds together - -A view writes to its output stream thanks to its attribute `w` (the -append method of an `UStreamIO`, except for binary views). - -At instantiation time, the standard `_cw` and `cw_rset` attributes are -added and the `w` attribute will be set at rendering time. - -The basic interface for views is as follows (remember that the result -set has a tabular structure with rows and columns, hence cells): - -* `render(**context)`, render the view by calling `call` or - `cell_call` depending on the context - -* `call(**kwargs)`, call the view for a complete result set or null - (the default implementation calls `cell_call()` on each cell of the - result set) - -* `cell_call(row, col, **kwargs)`, call the view for a given cell of a - result set (`row` and `col` being integers used to access the cell) - -* `url()`, returns the URL enabling us to get the view with the current - result set - -* `wview(__vid, rset, __fallback_vid=None, **kwargs)`, call the view of - identifier `__vid` on the given result set. It is possible to give a - fallback view identifier that will be used if the requested view is - not applicable to the result set. - -* `html_headers()`, returns a list of HTML headers to be set by the - main template - -* `page_title()`, returns the title to use in the HTML header `title` - -Other basic view classes -```````````````````````` -Here are some of the subclasses of `View` defined in `cubicweb.common.view` -that are more concrete as they relate to data rendering within the application: - -* `EntityView`, view applying to lines or cell containing an entity (e.g. an eid) -* `StartupView`, start view that does not require a result set to apply to -* `AnyRsetView`, view applicable to any result set - -Examples of views class -``````````````````````` - -- Using `templatable`, `content_type` and HTTP cache configuration - -.. sourcecode:: python - - class RSSView(XMLView): - __regid__ = 'rss' - title = _('rss') - templatable = False - content_type = 'text/xml' - http_cache_manager = MaxAgeHTTPCacheManager - cache_max_age = 60*60*2 # stay in http cache for 2 hours by default - - -- Using a custom selector - -.. sourcecode:: python - - class SearchForAssociationView(EntityView): - """view called by the edition view when the user asks - to search for something to link to the edited eid - """ - __regid__ = 'search-associate' - title = _('search for association') - __select__ = one_line_rset() & match_search_state('linksearch') & implements('Any') - - -XML views, binaries views... -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For views generating other formats than HTML (an image generated dynamically -for example), and which can not simply be included in the HTML page generated -by the main template (see above), you have to: - -* set the attribute `templatable` of the class to `False` -* set, through the attribute `content_type` of the class, the MIME - type generated by the view to `application/octet-stream` or any - relevant and more specialised mime type - -For views dedicated to binary content creation (like dynamically generated -images), we have to set the attribute `binary` of the class to `True` (which -implies that `templatable == False`, so that the attribute `w` of the view could be -replaced by a binary flow instead of unicode). diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/views/wdoc.rst --- a/doc/book/en/development/devweb/views/wdoc.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -.. -*- coding: utf-8 -*- - -Online documentation system ---------------------------- - -(:mod:`cubicweb.web.views.wdoc`) - -XXX describe the on-line documentation system - diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/devweb/views/xmlrss.rst --- a/doc/book/en/development/devweb/views/xmlrss.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -.. _XmlAndRss: - -XML and RSS views ------------------ - -(:mod:`cubicweb.web.views.xmlrss`) - -Overview -+++++++++ - -*rss* - Creates a RSS/XML view and call the view `rssitem` for each entity of - the result set. - -*rssitem* - Create a RSS/XML view for each entity based on the results of the dublin core - methods of the entity (`dc_*`) - -RSS Channel Example -++++++++++++++++++++ - -Assuming you have several blog entries, click on the title of the -search box in the left column. A larger search box should appear. Enter:: - - Any X ORDERBY D WHERE X is BlogEntry, X creation_date D - -and you get a list of blog entries. - -Click on your login at the top right corner. Chose "user preferences", -then "boxes", then "possible views box" and check "visible = yes" -before validating your changes. - -Enter the same query in the search box and you will see the same list, -plus a box titled "possible views" in the left column. Click on -"entityview", then "RSS". - -You just applied the "RSS" view to the RQL selection you requested. - -That's it, you have a RSS channel for your blog. - -Try again with:: - - Any X ORDERBY D WHERE X is BlogEntry, X creation_date D, - X entry_of B, B title "MyLife" - -Another RSS channel, but a bit more focused. - -A last one for the road:: - - Any C ORDERBY D WHERE C is Comment, C creation_date D LIMIT 15 - -displayed with the RSS view, that's a channel for the last fifteen -comments posted. - -[WRITE ME] - -* show that the RSS view can be used to display an ordered selection - of blog entries, thus providing a RSS channel - -* show that a different selection (by category) means a different channel diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/entityclasses/application-logic.rst --- a/doc/book/en/development/entityclasses/application-logic.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,166 +0,0 @@ -How to use entities objects ---------------------------- - -The previous chapters detailed the classes and methods available to -the developper at the so-called `ORM`_ level. However they say little -about the common patterns of usage of these objects. - -.. _`ORM`: http://en.wikipedia.org/wiki/Object-relational_mapping - -Entities objects are used in the repository and web sides of -CubicWeb. On the repository side of things, one should manipulate them -in Hooks and Operations. - -Hooks and Operations provide support for the implementation of rules -such as computed attributes, coherency invariants, etc (they play the -same role as database triggers, but in a way that is independant of -the actual data sources). - -So a lot of an application's business rules will be written in Hooks -(or Operations). - -On the web side, views also typically operate using entity -objects. Obvious entity methods for use in views are the dublin code -method like dc_title, etc. For separation of concerns reasons, one -should ensure no ui logic pervades the entities level, and also no -business logic should creep into the views. - -In the duration of a transaction, entities objects can be instantiated -many times, in views and hooks, even for the same database entity. For -instance, in a classic CubicWeb deployment setup, the repository and -the web frontend are separated process communicating over the -wire. There is no way state can be shared between these processes -(there is a specific API for that). Hence, it is not possible to use -entity objects as messengers between these components of an -application. It means that an attribute set as in `obj.x = 42`, -whether or not x is actually an entity schema attribute, has a short -life span, limited to the hook, operation or view within which the -object was built. - -Setting an attribute or relation value can be done in the context of a -Hook/Operation, using the obj.set_attributes(x=42) notation or a plain -RQL SET expression. - -In views, it would be preferable to encapsulate the necessary logic in -a method of the concerned entity class(es). But of course, this advice -is also reasonnable for Hooks/Operations, though the separation of -concerns here is less stringent than in the case of views. - -This leads to the practical role of entity objects: it's where an -important part of the application logic lie (the other part being -located in the Hook/Operations). - -Anatomy of an entity class --------------------------- - -We can look now at a real life example coming from the `tracker`_ -cube. Let us begin to study the entities/project.py content. - -.. sourcecode:: python - - class Project(TreeMixIn, AnyEntity): - __regid__ = 'Project' - __implements__ = AnyEntity.__implements__ + (ITree,) - fetch_attrs, fetch_order = fetch_config(('name', 'description', - 'description_format', 'summary')) - - TICKET_DEFAULT_STATE_RESTR = 'S name IN ("created","identified","released","scheduled")' - - tree_attribute = 'subproject_of' - parent_target = 'subject' - children_target = 'object' - - def dc_title(self): - return self.name - -First we see that it uses an ITree interface and the TreeMixIn default -implementation. The attributes `tree_attribute`, `parent_target` and -`children_target` are used by the TreeMixIn code. This is typically -used in views concerned with the representation of tree-like -structures (CubicWeb provides several such views). - -It is important that the views themselves try not to implement this -logic, not only because such views would be hardly applyable to other -tree-like relations, but also because it is perfectly fine and useful -to use such an interface in Hooks. - -In fact, Tree nature is a property of the data model that cannot be -fully and portably expressed at the level of database entities (think -about the transitive closure of the child relation). This is a further -argument to implement it at entity class level. - -The `dc_title` method provides a (unicode string) value likely to be -consummed by views, but note that here we do not care about output -encodings. We care about providing data in the most universal format -possible, because the data could be used by a web view (which would be -responsible of ensuring XHTML compliance), or a console or file -oriented output (which would have the necessary context about the -needed byte stream encoding). - -The fetch_attrs, fetch_order class attributes are parameters of the -`ORM`_ layer. They tell which attributes should be loaded at once on -entity object instantiation (by default, only the eid is known, other -attributes are loaded on demand), and which attribute is to be used to -order the .related() and .unrelated() methods output. - -Finally, we can observe the big TICKET_DEFAULT_STATE_RESTR is a pure -application domain piece of data. There is, of course, no limitation -to the amount of class attributes of this kind. - -Let us now dig into more substantial pieces of code. - -.. sourcecode:: python - - def latest_version(self, states=('published',), reverse=None): - """returns the latest version(s) for the project in one of the given - states. - - when no states specified, returns the latest published version. - """ - order = 'DESC' - if reverse is not None: - warn('reverse argument is deprecated', - DeprecationWarning, stacklevel=1) - if reverse: - order = 'ASC' - rset = self.versions_in_state(states, order, True) - if rset: - return rset.get_entity(0, 0) - return None - - def versions_in_state(self, states, order='ASC', limit=False): - """returns version(s) for the project in one of the given states, sorted - by version number. - - If limit is true, limit result to one version. - If reverse, versions are returned from the smallest to the greatest. - """ - if limit: - order += ' LIMIT 1' - rql = 'Any V,N ORDERBY version_sort_value(N) %s ' \ - 'WHERE V num N, V in_state S, S name IN (%s), ' \ - 'V version_of P, P eid %%(p)s' % (order, ','.join(repr(s) for s in states)) - return self._cw.execute(rql, {'p': self.eid}) - -.. _`tracker`: http://www.cubicweb.org/project/cubicweb-tracker/ - -These few lines exhibit the important properties we want to outline: - -* entity code is concerned with the application domain - -* it is NOT concerned with database coherency (this is the realm of - Hooks/Operations); in other words, it assumes a coherent world - -* it is NOT concerned with end-user interfaces - -* however it can be used in both contexts - -* it does not create or manipulate the internal object's state - -* it plays freely with RQL expression as needed - -* it is not concerned with internationalization - -* it does not raise exceptions - - diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/entityclasses/data-as-objects.rst --- a/doc/book/en/development/entityclasses/data-as-objects.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,136 +0,0 @@ -Access to persistent data --------------------------- - -Python-level access to persistent data is provided by the -:class:`Entity ` class. - -An entity class is bound to a schema entity type. Descriptors are added when -classes are registered in order to initialize the class according to its schema: - -* we can access the defined attributes in the schema thanks to the attributes of - the same name on instances (typed value) - -* we can access the defined relations in the schema thanks to the relations of - the same name on instances (entities instances list) - - -:Formatting and output generation: - - * `view(__vid, __registry='views', **kwargs)`, applies the given view to the entity - (and returns an unicode string) - - * `absolute_url(*args, **kwargs)`, returns an absolute URL to access the primary view - of an entity - - * `rest_path()`, returns a relative REST URL to get the entity - - * `printable_value(attr, value=_marker, attrtype=None, format='text/html', displaytime=True)`, - returns a string enabling the display of an attribute value in a given format - (the value is automatically recovered if necessary) - -:Data handling: - - * `as_rset()`, converts the entity into an equivalent result set simulating the - request `Any X WHERE X eid _eid_` - - * `complete(skip_bytes=True)`, executes a request that recovers at - once all the missing attributes of an entity - - * `get_value(name)`, returns the value associated to the attribute name given - in parameter - - * `related(rtype, role='subject', limit=None, entities=False)`, - returns a list of entities related to the current entity by the - relation given in parameter - - * `unrelated(rtype, targettype, role='subject', limit=None)`, - returns a result set corresponding to the entities not (yet) - related to the current entity by the relation given in parameter - and satisfying its constraints - - * `set_attributes(**kwargs)`, updates the attributes list with the corresponding - values given named parameters - - * `set_relations(**kwargs)`, add relations to the given object. To - set a relation where this entity is the object of the relation, - use `reverse_` as argument name. Values may be an - entity, a list of entities, or None (meaning that all relations of - the given type from or to this object should be deleted). - - * `copy_relations(ceid)`, copies the relations of the entities having the eid - given in the parameters on the current entity - - * `delete()` allows to delete the entity - - -The :class:`AnyEntity` class ----------------------------- - -To provide a specific behavior for each entity, we have to define a class -inheriting from `cubicweb.entities.AnyEntity`. In general, we define this class -in `mycube.entities` module (or in a submodule if we want to split code among -multiple files) so that it will be available on both server and client side. - -The class `AnyEntity` is a sub-class of Entity that add methods to it, -and helps specializing (by further subclassing) the handling of a -given entity type. - -Most methods defined for `AnyEntity`, in addition to `Entity`, add -support for the `Dublin Core`_ metadata. - -.. _`Dublin Core`: http://dublincore.org/ - -:Standard meta-data (Dublin Core): - - * `dc_title()`, returns a unicode string corresponding to the - meta-data `Title` (used by default is the first non-meta attribute - of the entity schema) - - * `dc_long_title()`, same as dc_title but can return a more - detailed title - - * `dc_description(format='text/plain')`, returns a unicode string - corresponding to the meta-data `Description` (looks for a - description attribute by default) - - * `dc_authors()`, returns a unicode string corresponding to the meta-data - `Authors` (owners by default) - - * `dc_creator()`, returns a unicode string corresponding to the - creator of the entity - - * `dc_date(date_format=None)`, returns a unicode string corresponding to - the meta-data `Date` (update date by default) - - * `dc_type(form='')`, returns a string to display the entity type by - specifying the preferred form (`plural` for a plural form) - - * `dc_language()`, returns the language used by the entity - - -:Misc methods: - - * `after_deletion_path`, return (path, parameters) which should be - used as redirect information when this entity is being deleted - - * `pre_web_edit`, callback called by the web editcontroller when an - entity will be created/modified, to let a chance to do some entity - specific stuff (does nothing by default) - -Inheritance ------------ - -When describing a data model, entities can inherit from other entities as is -common in object-oriented programming. - -You have the possibility to adapt some entity attributes, as follow: - -.. sourcecode:: python - - from cubes.OTHER_CUBE import entities - class EntityExample(entities.EntityExample): - def dc_long_title(self): - return '%s (%s)' % (self.name, self.description) - -Notice this is different than yams schema inheritance. - diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/entityclasses/index.rst --- a/doc/book/en/development/entityclasses/index.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -Data as objects -=============== - -In this chapter, we will introduce the objects that are used to handle -the logic associated to the data stored in the database. - -.. toctree:: - :maxdepth: 1 - - data-as-objects - load-sort - interfaces - application-logic diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/entityclasses/interfaces.rst --- a/doc/book/en/development/entityclasses/interfaces.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -Interfaces ----------- - -This is the same thing as object-oriented programming `interfaces`_. - -.. _`interfaces`: http://java.sun.com/docs/books/tutorial/java/concepts/interface.html - -Definition of an interface is quite trivial. An example from cubicweb -itself (found in cubicweb/interfaces.py): - -.. sourcecode:: python - - class ITree(Interface): - - def parent(self): - """returns the parent entity""" - - def children(self): - """returns the item's children""" - - def children_rql(self): - """returns RQL to get children""" - - def iterchildren(self): - """iterates over the item's children""" - - def is_leaf(self): - """returns true if this node as no child""" - - def is_root(self): - """returns true if this node has no parent""" - - def root(self): - """returns the root object""" - - -Declaration of interfaces implemented by a class -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. sourcecode:: python - - from cubicweb.interfaces import ITree - from cubicweb.mixins import TreeMixIn - - class MyEntity(TreeMixIn, AnyEntity): - __regid__ = 'MyEntity' - __implements__ = AnyEntity.__implements__ + ('ITree',) - - tree_attribute = 'filed_under' - -The TreeMixIn here provides a default implementation for the -interface. The tree_attribute class attribute is actually used by this -implementation to help implement correct behaviour. - -Interfaces (and some implementations as mixins) defined in the library -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automodule:: cubicweb.interfaces - :members: - -.. automodule:: cubicweb.mixins - :members: - - - diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/entityclasses/load-sort.rst --- a/doc/book/en/development/entityclasses/load-sort.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ - -.. _FetchAttrs: - -Loaded attributes and default sorting management -```````````````````````````````````````````````` - -* The class attribute `fetch_attrs` allows to define in an entity class a list - of names of attributes or relations that should be automatically loaded when - entities of this type are fetched from the database. In the case of relations, - we are limited to *subject of cardinality `?` or `1`* relations. - -* The class method `fetch_order(attr, var)` expects an attribute (or relation) - name as a parameter and a variable name, and it should return a string - to use in the requirement `ORDERBY` of an RQL query to automatically - sort the list of entities of such type according to this attribute, or - `None` if we do not want to sort on the attribute given in the parameter. - By default, the entities are sorted according to their creation date. - -* The class method `fetch_unrelated_order(attr, var)` is similar to - the method `fetch_order` except that it is essentially used to - control the sorting of drop-down lists enabling relations creation - in the editing view of an entity. The default implementation uses - the modification date. Here's how to adapt it for one entity (sort - on the name attribute): :: - - class MyEntity(AnyEntity): - __regid__ = 'MyEntity' - fetch_attrs = ('modification_date', 'name') - - @classmethod - def fetch_unrelated_order(cls, attr, var): - if attr == 'name': - return '%s ASC' % var - return None - - -The function `fetch_config(fetchattrs, mainattr=None)` simplifies the -definition of the attributes to load and the sorting by returning a -list of attributes to pre-load (considering automatically the -attributes of `AnyEntity`) and a sorting function based on the main -attribute (the second parameter if specified, otherwise the first -attribute from the list `fetchattrs`). This function is defined in -`cubicweb.entities`. - -For example: :: - - class Transition(AnyEntity): - """...""" - __regid__ = 'Transition' - fetch_attrs, fetch_order = fetch_config(['name']) - -Indicates that for the entity type "Transition", you have to pre-load -the attribute `name` and sort by default on this attribute. diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/index.rst --- a/doc/book/en/development/index.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ -.. _Part2: - ------------ -Development ------------ - -This part is about developing web applications with the *CubicWeb* framework. - -.. toctree:: - :maxdepth: 2 - :numbered: - - cubes/index - vreg.rst - datamodel/index - entityclasses/index - devcore/index - devweb/index - devrepo/index - testing.rst - migration.rst - profiling.rst diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/migration.rst --- a/doc/book/en/development/migration.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,198 +0,0 @@ -.. -*- coding: utf-8 -*- - -.. _migration: - -Migration -========= - -One of the main design goals of *CubicWeb* was to support iterative and agile -development. For this purpose, multiple actions are provided to facilitate the -improvement of an instance, and in particular to handle the changes to be -applied to the data model, without loosing existing data. - -The current version of a cube (and of cubicweb itself) is provided in the file -`__pkginfo__.py` as a tuple of 3 integers. - -Migration scripts management ----------------------------- - -Migration scripts has to be located in the directory `migration` of your -cube and named accordingly: - -:: - - [_]_.py - -in which : - -* X.Y.Z is the model version number to which the script enables to migrate. - -* *mode* (between the last "_" and the extension ".py") is used for - distributed installation. It indicates to which part - of the application (RQL server, web server) the script applies. - Its value could be : - - * `common`, applies to the RQL server as well as the web server and updates - files on the hard drive (configuration files migration for example). - - * `web`, applies only to the web server and updates files on the hard drive. - - * `repository`, applies only to the RQL server and updates files on the - hard drive. - - * `Any`, applies only to the RQL server and updates data in the database - (schema and data migration for example). - -Again in the directory `migration`, the file `depends.map` allows to indicate -that for the migration to a particular model version, you always have to first -migrate to a particular *CubicWeb* version. This file can contain comments (lines -starting by `#`) and a dependancy is listed as follows: :: - - : - -For example: :: - - 0.12.0: 2.26.0 - 0.13.0: 2.27.0 - # 0.14 works with 2.27 <= cubicweb <= 2.28 at least - 0.15.0: 2.28.0 - -Base context ------------- - -The following identifiers are pre-defined in migration scripts: - -* `config`, instance configuration - -* `interactive_mode`, boolean indicating that the script is executed in - an interactive mode or not - -* `versions_map`, dictionary of migrated versions (key are cubes - names, including 'cubicweb', values are (from version, to version) - -* `confirm(question)`, function asking the user and returning true - if the user answers yes, false otherwise (always returns true in - non-interactive mode) - -* `_()` is equivalent to `unicode` allowing to flag the strings to - internationalize in the migration scripts. - -In the `repository` scripts, the following identifiers are also defined: - -* `commit(ask_confirm=True)`, request confirming and executing a "commit" - -* `schema`, instance schema (readen from the database) - -* `fsschema`, installed schema on the file system (e.g. schema of - the updated model and cubicweb) - -* `repo`, repository object - -* `session`, repository session object - - -Schema migration ----------------- -The following functions for schema migration are available in `repository` -scripts: - -* `add_attribute(etype, attrname, attrtype=None, commit=True)`, adds a new - attribute to an existing entity type. If the attribute type is not specified, - then it is extracted from the updated schema. - -* `drop_attribute(etype, attrname, commit=True)`, removes an attribute from an - existing entity type. - -* `rename_attribute(etype, oldname, newname, commit=True)`, renames an attribute - -* `add_entity_type(etype, auto=True, commit=True)`, adds a new entity type. - If `auto` is True, all the relations using this entity type and having a known - entity type on the other hand will automatically be added. - -* `drop_entity_type(etype, commit=True)`, removes an entity type and all the - relations using it. - -* `rename_entity_type(oldname, newname, commit=True)`, renames an entity type - -* `add_relation_type(rtype, addrdef=True, commit=True)`, adds a new relation - type. If `addrdef` is True, all the relations definitions of this type will - be added. - -* `drop_relation_type(rtype, commit=True)`, removes a relation type and all the - definitions of this type. - -* `rename_relation(oldname, newname, commit=True)`, renames a relation. - -* `add_relation_definition(subjtype, rtype, objtype, commit=True)`, adds a new - relation definition. - -* `drop_relation_definition(subjtype, rtype, objtype, commit=True)`, removes - a relation definition. - -* `sync_schema_props_perms(ertype=None, syncperms=True, syncprops=True, syncrdefs=True, commit=True)`, - synchronizes properties and/or permissions on: - - the whole schema if ertype is None - - an entity or relation type schema if ertype is a string - - a relation definition if ertype is a 3-uple (subject, relation, object) - -* `change_relation_props(subjtype, rtype, objtype, commit=True, **kwargs)`, changes - properties of a relation definition by using the named parameters of the properties - to change. - -* `set_widget(etype, rtype, widget, commit=True)`, changes the widget used for the - relation of entity type . - -* `set_size_constraint(etype, rtype, size, commit=True)`, changes the size constraints - for the relation of entity type . - -Data migration --------------- -The following functions for data migration are available in `repository` scripts: - -* `rql(rql, kwargs=None, cachekey=None, ask_confirm=True)`, executes an arbitrary RQL - query, either to interrogate or update. A result set object is returned. - -* `add_entity(etype, *args, **kwargs)`, adds a nes entity type of the given - type. The attribute and relation values are specified using the named and - positionned parameters. - -Workflow creation ------------------ - -The following functions for workflow creation are available in `repository` -scripts: - -* `add_workflow(label, workflowof, initial=False, commit=False, **kwargs)`, adds a new workflow - for a given type(s) - -You can find more details about workflows in the chapter :ref:`Workflow` . - -Configuration migration ------------------------ - -The following functions for configuration migration are available in all -scripts: - -* `option_renamed(oldname, newname)`, indicates that an option has been renamed - -* `option_group_change(option, oldgroup, newgroup)`, indicates that an option does not - belong anymore to the same group. - -* `option_added(oldname, newname)`, indicates that an option has been added. - -* `option_removed(oldname, newname)`, indicates that an option has been deleted. - - -Others migration functions --------------------------- -Those functions are only used for low level operations that could not be -accomplished otherwise or to repair damaged databases during interactive -session. They are available in `repository` scripts: - -* `sql(sql, args=None, ask_confirm=True)`, executes an arbitrary SQL query on the system source -* `add_entity_type_table(etype, commit=True)` -* `add_relation_type_table(rtype, commit=True)` -* `uninline_relation(rtype, commit=True)` - - -[FIXME] Add explanation on how to use cubicweb-ctl shell diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/development/profiling.rst --- a/doc/book/en/development/profiling.rst Fri Apr 23 17:07:55 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -Profiling and performance -========================= - -If you feel that one of your pages takes more time than it should to be -generated, chances are that you're making too many RQL queries. Obviously, -there are other reasons but experience tends to show this is the first thing to -track down. Luckily, CubicWeb provides a configuration option to log RQL -queries. In your ``all-in-one.conf`` file, set the **query-log-file** option:: - - # web application query log file - query-log-file=~/myapp-rql.log - -Then restart your application, reload your page and stop your application. -The file ``myapp-rql.log`` now contains the list of RQL queries that were -executed during your test. It's a simple text file containing lines such as:: - - Any A WHERE X eid %(x)s, X lastname A {'x': 448} -- (0.002 sec, 0.010 CPU sec) - Any A WHERE X eid %(x)s, X firstname A {'x': 447} -- (0.002 sec, 0.000 CPU sec) - -The structure of each line is:: - - --
    ') + 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' + + +.. Example of entity fields form diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/httpcaching.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/httpcaching.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,3 @@ +HTTP cache management +--------------------- +XXX feedme \ No newline at end of file diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/index.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/index.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,21 @@ +Web side development +==================== + +In this chapter, we will describe the core APIs for web development in +the *CubicWeb* framework. + +.. toctree:: + :maxdepth: 2 + + publisher + controllers + request + views/index + rtags + js + css + form + facets + internationalization + property + httpcaching diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/internationalization.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/internationalization.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,222 @@ +.. -*- coding: utf-8 -*- + +.. _internationalization: + +Internationalization +--------------------- + +Cubicweb fully supports the internalization of its content and interface. + +Cubicweb's interface internationalization is based on the translation project `GNU gettext`_. + +.. _`GNU gettext`: http://www.gnu.org/software/gettext/ + +Cubicweb' internalization involves two steps: + +* in your Python code and cubicweb-tal templates : mark translatable strings + +* in your instance : handle the translation catalog, edit translations + +String internationalization +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +User defined string +``````````````````` + +In the Python code and cubicweb-tal templates translatable strings can be +marked in one of the following ways : + + * by using the *built-in* function `_` :: + + class PrimaryView(EntityView): + """the full view of an non final entity""" + __regid__ = 'primary' + title = _('primary') + + OR + + * by using the equivalent request's method :: + + class NoResultView(View): + """default view when no result has been found""" + __regid__ = 'noresult' + + def call(self, **kwargs): + self.w(u'
    %s
    \n' + % self._cw._('No result matching query')) + +The goal of the *built-in* function `_` is only **to mark the +translatable strings**, it will only return the string to translate +itself, but not its translation (it's actually another name for the +`unicode` builtin). + +In the other hand the request's method `self._cw._` is also meant to +retrieve the proper translation of translation strings in the +requested language. + +Finally you can also use the `__` attribute of request object to get a +translation for a string *which should not itself added to the catalog*, +usually in case where the actual msgid is created by string interpolation :: + + self._cw.__('This %s' % etype) + +In this example ._cw.__` is used instead of ._cw._` so we don't have 'This %s' in +messages catalogs. + +Translations in cubicweb-tal template can also be done with TAL tags +`i18n:content` and `i18n:replace`. + +If you need to add messages on top of those that can be found in the source, +you can create a file named `i18n/static-messages.pot`. + +You could put there messages not found in the python sources or +overrides for some messages of used cubes. + +Generated string +```````````````` + +We do not need to mark the translation strings of entities/relations used by a +particular instance's schema as they are generated automatically. String for +various actions are also generated. + +For exemple the following schema :: + + Class EntityA(EntityType): + relation_a2b = SubjectRelation('EntityB') + + class EntityB(EntityType): + pass + +May generate the following message :: + + add EntityA relation_a2b EntityB subject + +This message will be used in views of ``EntityA`` for creation of a new +``EntityB`` with a preset relation ``relation_a2b`` between the current +``EntityA`` and the new ``EntityB``. The opposite message :: + + add EntityA relation_a2b EntityB object + +Is used for similar creation of an ``EntityA`` from a view of ``EntityB``. The +title of they respective creation form will be :: + + creating EntityB (EntityA %(linkto)s relation_a2b EntityB) + + creating EntityA (EntityA relation_a2b %(linkto)s EntityA) + +In the translated string you can use ``%(linkto)s`` for reference to the source +``entity``. + +Handling the translation catalog +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Once the internationalization is done in your code, you need to populate and +update the translation catalog. Cubicweb provides the following commands for this +purpose: + + +* `i18ncubicweb` updates Cubicweb framework's translation + catalogs. Unless you actually work on the framework itself, you + don't need to use this command. + +* `i18ncube` updates the translation catalogs of *one particular cube* + (or of all cubes). After this command is executed you must update + the translation files *.po* in the "i18n" directory of your + cube. This command will of course not remove existing translations + still in use. It will mark unused translation but not remove them. + +* `i18ninstance` recompiles the translation catalogs of *one particular + instance* (or of all instances) after the translation catalogs of + its cubes have been updated. This command is automatically + called every time you create or update your instance. The compiled + catalogs (*.mo*) are stored in the i18n//LC_MESSAGES of + instance where `lang` is the language identifier ('en' or 'fr' + for exemple). + + +Example +``````` + +You have added and/or modified some translation strings in your cube +(after creating a new view or modifying the cube's schema for exemple). +To update the translation catalogs you need to do: + +1. `cubicweb-ctl i18ncube ` +2. Edit the /i18n/xxx.po files and add missing translations (empty `msgstr`) +3. `hg ci -m "updated i18n catalogs"` +4. `cubicweb-ctl i18ninstance ` + +Editing po files +~~~~~~~~~~~~~~~~ + +Using a PO aware editor +```````````````````````` + +Many tools exist to help maintain .po (PO) files. Common editors or +development environment provides modes for these. One can also find +dedicated PO files editor, such as `poedit`_. + +.. _`poedit`: http://www.poedit.net/ + +While usage of such a tool is commendable, PO files are perfectly +editable with a (unicode aware) plain text editor. It is also useful +to know their structure for troubleshooting purposes. + +Structure of a PO file +`````````````````````` + +In this section, we selectively quote passages of the `GNU gettext`_ +manual chapter on PO files, available there:: + + http://www.gnu.org/software/hello/manual/gettext/PO-Files.html + +One PO file entry has the following schematic structure:: + + white-space + # translator-comments + #. extracted-comments + #: reference... + #, flag... + #| msgid previous-untranslated-string + msgid untranslated-string + msgstr translated-string + + +A simple entry can look like this:: + + #: lib/error.c:116 + msgid "Unknown system error" + msgstr "Error desconegut del sistema" + +It is also possible to have entries with a context specifier. They +look like this:: + + white-space + # translator-comments + #. extracted-comments + #: reference... + #, flag... + #| msgctxt previous-context + #| msgid previous-untranslated-string + msgctxt context + msgid untranslated-string + msgstr translated-string + + +The context serves to disambiguate messages with the same +untranslated-string. It is possible to have several entries with the +same untranslated-string in a PO file, provided that they each have a +different context. Note that an empty context string and an absent +msgctxt line do not mean the same thing. + +Contexts and CubicWeb +````````````````````` + +CubicWeb PO files have both non-contextual and contextual msgids. + +Contextual entries are automatically used in some cases. For instance, +entity.dc_type(), eschema.display_name(req) or display_name(etype, +req, form, context) methods/function calls will use them. + +It is also possible to explicitly use the with _cw.pgettext(context, +msgid). diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/js.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/js.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,355 @@ +.. -*- coding: utf-8 -*- + +Javascript +---------- + +*CubicWeb* uses quite a bit of javascript in its user interface and +ships with jquery (1.3.x) and parts of the jquery UI library, plus a +number of homegrown files and also other third party libraries. + +All javascript files are stored in cubicweb/web/data/. There are +around thirty js files there. In a cube it goes to data/. + +Obviously one does not want javascript pieces to be loaded all at +once, hence the framework provides a number of mechanisms and +conventions to deal with javascript resources. + +Conventions +~~~~~~~~~~~ + +It is good practice to name cube specific js files after the name of +the cube, like this : 'cube.mycube.js', so as to avoid name clashes. + +XXX external_resources variable (which needs love) + +CubicWeb javascript API +~~~~~~~~~~~~~~~~~~~~~~~ + +Javascript resources are typically loaded on demand, from views. The +request object (available as self._cw from most application objects, +for instance views and entities objects) has a few methods to do that: + +* `add_js(self, jsfiles, localfile=True)` which takes a sequence of + javascript files and writes proper entries into the HTML header + section. The localfile parameter allows to declare resources which + are not from web/data (for instance, residing on a content delivery + network). + +* `add_onload(self, jscode)` which adds one raw javascript code + snippet inline in the html headers. This is quite useful for setting + up early jQuery(document).ready(...) initialisations. + +CubicWeb javascript events +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* ``server-response``: this event is triggered on HTTP responses (both + standard and ajax). The two following extra parameters are passed + to callbacks : + + - ``ajax``: a boolean that says if the reponse was issued by an + ajax request + + - ``node``: the DOM node returned by the server in case of an + ajax request, otherwise the document itself for standard HTTP + requests. + +Important AJAX APIS +~~~~~~~~~~~~~~~~~~~ + +* `asyncRemoteExec` and `remoteExec` are the base building blocks for + doing arbitrary async (resp. sync) communications with the server + +* `reloadComponent` is a convenience function to replace a DOM node + with server supplied content coming from a specific registry (this + is quite handy to refresh the content of some boxes for instances) + +* `jQuery.fn.loadxhtml` is an important extension to jQuery which + allows proper loading and in-place DOM update of xhtml views. It is + suitably augmented to trigger necessary events, and process CubicWeb + specific elements such as the facet system, fckeditor, etc. + + +A simple example with asyncRemoteExec +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In the python side, we have to extend the BaseController class. The +@jsonize decorator ensures that the `return value` of the method is +encoded as JSON data. By construction, the JSonController inputs +everything in JSON format. + +.. sourcecode: python + + from cubicweb.web.views.basecontrollers import JSonController, jsonize + + @monkeypatch(JSonController) + @jsonize + def js_say_hello(self, name): + return u'hello %s' % name + +In the javascript side, we do the asynchronous call. Notice how it +creates a `deferred` object. Proper treatment of the return value or +error handling has to be done through the addCallback and addErrback +methods. + +.. sourcecode: javascript + + function asyncHello(name) { + var deferred = asyncRemoteExec('say_hello', name); + deferred.addCallback(function (response) { + alert(response); + }); + deferred.addErrback(function (error) { + alert('something fishy happened'); + }); + } + + function syncHello(name) { + alert( remoteExec('say_hello', name) ); + } + +Anatomy of a reloadComponent call +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`reloadComponent` allows to dynamically replace some DOM node with new +elements. It has the following signature: + +* `compid` (mandatory) is the name of the component to be reloaded + +* `rql` (optional) will be used to generate a result set given as + argument to the selected component + +* `registry` (optional) defaults to 'components' but can be any other + valid registry name + +* `nodeid` (optional) defaults to compid + 'Component' but can be any + explicitly specified DOM node id + +* `extraargs` (optional) should be a dictionary of values that will be + given to the cell_call method of the component + +A simple reloadComponent example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The server side implementation of `reloadComponent` is the +js_component method of the JSonController. + +The following function implements a two-steps method to delete a +standard bookmark and refresh the UI, while keeping the UI responsive. + +.. sourcecode:: javascript + + function removeBookmark(beid) { + d = asyncRemoteExec('delete_bookmark', beid); + d.addCallback(function(boxcontent) { + reloadComponent('bookmarks_box', '', 'boxes', 'bookmarks_box'); + document.location.hash = '#header'; + updateMessage(_("bookmark has been removed")); + }); + } + +`reloadComponent` is called with the id of the bookmark box as +argument, no rql expression (because the bookmarks display is actually +independant of any dataset context), a reference to the 'boxes' +registry (which hosts all left, right and contextual boxes) and +finally an explicit 'bookmarks_box' nodeid argument that stipulates +the target DOM node. + +Anatomy of a loadxhtml call +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`jQuery.fn.loadxhtml` is an important extension to jQuery which allows +proper loading and in-place DOM update of xhtml views. The existing +`jQuery.load`_ function does not handle xhtml, hence the addition. The +API of loadxhtml is roughly similar to that of `jQuery.load`_. + +.. _`jQuery.load`: http://api.jquery.com/load/ + + +* `url` (mandatory) should be a complete url (typically referencing + the JSonController, but this is not strictly mandatory) + +* `data` (optional) is a dictionary of values given to the + controller specified through an `url` argument; some keys may have a + special meaning depending on the choosen controller (such as `fname` + for the JSonController); the `callback` key, if present, must refer + to a function to be called at the end of loadxhtml (more on this + below) + +* `reqtype` (optional) specifies the request method to be used (get or + post); if the argument is 'post', then the post method is used, + otherwise the get method is used + +* `mode` (optional) is one of `replace` (the default) which means the + loaded node will replace the current node content, `swap` to replace + the current node with the loaded node, and `append` which will + append the loaded node to the current node content + +About the `callback` option: + +* it is called with two parameters: the current node, and a list + containing the loaded (and post-processed node) + +* whenever is returns another function, this function is called in + turn with the same parameters as above + +This mechanism allows callback chaining. + + +A simple example with loadxhtml +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Here we are concerned with the retrieval of a specific view to be +injected in the live DOM. The view will be of course selected +server-side using an entity eid provided by the client side. + +.. sourcecode:: python + + from cubicweb import typed_eid + from cubicweb.web.views.basecontrollers import JSonController, xhtmlize + + @monkeypatch(JSonController) + @xhtmlize + def js_frob_status(self, eid, frobname): + entity = self._cw.entity_from_eid(typed_eid(eid)) + return entity.view('frob', name=frobname) + +.. sourcecode:: javascript + + function update_some_div(divid, eid, frobname) { + var params = {fname:'frob_status', eid: eid, frobname:frobname}; + jQuery('#'+divid).loadxhtml(JSON_BASE_URL, params, 'post'); + } + +In this example, the url argument is the base json url of a cube +instance (it should contain something like +`http://myinstance/json?`). The actual JSonController method name is +encoded in the `params` dictionary using the `fname` key. + +A more real-life example from CubicWeb +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A frequent use case of Web 2 applications is the delayed (or +on-demand) loading of pieces of the DOM. This is typically achieved +using some preparation of the initial DOM nodes, jQuery event handling +and proper use of loadxhtml. + +We present here a skeletal version of the mecanism used in CubicWeb +and available in web/views/tabs.py, in the `LazyViewMixin` class. + +.. sourcecode:: python + + def lazyview(self, vid, rql=None): + """ a lazy version of wview """ + w = self.w + self._cw.add_js('cubicweb.lazy.js') + urlparams = {'vid' : vid, 'fname' : 'view'} + if rql is not None: + urlparams['rql'] = rql + w(u'
    ' % ( + vid, xml_escape(self._cw.build_url('json', **urlparams)))) + w(u'
    ') + self._cw.add_onload(u""" + jQuery('#lazy-%(vid)s').bind('%(event)s', function() { + load_now('#lazy-%(vid)s');});""" + % {'event': 'load_%s' % vid, 'vid': vid}) + +This creates a `div` with a specific event associated to it. + +The full version deals with: + +* optional parameters such as an entity eid, an rset + +* the ability to further reload the fragment + +* the ability to display a spinning wheel while the fragment is still + not loaded + +* handling of browsers that do not support ajax (search engines, + text-based browsers such as lynx, etc.) + +The javascript side is quite simple, due to loadxhtml awesomeness. + +.. sourcecode:: javascript + + function load_now(eltsel) { + var lazydiv = jQuery(eltsel); + lazydiv.loadxhtml(lazydiv.attr('cubicweb:loadurl')); + } + +This is all significantly different of the previous `simple example` +(albeit this example actually comes from real-life code). + +Notice how the `cubicweb:loadurl` is used to convey the url +information. The base of this url is similar to the global javascript +JSON_BASE_URL. According to the pattern described earlier, +the `fname` parameter refers to the standard `js_view` method of the +JSonController. This method renders an arbitrary view provided a view +id (or `vid`) is provided, and most likely an rql expression yielding +a result set against which a proper view instance will be selected. + +The `cubicweb:loadurl` is one of the 29 attributes extensions to XHTML +in a specific cubicweb namespace. It is a means to pass information +without breaking HTML nor XHTML compliance and without resorting to +ungodly hacks. + +Given all this, it is easy to add a small nevertheless useful feature +to force the loading of a lazy view (for instance, a very +computation-intensive web page could be scinded into one fast-loading +part and a delayed part). + +On the server side, a simple call to a javascript function is +sufficient. + +.. sourcecode:: python + + def forceview(self, vid): + """trigger an event that will force immediate loading of the view + on dom readyness + """ + self._cw.add_onload("trigger_load('%s');" % vid) + +The browser-side definition follows. + +.. sourcecode:: javascript + + function trigger_load(divid) { + jQuery('#lazy-' + divd).trigger('load_' + divid); + } + + + + +XXX reloadComponent +XXX userCallback / user_callback + +Javascript library: overview +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* jquery.* : jquery and jquery UI library + +* cubicweb.ajax.js : concentrates all ajax related facilities (it + extends jQuery with the loahxhtml function, provides a handfull of + high-level ajaxy operations like asyncRemoteExec, reloadComponent, + replacePageChunk, getDomFromResponse) + +* cubicweb.python.js : adds a number of practical extension to stdanrd + javascript objects (on Date, Array, String, some list and dictionary + operations), and a pythonesque way to build classes. Defines a + CubicWeb namespace. + +* cubicweb.htmlhelpers.js : a small bag of convenience functions used + in various other cubicweb javascript resources (baseuri, progress + cursor handling, popup login box, html2dom function, etc.) + +* cubicweb.widgets.js : provides a widget namespace and constructors + and helpers for various widgets (mainly facets and timeline) + +* cubicweb.edition.js : used by edition forms + +* cubicweb.preferences.js : used by the preference form + +* cubicweb.facets.js : used by the facets mechanism + +There is also javascript support for massmailing, gmap (google maps), +fckcwconfig (fck editor), timeline, calendar, goa (CubicWeb over +AppEngine), flot (charts drawing), tabs and bookmarks. diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/property.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/property.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,12 @@ +The property mecanism +--------------------- +XXX CWProperty and co + + +Property API +~~~~~~~~~~~~ +XXX feed me + +Registering and using your own property +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +XXX feed me diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/publisher.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/publisher.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,63 @@ +.. _publisher: + +Publisher +--------- + +What happens when an HTTP request is issued ? + +The story begins with the ``CubicWebPublisher.main_publish`` +method. We do not get upper in the bootstrap process because it is +dependant on the used HTTP library. With `twisted`_ however, +``cubicweb.etwist.server.CubicWebRootResource.render_request`` is the +real entry point. + +What main_publish does: + +* get a controller id and a result set from the path (this is actually + delegated to the `urlpublisher` component) + +* the controller is then selected (if not, this is considered an + authorization failure and signaled as such) and called + +* then either a proper result is returned, in which case the + request/connection object issues a ``commit`` and returns the result + +* or error handling must happen: + + * ``ValidationErrors`` pop up there and may lead to a redirect to a + previously arranged url or standard error handling applies + * an HTTP 500 error (`Internal Server Error`) is issued + + +Now, let's turn to the controller. There are many of them in +:mod:`cubicweb.web.views.basecontrollers`. We can just follow the +default `view` controller that is selected on a `view` path. See the +:ref:`controllers` chapter for more information on controllers. + +The `View` controller's entry point is the `publish` method. It does +the following: + +* compute the `main` view to be applied, using either the given result + set or building one from a user provided rql string (`rql` and `vid` + can be forced from the url GET parameters), that is: + + * compute the `vid` using the result set and the schema (see + `cubicweb.web.views.vid_from_rset`) + * handle all error cases that could happen in this phase + +* do some cache management chores + +* select a main template (typically `TheMainTemplate`, see chapter + :ref:`templates`) + +* call it with the result set and the computed view. + +What happens next actually depends on the template and the view, but +in general this is the rendering phase. + + +CubicWebPublisher API +````````````````````` + +.. autoclass:: cubicweb.web.application.CubicWebPublisher + :members: diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/request.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/request.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,79 @@ +The `Request` class (`cubicweb.web`) +------------------------------------ + +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. + +**Globally, a request represents a user query, either through HTTP or not +(we also talk about RQL queries on the server side for example).** + +An instance of `Request` has the following attributes: + +* `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 + +But also: + +* `Session data handling` + + * `session_data()`, returns a dictionary containing all the session data + * `get_session_data(key, default=None)`, returns a value associated to the given + key or the value `default` if the key is not defined + * `set_session_data(key, value)`, assign a value to a key + * `del_session_data(key)`, suppress the value associated to a key + +* `Cookies handling` + + * `get_cookie()`, returns a dictionary containing the value of the header + HTTP 'Cookie' + * `set_cookie(cookie, key, maxage=300)`, adds a header HTTP `Set-Cookie`, + with a minimal 5 minutes length of duration by default (`maxage` = None + returns a *session* cookie which will expire when the user closes the browser + window) + * `remove_cookie(cookie, key)`, forces a value to expire + +* `URL handling` + + * `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 + +* `And more...` + + * `set_content_type(content_type, filename=None)`, adds the header HTTP + 'Content-Type' + * `get_header(header)`, returns the value associated to an arbitrary header + of the HTTP request + * `set_header(header, value)`, adds an arbitrary header in the response + * `cursor()` returns a RQL cursor on the session + * `execute(*args, **kwargs)`, shortcut to ``.cursor().execute()`` + * `property_value(key)`, properties management (`CWProperty`) + * dictionary `data` to store data to share informations between components + *while a request is executed* + +Please note that this class is abstract and that a concrete implementation +will be provided by the *frontend* web used (in particular *twisted* as of +today). For the views or others that are executed on the server side, +most of the interface of `Request` is defined in the session associated +to the client. + +API +``` + +The elements we gave in overview for above are built in three layers, +from ``cubicweb.req.RequestSessionBase``, ``cubicweb.dbapi.DBAPIRequest`` and +``cubicweb.web.CubicWebRequestBase``. + +.. autoclass:: cubicweb.req.RequestSessionBase + :members: + +.. autoclass:: cubicweb.dbapi.DBAPIRequest + :members: + +.. autoclass:: cubicweb.web.request.CubicWebRequestBase + :members: diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/rtags.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/rtags.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,21 @@ +Configuring the user interface +------------------------------ + +.. _relation_tags: + +Relation tags +~~~~~~~~~~~~~ +.. automodule:: cubicweb.rtags + +.. _uicfg: + +The ``uicfg`` module +~~~~~~~~~~~~~~~~~~~~ + +.. note:: + + The part of uicfg that deals with primary views is in the + :ref:`primary_view_configuration` chapter. + +.. automodule:: cubicweb.web.uicfg + diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/views/basetemplates.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/views/basetemplates.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,115 @@ +.. -*- coding: utf-8 -*- + +.. |cubicweb| replace:: *CubicWeb* + +.. _templates: + +Templates +========= + +Templates are the entry point for the |cubicweb| view system. As seen +in :ref:`views_base_class`, there are two kinds of views: the +templatable and non-templatable. + +Non-templatable views are standalone. They are responsible for all the +details such as setting a proper content type (or mime type), the +proper document headers, namespaces, etc. Examples are pure xml views +such as RSS or Semantic Web views (`SIOC`_, `DOAP`_, `FOAF`_, `Linked +Data`_, etc.). + +.. _`SIOC`: http://sioc-project.org/ +.. _`DOAP`: http://trac.usefulinc.com/doap +.. _`FOAF`: http://www.foaf-project.org/ +.. _`Linked Data`: http://linkeddata.org/ + +Templatable views are not concerned with such pesky details. They +leave it to the template. Conversely, the template's main job is to: + +* set up the proper document header and content type +* define the general layout of a document +* invoke adequate views in the various sections of the document + + +Look at :mod:`cubicweb.web.views.basetemplates` and you will find the +base templates used to generate (X)HTML for your application. The most +important template there is `TheMainTemplate`. + +.. _the_main_template_layout: + +TheMainTemplate +--------------- + +.. _the_main_template_sections: + +Layout and sections +``````````````````` + +A page is composed as indicated on the schema below : + +.. image:: ../../images/main_template.png + +The sections dispatches specific views: + +* `header`: the rendering of the header is delegated to the + `htmlheader` view, whose default implementation can be found in + ``basetemplates.py`` and which does the following things: + + * inject the favicon if there is one + * inject the global style sheets and javascript resources + * call and display a link to an rss component if there is one available + + it also sets up the page title, and fills the actual + `header` section with top-level components, using the `header` view, which: + + * tries to display a logo, the name of the application and the `breadcrumbs` + * provides a login status area + * provides a login box (hiden by default) + +* `left column`: this is filled with all selectable boxes matching the + `left` context (there is also a right column but nowadays it is + seldom used due to bad usability) + +* `contentcol`: this is the central column; it is filled with: + + * the `rqlinput` view (hidden by default) + * the `applmessages` component + * the `contentheader` view which in turns dispatches all available + content navigation components having the `navtop` context (this + is used to navigate through entities implementing the IPrevNext + interface) + * the view that was given as input to the template's `call` + method, also dealing with pagination concerns + * the `contentfooter` + +* `footer`: adds all footer actions + +.. note:: + + How and why a view object is given to the main template is explained + in the :ref:`publisher` chapter. + +Class attributes +```````````````` + +We can also control certain aspects of the main template thanks to the following +forms parameters: + +* `__notemplate`, if present (whatever the value assigned), only the content view + is returned +* `__force_display`, if present and its value is not null, no navigation + whatever the number of entities to display +* `__method`, if the result set to render contains only one entity and this + parameter is set, it refers to a method to call on the entity by passing it + the dictionary of the forms parameters, before going the classic way (through + step 1 and 2 described juste above) + +Other templates +--------------- + +Other standard templates include: + +* `login` and `logout` + +* `error-template` specializes TheMainTemplate to do proper end-user + output if an error occurs during the computation of TheMainTemplate + (it is a fallback view). diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/views/baseviews.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/views/baseviews.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,84 @@ +.. -*- coding: utf-8 -*- + +Base views +---------- + +*CubicWeb* provides a lot of standard views, that can be found in + :mod:`cubicweb.web.views` and :mod:`cubicweb.web.views.baseviews`. + +A certain number of views are used to build the web interface, which +apply to one or more entities. Their identifier is what distinguish +them from each others and the main ones are: + +HTML views +~~~~~~~~~~ + +Special views +````````````` + +*noresult* + This view is the default view used when no result has been found + (e.g. empty result set). + +*final* + Display the value of a cell without trasnformation (in case of a non final + entity, we see the eid). Applicable on any result set. + +.. note:: + + `final` entities are merely attributes. + +*null* + This view is the default view used when nothing needs to be rendered. + It is always applicable. + +Entity views +```````````` + +*incontext, outofcontext* + Those are used to display a link to an entity, depending on the + entity having to be displayed in or out of context + (of another entity). By default it respectively produces the + result of `textincontext` and `textoutofcontext` wrapped in a link + leading to the primary view of the entity. + +*oneline* + This view is used when we can't tell if the entity should be considered as + displayed in or out of context. By default it produces the result of `text` + in a link leading to the primary view of the entity. + +List +````` + +*list* + This view displays a list of entities by creating a HTML list (`
      `) + and call the view `listitem` for each entity of the result set. + +*listitem* + This view redirects by default to the `outofcontext` view. + +*sameetypelist* + This view displays a list of entities of the same type, in HTML section (`
      `) + and call the view `sameetypelistitem` for each entity of the result set. + +*sameetypelistitem* + This view redirects by default to the `listitem` view. + +*csv* + This view applies to entity groups, which are individually + displayed using the `incontext` view. It displays each entity as a + coma separated list. It is NOT related to the well-known text file + format. + +Text entity views +~~~~~~~~~~~~~~~~~ + +*text* + This is the simplest text view for an entity. By default it returns the + result of the `.dc_title` method, which is cut to fit the + `navigation.short-line-size` property if necessary. + +*textincontext, textoutofcontext* + Similar to the `text` view, but called when an entity is considered out or + in context. By default it returns respectively the result of the + methods `.dc_title` and `.dc_long_title` of the entity. diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/views/boxes.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/views/boxes.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,36 @@ +Boxes +----- + +(:mod:`cubicweb.web.views.boxes`) + +*sidebox* + This view displays usually a side box of some related entities + in a primary view. + +The action box +~~~~~~~~~~~~~~~ + +The ``add_related`` is an automatic menu in the action box that allows to create +an entity automatically related to the initial entity (context in +which the box is displayed). By default, the links generated in this +box are computed from the schema properties of the displayed entity, +but it is possible to explicitly specify them thanks to the +`cubicweb.web.uicfg.rmode` *relation tag*: + +* `link`, indicates that a relation is in general created pointing + to an existing entity and that we should not to display a link + for this relation + +* `create`, indicates that a relation is in general created pointing + to new entities and that we should display a link to create a new + entity and link to it automatically + + +If necessary, it is possible to overwrite the method +`relation_mode(rtype, targettype, x='subject')` to dynamically +compute a relation creation category. + +Please note that if at least one action belongs to the `addrelated` category, +the automatic behavior is desactivated in favor of an explicit behavior +(e.g. display of `addrelated` category actions only). + diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/views/breadcrumbs.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/views/breadcrumbs.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,58 @@ +Breadcrumbs +----------- + +Breadcrumbs are a navigation component to help the user locate himself +along a path of entities. + +Display +~~~~~~~ + +Breadcrumbs are displayed by default in the header section (see +:ref:`the_main_template_sections`). With the default main +template, the header section is composed by the logo, the application +name, breadcrumbs and, at the most right, the login box. Breadcrumbs +are displayed just next to the application name, thus breadcrumbs +begin with a separator. + +Here is the header section of the CubicWeb's forge: + +.. image:: ../../images/breadcrumbs_header.png + +There are three breadcrumbs components defined in +:mod:`cubicweb.web.views.ibreadcrumbs`: + +- `BreadCrumbEntityVComponent`: displayed for a result set with one line + if the entity implements the ``IBreadCrumbs`` interface. +- `BreadCrumbETypeVComponent`: displayed for a result set with more than + one line, but with all entities of the same type which implement the + ``IBreadCrumbs`` interface. +- `BreadCrumbAnyRSetVComponent`: displayed for any other result set. + +Building breadcrumbs +~~~~~~~~~~~~~~~~~~~~ + +The ``IBreadCrumbs`` interface is defined in the +:mod:`cubicweb.interfaces` module. It specifies that an entity which +implements this interface must have a ``breadcrumbs`` method. + +.. note:: + + Redefining the breadcrumbs is the hammer way to do it. Another way + is to define the `parent` method on an entity (as defined in the + `ITree` interface). If available, it will be used to compute + breadcrumbs. + +Here is the API of the ``breadcrumbs`` method: + +.. automethod:: cubicweb.interfaces.IBreadCrumbs.breadcrumbs + +If the breadcrumbs method return a list of entities, the +``cubicweb.web.views.ibreadcrumbs.BreadCrumbView`` is used to display +the elements. + +By default, for any entity, if recurs=True, breadcrumbs method returns +a list of entities, else a list of a simple string. + +In order to see a hierarchical breadcrumbs, entities must have a +``parent`` method which returns the parent entity. By default this +method doesn't exist on entity, given that it can not be guessed. diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/views/editforms.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/views/editforms.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,6 @@ +Standard forms +-------------- + + (:mod:`cubicweb.web.views.editforms`) + +XXX feed me diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/views/embedding.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/views/embedding.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,9 @@ +.. -*- coding: utf-8 -*- + +Embedding external pages +------------------------ + +(:mod:`cubicweb.web.views.embedding`) + +including external content + diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/views/idownloadable.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/views/idownloadable.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,5 @@ +The 'download' view +------------------- + +(:mod:`cubicweb.web.views.idownloadable`) + diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/views/index.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/views/index.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,30 @@ +The View system +=============== + +This chapter aims to describe the concept of a `view` used all along +the development of a web application and how it has been implemented +in |cubicweb|. + + +.. toctree:: + :maxdepth: 3 + + views + basetemplates + primary + baseviews + startup + boxes + table + xmlrss +.. editforms + +.. toctree:: + :maxdepth: 3 + + urlpublish + breadcrumbs +.. wdoc +.. embedding +.. idownloadable + diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/views/primary.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/views/primary.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,312 @@ +.. _primary_view: + +The Primary View +----------------- + +(:mod:`cubicweb.web.views.primary`) + +By default, *CubicWeb* provides a view that fits every available +entity type. This is the first view you might be interested in +modifying. It is also one of the richest and most complex. + +It is automatically selected on a one line result set containing an +entity. + +This view is supposed to render a maximum of informations about the +entity. + +.. _primary_view_layout: + +Layout +`````` + +The primary view has the following layout. + +.. image:: ../../images/primaryview_template.png + +.. _primary_view_configuration: + +Primary view configuration +`````````````````````````` + +If you want to customize the primary view of an entity, overriding the primary +view class may not be necessary. For simple adjustments (attributes or relations +display locations and styles), a much simpler way is to use uicfg. + +Attributes/relations display location +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In the primary view, there are 3 sections where attributes and +relations can be displayed (represented in pink in the image above): + +* attributes +* relations +* sideboxes + +**Attributes** can only be displayed in the attributes section (default + behavior). They can also be hidden. + +For instance, to hide the ``title`` attribute of the ``Blog`` entity: + +.. sourcecode:: python + + from cubicweb.web import uicfg + uicfg.primaryview_section.tag_attribute(('Blog', 'title'), 'hidden') + +**Relations** can be either displayed in one of the three sections or hidden. + +For relations, there are two methods: + +* ``tag_object_of`` for modifying the primary view of the object +* ``tag_subject_of`` for modifying the primary view of the subject + +These two methods take two arguments: + +* a triplet ``(subject, relation_name, object)``, where subject or object can be replaced with ``'*'`` +* the section name or ``hidden`` + +.. sourcecode:: python + + pv_section = uicfg.primaryview_section + # hide every relation `entry_of` in the `Blog` primary view + pv_section.tag_object_of(('*', 'entry_of', 'Blog'), 'hidden') + + # display `entry_of` relations in the `relations` + # section in the `BlogEntry` primary view + pv_section.tag_subject_of(('BlogEntry', 'entry_of', '*'), 'relations') + + +Display content +^^^^^^^^^^^^^^^ + +You can use ``primaryview_display_ctrl`` to customize the display of attributes +or relations. Values of ``primaryview_display_ctrl`` are dictionaries. + + +Common keys for attributes and relations are: + +* ``vid``: specifies the regid of the view for displaying the attribute or the relation. + + If ``vid`` is not specified, the default value depends on the section: + * ``attributes`` section: 'reledit' view + * ``relations`` section: 'autolimited' view + * ``sideboxes`` section: 'sidebox' view + +* ``order``: int used to control order within a section. When not specified, + automatically set according to order in which tags are added. + +.. sourcecode:: python + + # let us remind the schema of a blog entry + class BlogEntry(EntityType): + title = String(required=True, fulltextindexed=True, maxsize=256) + publish_date = Date(default='TODAY') + content = String(required=True, fulltextindexed=True) + entry_of = SubjectRelation('Blog', cardinality='?*') + + # now, we want to show attributes + # with an order different from that in the schema definition + view_ctrl = uicfg.primaryview_display_ctrl + for index, attr in enumerate('title', 'content', 'publish_date'): + view_ctrl.tag_attribute(('BlogEntry', attr), {'order': index}) + +Keys for relations only: + +* ``label``: label for the relations section or side box + +* ``showlabel``: boolean telling whether the label is displayed + +* ``limit``: boolean telling if the results should be limited. If so, a link to all results is displayed + +* ``filter``: callback taking the related result set as argument and returning it filtered + +.. sourcecode:: python + + pv_section = uicfg.primaryview_section + # in `CWUser` primary view, display `created_by` + # relations in relations section + pv_section.tag_object_of(('*', 'created_by', 'CWUser'), 'relations') + + # display this relation as a list, sets the label, + # limit the number of results and filters on comments + def filter_comment(rset): + return rset.filtered_rset(lambda x: x.e_schema == 'Comment') + pv_ctrl = uicfg.primaryview_display_ctrl + pv_ctrl.tag_object_of(('*', 'created_by', 'CWUser'), + {'vid': 'list', 'label': _('latest comment(s):'), + 'limit': True, + 'filter': filter_comment}) + +.. warning:: with the ``primaryview_display_ctrl`` rtag, the subject or the + object of the relation is ignored for respectively ``tag_object_of`` or + ``tag_subject_of``. To avoid warnings during execution, they should be set to + ``'*'``. + +Rendering methods and attributes +```````````````````````````````` + +The basic layout of a primary view is as in the +:ref:`primary_view_layout` section. This layout is actually drawn by +the `render_entity` method. + +The methods you may want to modify while customizing a ``PrimaryView`` +are: + +*render_entity_title(self, entity)* + Renders the entity title using the ``def dc_title(self)`` method. + +*render_entity_metadata(self, entity)* + Renders the entity metadata by calling the ``metadata`` view on the + entity. This generic view is in cubicweb.views.baseviews. + +*render_entity_attributes(self, entity)* + Renders all the attribute of an entity with the exception of + attribute of type `Password` and `Bytes`. The skip_none class + attribute controls the display of None valued attributes. + +*render_entity_relations(self, entity)* + Renders all the relations of the entity in the main section of the page. + +*render_side_boxes(self, entity, boxes)* + Renders relations of the entity in a side box. + +The placement of relations in the relations section or in side boxes +can be controlled through the :ref:`primary_view_configuration` mechanism. + +*content_navigation_components(self, context)* + This method is applicable only for entity type implementing the interface + `IPrevNext`. This interface is for entities which can be linked to a previous + and/or next entity. This method will render the navigation links between + entities of this type, either at the top or at the bottom of the page + given the context (navcontent{top|bottom}). + +Also, please note that by setting the following attributes in your +subclass, you can already customize some of the rendering: + +*show_attr_label* + Renders the attribute label next to the attribute value if set to True. + Otherwise, does only display the attribute value. + +*show_rel_label* + Renders the relation label next to the relation value if set to True. + Otherwise, does only display the relation value. + +*skip_none* + Does not render an attribute value that is None if set to True. + +*main_related_section* + Renders the relations of the entity if set to True. + +A good practice is for you to identify the content of your entity type for which +the default rendering does not answer your need so that you can focus on the specific +method (from the list above) that needs to be modified. We do not advise you to +overwrite ``render_entity`` unless you want a completely different layout. + +Example of customization and creation +````````````````````````````````````` + +We'll show you now an example of a ``primary`` view and how to customize it. + +We continue along the basic tutorial :ref:`tuto_blog`. + +If you want to change the way a ``BlogEntry`` is displayed, just override +the method ``cell_call()`` of the view ``primary`` in ``BlogDemo/views.py``. + +.. sourcecode:: python + + from cubicweb.selectors import implements + from cubicweb.web.views.primary import Primaryview + + class BlogEntryPrimaryView(PrimaryView): + __select__ = PrimaryView.__select__ & implements('BlogEntry') + + def render_entity_attributes(self, entity): + self.w(u'

      published on %s

      ' % + entity.publish_date.strftime('%Y-%m-%d')) + super(BlogEntryPrimaryView, self).render_entity_attributes(entity) + +The above source code defines a new primary view for +``BlogEntry``. The `id` class attribute is not repeated there since it +is inherited through the `primary.PrimaryView` class. + +The selector for this view chains the selector of the inherited class +with its own specific criterion. + +The view method ``self.w()`` is used to output data. Here `lines +08-09` output HTML for the publication date of the entry. + +.. image:: ../../images/lax-book_09-new-view-blogentry_en.png + :alt: blog entries now look much nicer + +Let us now improve the primary view of a blog + +.. sourcecode:: python + + from logilab.mtconverter import xml_escape + from cubicweb.selectors import implements, one_line_rset + from cubicweb.web.views.primary import Primaryview + + class BlogPrimaryView(PrimaryView): + __regid__ = 'primary' + __select__ = PrimaryView.__select__ & implements('Blog') + rql = 'Any BE ORDERBY D DESC WHERE BE entry_of B, BE publish_date D, B eid %(b)s' + + def render_entity_relations(self, entity): + rset = self._cw.execute(self.rql, {'b' : entity.eid}) + for entry in rset.entities(): + self.w(u'

      %s

      ' % entry.view('inblogcontext')) + + class BlogEntryInBlogView(EntityView): + __regid__ = 'inblogcontext' + __select__ = implements('BlogEntry') + + def cell_call(self, row, col): + entity = self.cw_rset.get_entity(row, col) + self.w(u'%s' % + entity.absolute_url(), + xml_escape(entity.content[:50]), + xml_escape(entity.description)) + +This happens in two places. First we override the +render_entity_relations method of a Blog's primary view. Here we want +to display our blog entries in a custom way. + +At `line 10`, a simple request is made to build a result set with all +the entities linked to the current ``Blog`` entity by the relationship +``entry_of``. The part of the framework handling the request knows +about the schema and infers that such entities have to be of the +``BlogEntry`` kind and retrieves them (in the prescribed publish_date +order). + +The request returns a selection of data called a result set. Result +set objects have an .entities() method returning a generator on +requested entities (going transparently through the `ORM` layer). + +At `line 13` the view 'inblogcontext' is applied to each blog entry to +output HTML. (Note that the 'inblogcontext' view is not defined +whatsoever in *CubicWeb*. You are absolutely free to define whole view +families.) We juste arrange to wrap each blogentry output in a 'p' +html element. + +Next, we define the 'inblogcontext' view. This is NOT a primary view, +with its well-defined sections (title, metadata, attribtues, +relations/boxes). All a basic view has to define is cell_call. + +Since views are applied to result sets which can be tables of data, we +have to recover the entity from its (row,col)-coordinates (`line +20`). Then we can spit some HTML. + +.. warning:: + + Be careful: all strings manipulated in *CubicWeb* are actually + unicode strings. While web browsers are usually tolerant to + incoherent encodings they are being served, we should not abuse + it. Hence we have to properly escape our data. The xml_escape() + function has to be used to safely fill (X)HTML elements from Python + unicode strings. + +Assuming we added entries to the blog titled `MyLife`, displaying it +now allows to read its description and all its entries. + +.. image:: ../../images/lax-book_10-blog-with-two-entries_en.png + :alt: a blog and all its entries diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/views/startup.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/views/startup.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,15 @@ +Startup views +------------- + +(:mod:`cubicweb.web.views.startup`) + +The usual selectors are no_rset or yes. These views don't apply to a +result set. + +*index* + This view defines the home page of your application. It does not require + a result set to apply to. + +*schema* + A view dedicated to the display of the schema of the instance + diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/views/table.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/views/table.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,19 @@ +Table view +---------- + +(:mod:`cubicweb.web.views.tableview`) + +*table* + Creates a HTML table (``) and call the view `cell` for each cell of + the result set. Applicable on any result set. + +*cell* + By default redirects to the `final` view if this is a final entity or + `outofcontext` view otherwise + + +API +``` + +.. autoclass:: cubicweb.web.views.tableview.TableView + :members: diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/views/urlpublish.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/views/urlpublish.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,73 @@ +.. -*- coding: utf-8 -*- + +URL publishing +-------------- + +(:mod:`cubicweb.web.views.urlpublishing`) + +.. automodule:: cubicweb.web.views.urlpublishing + +.. autoclass:: cubicweb.web.views.urlpublishing.URLPublisherComponent + :members: + +URL rewriting +------------- + +(:mod:`cubicweb.web.views.urlrewrite`) + +.. autoclass:: cubicweb.web.views.urlrewrite.URLRewriter + :members: + +.. autoclass:: cubicweb.web.views.urlrewrite.SimpleReqRewriter + :members: + +.. autoclass:: cubicweb.web.views.urlrewrite.SchemaBasedRewriter + :members: + + +``SimpleReqRewriter`` is enough for a certain number of simple cases. If it is not sufficient, ``SchemaBasedRewriter`` allows to do more elaborate things. + +Here is an example of ``SimpleReqRewriter`` usage with plain string: + +.. sourcecode:: python + + from cubicweb.web.views.urlrewrite import SimpleReqRewriter + class TrackerSimpleReqRewriter(SimpleReqRewriter): + rules = [ + ('/versions', dict(vid='versionsinfo')), + ] + +When the url is `/versions`, the view with the __regid__ `versionsinfo` is displayed. + +Here is an example of ``SimpleReqRewriter`` usage with regular expressions: + +.. sourcecode:: python + + from cubicweb.web.views.urlrewrite import ( + SimpleReqRewriter, rgx) + + class BlogReqRewriter(SimpleReqRewriter): + rules = [ + (rgx('/blogentry/([a-z_]+)\.rss'), + dict(rql=('Any X ORDERBY CD DESC LIMIT 20 WHERE X is BlogEntry,' + 'X creation_date CD, X created_by U, ' + 'U login "%(user)s"' + % {'user': r'\1'}, vid='rss'))), + ] + +When a url matches the regular expression, the view with the __regid__ +`rss` which match the result set is displayed. + +Here is an example of ``SchemaBasedRewriter`` usage: + +.. sourcecode:: python + + from cubicweb.web.views.urlrewrite import ( + SchemaBasedRewriter, rgx, build_rset) + + class TrackerURLRewriter(SchemaBasedRewriter): + rules = [ + (rgx('/project/([^/]+)/([^/]+)/tests'), + build_rset(rql='Version X WHERE X version_of P, P name %(project)s, X num %(num)s', + rgxgroups=[('project', 1), ('num', 2)], vid='versiontests')), + ] diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/views/views.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/views/views.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,142 @@ + +.. _Views: + +Principles +---------- + +We'll start with a description of the interface providing a basic +understanding of the available classes and methods, then detail the +view selection principle. + +A `View` is an object responsible for the rendering of data from the +model into an end-user consummable form. They typically churn out an +XHTML stream, but there are views concerned with email other non-html +outputs. + +.. _views_base_class: + +Discovering possible views +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is possible to configure the web user interface to have a left box +showing all the views than can be applied to the current result set. + +To enable this, click on your login at the top right corner. Chose +"user preferences", then "boxes", then "possible views box" and check +"visible = yes" before validating your changes. + +The views listed there we either not selected because of a lower +score, or they were deliberately excluded by the main template logic. + + +Basic class for views +~~~~~~~~~~~~~~~~~~~~~ + +Class `View` (`cubicweb.view`) +``````````````````````````````` + +This class is an abstraction of a view class, used as a base class for +every renderable object such as views, templates and other user +interface components. + +A `View` is instantiated to render a result set or part of a result +set. `View` subclasses may be parametrized using the following class +attributes: + +* `templatable` indicates if the view may be embedded in a main + template or if it has to be rendered standalone (i.e. pure XML views + must not be embedded in the main template of HTML pages) + +* if the view is not templatable, it should set the `content_type` + class attribute to the correct MIME type (text/xhtml being the + default) + +* the `category` attribute may be used in the interface to regroup + related view kinds together + +A view writes to its output stream thanks to its attribute `w` (the +append method of an `UStreamIO`, except for binary views). + +At instantiation time, the standard `_cw` and `cw_rset` attributes are +added and the `w` attribute will be set at rendering time. + +The basic interface for views is as follows (remember that the result +set has a tabular structure with rows and columns, hence cells): + +* `render(**context)`, render the view by calling `call` or + `cell_call` depending on the context + +* `call(**kwargs)`, call the view for a complete result set or null + (the default implementation calls `cell_call()` on each cell of the + result set) + +* `cell_call(row, col, **kwargs)`, call the view for a given cell of a + result set (`row` and `col` being integers used to access the cell) + +* `url()`, returns the URL enabling us to get the view with the current + result set + +* `wview(__vid, rset, __fallback_vid=None, **kwargs)`, call the view of + identifier `__vid` on the given result set. It is possible to give a + fallback view identifier that will be used if the requested view is + not applicable to the result set. + +* `html_headers()`, returns a list of HTML headers to be set by the + main template + +* `page_title()`, returns the title to use in the HTML header `title` + +Other basic view classes +```````````````````````` +Here are some of the subclasses of `View` defined in `cubicweb.common.view` +that are more concrete as they relate to data rendering within the application: + +* `EntityView`, view applying to lines or cell containing an entity (e.g. an eid) +* `StartupView`, start view that does not require a result set to apply to +* `AnyRsetView`, view applicable to any result set + +Examples of views class +``````````````````````` + +- Using `templatable`, `content_type` and HTTP cache configuration + +.. sourcecode:: python + + class RSSView(XMLView): + __regid__ = 'rss' + title = _('rss') + templatable = False + content_type = 'text/xml' + http_cache_manager = MaxAgeHTTPCacheManager + cache_max_age = 60*60*2 # stay in http cache for 2 hours by default + + +- Using a custom selector + +.. sourcecode:: python + + class SearchForAssociationView(EntityView): + """view called by the edition view when the user asks + to search for something to link to the edited eid + """ + __regid__ = 'search-associate' + title = _('search for association') + __select__ = one_line_rset() & match_search_state('linksearch') & implements('Any') + + +XML views, binaries views... +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For views generating other formats than HTML (an image generated dynamically +for example), and which can not simply be included in the HTML page generated +by the main template (see above), you have to: + +* set the attribute `templatable` of the class to `False` +* set, through the attribute `content_type` of the class, the MIME + type generated by the view to `application/octet-stream` or any + relevant and more specialised mime type + +For views dedicated to binary content creation (like dynamically generated +images), we have to set the attribute `binary` of the class to `True` (which +implies that `templatable == False`, so that the attribute `w` of the view could be +replaced by a binary flow instead of unicode). diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/views/wdoc.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/views/wdoc.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,9 @@ +.. -*- coding: utf-8 -*- + +Online documentation system +--------------------------- + +(:mod:`cubicweb.web.views.wdoc`) + +XXX describe the on-line documentation system + diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/views/xmlrss.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/views/xmlrss.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,60 @@ +.. _XmlAndRss: + +XML and RSS views +----------------- + +(:mod:`cubicweb.web.views.xmlrss`) + +Overview ++++++++++ + +*rss* + Creates a RSS/XML view and call the view `rssitem` for each entity of + the result set. + +*rssitem* + Create a RSS/XML view for each entity based on the results of the dublin core + methods of the entity (`dc_*`) + +RSS Channel Example +++++++++++++++++++++ + +Assuming you have several blog entries, click on the title of the +search box in the left column. A larger search box should appear. Enter:: + + Any X ORDERBY D WHERE X is BlogEntry, X creation_date D + +and you get a list of blog entries. + +Click on your login at the top right corner. Chose "user preferences", +then "boxes", then "possible views box" and check "visible = yes" +before validating your changes. + +Enter the same query in the search box and you will see the same list, +plus a box titled "possible views" in the left column. Click on +"entityview", then "RSS". + +You just applied the "RSS" view to the RQL selection you requested. + +That's it, you have a RSS channel for your blog. + +Try again with:: + + Any X ORDERBY D WHERE X is BlogEntry, X creation_date D, + X entry_of B, B title "MyLife" + +Another RSS channel, but a bit more focused. + +A last one for the road:: + + Any C ORDERBY D WHERE C is Comment, C creation_date D LIMIT 15 + +displayed with the RSS view, that's a channel for the last fifteen +comments posted. + +[WRITE ME] + +* show that the RSS view can be used to display an ordered selection + of blog entries, thus providing a RSS channel + +* show that a different selection (by category) means a different channel diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/index.rst --- a/doc/book/en/index.rst Fri Apr 23 17:07:55 2010 +0200 +++ b/doc/book/en/index.rst Fri Apr 23 17:31:46 2010 +0200 @@ -49,7 +49,8 @@ .. toctree:: :maxdepth: 3 - development/index + devrepo/index + devweb/index .. toctree:: :maxdepth: 2