# HG changeset patch # User Laure Bourgois # Date 1227285447 -3600 # Node ID 80c65c9f7c41fc4df455219fa968c12652133c07 # Parent 979dbe0cade3c2650cbaf29fc8035fb63bce6eb7# Parent 451061423290ee3dc87fd9bc7336625aa337574f merge diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/01-00-introduction.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/01-00-introduction.en.txt Fri Nov 21 17:37:27 2008 +0100 @@ -0,0 +1,24 @@ +.. -*- coding: utf-8 -*- + +.. _Overview: + +Quick overview of `CubicWeb` +============================ + +`CubicWeb` allows us to develop web applications instances based on +one or more `cubes`. + +What we call a `cube` is a model defining the data types and views. +A `cube` is a reusable component grouped with others cubes in the file +system. + +An `instance` refers to a specific installation of one or more `cubes` + where are grouped configuration files of the final web application. + +In this document, we will show you how to create a `cube` and how to use it +in an `instance` for your web application. + +.. include:: 01-01-create-cube.en.txt +.. include:: 01-05-components.en.txt +.. include:: 01-06-maintemplate.en.txt +.. include:: 01-07-rss-xml.en.txt diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/01-01-create-cube.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/01-01-create-cube.en.txt Fri Nov 21 17:37:27 2008 +0100 @@ -0,0 +1,314 @@ +.. -*- coding: utf-8 -*- + +Create your cube +---------------- + +After you installed your `CubicWeb` development environment, you can start +to build your first cube: :: + + cubicweb-ctl newcube blog + +This will create in ``/path/to/forest/cubes`` a directory containing: :: + + blog/ + | + |-- data/ + | |-- cubes.blog.css + | |-- cubes.blog.js + | |-- external_resources + | + |-- debian/ + | |-- changelog + | |-- compat + | |-- control + | |-- copyright + | |-- cubicweb-blog.prerm + | |-- rules + | + |-- entities.py + | + |-- i18n/ + | |-- en.po + | |-- fr.po + | + |-- __init__.py + | + |-- MANIFEST.in + | + |-- migration/ + | |-- postcreate.py + | |-- precreate.py + | + |-- __pkginfo__.py + | + |-- schema.py + | + |-- setup.py + | + |-- site_cubicweb.py + | + |-- sobjects.py + | + |-- test/ + | |-- data/ + | |-- bootstrap_cubes + | |-- pytestconf.py + | |-- realdb_test_blog.py + | |-- test_blog.py + | + |-- views.py + +Any changes applied to your data model should be done in this +directory. + + +Define your data schema +----------------------- + +The data model or schema is hte core of your `CubicWeb` application. +This is where is defined the type of content you application will handle. + +The data model is defined in the file ``schema.py`` of your cube +``blog`` such as follows. + +:: + + from cubicweb.schema import format_constraint + class Blog(EntityType): + title = String(maxsize=50, required=True) + description = String() + + 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='?*') + + +A Blog has a title and a description. The title is a string that is +required by the class EntityType and must be less than 50 characters. +The description is a string that is not constrained. + +A BlogEntry has a title, a publish_date and a content. The title is a +string that is required and must be less than 100 characters. The +publish_date is a Date with a default value of TODAY, meaning that +when a BlogEntry is created, its publish_date will be the current day +unless it is modified. The content is a string that will be indexed in +the full-text index and has no constraint. + +A BlogEntry also has a relationship ``entry_of`` that links it to a +Blog. The cardinality ``?*`` means that a BlogEntry can be part of +zero or one Blog (``?`` means `zero or one`) and that a Blog can +have any number of BlogEntry (``*`` means `any number including +zero`). For completeness, remember that ``+`` means `one or more`. + + +Create your instance +-------------------- + +:: + + cubicweb-ctl create blog blogdemo + + +This command will create a directory ``~/etc/cubicweb.d/blogdemo`` +which will contain all the configuration files required to start +you web application. + +The instance ``blogdemo`` is based on the cube ``blog``. + + +Welcome in your web application +------------------------------- + +Run your application with the following command: :: + + cubicweb-ctl start -D blogdemo + + +You can now access to your web application to create blogs and post messages +by visitin the URL http://localhost:8080/. +A login form will first be prompted. By default, the application will not allow +anonymous user to get in the application. You should then use the admin +account you created at the time you initialized the database with +``cubicweb-ctl create``. + +.. image:: images/login-form.png + + +Once authenticated, you can start playing with your application +and create entities. Bravo! + +.. image:: images/blog-demo-first-page.png + +Please notice that so far, `CubicWeb` franework managed all aspects of +the web application based in the schema provided at first. + + +Create entities +--------------- + +We will now create a couple of entities in our web application. + +Create a Blog +~~~~~~~~~~~~~ + +Let us create a few of these entities. Click on the `[+]` at the right +of the link Blog. Call this new Blog ``Tech-blog`` and type in +``everything about technology`` as the description, then validate the +form by clicking on ``Validate``. + +.. image:: images/cbw-create-blog.en.png + :alt: from to create blog + +Click on the logo at top left to get back to the home page, then +follow the Blog link that will list for you all the existing Blog. +You should be seeing a list with a single item ``Tech-blog`` you +just created. + +.. image:: images/cbw-list-one-blog.en.png + :alt: displaying a list of a single blog + +Clicking on this item will get you to its detailed description except +that in this case, there is not much to display besides the name and +the phrase ``everything about technology``. + +Now get back to the home page by clicking on the top-left logo, then +create a new Blog called ``MyLife`` and get back to the home page +again to follow the Blog link for the second time. The list now +has two items. + +.. image:: images/cbw-list-two-blog.en.png + :alt: displaying a list of two blogs + +Create a BlogEntry +~~~~~~~~~~~~~~~~~~ + +Get back to the home page and click on [+] at the right of the link +BlogEntry. Call this new entry ``Hello World`` and type in some text +before clicking on ``Validate``. You added a new blog entry without +saying to what blog it belongs. There is a box on the left entitled +``actions``, click on the menu item ``modify``. You are back to the form +to edit the blog entry you just created, except that the form now has +another section with a combobox titled ``add relation``. Chose +``entry_of`` in this menu and a second combobox appears where you pick +``MyLife``. + +You could also have, at the time you started to fill the form for a +new entity BlogEntry, hit ``Apply`` instead of ``Validate`` and the +combobox titled ``add relation`` would have showed up. + + +.. image:: images/cbw-add-relation-entryof.en.png + :alt: editing a blog entry to add a relation to a blog + +Validate the changes by clicking ``Validate``. The entity BlogEntry +that is displayed now includes a link to the entity Blog named +``MyLife``. + +.. image:: images/cbw-detail-one-blogentry.en.png + :alt: displaying the detailed view of a blogentry + +Note that all of this was handled by the framework and that the only input +that was provided so far is the schema. To get a graphical view of the schema, +point your browser to the URL http://localhost:8080/schema + +.. image:: images/cbw-schema.en.png + :alt: graphical view of the schema (aka data-model) + + +Define your entities views +-------------------------- + +The views selection principle +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A view is defined by a Python class which includes: + + - an identifier (all objects in `CubicWeb` are entered in a registry + and this identifier will be used as a key) + + - a filter to select the resulsets it can be applied to + + +`CubicWeb` provides a lot of standard views for the type +`EntityView`, for a complete list, you +will have to read the code in directory ``cubicweb/web/views/`` + +A view is applied on a `result set` which contains a set of +entities we are trying to display. `CubicWeb` uses a selector +mecanism which computes a score used to identify which view +is the best to apply for the `result set` we are trying to +display. The standard library of selectors is in +``cubicweb.common.selector`` and a library of methods used to +compute scores is available in ``cubicweb.vregistry.vreq``. + +It is possible to define multiple views for the same identifier +and to associate selectors and filters to allow the application +to find the best way to render the data. We will see more details +on this in :ref:`DefinitionVues`. + +For example, the view named ``primary`` is the one used to display +a single entity. We will now show you hos to customize this view. + + +View customization +~~~~~~~~~~~~~~~~~~ + +If you wish to modify the way a `BlogEntry` is rendered, you will have to +overwrite the `primary` view defined in the module ``views`` of the cube +``cubes/blog/views.py``. + +We can by example add in front of the pulication date a prefix specifying +the date we see is the publication date. + +To do so, please apply the following changes: + +:: + + from cubicweb.web.views import baseviews + + + class BlogEntryPrimaryView(baseviews.PrimaryView): + + accepts = ('BlogEntry',) + + def render_entity_title(self, entity): + self.w(u'

%s

' % html_escape(entity.dc_title())) + + def content_format(self, entity): + return entity.view('reledit', rtype='content_format') + + def cell_call(self, row, col): + entity = self.entity(row, col) + + # display entity attributes with prefixes + self.w(u'

%s

' % entity.title) + self.w(u'

published on %s

' % entity.publish_date.strftime('%Y-%m-%d')) + self.w(u'

%s

' % entity.content) + + # display relations + siderelations = [] + if self.main_related_section: + self.render_entity_relations(entity, siderelations) + +.. note:: + When a view is modified, it is not required to restart the application + server. Save the Python file and reload the page in your web browser + to view the changes. + +You can now see that the publication date has a prefix. + +.. image:: images/cbw-update-primary-view.en.png + :alt: modified primary view + + +The above source code defines a new primary view for +``BlogEntry``. + +Since views are applied to resultsets and resulsets can be tables of +data, it is needed to recover the entity from its (row,col) +coordinates. We will get to this in more detail later. + +The view has a ``self.w()`` method that is used to output data. In our +example we use it to output HTML tags and values of the entity's attributes. diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/01-05-components.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/01-05-components.en.txt Fri Nov 21 17:37:27 2008 +0100 @@ -0,0 +1,150 @@ +.. -*- coding: utf-8 -*- + +.. _components: + +Components +---------- + +What is a component +~~~~~~~~~~~~~~~~~~~ + +A component is a model grouping one or more entity types and/or views associated +in order to provide a specific feature or even a complete application using +others components. +You can decide to write your own set of components if you wish to re-use the +entity types you develop. By default, LAX comes with its owns set of components +that you can start using right away. + + +Standard library +~~~~~~~~~~~~~~~~ + +A library of standard components is part of the `LAX` release (look at +``lax/skel/ginco-apps``). Components provide entities and views. With +``lax-0.4``, you should get a set of application entities and system +entities you can re-use. + +The available application entities are: + +* addressbook: PhoneNumber and PostalAddress + +* ebasket: Basket (like a shopping cart) + +* eblog: Blog (a *very* basic blog) + +* eclassfolder: Folder (to organize things but grouping them in folders) + +* eclasstags: Tag (to tag anything) + + +* efile: File (to allow users to upload and store binary or text files) + +* elink: Link (to collect links to web resources) + +* emailinglist: MailingList (to reference a mailing-list and the URLs + for its archives and its admin interface) + +* eperson: Person (easily mixed with addressbook) + +* etask: Task (something to be done between start and stop date) + +* ezone: Zone (to define places within larger places, for example a + city in a state in a country) + +The available system entities are: + +* ecomment: Comment (to attach comment threads to entities) + + + +Adding comments to BlogDemo +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To import a component in your application just change the line in the +``app.conf`` file. For example:: + + included-yams-components=ecomment + +will make the ``Comment`` entity available in your ``BlogDemo`` +application. + +Change the schema to add a relationship between ``BlogEntry`` and +``Comment`` and you are done. Since the ecomment component defines the +``comments`` relationship, adding the line:: + + comments = ObjectRelation('Comment', cardinality='1*', composite='object') + +to the definition of a ``BlogEntry`` will be enough. + +Clear the datastore and restart. + +Component structure +~~~~~~~~~~~~~~~~~~~ + +A complex component is structured as follows: +:: + + mycomponent/ + | + |-- schema.py + | + |-- entities/ + | + |-- sobjects/ + | + |-- views/ + | + |-- test/ + | + |-- i18n/ + | + |-- data/ + | + |-- migration/ + | |- postcreate.py + | \- depends.map + | + |-- debian/ + | + \-- __pkginfo__.py + +We can also define simple Python module instead of directories (packages), for example: +:: + + mycomponent/ + | + |-- entities.py + |-- hooks.py + \-- views.py + + +where: + +* ``schema`` contains the definition of the schema (server side only) +* ``entities`` contains entities definition (server side and web interface) +* ``sobjects`` contains hooks and/or notification views (server side only) +* ``views`` contains the web interface components (web interface only) +* ``test`` contains tests related to the application (not installed) +* ``i18n`` contains messages 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 file 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-components used by + the component. + +At least you should have: + +* the file ``__pkginfo__.py`` +* schema definition + +[WRITE ME] + +* explain the component architecture + +* add comments to the blog by importing the comments component diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/01-06-maintemplate.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/01-06-maintemplate.en.txt Fri Nov 21 17:37:27 2008 +0100 @@ -0,0 +1,209 @@ +.. -*- coding: utf-8 -*- + +Views & Templates +----------------- + +Look at ``lax/skel/ginco/web/views/basetemplates.py`` and you will +find the base templates used to generate HTML for your application. + +A page is composed as indicated on the schema below: + +.. image:: images/lax-book.06-main-template-layout.en.png + +In this section we will go through a couple of the primary templates +you must be interested in, that is to say, the HTMLPageHeader, +the HTMLPageFooter and the TheMainTemplate. + + +HTMLPageHeader +~~~~~~~~~~~~~~~ + +Let's use a different logo than the default one provided with LAX +and customize our header. + +Change logo +``````````` + +The easiest way to use a different logo is to replace the existing +``logo.png`` in ``myapp/data`` by your prefered icon and refresh. +By default all application will look for a ``logo.png`` to be +rendered in the logo section. + +.. image:: images/lax-book.06-main-template-logo.en.png + +[ADD] +customized external_resources in myapp/data cd crih for reference + +[WRITE ME] +ADD how to use external_resources variables used in ginco/web/webconfig.py + +Customize header +````````````````` + +Let's now move the search box in the header and remove the login form +from the header. We'll show how to move it to the left column of the application. + +Let's sat we do not want anymore the login menu to be in the header, but we +prefer it to be in the left column just below the logo. As the left column is +rendered by ``TheMainTemplate``, we will show how to do it in TheMainTemplate_. + +First, to remove the login menu, we just need to comment out the display of the +login component such as follows: :: + + class MyHTMLPageHeader(HTMLPageHeader): + + def main_header(self, view): + """build the top menu with authentification info and the rql box""" + self.w(u'\n') + self.w(u'\n') + # appliname and breadcrumbs + self.w(u'') + # logged user and help + #self.w(u'') + # lastcolumn + self.w(u'\n') + self.w(u'\n') + self.template('logform', rset=self.rset, id='popupLoginBox', klass='hidden', + title=False, message=False) + + + +.. image:: images/lax-book.06-header-no-login.en.png + +Let's now move the search box in the top-right header area. To do so, we will +first create a method to get the search box display and insert it in the header +table. + +:: + + from ginco.web.views.basetemplates import HTMLPageHeader + class MyHTMLPageHeader(HTMLPageHeader): + def main_header(self, view): + """build the top menu with authentification info and the rql box""" + self.w(u'\n') + self.w(u'\n') + # appliname and breadcrumbs + self.w(u'') + + # logged user and help + #self.w(u'') + + self.w(u'') + # lastcolumn + self.w(u'\n') + self.w(u'\n') + self.template('logform', rset=self.rset, id='popupLoginBox', klass='hidden', + title=False, message=False) + + def get_searchbox(self, view, context): + boxes = list(self.vreg.possible_vobjects('boxes', self.req, self.rset, + view=view, context=context)) + if boxes: + for box in boxes: + if box.id == 'search_box': + box.dispatch(w=self.w, view=view) + + + + +HTMLPageFooter +~~~~~~~~~~~~~~ + +If you want to change the footer for example, look +for HTMLPageFooter and override it in your views file as in: +:: + + form ginco.web.views.basetemplates import HTMLPageFooter + class MyHTMLPageFooter(HTMLPageFooter): + def call(self, **kwargs): + self.w(u'') + +Updating a view does not require any restart of the server. By reloading +the page you can see your new page footer. + + +TheMainTemplate +~~~~~~~~~~~~~~~ + +.. _TheMainTemplate: + +The MainTemplate is a bit complex as it tries to accomodate many +different cases. We are now about to go through it and cutomize entirely +our application. + +TheMainTemplate is responsible for the general layout of the entire application. +It defines the template of ``id = main`` that is used by the application. Is +also defined in ``ginco/web/views/basetemplates.py`` another template that can +be used based on TheMainTemplate called SimpleMainTemplate which does not have +a top section. + +.. image:: images/lax-book.06-simple-main-template.en.png + +CSS changes +~~~~~~~~~~~ + +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/``. + +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. + +[TODO] +Add login menu in left column + + +[WRITE ME] + +* customize MainTemplate and show that everything in the user + interface can be changed + +[TODO] +Rajouter une section pour definir la terminologie utilisee. +Dans ginco-doc rajouter une section pour erudi-ctl shell ou +on liste les commandes dispos. diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/01-07-rss-xml.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/01-07-rss-xml.en.txt Fri Nov 21 17:37:27 2008 +0100 @@ -0,0 +1,45 @@ +.. -*- coding: utf-8 -*- + +RSS Channel +----------- + +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 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/01-08-book-map.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/01-08-book-map.en.txt Fri Nov 21 17:37:27 2008 +0100 @@ -0,0 +1,10 @@ +.. -*- coding: utf-8 -*- + +Book map +========= + +[WRITE ME] + +* explain how to use this book and what chapters to read in what order depending on the + objectives of the reader + diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/01-intro.en.txt --- a/doc/book/en/01-intro.en.txt Fri Nov 21 17:36:42 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -.. -*- coding: utf-8 -*- - -Introduction -============ - -This book uses version 0.4.0 of `LAX`. - -What is `LAX` ? ----------------- - -`LAX` stands for `Logilab Appengine eXtension`. It is a web application framework -based on `Google AppEngine`. - -`LAX` is a port of the web framework Logilab_ has been developping since 2001. -This framework originally published data queried from different sources including -SQL databases, LDAP directories and concurrent versionning systems -(like subversion). In April/May 2008, it was adapted to run also on -top of `Google AppEngine`'s datastore. - -`Google AppEngine` is provided with a partial port of the `Django` -framework, but Google stated at Google IO 2008 that it would not -support a specific Python web framework and that all -community-supported frameworks would be more than welcome[1]_. - -`LAX` competes with other Python web application frameworks to get -developers' attention and support. It originates from Logilab and is -the result of about ten years of experience in developing large-scale -web applications. - -Distinctive features include a data-model driven engine, a full-blown -query language, a selection/view mechanism for HTML/XML/text -generation, reuseable components, etc. It all sums up to very fast and -efficient development. - -If you like Python and its standard library, chances are you will like -`LAX` for it comes with batteries included thanks to its standard -component library. - -Compare `LAX` with other frameworks and see for yourself what is your -best option. - -.. _Logilab: http://www.logilab.fr/ -.. [1] for more on this matter, read our blog at http://www.logilab.org/blog/5216 - -Essentials ----------- - -Schema - - The schema defines the data model of an application as entities and - relationships. It is the core of an application. Entities and - relationships are modeled with a comprehensive language made of - Python classes. - -Query language - - A full-blown query language named RQL is used to formulate - requests to the datastore. - -Result set - - A resultset encapsulates the results of a request sent to - the datastore and informations about this request. - -Views - - A view is applied to a `result set` to present it as HTML, XML, - JSON, CSV, etc. Views are implemented as Python classes. There is no - templating language. - -Generated user interface - - A user interface is generated on-the-fly from the schema definition: - entities can be created, displayed, updated and deleted. As display - views are not very fancy, it is usually necessary to develop your - own. Any generated view can be overridden by defining a new one with - the same identifier. - -Components - - Pieces of schema and sets of views can be combined into - components. Larger applications can be built faster by importing - components, adding entities and relationships and overriding the - views that need to display or edit informations not provided by - components. diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/01-introduction.en.txt --- a/doc/book/en/01-introduction.en.txt Fri Nov 21 17:36:42 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,335 +0,0 @@ -.. -*- coding: utf-8 -*- - -.. _Overview: - -Quick overview of `CubicWeb` -============================ - -`CubicWeb` allows us to develop web applications instances based on -one or more `cubes`. - -What we call a `cube` is a model defining the data types and views. -A `cube` is a reusable component grouped with others cubes in the file -system. - -An `instance` refers to a specific installation of one or more `cubes` - where are grouped configuration files of the final web application. - -In this document, we will show you how to create a `cube` and how to use it -in an `instance` for your web application. - -Create your cube ----------------- - -After you installed your `CubicWeb` development environment, you can start -to build your first cube: :: - - cubicweb-ctl newcube blog - -This will create in ``/path/to/forest/cubes`` a directory containing: :: - - blog/ - | - |-- data/ - | |-- cubes.blog.css - | |-- cubes.blog.js - | |-- external_resources - | - |-- debian/ - | |-- changelog - | |-- compat - | |-- control - | |-- copyright - | |-- cubicweb-blog.prerm - | |-- rules - | - |-- entities.py - | - |-- i18n/ - | |-- en.po - | |-- fr.po - | - |-- __init__.py - | - |-- MANIFEST.in - | - |-- migration/ - | |-- postcreate.py - | |-- precreate.py - | - |-- __pkginfo__.py - | - |-- schema.py - | - |-- setup.py - | - |-- site_cubicweb.py - | - |-- sobjects.py - | - |-- test/ - | |-- data/ - | |-- bootstrap_cubes - | |-- pytestconf.py - | |-- realdb_test_blog.py - | |-- test_blog.py - | - |-- views.py - -Any changes applied to your data model should be done in this -directory. - - -Define your data schema ------------------------ - -The data model or schema is hte core of your `CubicWeb` application. -This is where is defined the type of content you application will handle. - -The data model is defined in the file ``schema.py`` of your cube -``blog`` such as follows. - -:: - - from cubicweb.schema import format_constraint - class Blog(EntityType): - title = String(maxsize=50, required=True) - description = String() - - 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='?*') - - -A Blog has a title and a description. The title is a string that is -required by the class EntityType and must be less than 50 characters. -The description is a string that is not constrained. - -A BlogEntry has a title, a publish_date and a content. The title is a -string that is required and must be less than 100 characters. The -publish_date is a Date with a default value of TODAY, meaning that -when a BlogEntry is created, its publish_date will be the current day -unless it is modified. The content is a string that will be indexed in -the full-text index and has no constraint. - -A BlogEntry also has a relationship ``entry_of`` that links it to a -Blog. The cardinality ``?*`` means that a BlogEntry can be part of -zero or one Blog (``?`` means `zero or one`) and that a Blog can -have any number of BlogEntry (``*`` means `any number including -zero`). For completeness, remember that ``+`` means `one or more`. - - -Create your instance --------------------- - -:: - - cubicweb-ctl create blog blogdemo - - -This command will create a directory ``~/etc/cubicweb.d/blogdemo`` -which will contain all the configuration files required to start -you web application. - -The instance ``blogdemo`` is based on the cube ``blog``. - - -Welcome in your web application -------------------------------- - -Run your application with the following command: :: - - cubicweb-ctl start -D blogdemo - - -You can now access to your web application to create blogs and post messages -by visitin the URL http://localhost:8080/. -A login form will first be prompted. By default, the application will not allow -anonymous user to get in the application. You should then use the admin -account you created at the time you initialized the database with -``cubicweb-ctl create``. - -.. image:: images/login-form.png - - -Once authenticated, you can start playing with your application -and create entities. Bravo! - -.. image:: images/blog-demo-first-page.png - -Please notice that so far, `CubicWeb` franework managed all aspects of -the web application based in the schema provided at first. - - -Create entities ---------------- - -We will now create a couple of entities in our web application. - -Create a Blog -~~~~~~~~~~~~~ - -Let us create a few of these entities. Click on the `[+]` at the right -of the link Blog. Call this new Blog ``Tech-blog`` and type in -``everything about technology`` as the description, then validate the -form by clicking on ``Validate``. - -.. image:: images/cbw-create-blog.en.png - :alt: from to create blog - -Click on the logo at top left to get back to the home page, then -follow the Blog link that will list for you all the existing Blog. -You should be seeing a list with a single item ``Tech-blog`` you -just created. - -.. image:: images/cbw-list-one-blog.en.png - :alt: displaying a list of a single blog - -Clicking on this item will get you to its detailed description except -that in this case, there is not much to display besides the name and -the phrase ``everything about technology``. - -Now get back to the home page by clicking on the top-left logo, then -create a new Blog called ``MyLife`` and get back to the home page -again to follow the Blog link for the second time. The list now -has two items. - -.. image:: images/cbw-list-two-blog.en.png - :alt: displaying a list of two blogs - -Create a BlogEntry -~~~~~~~~~~~~~~~~~~ - -Get back to the home page and click on [+] at the right of the link -BlogEntry. Call this new entry ``Hello World`` and type in some text -before clicking on ``Validate``. You added a new blog entry without -saying to what blog it belongs. There is a box on the left entitled -``actions``, click on the menu item ``modify``. You are back to the form -to edit the blog entry you just created, except that the form now has -another section with a combobox titled ``add relation``. Chose -``entry_of`` in this menu and a second combobox appears where you pick -``MyLife``. - -You could also have, at the time you started to fill the form for a -new entity BlogEntry, hit ``Apply`` instead of ``Validate`` and the -combobox titled ``add relation`` would have showed up. - - -.. image:: images/cbw-add-relation-entryof.en.png - :alt: editing a blog entry to add a relation to a blog - -Validate the changes by clicking ``Validate``. The entity BlogEntry -that is displayed now includes a link to the entity Blog named -``MyLife``. - -.. image:: images/cbw-detail-one-blogentry.en.png - :alt: displaying the detailed view of a blogentry - -Please notice that so far, `CubicWeb` franework managed all aspects of -the web application based in the schema provided at first. -Also if you wish to get a graphical view of the schema, visit -the link `Application schema`` which will direct you to : -http://localhost:8080/view?vid=schema - -.. image:: images/cbw-schema.en.png - :alt: graphical view of the schema (aka data-model) - - -Define your entities views --------------------------- - -The views selection principle -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A view is defined by a Python class which includes: - - - an identifier (all objects in `CubicWeb` are entered in a registry - and this identifier will be used as a key) - - - a filter to select the resulsets it can be applied to - - -`CubicWeb` provides a lot of standard views for the type -`EntityView`, for a complete list, you -will have to read the code in directory ``cubicweb/web/views/`` - -A view is applied on a `result set` which contains a set of -entities we are trying to display. `CubicWeb` uses a selector -mecanism which computes a score used to identify which view -is the best to apply for the `result set` we are trying to -display. The standard library of selectors is in -``cubicweb.common.selector`` and a library of methods used to -compute scores is available in ``cubicweb.vregistry.vreq``. - -It is possible to define multiple views for the same identifier -and to associate selectors and filters to allow the application -to find the best way to render the data. We will see more details -on this in :ref:`DefinitionVues`. - -For example, the view named ``primary`` is the one used to display -a single entity. We will now show you hos to customize this view. - - -View customization -~~~~~~~~~~~~~~~~~~ - -If you wish to modify the way a `BlogEntry` is rendered, you will have to -overwrite the `primary` view defined in the module ``views`` of the cube -``cubes/blog/views.py``. - -We can by example add in front of the pulication date a prefix specifying -the date we see is the publication date. - -To do so, please apply the following changes: - -:: - - from cubicweb.web.views import baseviews - - - class BlogEntryPrimaryView(baseviews.PrimaryView): - - accepts = ('BlogEntry',) - - def render_entity_title(self, entity): - self.w(u'

%s

' % html_escape(entity.dc_title())) - - def content_format(self, entity): - return entity.view('reledit', rtype='content_format') - - def cell_call(self, row, col): - entity = self.entity(row, col) - - # display entity attributes with prefixes - self.w(u'

%s

' % entity.title) - self.w(u'

published on %s

' % entity.publish_date.strftime('%Y-%m-%d')) - self.w(u'

%s

' % entity.content) - - # display relations - siderelations = [] - if self.main_related_section: - self.render_entity_relations(entity, siderelations) - -.. note:: - When a view is modified, it is not required to restart the application - server. Save the Python file and reload the page in your web browser - to view the changes. - -You can now see that the publication date has a prefix. - -.. image:: images/cbw-update-primary-view.en.png - :alt: modified primary view - - -The above source code defines a new primary view for -``BlogEntry``. - -Since views are applied to resultsets and resulsets can be tables of -data, it is needed to recover the entity from its (row,col) -coordinates. We will get to this in more detail later. - -The view has a ``self.w()`` method that is used to output data. In our -example we use it to output HTML tags and values of the entity's attributes. - diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/02-00-foundation.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/02-00-foundation.en.txt Fri Nov 21 17:37:27 2008 +0100 @@ -0,0 +1,34 @@ +.. -*- coding: utf-8 -*- + +`CubicWeb` Foundations +====================== + +A little history... +------------------- + +`CubicWeb` is a web application framework developped by Logilab_ since 2001. + +Entirely written in Python, `CubicWeb` publishes data from all sorts +of sources such as SQL database, LDAP directory and versioning system such +as subversion. + +`CubicWeb` user interface was designed to let the final user a huge flexibility +on how to select and how to display content. It allows to browse the knowledge +database and to display the results with the best rendering according to +the context. +This interface flexibility gives back the user the control of the +rendering parameters that are usually reserved for developpers. + + +We can list a couple of web applications developped with `CubicWeb`, an online +public phone directory (see http://www.118000.fr/), a system for managing +digital studies and simulations for a research lab, a tool for shared children +babysitting (see http://garde-partagee.atoukontact.fr/), a tool to manage +software developpment (see http://www.logilab.org), etc. + +In 2008, `CubicWeb` was ported for a new type of source : the datastore +from GoogleAppEngine_. + +.. include:: 02-01-concepts.en.txt +.. include:: 02-02-registry.en.txt +.. include:: 02-03-configuration.en.txt diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/02-01-concepts.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/02-01-concepts.en.txt Fri Nov 21 17:37:27 2008 +0100 @@ -0,0 +1,411 @@ +.. -*- coding: utf-8 -*- + +Concepts +-------- + +Global architecture +~~~~~~~~~~~~~~~~~~~ +.. image:: images/archi_globale.png + +.. note:: + For real, the client and server sides are integrated in the same + process and interact directly, without the needs for distants + calls using Pyro. It is important to note down that those two + sides, client/server, are disjointed and it is possible to execute + a couple of calls in distincts processes to balance the load of + your web site on one or more machines. + +.. _TermsVocabulary: + +Terms and vocabulary +~~~~~~~~~~~~~~~~~~~~~ + +*schema* + The schema defines the data model of an application based on entities + and relations, modeled with a comprehensive language made of Python + classes based on `yams`_ library. This is the core piece + of an application. It is initially defined in the file system and is + stored in the database at the time an instance is created. `CubicWeb` + provides a certain number of system entities included automatically as + it is necessarry for the core of `CubicWeb` and a library of + cubes that can be explicitely included if necessary. + + +*entity type* + An entity is a set of attributes; the essential attribute of + an entity is its key, named eid + +*relation type* + Entities are linked to each others by relations. In `CubicWeb` + relations are binary: by convention we name the first item of + a relation the `subject` and the second the `object`. + +*final entity type* + Final types corresponds to the basic types such as string of characters, + integers... Those types have a main property which is that they can + only be used as `object` of a relation. The attributes of an entity + (non final) are entities (finals). + +*final relation type* + A relation is said final if its `object` is a final type. This is equivalent + to an entity attribute. + +*relation definition* + a relation definition is a 3-uple (subject entity type, relation type, object entity type), + with an associated set of property such as cardinality, constraints... + +*repository* + This is the RQL server side of `CubicWeb`. Be carefull not to get + confused with a Mercurial repository or a debian repository. + +*source* + A data source is a container of data (SGBD, LDAP directory, `Google + App Engine`'s datastore ...) integrated in the + `CubicWeb` repository. This repository has at least one source, `system` which + contains the schema of the application, plain-text index and others + vital informations for the system. + +*configuration* + It is possible to create differents configurations for an instance: + + - ``repository`` : repository only, accessible for clients using Pyro + - ``twisted`` : web interface only, access the repository using Pyro + - ``all-in-one`` : web interface and repository in a single process. + The repository could be or not accessible using Pyro. + +*cube* + A cube is a model grouping one or multiple data types and/or views + to provide a specific functionnality or a complete `CubicWeb` application + potentially using other cubes. The available subes are located in the file + system at `/path/to/forest/cubicweb/cubes`. + Larger applications can be built faster by importing cubes, + adding entities and relationships and overriding the + views that need to display or edit informations not provided by + cubes. + +*instance* + An instance is a specific installation of a cube. All the required + configuration files necessarry for the well being of your web application + are grouped in an instance. This will refer to the cube(s) your application + is based on. + By example logilab.org and our intranet are two instances of a single + cube jpl, developped internally. + The instances are defined in the directory `~/etc/cubicweb.d`. + +*application* + The term application is sometime used to talk about an instance + and sometimes to talk of a cube depending on the context. + So we would like to avoid using this term and try to use *cube* and + *instance* instead. + +*result set* + This object contains the results of an RQL query sent to the source + and information on the query. + +*Pyro* + `Python Remote Object`_, distributed objects system similar to Java's RMI + (Remote Method Invocation), which can be used for the dialog between the web + side of the framework and the RQL repository. + +*query language* + A full-blown query language named RQL is used to formulate requests + to the database or any sources such as LDAP or `Google App Engine`'s + datastore. + +*views* + A view is applied to a `result set` to present it as HTML, XML, + JSON, CSV, etc. Views are implemented as Python classes. There is no + templating language. + +*generated user interface* + A user interface is generated on-the-fly from the schema definition: + entities can be created, displayed, updated and deleted. As display + views are not very fancy, it is usually necessary to develop your + own. Any generated view can be overridden by defining a new one with + the same identifier. + +*rql* + XXX + +.. _`Python Remote Object`: http://pyro.sourceforge.net/ +.. _`yams`: http://www.logilab.org/project/yams/ + + +`CubicWeb` engine +~~~~~~~~~~~~~~~~~ + +The engine in `CubicWeb` is a set of classes managing a set of objects loaded +dynamically at the startup of `CubicWeb` (*appobjects*). Those dynamics objects, based on the schema +or the library, are building the final application. The differents dymanic components are +by example: + +* client and server side + + - entities definition, containing the logic which enables application data manipulation + +* client side + + - *views*, or more specifically + + - boxes + - header and footer + - forms + - page templates + + - *actions* + - *controllers* + +* server side + + - notification hooks + - notification views + +The components of the engine are: + +* a frontal web (only twisted is available so far), transparent for dynamic objects +* an object that encapsulates the configuration +* a `registry` (`cubicweb.cwvreg`) containing the dynamic objects loaded automatically + +Every *appobject* may access to the instance configuration using its *config* attribute +and to the registry using its *vreg* attribute. + +API Python/RQL +~~~~~~~~~~~~~~ + +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 : + +`execute(rqlstring, args=None, eid_key=None, build_descr=True)` + +:rqlstring: the RQL query to execute (unicode) +:args: if the query contains substitutions, a dictionnary containing the values to use +:eid_key: + an implementation detail of the RQL queries cache implies that if a substitution + is used to introduce an eid *susceptible to raise the ambiguities in the query + type resolution*, then we have to specify the correponding key in the dictionnary + through this argument + + +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. + +.. note:: + While executing updates queries (SET, INSERT, DELETE), if a query generates + an error related to security, a rollback is automatically done on the current + transaction. + + +The `Request` class (`cubicweb.web`) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A request instance is created when an HTPP request is sent to the web server. +It contains informations such as forms 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 by example)** + +An instance of `Request` has the following attributes: + +* `user`, instance of `cubicweb.common.utils.User` corresponding to the authenticated + user +* `form`, dictionnary containing the values of a web form +* `encoding`, characters encoding to use in the response + +But also: + +:Session data handling: + * `session_data()`, returns a dictinnary 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 dictionnary 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 (`EProperty`) + * dictionnary `data` to store data to share informations between components + *while a request is executed* + +Please note down 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. + +The `AppObject` class +~~~~~~~~~~~~~~~~~~~~~ + +In general: + +* we do not inherit directly from this class but from a more specific + class such as `AnyEntity`, `EntityView`, `AnyRsetView`, + `Action`... + +* to be recordable, a subclass has to define its own register (attribute + `__registry__`) and its identifier (attribute `id`). Usually we do not have + to take care of the register, only the identifier `id`. + +We can find a certain number of attributes and methods defined in this class +and so common to all the application objects: + +At the recording, the following attributes are dynamically added to +the *subclasses*: + +* `vreg`, the `vregistry` of the application +* `schema`, the application schema +* `config`, the application configuration + +We also find on instances, the following attributes: + +* `req`, `Request` instance +* `rset`, the *result set* associated to the object if necessarry +* `cursor`, rql cursor on the session + + +:URL handling: + * `build_url(method=None, **kwargs)`, returns an absolute URL based on + the given arguments. The *controller* supposed to handle the response + can be specified through the special parameter `method` (the connection + is theoretically done automatically :). + + * `datadir_url()`, returns the directory of the application data + (contains static files such as images, css, js...) + + * `base_url()`, shortcut to `req.base_url()` + + * `url_quote(value)`, version *unicode safe* of the function `urllib.quote` + +:Data manipulation: + + * `etype_rset(etype, size=1)`, shortcut to `vreg.etype_rset()` + + * `eid_rset(eid, rql=None, descr=True)`, returns a *result set* object for + the given eid + * `entity(row, col=0)`, returns the entity corresponding to the data position + in the *result set* associated to the object + + * `complete_entity(row, col=0, skip_bytes=True)`, is equivalent to `entity` but + also call the method `complete()` on the entity before returning it + +:Data formatting: + * `format_date(date, date_format=None, time=False)` + * `format_time(time)` + +:And more...: + + * `external_resource(rid, default=_MARKER)`, access to a value defined in the + configuration file `external_resource` + + * `tal_render(template, variables)`, + + +.. note:: + When we inherit from `AppObject` (even not directly), you *always* have to use + **super()** to get the methods and attributes of the superclasses, and not + use the class identifier. + By example, instead of writting: :: + + class Truc(PrimaryView): + def f(self, arg1): + PrimaryView.f(self, arg1) + + You'd better write: :: + + class Truc(PrimaryView): + def f(self, arg1): + super(Truc, self).f(arg1) + + +Standard structure for a cube +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A complex cube is structured as follows: + +:: + + mycube/ + | + |-- schema.py + | + |-- entities/ + | + |-- sobjects/ + | + |-- views/ + | + |-- test/ + | + |-- i18n/ + | + |-- data/ + | + |-- migration/ + | |- postcreate.py + | \- depends.map + | + |-- debian/ + | + \-- __pkginfo__.py + +We can use simple Python module instead of packages, by example: + +:: + + mycube/ + | + |-- entities.py + |-- hooks.py + \-- views.py + + +where : + +* ``schema`` contains the schema definition (server side only) +* ``entities`` contains the entities definition (server side and web interface) +* ``sobjects`` contains hooks and/or views notifications (server side only) +* ``views`` contains the different components of the web interface (web interface only) +* ``test`` contains tests specifics to the application (not installed) +* ``i18n`` contains the messages catalog for supported languages (server side and + web interface) +* ``data`` contains arbitrary data files served statically + (images, css, javascripts files)... (web interface only) +* ``migration`` contains the initialization file for new instances + (``postcreate.py``) and in general a file containing the `CubicWeb` dependancies + of the cube depending on its version (``depends.map``) +* ``debian`` contains all the files that manages the debian packaging + (you would find there the classical structure with ``control``, ``rules``, + ``changelog``... (not installed) +* the file ``__pkginfo__.py`` provides meta-data on the cube, especially the + distribution name and the current version (server side and web interface) or + also the sub-cubes used by this cube + +The only required files are: + +* the file ``__pkginfo__.py`` +* the schema definition + XXX false, we may want to have cubes which are only adding a service, no persistent data (eg embeding for instance) + diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/02-02-registry.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/02-02-registry.en.txt Fri Nov 21 17:37:27 2008 +0100 @@ -0,0 +1,147 @@ +.. -*- coding: utf-8 -*- + +The Registry +------------ + +[WRITE ME] + +* talk about the vreg singleton, appobjects, registration and selection + + +Details of the recording process +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +At startup, the `registry` or registers base, inspects a number of directories +looking for compatible classes definition. After a recording process, the objects +are assigned to registers so that they can be selected dynamically while the +application is running. + +The base class of those objects is `AppRsetObject` (module `cubicweb.common.appobject`). + +XXX registers example +XXX actual details of the recording process! + +Runtime objects selection +~~~~~~~~~~~~~~~~~~~~~~~~~ + +XXX tell why it's a cw foundation! + +Application objects are stored in the registry using a two level hierarchy : + + object's `__registry__` : object's `id` : [list of app objects] + +The following rules are applied to select an object given a register and an id and an input context: +* each object has a selector + - its selector may be derivated from a set of basic (or not :) + selectors using `chainall` or `chainfirst` combinators +* a selector return a score >= 0 +* a score of 0 means the objects can't be applied to the input context +* the object with the greatest score is selected. If multiple objects have an + identical score, one of them is selected randomly (this is usually a bug) + +The object's selector is the `__select__` class method on the object's class. + +The score is used to choose the most pertinent objects where there are more than +one selectable object. For instance, if you're selecting the primary +(eg `id = 'primary'`) view (eg `__registry__ = 'view'`) for a result set containing +a `Card` entity, 2 objects will probably be selectable: + +* the default primary view (`accepts = 'Any'`) +* the specific `Card` primary view (`accepts = 'Card'`) + +This is because primary views are using the `accept_selector` which is considering the +`accepts` class attribute of the object's class. Other primary views specific to other +entity types won't be selectable in this case. And among selectable objects, the +accept selector will return a higher score the the second view since it's more +specific, so it will be selected as expected. + +Usually, you won't define it directly but by defining the `__selectors__` tuple +on the class, with :: + + __selectors__ = (sel1, sel2) + +which is equivalent to :: + + __select__ = classmethod(chainall(sel1, sel2)) + +The former is prefered since it's shorter and it's ease overriding in +subclasses (you have access to sub-selectors instead of the wrapping function). + +:chainall(selectors...): if one selector return 0, return 0, else return the sum of scores + +:chainfirst(selectors...): return the score of the first selector which has a non zero score + +XXX describe standard selector (link to generated api doc!) + +Example +```````` + +Le but final : quand on est sur un Blog, on veut que le lien rss de celui-ci pointe +vers les entrées de ce blog, non vers l'entité blog elle-même. + +L'idée générale pour résoudre ça : on définit une méthode sur les classes d'entité +qui renvoie l'url du flux rss pour l'entité en question. Avec une implémentation +par défaut sur AnyEntity et une implémentation particulière sur Blog qui fera ce +qu'on veut. + +La limitation : on est embêté dans le cas ou par ex. on a un result set qui contient +plusieurs entités Blog (ou autre chose), car on ne sait pas sur quelle entité appeler +la méthode sus-citée. Dans ce cas, on va conserver le comportement actuel (eg appel +à limited_rql) + +Donc : on veut deux cas ici, l'un pour un rset qui contient une et une seule entité, +l'autre pour un rset qui contient plusieurs entité. + +Donc... On a déja dans web/views/boxes.py la classe RSSIconBox qui fonctionne. Son +sélecteur :: + + class RSSIconBox(ExtResourcesBoxTemplate): + """just display the RSS icon on uniform result set""" + __selectors__ = ExtResourcesBoxTemplate.__selectors__ + (nfentity_selector,) + + +indique qu'il prend en compte : + +* les conditions d'apparition de la boite (faut remonter dans les classes parentes + pour voir le détail) +* nfentity_selector, qui filtre sur des rset contenant une liste d'entité non finale + +ça correspond donc à notre 2eme cas. Reste à fournir un composant plus spécifique +pour le 1er cas :: + + class EntityRSSIconBox(RSSIconBox): + """just display the RSS icon on uniform result set for a single entity""" + __selectors__ = RSSIconBox.__selectors__ + (onelinerset_selector,) + + +Ici, on ajoute onelinerset_selector, qui filtre sur des rset de taille 1. Il faut +savoir que quand on chaine des selecteurs, le score final est la somme des scores +renvoyés par chaque sélecteur (sauf si l'un renvoie zéro, auquel cas l'objet est +non sélectionnable). Donc ici, sur un rset avec plusieurs entités, onelinerset_selector +rendra la classe EntityRSSIconBox non sélectionnable, et on obtiendra bien la +classe RSSIconBox. Pour un rset avec une entité, la classe EntityRSSIconBox aura un +score supérieur à RSSIconBox et c'est donc bien elle qui sera sélectionnée. + +Voili voilou, il reste donc pour finir tout ça : + +* à définir le contenu de la méthode call de EntityRSSIconBox +* fournir l'implémentation par défaut de la méthode renvoyant l'url du flux rss sur + AnyEntity +* surcharger cette methode dans blog.Blog + + +When to use selectors? +``````````````````````` + +Il faut utiliser les sélecteurs pour faire des choses différentes en +fonction de ce qu'on a en entrée. Dès qu'on a un "if" qui teste la +nature de `self.rset` dans un objet, il faut très sérieusement se +poser la question s'il ne vaut pas mieux avoir deux objets différent +avec des sélecteurs approprié. + +If this is so fundamental, why don't I see them more often? +``````````````````````````````````````````````````````````` + +Because you're usually using base classes which are hiding the plumbing +of __registry__ (almost always), id (often when using "standard" object), +register and selector. diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/02-03-configuration.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/02-03-configuration.en.txt Fri Nov 21 17:37:27 2008 +0100 @@ -0,0 +1,9 @@ +.. -*- coding: utf-8 -*- + +Configuration +------------- + +[WRITE ME] + +* the config object. adding configuration option + diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/02-foundation.en.txt --- a/doc/book/en/02-foundation.en.txt Fri Nov 21 17:36:42 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,411 +0,0 @@ -.. -*- coding: utf-8 -*- - -`CubicWeb` concepts -=================== - -A little history... -------------------- - -`CubicWeb` is a web application framework developped by Logilab_ since 2001. - -Entirely written in Python, `CubicWeb` publishes data from all sorts -of sources such as SQL database, LDAP directory and versioning system such -as subversion. - -`CubicWeb` user interface was designed to let the final user a huge flexibility -on how to select and how to display content. It allows to browse the knowledge -database and to display the results with the best rendering according to -the context. -This interface flexibility gives back the user the control of the -rendering parameters that are usually reserved for developpers. - - -We can list a couple of web applications developped with `CubicWeb`, an online -public phone directory (see http://www.118000.fr/), a system for managing -digital studies and simulations for a research lab, a tool for shared children -babysitting (see http://garde-partagee.atoukontact.fr/), a tool to manage -software developpment (see http://www.logilab.org), etc. - -In 2008, `CubicWeb` was ported for a new type of source : the datastore -from GoogleAppEngine_. - -Global architecture -------------------- -.. image:: images/archi_globale.png - -.. note:: - For real, the client and server sides are integrated in the same - process and interact directly, without the needs for distants - calls using Pyro. It is important to note down that those two - sides, client/server, are disjointed and it is possible to execute - a couple of calls in distincts processes to balance the load of - your web site on one or more machines. - - -Terms and vocabulary --------------------- - -*schema* - the schema defines the data model of an application based on entities - and relations, thanks to the `yams`_ library. This is the core piece - of an application. It is initially defined in the file system and is - stored in the database at the time an instance is created. `CubicWeb` - provides a certain number of system entities included automatically as - it is necessarry for the core of `CubicWeb` and a library of - cubes that can be explicitely included if necessarry. - - -*entity type* - an entity is a set of attributes; the essential attribute of - an entity is its key, named eid - -*relation type* - entities are linked to each others by relations. In `CubicWeb` - relations are binary: by convention we name the first item of - a relation the `subject` and the second the `object`. - -*final entity type* - final types corresponds to the basic types such as string of characters, - integers... Those types have a main property which is that they can - only be used as `object` of a relation. The attributes of an entity - (non final) are entities (finals). - -*final relation type* - a relation is said final if its `object` is a final type. This is equivalent - to an entity attribute. - -*repository* - this is the RQL server side of `CubicWeb`. Be carefull not to get - confused with a Mercurial repository or a debian repository. - -*source* - a data source is a container of data (SGBD, LDAP directory...) integrated in the - `CubicWeb` repository. This repository has at least one source, `system` which - contains the schema of the application, plain-text index and others - vital informations for the system. - -*configuration* - it is possible to create differents configurations for an instance: - - - ``repository`` : repository only, accessible for clients using Pyro - - ``twisted`` : web interface only, access the repository using Pyro - - ``all-in-one`` : web interface and repository in a single process. - The repository could be or not accessible using Pyro. - -*cube* - a cube is a model grouping one or multiple data types and/or views - to provide a specific functionnality or a complete `CubicWeb` application - potentially using other cubes. The available subes are located in the file - system at `/path/to/forest/cubicweb/cubes` - -*instance* - an instance is a specific installation of a cube. All the required - configuration files necessarry for the well being of your web application - are grouped in an instance. This will refer to the cube(s) your application - is based on. - By example logilab.org and our intranet are two instances of a single - cube jpl, developped internally. - The instances are defined in the directory `~/etc/cubicweb.d`. - -*application* - the term application is sometime used to talk about an instance - and sometimes to talk of a cube depending on the context. - So we would like to avoid using this term and try to use *cube* and - *instance* instead. - -*result set* - this object contains the results of an RQL query and information on the query. - -*Pyro* - `Python Remote Object`_, distributed objects system similar to Java's RMI - (Remote Method Invocation), which can be used for the dialog between the web - side of the framework and the RQL repository. - -.. _`Python Remote Object`: http://pyro.sourceforge.net/ -.. _`yams`: http://www.logilab.org/project/name/yams/ - - -`CubicWeb` engine ------------------ - -The web engine in `CubicWeb` is a set of classes managing a set of objects loaded -dynamically at the startup of `CubicWeb`. Those dynamics objects, based on the schema -or the library, are building the final web site. The differents dymanic components are -by example: - -* client and server side - - - entities definition, containing the logic which enables application data manipulation - -* client side - - - *views*, or more specifically - - - boxes - - header and footer - - forms - - page templates - - - *actions* - - *controllers* - -* server side - - - notification hooks - - notification views - -The components of the engine are: - -* a frontal web (only twisted is available so far), transparent for dynamic objects -* an object that encapsulates the configuration -* a `vregistry` (`cubicweb.cwvreg`) containing the dynamic objects loaded automatically - - -Details of a recording process ------------------------------- - -At startup, the `vregistry` or registers base, inspects a number of directories -looking for compatible classes definition. After a recording process, the objects -are assigned to registers so that they can be selected dynamically while the -application is running. - -The base class of those objects is `AppRsetObject` (module `cubicweb.common.appobject`). - -API Python/RQL --------------- - -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 : - -`execute(rqlstring, args=None, eid_key=None, build_descr=True)` - -:rqlstring: the RQL query to execute (unicode) -:args: if the query contains substitutions, a dictionnary containing the values to use -:eid_key: - an implementation detail of the RQL queries cache implies that if a substitution - is used to introduce an eid *susceptible to raise the ambiguities in the query - type resolution*, then we have to specify the correponding key in the dictionnary - through this argument - - -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. - -.. note:: - While executing updates queries (SET, INSERT, DELETE), if a query generates - an error related to security, a rollback is automatically done on the current - transaction. - - -The `Request` class (`cubicweb.web`) ------------------------------------- - -A request instance is created when an HTPP request is sent to the web server. -It contains informations such as forms 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 by example)** - -An instance of `Request` has the following attributes: - -* `user`, instance of `cubicweb.common.utils.User` corresponding to the authenticated - user -* `form`, dictionnary containing the values of a web form -* `encoding`, characters encoding to use in the response - -But also: - -:Session data handling: - * `session_data()`, returns a dictinnary 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 dictionnary 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 (`EProperty`) - * dictionnary `data` to store data to share informations between components - *while a request is executed* - -Please note down 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. - -The `AppObject` class ---------------------- - -In general: - -* we do not inherit directly from this class but from a more specific - class such as `AnyEntity`, `EntityView`, `AnyRsetView`, - `Action`... - -* to be recordable, a subclass has to define its own register (attribute - `__registry__`) and its identifier (attribute `id`). Usually we do not have - to take care of the register, only the identifier `id`. - -We can find a certain number of attributes and methods defined in this class -and so common to all the application objects: - -At the recording, the following attributes are dynamically added to -the *subclasses*: - -* `vreg`, the `vregistry` of the application -* `schema`, the application schema -* `config`, the application configuration - -We also find on instances, the following attributes: - -* `req`, `Request` instance -* `rset`, the *result set* associated to the object if necessarry -* `cursor`, rql cursor on the session - - -:URL handling: - * `build_url(method=None, **kwargs)`, returns an absolute URL based on - the given arguments. The *controller* supposed to handle the response - can be specified through the special parameter `method` (the connection - is theoretically done automatically :). - - * `datadir_url()`, returns the directory of the application data - (contains static files such as images, css, js...) - - * `base_url()`, shortcut to `req.base_url()` - - * `url_quote(value)`, version *unicode safe* of the function `urllib.quote` - -:Data manipulation: - - * `etype_rset(etype, size=1)`, shortcut to `vreg.etype_rset()` - - * `eid_rset(eid, rql=None, descr=True)`, returns a *result set* object for - the given eid - * `entity(row, col=0)`, returns the entity corresponding to the data position - in the *result set* associated to the object - - * `complete_entity(row, col=0, skip_bytes=True)`, is equivalent to `entity` but - also call the method `complete()` on the entity before returning it - -:Data formatting: - * `format_date(date, date_format=None, time=False)` - * `format_time(time)` - -:And more...: - - * `external_resource(rid, default=_MARKER)`, access to a value defined in the - configuration file `external_resource` - - * `tal_render(template, variables)`, - - -.. note:: - When we inherit from `AppObject` (even not directly), you *always* have to use - **super()** to get the methods and attributes of the superclasses, and not - use the class identifier. - By example, instead of writting: :: - - class Truc(PrimaryView): - def f(self, arg1): - PrimaryView.f(self, arg1) - - You'd better write: :: - - class Truc(PrimaryView): - def f(self, arg1): - super(Truc, self).f(arg1) - - -Standard structure for a cube ------------------------------ - -A complex cube is structured as follows: - -:: - - mycube/ - | - |-- schema.py - | - |-- entities/ - | - |-- sobjects/ - | - |-- views/ - | - |-- test/ - | - |-- i18n/ - | - |-- data/ - | - |-- migration/ - | |- postcreate.py - | \- depends.map - | - |-- debian/ - | - \-- __pkginfo__.py - -We can use simple Python module instead of packages, by example: - -:: - - mycube/ - | - |-- entities.py - |-- hooks.py - \-- views.py - - -where : - -* ``schema`` contains the schema definition (server side only) -* ``entities`` contains the entities definition (server side and web interface) -* ``sobjects`` contains hooks and/or views notifications (server side only) -* ``views`` contains the different components of the web interface (web interface only) -* ``test`` contains tests specifics to the application (not installed) -* ``i18n`` contains the messages catalog for supported languages (server side and - web interface) -* ``data`` contains arbitrary data files served statically - (images, css, javascripts files)... (web interface only) -* ``migration`` contains the initialization file for new instances - (``postcreate.py``) and in general a file containing the `CubicWeb` dependancies - of the cube depending on its version (``depends.map``) -* ``debian`` contains all the files that manages the debian packaging - (you would find there the classical structure with ``control``, ``rules``, - ``changelog``... (not installed) -* the file ``__pkginfo__.py`` provides meta-data on the cube, especially the - distribution name and the current version (server side and web interface) or - also the sub-cubes used by this cube - -The only required files are: - -* the file ``__pkginfo__.py`` -* the schema definition diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/02-install.en.txt --- a/doc/book/en/02-install.en.txt Fri Nov 21 17:36:42 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,220 +0,0 @@ -.. -*- coding: utf-8 -*- - -.. _installation: - -Installation -============ - -Download the source -------------------- - -- The `Google AppEngine SDK` can be downloaded from: - http://code.google.com/appengine/downloads.html - -- `LAX` is available as an extension of `CubicWeb` under the GPLv2 - license which can be downloaded from : http://cubicweb.org/ - -Please follow instructions on how to install `CubicWeb` framework. - -Once ``cubicweb-ctl`` is installed, then you can create a Google -App Engine extension of our framework by running the command :: - - cubicweb-ctl newgapp - -This will create a directory containing :: - - `-- myapp/ - |-- app.conf - |-- app.yaml - |-- bin/ - | `-- laxctl - |-- boostrap_cubes - |-- cubes/ - | |-- addressbook/ - | .. - | |-- comment - | .. - | `-- zone/ - |-- cubicweb/ - |-- custom.py - |-- cw-cubes/ - |-- dateutil/ - |-- docutils/ - |-- fckeditor/ - |-- i18n/ - |-- index.yaml - |-- loader.py - |-- logilab/ - |-- main.py - |-- migration.py - |-- mx/ - |-- roman.py - |-- rql/ - |-- schema.py - |-- simplejson/ - |-- tools/ - |-- views.py - |-- vobject/ - |-- yams/ - `-- yapps/ - - -This skeleton directory is a working `AppEngine` application. You will -recognize the files ``app.yaml`` and ``main.py``. All the rest is the -`LAX` framework and its third-party libraries. You will notice that -the directory ``cubes`` is a library of reusable components. - -The main directories that you should know about are: - - - ``cubes`` : this is a library of reusable yams components. To use - those components you will list them in the variable - `included-yams-cubes` of ``app.conf``. See also :ref:`components`. - - [WHICH OTHER ONES SHOULD BE LISTED HERE?] - -Dependencies ------------- - -Before starting anything, please make sure the following packages are installed: - - yaml : by default google appengine is providing yaml; make sure you can - import it. We recommend you create a symbolic link yaml instead of installing - and using python-yaml: - yaml -> full/path/to/google_appengine/lib/yaml/lib/yaml/ - - gettext - -Setup ------ - -Once you executed ``cubicweb-ctl newgapp ``, you can use that ``myapp/`` -as an application directory and do as follows. - -This installation directory provides a configuration for an instance of `CubicWeb` -ported for Google App Engine. It is installed with its own command ``laxctl`` -which is a port of the command tool ``cubicweb-ctl`` originally developped for -`CubicWeb`. - -You can have the details of available commands by running :: - - $ python myapp/bin/laxctl --help - - -Generating translation files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -`LAX` is fully internationalized. Translation catalogs are found in -``myapp/i18n``. To compile the translation files, use the `gettext` tools -or the ``laxctl`` command :: - - $ python myapp/bin/laxctl i18nupdate - $ python myapp/bin/laxctl i18ncompile - -Ignore the errors that print "No translation file found for domain -'erudi'". They disappear after the first run of i18ncompile. - -.. note:: The command myapp/bin/laxctl i18nupdate needs to be executed - only if your application is using components from ginco-apps. - Otherwise, please skip it. - -You will never need to add new entries in the translation catalog. Instead we would -recommand you to use ``self.req._("msgId")`` in your application code -to flag new message id to add to the catalog, where ``_`` refers to -xgettext that is used to collect new strings to translate. -While running ``laxctl i18nupdate``, new string will be added to the catalogs. - -Generating the data directory -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In order to generate the ``myapp/data`` directory that holds the static -files like stylesheets and icons, you need to run the command:: - - $ python myapp/bin/laxctl populatedata - -Generating the schema diagram -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -There is a view named ``schema`` that displays a diagram of the -entity-relationship graph defined by the schema. This diagram has to -be generated from the command line:: - - $ python myapp/bin/laxctl genschema - -Application configuration -------------------------- - -Authentication -~~~~~~~~~~~~~~ - -You have the option of using or not google authentication for your application. -This has to be define in ``app.conf`` and ``app.yaml``. - -In ``app.conf`` modify the following variable:: -  - # does this application rely on google authentication service or not. - use-google-auth=no - -In ``app.yaml`` comment the `login: required` set by default in the handler:: - - - url: .* - script: main.py - # comment the line below to allow anonymous access or if you don't want to use - # google authentication service - #login: required - - - - -Quickstart : launch the application ------------------------------------ - -On Mac OS X platforms, drag that directory on the -`GoogleAppEngineLauncher`. - -On Unix and Windows platforms, run it with the dev_appserver:: - - $ python /path/to/google_appengine/dev_appserver.py /path/to/myapp/ - -Once the local server is started, visit `http://MYAPP_URL/_load `_ and sign in as administrator. -This will initialize the repository and enable you to log in into -the application and continue the installation. - -You should be redirected to a page displaying a message `content initialized`. - -Initialize the datastore -~~~~~~~~~~~~~~~~~~~~~~~~ - -You, then, want to visit `http://MYAPP_URL/?vid=authinfo `_ . -If you selected not to use google authentication, you will be prompted to a -login form where you should initialize the administrator login (recommended -to use admin/admin at first). You will then be redirected to a page providing -you the value to provide to ``./bin/laxctl --cookie``. - -If you choosed to use google authentication, then you will not need to set up -and administrator login but you will get the cookie value as well. - -This cookie values needs to be provided to ``laxctl`` commands -in order to handle datastore administration requests. - -.. image:: images/lax-book.02-cookie-values.en.png - :alt: displaying the detailed view of the cookie values returned - - -.. note:: In case you are not redirected to a page providing the - option --cookie value, please visit one more time - `http://MYAPP_URL/?vid=authinfo `_ . - -Once, you have this value, then return to the shell and execute :: - - $ python myapp/bin/laxctl db-init --cookie='dev_appserver_login=test@example.com:True; __session=7bbe973a6705bc5773a640f8cf4326cc' localhost:8080 - -.. note:: In the case you are not using google authentication, the value returned - by `http://MYAPP_URL/?vid=authinfo `_ - will look like : - --cookie='__session=2b45d1a9c36c03d2a30cedb04bc37b6d' - -Log out by clicking in the menu at the top right corner -and restart browsing from `http://MYAPP_URL/ `_ -as a normal user. - -Unless you did something to change it, http://MYAPP_URL should be -http://localhost:8080/ - - diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/03-00-definition-schema.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/03-00-definition-schema.en.txt Fri Nov 21 17:37:27 2008 +0100 @@ -0,0 +1,20 @@ +.. -*- coding: utf-8 -*- + +Data Model definition (*schema*) +================================ + +The schema is the main concept of `LAX` applications as it defines the +data model we will handle. It is based on entities types already defined +in the library and others, more specific, we would expect to find in one or +more Python files under the `schema` directory. + +It is important to make clear the difference between relation type and relation +definition: a relation type is only a relation name with potentially other +additionnal properties (see XXXX), whereas a relation definition is a complete +triplet " ". A relation +type could have been implied if none is related to a relation definition of the +schema. + + +.. include:: 03-10-stdlib-schemas.en.txt +.. include:: 03-11-definition-schema.en.txt diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/03-00-setup.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/03-00-setup.en.txt Fri Nov 21 17:37:27 2008 +0100 @@ -0,0 +1,13 @@ +.. -*- coding: utf-8 -*- + +.. _MiseEnPlaceEnv: + +=================================================== +Installation and set-up of a `CubicWeb` environment +=================================================== +.. contents:: + +.. include:: 03-01-installation.en.txt +.. include:: 03-02-create-instance.en.txt +.. include:: 03-03-cubicweb-ctl.en.txt + diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/03-10-stdlib-schemas.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/03-10-stdlib-schemas.en.txt Fri Nov 21 17:37:27 2008 +0100 @@ -0,0 +1,86 @@ +.. -*- coding: utf-8 -*- + +Pre-defined schemas in the library +---------------------------------- + +The library defines a set of entities schemas that are required by the system +or commonly used in `LAX` applications. +Of course, you can extend those schemas if necessarry. + +System schemas +`````````````` +Those are defined in:: + + ./myapp/ginco/schemas/ + ./myapp/ginco/entities/ + +``schemas/`` defines the data model you will use in your application. +It allows you to describre the entities and the relations you will need. + +``entities/`` deifnes the methods you might need on the entities you +defined in your schema. + +The system entities available are: + +* `EUser`, system users +* `EGroup`, users groups +* `EEType`, entity type +* `ERType`, relation type + +* `State`, workflow state +* `Transition`, workflow transition +* `TrInfo`, record of a transition trafic for an entity + +* `EmailAddress`, email address, used by the system to send notifications + to the users and also used by others optionnals schemas + +* `EProperty`, used to configure the application +* `EPermission`, used to configure the security of the application + +* `Card`, generic documenting card +* `Bookmark`, an entity type used to allow a user to customize his links within + the application + + +Components in the library +````````````````````````` + +Those are defined in:: + + ./myapp/ginco-apps/ + +An application is based on several basic components. In the set of available +basic components we can find by example: + +* `ecomment`, provides an entity type for `Comment` allowing us to comment others + site's entities + +* `emailinglist`, provides an entity type for `Mailinglist` which groups informations + in a discussion list + +* `efile`, provides entity types for `File` et `Image` used to represent + files (text or binary) with additionnal informations such as MIME type or + encoding. + +* `elink`, provides an entity type for hypertext link (`Link`) + +* `eblog`, provides an entity type weblog (`Blog`) + +* `eperson`, provides an entity type for a person (`Person`) + +* `eaddressbook`, provides an entity type used to represent phone + numbers (`PhoneNumber`) and mailing address (`PostalAddress`) + +* `eclasstags`, categorization system based on tags (`Tag`) + +* `eclassfolders`, categorization system based on folders hierarchy in order + to create navigation sections (`Folder`) + +* `eemail`, archiving management for emails (`Email`, `Emailpart`, + `Emailthread`) + +* `ebasket`, basket management (`Basket`) allowing to group entities + +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. + diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/03-11-definition-schema.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/03-11-definition-schema.en.txt Fri Nov 21 17:37:27 2008 +0100 @@ -0,0 +1,344 @@ +.. -*- coding: utf-8 -*- + +Entity type definition +---------------------- + +An entity type is defined by a Python class which inherits `EntityType`. The +class name correponds to the type name. Then the content of the class contains +the description of attributes and relations for the defined entity type, +by example :: + + class Personne(EntityType): + """A person with the properties and the relations necessarry for my + application""" + + last_name = String(required=True, fulltextindexed=True) + first_name = String(required=True, fulltextindexed=True) + title = String(vocabulary=('M', 'Mme', 'Mlle')) + date_of_birth = Date() + works_for = SubjectRelation('Company', cardinality='?*') + +* the name of the Python attribute corresponds to the name of the attribute + or the relation in `LAX` application. + +* all built-in types are available: `String`, `Int`, `Float`, + `Boolean`, `Date`, `Datetime`, `Time`, `Byte`. + +* each entity has at least the following meta-relations: + + - `eid` (`Int`) + + - `creation_date` (`Datetime`) + + - `modification_date` (`Datetime`) + + - `created_by` (`EUser`) (which user created the entity) + + - `owned_by` (`EUser`) (who does the entity belongs to, by default the + creator but not necessarry and it could have multiple owners) + + - `is` (`EEType`) + + +* it is also possible to define relations of type object by using `ObjectRelation` + instead of `SubjectRelation` + +* the first argument of `SubjectRelation` and `ObjectRelation` gives respectively + the object/subject entity type of the relation. This could be: + + * a string corresponding to an entity type + + * a tuple of string correponding to multiple entities types + + * special string such as follows: + + - "**" : all types of entities + - "*" : all types of entities non meta + - "@" : all types of meta entities but not system entities (e.g. used for + the basis schema description) + +* it is possible to use the attribute `meta` to flag an entity type as a `meta` + (e.g. used to describe/categorize other entities) + +* optional properties for attributes and relations: + + - `description` : 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` : list of conditions/constraints that the relation needs to + satisfy (c.f. `Contraints`_) + + - `cardinality` : two characters string which specify the cardinality of the + relation. The first character defines the cardinality of the relation on + the subject, the second on the object of the relation. When a relation + has multiple possible subjects or objects, the cardinality applies to all + and not on a one to one basis (so it must be consistent...). The possible + values are inspired from regular expressions syntax: + + * `1`: 1..1 + * `?`: 0..1 + * `+`: 1..n + * `*`: 0..n + + - `meta` : boolean indicating that the relation is a meta-relation (false by + default) + +* optionnal properties for attributes: + + - `required` : boolean indicating if the attribute is required (false by default) + + - `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 usefull 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 correpond to the RQL keywords `TODAY` and `NOW`. + + - `vocabulary` : specify static possible values of an attribute + +* optionnal properties of type `String`: + + - `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) + + - `maxsize` : integer providing the maximum size of the string (no limit by default) + +* optionnal properties for relations: + + - `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 need + to set 'object' as the value. The composition implies that when the relation + is deleted (so when the composite is deleted), the composed are also deleted. + [PAS CLAIR] + +Contraints +`````````` +By default, the available constraints types are: + +* `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 + +* `UniqueConstraint` : identical to "unique=True" + +* `StaticVocabularyConstraint` : identical to "vocabulary=(...)" + +* `RQLConstraint` : allows to specify a RQL query that needs to be satisfied + by the subject and/or the object of the relation. In this query the variables + `S` and `O` are reserved for the entities subject and object of the + relation. + +* `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 + [PAS CLAIR] + + +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, as well as 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)[PAS CLAIR] by example :: + + class locked_by(RelationType): + """relation on all entities indicating that they are locked""" + inlined = True + cardinality = '?*' + subject = '*' + object = 'EUser' + +In addition to the permissions, the own properties of the relation types +(shared also by all definition of relation of this type) are: + + +* `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 the relation when the cardinality + of subject->relation->object is 0..1 ('?') or 1..1 ('1') + +* `symetric` : boolean indication that the relation is symetrical, which + means `X relation Y` implies `Y relation X` + +In the case of simultaneous relations definitions, `subject` and `object` +can both be equal to the value of the first argument of `SubjectRelation` +and `ObjectRelation`. + +When a relation is not inlined and not symetrical, and it does not require +specific permissions, its definition (by using `SubjectRelation` and +`ObjectRelation`) is all we need. + +Permissions definition +---------------------- + +Define permissions is set through to the attribute `permissions` of entities and +relations types. It defines a dictionnary 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 once the user is in the listed groups or one of the RQL condition is +satisfied. + +The standard groups are: + +* `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 define in the precreate +of the application (``migration/precreate.py``). + +Use of RQL expression for writing rights +```````````````````````````````````````` +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 a 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 `RQLExpression` in the case of a non-final relation + [WHAT IS A NON FINALE 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 defined rights on attributes of an entity (non-final relation), + knowing that: + + - to defines 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. + + + +In addition to thatm the entity type `EPermission` from the standard library +allow to build very complex and dynamic security architecture. The schema of +this entity type is as follow: :: + + class EPermission(MetaEntityType): + """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('EGroup', cardinality='+*', + description=_('groups to which the permission is granted')) + require_state = SubjectRelation('State', + description=_("entity'state in which the permission is applyable")) + # 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 :: + + ... + + 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 assumes/indicates [???] that an entity `EPermission` 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 genricity of the entity type `EPermission`, 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) + + +Use of RQL expression for reading rights +```````````````````````````````````````` + +The principles are the same but with the following restrictions: + +* we can not [??] `RRQLExpression` on relation types for reading + +* special relations "has__permission" can not be used + + +Note on the use of RQL expression for `add` permission +`````````````````````````````````````````````````````` +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 checks methods +(check_perm, 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. +[PAS CLAIR] diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/03-create-app.en.txt --- a/doc/book/en/03-create-app.en.txt Fri Nov 21 17:36:42 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,201 +0,0 @@ -.. -*- coding: utf-8 -*- - - -Creating your first application -=============================== - -This tutorial will guide you step by step to build a blog application -and discover the unique features of `LAX`. It assumes that you followed -the installation guidelines and that both the `AppEngine SDK` and the -`LAX` framework are setup on your computer. - -Creating a new application --------------------------- - -When you installed `LAX`, you saw a directory named ``skel``. Make a copy of -this directory and call it ``BlogDemo``. - -Defining a schema ------------------ - -With `LAX`, the schema/datamodel is the core of the application. - -Let us start with something simple and improve on it iteratively. - -In schema.py, we define two entities : ``Blog`` and ``BlogEntry``. - -:: - - class Blog(EntityType): - title = String(maxsize=50, required=True) - description = String() - - class BlogEntry(EntityType): - title = String(maxsize=100, required=True) - publish_date = Date(default='TODAY') - text = String(fulltextindexed=True) - category = String(vocabulary=('important','business')) - entry_of = SubjectRelation('Blog', cardinality='?*') - -A Blog has a title and a description. The title is a string that is -required and must be less than 50 characters. The description is a -string that is not constrained. - -A BlogEntry has a title, a publish_date and a text. The title is a -string that is required and must be less than 100 characters. The -publish_date is a Date with a default value of TODAY, meaning that -when a BlogEntry is created, its publish_date will be the current day -unless it is modified. The text is a string that will be indexed in -the full-text index and has no constraint. - -A BlogEntry also has a relationship ``entry_of`` that link it to a -Blog. The cardinality ``?*`` means that a BlogEntry can be part of -zero or one Blog (``?`` means `zero or one`) and that a Blog can -have any number of BlogEntry (``*`` means `any number including -zero`). For completeness, remember that ``+`` means `one or more`. - -Using the application ---------------------- - -Defining this simple schema is enough to get us started. Make sure you -followed the setup steps described in detail in the installation -chapter (especially visiting http://localhost:8080/_load as an -administrator), then launch the application with the command:: - - python dev_appserver.py BlogDemo - -and point your browser at http://localhost:8080/ (if it is easier for -you, use the on-line demo at http://lax.appspot.com/). - -.. image:: images/lax-book.00-login.en.png - :alt: login screen - -After you log in, you will see the home page of your application. It -lists the entity types: Blog and BlogEntry. If these links read -``blog_plural`` and ``blogentry_plural`` it is because -internationalization (i18n) is not working for you yet. Please ignore -this for now. - -.. image:: images/lax-book.01-start.en.png - :alt: home page - -Let us create a few of these entities. Click on the [+] at the right -of the link Blog. Call this new Blog ``Tech-blog`` and type in -``everything about technology`` as the description, then validate the -form by clicking on ``button_ok``. - -.. image:: images/lax-book.02-create-blog.en.png - :alt: from to create blog - -Click on the logo at top left to get back to the home page, then -follow the Blog link. You should be seeing a list with a single item -``Tech-blog``. - -.. image:: images/lax-book.03-list-one-blog.en.png - :alt: displaying a list of a single blog - -Clicking on this item will get you to its detailed description except -that in this case, there is not much to display besides the name and -the phrase ``everything about technology``. - -.. image:: images/lax-book.04-detail-one-blog.en.png - :alt: displaying the detailed view of a blog - -Now get back to the home page by clicking on the top-left logo, then -create a new Blog called ``MyLife`` and get back to the home page -again to follow the Blog link for the second time. The list now -has two items. - -.. image:: images/lax-book.05-list-two-blog.en.png - :alt: displaying a list of two blogs - -Get back to the home page and click on [+] at the right of the link -BlogEntry. Call this new entry ``Hello World`` and type in some text -before clicking on ``button_ok``. You added a new blog entry without -saying to what blog it belongs. There is a box on the left entitled -``actions``, click on the menu item ``modify``. You are back to the form -to edit the blog entry you just created, except that the form now has -another section with a combobox titled ``add relation``. Chose -``entry_of`` in this menu and a second combobox appears where you pick -``MyLife``. - -.. image:: images/lax-book.06-add-relation-entryof.en.png - :alt: editing a blog entry to add a relation to a blog - -Validate the changes by clicking ``button_ok``. The entity BlogEntry -that is displayed now includes a link to the entity Blog named -``MyLife``. - -.. image:: images/lax-book.07-detail-one-blogentry.en.png - :alt: displaying the detailed view of a blogentry - -Remember that all of this was handled by the framework and that the -only input that was provided so far is the schema. To get a graphical -view of the schema, run the ``laxctl genschema BlogDemo`` command as -explained in the installation section and point your browser to the -URL http://localhost:8080/schema - -.. image:: images/lax-book.08-schema.en.png - :alt: graphical view of the schema (aka data-model) - -Set-up a workflow ------------------ - -Before starting, make sure you refresh your mind by reading [link to -definition_workflow chapter]. - -We want to create a workflow to control the quality of the BlogEntry -submitted on your application. When a BlogEntry is created by a user -its state should be `submitted`. To be visible to all, it needs to -be in the state `published`. To move from `submitted` to `published` -we need a transition that we can name `approve_blogentry`. - -We do not want every user to be allowed to change the state of a -BlogEntry. We need to define a group of user, `moderators`, and -this group will have appropriate permissions to approve BlogEntry -to be published and visible to all. - -Create states and transitions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Let us create a state `submitted`. Click on the [+] at the right -of the link States. Call this new Blog ``submitted`` and type in -``Initial State of a BlogEntry`` as the description, then validate the -form by clicking on ``Apply``. This will leave us in the editing form -with an additional section to create the relations related to the -entity State we juste created. Select the relation ``initial_state_of`` -and select the entity type ``BlogEntry``. - -.. image:: images/lax-book.03-state-submitted.en.png - - - - -Create group and set permissions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - - -Change view permission -~~~~~~~~~~~~~~~~~~~~~~ - - -Conclusion ----------- - -Exercise -~~~~~~~~ - -Create new blog entries in ``Tech-blog``. - -What we learned -~~~~~~~~~~~~~~~ - -Creating a simple schema was enough to set up a new application that -can store blogs and blog entries. - -What is next ? -~~~~~~~~~~~~~~ - -Although the application is fully functionnal, its look is very -basic. In the following section we will learn to create views to -customize how data is displayed. diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/03-definition-schema.en.txt --- a/doc/book/en/03-definition-schema.en.txt Fri Nov 21 17:36:42 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -.. -*- coding: utf-8 -*- - -Data Model definition (*schema*) -================================ - -The schema is the main concept of `LAX` applications as it defines the -data model we will handle. It is based on entities types already defined -in the library and others, more specific, we would expect to find in one or -more Python files under the `schema` directory. - -It is important to make clear the difference between relation type and relation -definition: a relation type is only a relation name with potentially other -additionnal properties (see XXXX), whereas a relation definition is a complete -triplet " ". A relation -type could have been implied if none is related to a relation definition of the -schema. - - -.. include:: 03-sect-stdlib-schemas.en.txt -.. include:: 03-sect-definition-schema.en.txt diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/03-sect-definition-schema.en.txt --- a/doc/book/en/03-sect-definition-schema.en.txt Fri Nov 21 17:36:42 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,344 +0,0 @@ -.. -*- coding: utf-8 -*- - -Entity type definition ----------------------- - -An entity type is defined by a Python class which inherits `EntityType`. The -class name correponds to the type name. Then the content of the class contains -the description of attributes and relations for the defined entity type, -by example :: - - class Personne(EntityType): - """A person with the properties and the relations necessarry for my - application""" - - last_name = String(required=True, fulltextindexed=True) - first_name = String(required=True, fulltextindexed=True) - title = String(vocabulary=('M', 'Mme', 'Mlle')) - date_of_birth = Date() - works_for = SubjectRelation('Company', cardinality='?*') - -* the name of the Python attribute corresponds to the name of the attribute - or the relation in `LAX` application. - -* all built-in types are available: `String`, `Int`, `Float`, - `Boolean`, `Date`, `Datetime`, `Time`, `Byte`. - -* each entity has at least the following meta-relations: - - - `eid` (`Int`) - - - `creation_date` (`Datetime`) - - - `modification_date` (`Datetime`) - - - `created_by` (`EUser`) (which user created the entity) - - - `owned_by` (`EUser`) (who does the entity belongs to, by default the - creator but not necessarry and it could have multiple owners) - - - `is` (`EEType`) - - -* it is also possible to define relations of type object by using `ObjectRelation` - instead of `SubjectRelation` - -* the first argument of `SubjectRelation` and `ObjectRelation` gives respectively - the object/subject entity type of the relation. This could be: - - * a string corresponding to an entity type - - * a tuple of string correponding to multiple entities types - - * special string such as follows: - - - "**" : all types of entities - - "*" : all types of entities non meta - - "@" : all types of meta entities but not system entities (e.g. used for - the basis schema description) - -* it is possible to use the attribute `meta` to flag an entity type as a `meta` - (e.g. used to describe/categorize other entities) - -* optional properties for attributes and relations: - - - `description` : 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` : list of conditions/constraints that the relation needs to - satisfy (c.f. `Contraints`_) - - - `cardinality` : two characters string which specify the cardinality of the - relation. The first character defines the cardinality of the relation on - the subject, the second on the object of the relation. When a relation - has multiple possible subjects or objects, the cardinality applies to all - and not on a one to one basis (so it must be consistent...). The possible - values are inspired from regular expressions syntax: - - * `1`: 1..1 - * `?`: 0..1 - * `+`: 1..n - * `*`: 0..n - - - `meta` : boolean indicating that the relation is a meta-relation (false by - default) - -* optionnal properties for attributes: - - - `required` : boolean indicating if the attribute is required (false by default) - - - `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 usefull 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 correpond to the RQL keywords `TODAY` and `NOW`. - - - `vocabulary` : specify static possible values of an attribute - -* optionnal properties of type `String`: - - - `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) - - - `maxsize` : integer providing the maximum size of the string (no limit by default) - -* optionnal properties for relations: - - - `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 need - to set 'object' as the value. The composition implies that when the relation - is deleted (so when the composite is deleted), the composed are also deleted. - [PAS CLAIR] - -Contraints -`````````` -By default, the available constraints types are: - -* `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 - -* `UniqueConstraint` : identical to "unique=True" - -* `StaticVocabularyConstraint` : identical to "vocabulary=(...)" - -* `RQLConstraint` : allows to specify a RQL query that needs to be satisfied - by the subject and/or the object of the relation. In this query the variables - `S` and `O` are reserved for the entities subject and object of the - relation. - -* `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 - [PAS CLAIR] - - -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, as well as 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)[PAS CLAIR] by example :: - - class locked_by(RelationType): - """relation on all entities indicating that they are locked""" - inlined = True - cardinality = '?*' - subject = '*' - object = 'EUser' - -In addition to the permissions, the own properties of the relation types -(shared also by all definition of relation of this type) are: - - -* `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 the relation when the cardinality - of subject->relation->object is 0..1 ('?') or 1..1 ('1') - -* `symetric` : boolean indication that the relation is symetrical, which - means `X relation Y` implies `Y relation X` - -In the case of simultaneous relations definitions, `subject` and `object` -can both be equal to the value of the first argument of `SubjectRelation` -and `ObjectRelation`. - -When a relation is not inlined and not symetrical, and it does not require -specific permissions, its definition (by using `SubjectRelation` and -`ObjectRelation`) is all we need. - -Permissions definition ----------------------- - -Define permissions is set through to the attribute `permissions` of entities and -relations types. It defines a dictionnary 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 once the user is in the listed groups or one of the RQL condition is -satisfied. - -The standard groups are: - -* `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 define in the precreate -of the application (``migration/precreate.py``). - -Use of RQL expression for writing rights -```````````````````````````````````````` -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 a 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 `RQLExpression` in the case of a non-final relation - [WHAT IS A NON FINALE 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 defined rights on attributes of an entity (non-final relation), - knowing that: - - - to defines 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. - - - -In addition to thatm the entity type `EPermission` from the standard library -allow to build very complex and dynamic security architecture. The schema of -this entity type is as follow: :: - - class EPermission(MetaEntityType): - """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('EGroup', cardinality='+*', - description=_('groups to which the permission is granted')) - require_state = SubjectRelation('State', - description=_("entity'state in which the permission is applyable")) - # 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 :: - - ... - - 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 assumes/indicates [???] that an entity `EPermission` 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 genricity of the entity type `EPermission`, 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) - - -Use of RQL expression for reading rights -```````````````````````````````````````` - -The principles are the same but with the following restrictions: - -* we can not [??] `RRQLExpression` on relation types for reading - -* special relations "has__permission" can not be used - - -Note on the use of RQL expression for `add` permission -`````````````````````````````````````````````````````` -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 checks methods -(check_perm, 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. -[PAS CLAIR] diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/03-sect-stdlib-schemas.en.txt --- a/doc/book/en/03-sect-stdlib-schemas.en.txt Fri Nov 21 17:36:42 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,86 +0,0 @@ -.. -*- coding: utf-8 -*- - -Pre-defined schemas in the library ----------------------------------- - -The library defines a set of entities schemas that are required by the system -or commonly used in `LAX` applications. -Of course, you can extend those schemas if necessarry. - -System schemas -`````````````` -Those are defined in:: - - ./myapp/ginco/schemas/ - ./myapp/ginco/entities/ - -``schemas/`` defines the data model you will use in your application. -It allows you to describre the entities and the relations you will need. - -``entities/`` deifnes the methods you might need on the entities you -defined in your schema. - -The system entities available are: - -* `EUser`, system users -* `EGroup`, users groups -* `EEType`, entity type -* `ERType`, relation type - -* `State`, workflow state -* `Transition`, workflow transition -* `TrInfo`, record of a transition trafic for an entity - -* `EmailAddress`, email address, used by the system to send notifications - to the users and also used by others optionnals schemas - -* `EProperty`, used to configure the application -* `EPermission`, used to configure the security of the application - -* `Card`, generic documenting card -* `Bookmark`, an entity type used to allow a user to customize his links within - the application - - -Components in the library -````````````````````````` - -Those are defined in:: - - ./myapp/ginco-apps/ - -An application is based on several basic components. In the set of available -basic components we can find by example: - -* `ecomment`, provides an entity type for `Comment` allowing us to comment others - site's entities - -* `emailinglist`, provides an entity type for `Mailinglist` which groups informations - in a discussion list - -* `efile`, provides entity types for `File` et `Image` used to represent - files (text or binary) with additionnal informations such as MIME type or - encoding. - -* `elink`, provides an entity type for hypertext link (`Link`) - -* `eblog`, provides an entity type weblog (`Blog`) - -* `eperson`, provides an entity type for a person (`Person`) - -* `eaddressbook`, provides an entity type used to represent phone - numbers (`PhoneNumber`) and mailing address (`PostalAddress`) - -* `eclasstags`, categorization system based on tags (`Tag`) - -* `eclassfolders`, categorization system based on folders hierarchy in order - to create navigation sections (`Folder`) - -* `eemail`, archiving management for emails (`Email`, `Emailpart`, - `Emailthread`) - -* `ebasket`, basket management (`Basket`) allowing to group entities - -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. - diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/03-setup.en.txt --- a/doc/book/en/03-setup.en.txt Fri Nov 21 17:36:42 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -.. -*- coding: utf-8 -*- - -.. _MiseEnPlaceEnv: - -=================================================== -Installation and set-up of a `CubicWeb` environment -=================================================== -.. contents:: - -.. include:: 03-01-installation.en.txt -.. include:: 03-02-create-instance.en.txt -.. include:: 03-03-cubicweb-ctl.en.txt - diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/04-00-define-schema.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/04-00-define-schema.en.txt Fri Nov 21 17:37:27 2008 +0100 @@ -0,0 +1,22 @@ +.. -*- coding: utf-8 -*- + +Data model definition (*schema*) +================================ + +The schema is the core piece of a `CubicWeb` application as it defines +the data model handled. It is based on entities types already defined +in the `CubicWeb` standard library and others, more specific, we would +expect to find in one or more Python files under the `schema` directory. + +At this point, it is important to make clear the difference between +relation type and relation definition: a relation type is only a relation +name with potentially other additionnal properties (see XXXX), whereas a +relation definition is a complete triplet +" ". +A relation type could have been implied if none is related to a +relation definition of the schema. + + +.. include:: 04-01-schema-stdlib.en.txt +.. include:: 04-02-schema-definition.en.txt + diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/04-02-schema-definition.en.txt --- a/doc/book/en/04-02-schema-definition.en.txt Fri Nov 21 17:36:42 2008 +0100 +++ b/doc/book/en/04-02-schema-definition.en.txt Fri Nov 21 17:37:27 2008 +0100 @@ -146,8 +146,10 @@ not prevent another entity to be selected -Relation type definition ------------------------- +Relation definition +------------------- + +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 @@ -183,8 +185,46 @@ specific permissions, its definition (by using `SubjectRelation` and `ObjectRelation`) is all we need. + +The security model +------------------ + +Le modèle de sécurité de CubicWeb est un modèle fondé sur des `Access +Control List`. Les notions sont les suivantes : + +* utilisateurs et groupes d'utilisateurs +* un utilisateur appartient à au moins un groupe +* droits (lire, modifier, créer, supprimer) +* les droits sont attribués aux groupes (et non aux utilisateurs) + +Pour CubicWeb plus spécifiquement : + +* on associe les droits au niveau des schemas d'entites / relations + +* pour chaque type d'entité, on distingue les droits de lecture, + ajout, modification et suppression + +* pour chaque type de relation, on distingue les droits de lecture, + ajout et suppression (on ne peut pas modifer une relation) + +* les groupes de base sont : Administrateurs, Utilisateurs, Invités + +* les utilisateurs font par défaut parti du groupe Utilisateurs + +* on a un groupe virtuel "Utilisateurs Propriétaires", auquel on peut + associer uniquement les droits de suppression et de modification + +* on ne peut pas mettre d'utilisateurs dans ce groupe, ils y sont + ajoutés implicitement dans le contexte des objets dont ils sont + propriétaires + +* les droits de ce groupe ne sont vérifiés que sur + modification / suppression si tous les autres groupes auxquels + l'utilisateur appartient se sont vu interdir l'accès + + Permissions definition ----------------------- +`````````````````````` Define permissions is set through to the attribute `permissions` of entities and relations types. It defines a dictionnary where the keys are the access types @@ -212,11 +252,12 @@ 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 define in the precreate -of the application (``migration/precreate.py``). +It is also possible to use specific groups if they are defined in the precreate +of the cube (``migration/precreate.py``). + Use of RQL expression for writing rights -```````````````````````````````````````` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ It is possible to define RQL expression to provide update permission (`add`, `delete` and `update`) on relation and entity types. @@ -314,7 +355,7 @@ ("U in_group G, P require_group G" in the above example) Use of RQL expression for reading rights -```````````````````````````````````````` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The principles are the same but with the following restrictions : @@ -324,7 +365,7 @@ Note on the use of RQL expression for `add` permission -`````````````````````````````````````````````````````` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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 diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/04-define-schema.en.txt --- a/doc/book/en/04-define-schema.en.txt Fri Nov 21 17:36:42 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ -.. -*- coding: utf-8 -*- - -Data model definition (*schema*) -================================ - -The schema is the core piece of a `CubicWeb` application as it defines -the data model handled. It is based on entities types already defined -in the `CubicWeb` standard library and others, more specific, we would -expect to find in one or more Python files under the `schema` directory. - -At this point, it is important to make clear the difference between -relation type and relation definition: a relation type is only a relation -name with potentially other additionnal properties (see XXXX), whereas a -relation definition is a complete triplet -" ". -A relation type could have been implied if none is related to a -relation definition of the schema. - - -.. include:: 04-01-schema-stdlib.en.txt -.. include:: 04-02-schema-definition.en.txt - diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/04-develop-views.en.txt --- a/doc/book/en/04-develop-views.en.txt Fri Nov 21 17:36:42 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,126 +0,0 @@ -.. -*- coding: utf-8 -*- - - -Developing the user interface with Views -======================================== - -Before moving to this section, make sure you read the `Essentials` -section in the Introduction. - -Tip: when modifying views, you do not need to restart the local -server. Just save the file in your editor and reload the page in your -browser to see the changes. - -The selection/view principle ----------------------------- - -With `LAX`, views are defined by Python classes. A view includes: - -- an identifier (all objects in `LAX` are entered in a registry - and this identifier will be used as a key) - -- a filter to select the resulsets it can be applied to - -`LAX` provides a lot of standard views, for a complete list, you -will have to read the code in directory ``ginco/web/views/`` (XXX -improve doc). - -For example, the view named ``primary`` is the one used to display -a single entity. - -If you want to change the way a ``BlogEntry`` is displayed, just -override the view ``primary`` in ``BlogDemo/views.py`` :: - - 01. from ginco.web.views import baseviews - 02. - 03. class BlogEntryPrimaryView(baseviews.PrimaryView): - 04. - 05. accepts = ('BlogEntry',) - 06. - 07. def cell_call(self, row, col): - 08. entity = self.entity(row, col) - 09. self.w(u'

%s

' % entity.title) - 10. self.w(u'

published on %s in category %s

' % \ - 11. (entity.publish_date.strftime('%Y-%m-%d'), entity.category)) - 12. self.w(u'

%s

' % entity.text) - -The above source code defines a new primary view (`line 03`) for -``BlogEntry`` (`line 05`). - -Since views are applied to resultsets and resulsets can be tables of -data, it is needed to recover the entity from its (row,col) -coordinates (`line 08`). We will get to this in more detail later. - -The view has a ``self.w()`` method that is used to output data. Here `lines -09-12` output HTML tags and values of the entity's attributes. - -When displaying same blog entries as before, you will notice that the -page is now looking much nicer. - -.. 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 :: - - 01. class BlogPrimaryView(baseviews.PrimaryView): - 02. - 03. accepts = ('Blog',) - 04. - 05. def cell_call(self, row, col): - 06. entity = self.entity(row, col) - 07. self.w(u'

%s

' % entity.title) - 08. self.w(u'

%s

' % entity.description) - 09. rset = self.req.execute('Any E WHERE E entry_of B, B eid "%s"' % entity.eid) - 10. self.wview('primary', rset) - -In the above source code, `lines 01-08` are similar to the previous -view we defined. - -At `line 09`, a simple request in made to build a resultset 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 infer that such entities have to be of the -``BlogEntry`` kind and retrieves them. - -The request returns a selection of data called a resultset. At -`line 10` the view 'primary' is applied to this resultset to output -HTML. - -**This is to be compared to interfaces and protocols in object-oriented -languages. Applying a given view to all the entities of a resultset only -requires the availability, for each entity of this resultset, of a -view with that name that can accepts the entity.** - -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 - -**Before we move forward, remember that the selection/view principle is -at the core of `LAX`. Everywhere in the engine, data is requested -using the RQL language, then HTML/XML/text/PNG is output by applying a -view to the resultset returned by the query. That is where most of the -flexibility comes from.** - -[WRITE ME] - -* implementing interfaces, calendar for blog entries -* show that a calendar view can export data to ical - -We will implement the ginco.interfaces.ICalendarable interfaces on -entities.BloEntry and apply the OneMonthCalendar and iCalendar views -to resultsets like "Any E WHERE E is BlogEntry" - -* create view "blogentry table" with title, publish_date, category - -We will show that by default the view that displays -"Any E,D,C WHERE E publish_date D, E category C" is the table view. -Of course, the same can be obtained by calling -self.wview('table',rset) - -* in view blog, select blogentries and apply view "blogentry table" -* demo ajax by filtering blogentry table on category - -we did the same with 'primary', but with tables we can turn on filters -and show that ajax comes for free. diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/05-00-define-views.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/05-00-define-views.en.txt Fri Nov 21 17:37:27 2008 +0100 @@ -0,0 +1,238 @@ +.. -*- coding: utf-8 -*- + +.. _DefinitionVues: + +Views definition +================ + +Basic class for views +--------------------- + +Class `View` (`cubicweb.common.view`) +````````````````````````````````````` + +A view writes in its output exit thanks to its attribute `w` (`UStreamIO`). + +The basic interface for views is as follows: + +* `dispatch(**context)`, render the view by calling `call` or + `cell_call` depending on the given parameters +* `call(**kwargs)`, call the view for a complete result set or null +* `cell_call(row, col, **kwargs)`, call the view for a given cell of a result set +* `url()`, returns the URL enabling us to get the view with the current + result set +* `view(__vid, rset, __fallback_vid=None, **kwargs)`, call the view of identifier + `__vid` on the given result set. It is possible to give a view identifier + of fallback that will be used if the view requested is not applicable to the + result set + +* `wview(__vid, rset, __fallback_vid=None, **kwargs)`, similar to `view` except + the flow is automatically passed in the parameters + +* `html_headers()`, returns a list of HTML headers to set by the main template + +* `page_title()`, returns the title to use in the HTML header `title` + +* `creator(eid)`, returns the eid and the login of the entity creator of the entity + having the eid given in the parameter + +Other basic classes: + +* `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 applied to any result set + + +The selection view principle +---------------------------- + +A view includes : + +- an identifier (all objects in `LAX` are entered in a registry + and this identifier will be used as a key) + +- a filter to select the resulsets it can be applied to + + +For a given identifier, multiple views can be defined. `CubicWeb` uses +a selector which computes scores so that it can identify and select the +best view to apply in context. The selector library is in +``cubicweb.common.selector`` and a library of the methods used to +compute scores is in ``cubicweb.vregistry.vreq``. + + +`CubicWeb` provides a lot of standard views, for a complete list, you +will have to read the code in directory ``cubicweb/web/views/`` (XXX +improve doc). + +For example, the view named ``primary`` is the one used to display +a single entity. + +If you want to change the way a ``BlogEntry`` is displayed, just +override the view ``primary`` in ``BlogDemo/views.py`` :: + + 01. from ginco.web.views import baseviews + 02. + 03. class BlogEntryPrimaryView(baseviews.PrimaryView): + 04. + 05. accepts = ('BlogEntry',) + 06. + 07. def cell_call(self, row, col): + 08. entity = self.entity(row, col) + 09. self.w(u'

%s

' % entity.title) + 10. self.w(u'

published on %s in category %s

' % \ + 11. (entity.publish_date.strftime('%Y-%m-%d'), entity.category)) + 12. self.w(u'

%s

' % entity.text) + +The above source code defines a new primary view (`line 03`) for +``BlogEntry`` (`line 05`). + +Since views are applied to resultsets and resulsets can be tables of +data, it is needed to recover the entity from its (row,col) +coordinates (`line 08`). We will get to this in more detail later. + +The view has a ``self.w()`` method that is used to output data. Here `lines +09-12` output HTML tags and values of the entity's attributes. + +When displaying same blog entry as before, you will notice that the +page is now looking much nicer. + +.. 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 :: + + 01. class BlogPrimaryView(baseviews.PrimaryView): + 02. + 03. accepts = ('Blog',) + 04. + 05. def cell_call(self, row, col): + 06. entity = self.entity(row, col) + 07. self.w(u'

%s

' % entity.title) + 08. self.w(u'

%s

' % entity.description) + 09. rset = self.req.execute('Any E WHERE E entry_of B, B eid "%s"' % entity.eid) + 10. self.wview('primary', rset) + +In the above source code, `lines 01-08` are similar to the previous +view we defined. + +At `line 09`, a simple request in made to build a resultset 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 infer that such entities have to be of the +``BlogEntry`` kind and retrieves them. + +The request returns a selection of data called a resultset. At +`line 10` the view 'primary' is applied to this resultset to output +HTML. + +**This is to be compared to interfaces and protocols in object-oriented +languages. Applying a given view to all the entities of a resultset only +requires the availability, for each entity of this resultset, of a +view with that name that can accepts the entity.** + +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 + +**Before we move forward, remember that the selection/view principle is +at the core of `CubicWeb`. Everywhere in the engine, data is requested +using the RQL language, then HTML/XML/text/PNG is output by applying a +view to the resultset returned by the query. That is where most of the +flexibility comes from.** + +[WRITE ME] + +* implementing interfaces, calendar for blog entries +* show that a calendar view can export data to ical + +We will implement the cubicwweb.interfaces.ICalendarable interfaces on +entities.BloEntry and apply the OneMonthCalendar and iCalendar views +to resultsets like "Any E WHERE E is BlogEntry" + +* create view "blogentry table" with title, publish_date, category + +We will show that by default the view that displays +"Any E,D,C WHERE E publish_date D, E category C" is the table view. +Of course, the same can be obtained by calling +self.wview('table',rset) + +* in view blog, select blogentries and apply view "blogentry table" +* demo ajax by filtering blogentry table on category + +we did the same with 'primary', but with tables we can turn on filters +and show that ajax comes for free. +[FILLME] + + +Templates +--------- + +*Templates* are specific view that does not depend on a result set. The basic +class `Template` (`cubicweb.common.view`) is derived from the class `View`. + +To build a HTML page, a *main template* is used. In general, the template of +identifier `main` is the one (it is not used in case an error is raised or for +the login form by example). This template uses other templates in addition +to the views which depends on the content to generate the HTML page to return. + +A *template* is responsible for: + +1. executing RQL query of data to render if necessarry +2. identifying the view to use to render data if it is not specified +3. composing the HTML page to return + + +The default main template (`cubicweb.web.views.basetemplates.TheMainTemplate`) +------------------------------------------------------------------------------ + +The default main template build the page based on the following pattern: + +.. image:: images/main_template_layout.png + +The rectangle containing `view.dispathc()` represents the area where the content +view has to be displayed. The others represents sub-templates called to complete +the page. A default implementation of those is provided in +`cubicweb.views.basetemplates`. You can, of course, overload those sub-templates +to implement your own customization of the HTML page. + +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 dictionnary of the forms parameters, before going the classic way (through + step 1 and 2 described juste above) + +.. include:: 05-01-views-stdlib.en.txt + + +XML views, binaries... +---------------------- +For the views generating other formats that HTML (an image generated dynamically +by example), and which can not usually be included in the HTML page generated +by the main template (see above), you have to: + +* set the atribute `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` + +For the views dedicated to binary content creation (an image dynamically generated +by example), we have to set the attribute `binary` of the class to `True` (which +implies that `templateable == False`, so that the attribute `w` of the view could be +replaced by a binary flow instead of unicode). + +(X)HTML tricks to apply +----------------------- + +Some web browser (Firefox by example) are not happy with empty `
` +(by empty we mean that there is no content in the tag, but there +could be attributes), so we should always use `
` even if +it is empty and not use `
`. + diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/05-components.en.txt --- a/doc/book/en/05-components.en.txt Fri Nov 21 17:36:42 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,150 +0,0 @@ -.. -*- coding: utf-8 -*- - -.. _components: - -Components -=========== - -What is a component -------------------- - -A component is a model grouping one or more entity types and/or views associated -in order to provide a specific feature or even a complete application using -others components. -You can decide to write your own set of components if you wish to re-use the -entity types you develop. By default, LAX comes with its owns set of components -that you can start using right away. - - -Standard library ----------------- - -A library of standard components is part of the `LAX` release (look at -``lax/skel/ginco-apps``). Components provide entities and views. With -``lax-0.4``, you should get a set of application entities and system -entities you can re-use. - -The available application entities are: - -* addressbook: PhoneNumber and PostalAddress - -* ebasket: Basket (like a shopping cart) - -* eblog: Blog (a *very* basic blog) - -* eclassfolder: Folder (to organize things but grouping them in folders) - -* eclasstags: Tag (to tag anything) - - -* efile: File (to allow users to upload and store binary or text files) - -* elink: Link (to collect links to web resources) - -* emailinglist: MailingList (to reference a mailing-list and the URLs - for its archives and its admin interface) - -* eperson: Person (easily mixed with addressbook) - -* etask: Task (something to be done between start and stop date) - -* ezone: Zone (to define places within larger places, for example a - city in a state in a country) - -The available system entities are: - -* ecomment: Comment (to attach comment threads to entities) - - - -Adding comments to BlogDemo ---------------------------- - -To import a component in your application just change the line in the -``app.conf`` file. For example:: - - included-yams-components=ecomment - -will make the ``Comment`` entity available in your ``BlogDemo`` -application. - -Change the schema to add a relationship between ``BlogEntry`` and -``Comment`` and you are done. Since the ecomment component defines the -``comments`` relationship, adding the line:: - - comments = ObjectRelation('Comment', cardinality='1*', composite='object') - -to the definition of a ``BlogEntry`` will be enough. - -Clear the datastore and restart. - -Component structure -------------------- - -A complex component is structured as follows: -:: - - mycomponent/ - | - |-- schema.py - | - |-- entities/ - | - |-- sobjects/ - | - |-- views/ - | - |-- test/ - | - |-- i18n/ - | - |-- data/ - | - |-- migration/ - | |- postcreate.py - | \- depends.map - | - |-- debian/ - | - \-- __pkginfo__.py - -We can also define simple Python module instead of directories (packages), for example: -:: - - mycomponent/ - | - |-- entities.py - |-- hooks.py - \-- views.py - - -where: - -* ``schema`` contains the definition of the schema (server side only) -* ``entities`` contains entities definition (server side and web interface) -* ``sobjects`` contains hooks and/or notification views (server side only) -* ``views`` contains the web interface components (web interface only) -* ``test`` contains tests related to the application (not installed) -* ``i18n`` contains messages 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 file 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-components used by - the component. - -At least you should have: - -* the file ``__pkginfo__.py`` -* schema definition - -[WRITE ME] - -* explain the component architecture - -* add comments to the blog by importing the comments component diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/05-define-views.en.txt --- a/doc/book/en/05-define-views.en.txt Fri Nov 21 17:36:42 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,238 +0,0 @@ -.. -*- coding: utf-8 -*- - -.. _DefinitionVues: - -Views definition -================ - -Basic class for views ---------------------- - -Class `View` (`cubicweb.common.view`) -````````````````````````````````````` - -A view writes in its output exit thanks to its attribute `w` (`UStreamIO`). - -The basic interface for views is as follows: - -* `dispatch(**context)`, render the view by calling `call` or - `cell_call` depending on the given parameters -* `call(**kwargs)`, call the view for a complete result set or null -* `cell_call(row, col, **kwargs)`, call the view for a given cell of a result set -* `url()`, returns the URL enabling us to get the view with the current - result set -* `view(__vid, rset, __fallback_vid=None, **kwargs)`, call the view of identifier - `__vid` on the given result set. It is possible to give a view identifier - of fallback that will be used if the view requested is not applicable to the - result set - -* `wview(__vid, rset, __fallback_vid=None, **kwargs)`, similar to `view` except - the flow is automatically passed in the parameters - -* `html_headers()`, returns a list of HTML headers to set by the main template - -* `page_title()`, returns the title to use in the HTML header `title` - -* `creator(eid)`, returns the eid and the login of the entity creator of the entity - having the eid given in the parameter - -Other basic classes: - -* `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 applied to any result set - - -The selection view principle ----------------------------- - -A view includes : - -- an identifier (all objects in `LAX` are entered in a registry - and this identifier will be used as a key) - -- a filter to select the resulsets it can be applied to - - -For a given identifier, multiple views can be defined. `CubicWeb` uses -a selector which computes scores so that it can identify and select the -best view to apply in context. The selector library is in -``cubicweb.common.selector`` and a library of the methods used to -compute scores is in ``cubicweb.vregistry.vreq``. - - -`CubicWeb` provides a lot of standard views, for a complete list, you -will have to read the code in directory ``cubicweb/web/views/`` (XXX -improve doc). - -For example, the view named ``primary`` is the one used to display -a single entity. - -If you want to change the way a ``BlogEntry`` is displayed, just -override the view ``primary`` in ``BlogDemo/views.py`` :: - - 01. from ginco.web.views import baseviews - 02. - 03. class BlogEntryPrimaryView(baseviews.PrimaryView): - 04. - 05. accepts = ('BlogEntry',) - 06. - 07. def cell_call(self, row, col): - 08. entity = self.entity(row, col) - 09. self.w(u'

%s

' % entity.title) - 10. self.w(u'

published on %s in category %s

' % \ - 11. (entity.publish_date.strftime('%Y-%m-%d'), entity.category)) - 12. self.w(u'

%s

' % entity.text) - -The above source code defines a new primary view (`line 03`) for -``BlogEntry`` (`line 05`). - -Since views are applied to resultsets and resulsets can be tables of -data, it is needed to recover the entity from its (row,col) -coordinates (`line 08`). We will get to this in more detail later. - -The view has a ``self.w()`` method that is used to output data. Here `lines -09-12` output HTML tags and values of the entity's attributes. - -When displaying same blog entry as before, you will notice that the -page is now looking much nicer. - -.. 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 :: - - 01. class BlogPrimaryView(baseviews.PrimaryView): - 02. - 03. accepts = ('Blog',) - 04. - 05. def cell_call(self, row, col): - 06. entity = self.entity(row, col) - 07. self.w(u'

%s

' % entity.title) - 08. self.w(u'

%s

' % entity.description) - 09. rset = self.req.execute('Any E WHERE E entry_of B, B eid "%s"' % entity.eid) - 10. self.wview('primary', rset) - -In the above source code, `lines 01-08` are similar to the previous -view we defined. - -At `line 09`, a simple request in made to build a resultset 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 infer that such entities have to be of the -``BlogEntry`` kind and retrieves them. - -The request returns a selection of data called a resultset. At -`line 10` the view 'primary' is applied to this resultset to output -HTML. - -**This is to be compared to interfaces and protocols in object-oriented -languages. Applying a given view to all the entities of a resultset only -requires the availability, for each entity of this resultset, of a -view with that name that can accepts the entity.** - -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 - -**Before we move forward, remember that the selection/view principle is -at the core of `CubicWeb`. Everywhere in the engine, data is requested -using the RQL language, then HTML/XML/text/PNG is output by applying a -view to the resultset returned by the query. That is where most of the -flexibility comes from.** - -[WRITE ME] - -* implementing interfaces, calendar for blog entries -* show that a calendar view can export data to ical - -We will implement the cubicwweb.interfaces.ICalendarable interfaces on -entities.BloEntry and apply the OneMonthCalendar and iCalendar views -to resultsets like "Any E WHERE E is BlogEntry" - -* create view "blogentry table" with title, publish_date, category - -We will show that by default the view that displays -"Any E,D,C WHERE E publish_date D, E category C" is the table view. -Of course, the same can be obtained by calling -self.wview('table',rset) - -* in view blog, select blogentries and apply view "blogentry table" -* demo ajax by filtering blogentry table on category - -we did the same with 'primary', but with tables we can turn on filters -and show that ajax comes for free. -[FILLME] - - -Templates ---------- - -*Templates* are specific view that does not depend on a result set. The basic -class `Template` (`cubicweb.common.view`) is derived from the class `View`. - -To build a HTML page, a *main template* is used. In general, the template of -identifier `main` is the one (it is not used in case an error is raised or for -the login form by example). This template uses other templates in addition -to the views which depends on the content to generate the HTML page to return. - -A *template* is responsible for: - -1. executing RQL query of data to render if necessarry -2. identifying the view to use to render data if it is not specified -3. composing the HTML page to return - - -The default main template (`cubicweb.web.views.basetemplates.TheMainTemplate`) ------------------------------------------------------------------------------- - -The default main template build the page based on the following pattern: - -.. image:: images/main_template_layout.png - -The rectangle containing `view.dispathc()` represents the area where the content -view has to be displayed. The others represents sub-templates called to complete -the page. A default implementation of those is provided in -`cubicweb.views.basetemplates`. You can, of course, overload those sub-templates -to implement your own customization of the HTML page. - -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 dictionnary of the forms parameters, before going the classic way (through - step 1 and 2 described juste above) - -.. include:: 05-01-views-stdlib.en.txt - - -XML views, binaries... ----------------------- -For the views generating other formats that HTML (an image generated dynamically -by example), and which can not usually be included in the HTML page generated -by the main template (see above), you have to: - -* set the atribute `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` - -For the views dedicated to binary content creation (an image dynamically generated -by example), we have to set the attribute `binary` of the class to `True` (which -implies that `templateable == False`, so that the attribute `w` of the view could be -replaced by a binary flow instead of unicode). - -(X)HTML tricks to apply ------------------------ - -Some web browser (Firefox by example) are not happy with empty `
` -(by empty we mean that there is no content in the tag, but there -could be attributes), so we should always use `
` even if -it is empty and not use `
`. - diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/06-00-define-workflows.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/06-00-define-workflows.en.txt Fri Nov 21 17:37:27 2008 +0100 @@ -0,0 +1,155 @@ +.. -*- coding: utf-8 -*- + +Workflow definition +====================== + +On peut mettre une condition rql ou/et un groupe auquel doit appartenir l'utilisateur. + +Si on met à la fois un(ou plusieurs) groupe et une condition RQL, il faut que les deux soient respectés. + +Si on met plusieurs groupes, il faut que l'utilisateur soit dans un des groupes. + +Pour la condition RQL sur une transition, on peut y mettre les substitutions suivantes : + +* `%(eid)s`, eid de l'objet +* `%(ueid)s`, eid de l'utilisateur qui fait la requête +* `%(seid)s`, eid de l'état courant de l'objet + +Dans le script de création d'un workflow, penser à mettre `_()` autour des noms d'états et de transitions +pour que ceux si soient pris en compte par les scripts de gestion des catalogues i18n. + +General +------- + +A workflow can be defined in a `LAX` application thanks to the system +entities ``State`` and ``Transition``. Those are defined within all +LAX application and can be set-up through the main administrator interface. + +Once your schema is defined, you can start creating the set of states and +the required transitions for your applications entities. + +You first need to define the states and then the transitions between those +to complete your workflow. + +A ``State`` defines the status of an entity. While creating a new state, +you will be first given the option to select the entity type the state +can be applied to. By choosing ``Apply``, a new section will be displayed +in the editing screen to enable you to add relation to the state you are +creating. + +A ``Transition`` is also based on an entity type it can be applied to. +By choosing ``Apply``, a new section will be displayed in the editing +screen to enable you to add relation to the transition you are +creating. + +At the transition level you will also define the group of user which can +aplly this transition to an object. + + +Example of a simple workflow +---------------------------- + +Please see the tutorial to view and example of a simple workflow. + + +[Create a simple workflow for BlogDemo, to have a moderator approve new blog +entry to be published. This implies, specify a dedicated group of blog +moderator as well as hide the view of a blog entry to the user until +it reaches the state published] + +Set-up a workflow +----------------- + +Before starting, make sure you refresh your mind by reading [link to +definition_workflow chapter]. + +We want to create a workflow to control the quality of the BlogEntry +submitted on your application. When a BlogEntry is created by a user +its state should be `submitted`. To be visible to all, it needs to +be in the state `published`. To move from `submitted` to `published` +we need a transition that we can name `approve_blogentry`. + +We do not want every user to be allowed to change the state of a +BlogEntry. We need to define a group of user, `moderators`, and +this group will have appropriate permissions to approve BlogEntry +to be published and visible to all. + +There are two ways to create a workflow, form the user interface, +and also by defining it in ``migration/postcreate.py``. This script +is executed each time a new ``./bin/laxctl db-init`` is done. +If you create the states and transitions through the user interface +this means that next time you will need to initialize the database +you will have to re-create all the entities. +We strongly recommand you create the workflow in ``migration\postcreate.py`` +and we will now show you how. +The user interface would only be a reference for you to view the states +and transitions but is not the appropriate interface to define your +application workflow. + +Update the schema +~~~~~~~~~~~~~~~~~ +To enable a BlogEntry to have a State, we have to define a relation +``in_state`` in the schema of BlogEntry. Please do as follows, add +the line ``in_state (...)``:: + + class BlogEntry(EntityType): + title = String(maxsize=100, required=True) + publish_date = Date(default='TODAY') + text_format = String(meta=True, internationalizable=True, maxsize=50, + default='text/rest', constraints=[format_constraint]) + text = String(fulltextindexed=True) + category = String(vocabulary=('important','business')) + entry_of = SubjectRelation('Blog', cardinality='?*') + in_state = SubjectRelation('State', cardinality='1*') + +As you updated the schema, you will have re-execute ``./bin/laxctl db-init`` +to initialize the database and migrate your existing entities. +[WRITE ABOUT MIGRATION] + +Create states, transitions and group permissions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +At the time the ``postcreate.py`` script is executed, several methods +can be used. They are all defined in the ``class ServerMigrationHelper``. +We will only discuss the method we use to create a wrokflow here. + +To define our workflow for BlogDemo, please add the following lines +to ``migration/postcreate.py``:: + + _ = unicode + + moderators = add_entity('EGroup', name=u"moderators") + + submitted = add_state(_('submitted'), 'BlogEntry', initial=True) + published = add_state(_('published'), 'BlogEntry') + + add_transition(_('approve_blogentry'), 'BlogEntry', (submitted,), published, ('moderators', 'managers'),) + + checkpoint() + +``add_entity`` is used here to define the new group of users that we +need to define the transitions, `moderators`. +If this group required by the transition is not defined before the +transition is created, it will not create the relation `transition +require the group moderator`. + +``add_state`` expects as the first argument the name of the state you are +willing to create, then the entity type on which the state can be applied, +and an optionnal argument to set if the state is the initial state +of the entity type or not. + +``add_transition`` expects as the first argument the name of the +transition, then the entity type on which we can apply the transition, +then the list of possible initial states from which the transition +can be applied, the target state of the transition, and the permissions +(e.g. list of the groups of users who can apply the transition). + +.. image:: images/lax-book.03-transitions-view.en.png + +You can now notice that in the actions box of a BlogEntry, the state +is now listed as well as the possible transitions from this state +defined by the workflow. This transition, as defined in the workflow, +will only being displayed for the users belonging to the group +moderators of managers. + + diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/06-define-workflows.en.txt --- a/doc/book/en/06-define-workflows.en.txt Fri Nov 21 17:36:42 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,154 +0,0 @@ -.. -*- coding: utf-8 -*- - -Définition de workflow -====================== -On peut mettre une condition rql ou/et un groupe auquel doit appartenir l'utilisateur. - -Si on met à la fois un(ou plusieurs) groupe et une condition RQL, il faut que les deux soient respectés. - -Si on met plusieurs groupes, il faut que l'utilisateur soit dans un des groupes. - -Pour la condition RQL sur une transition, on peut y mettre les substitutions suivantes : - -* `%(eid)s`, eid de l'objet -* `%(ueid)s`, eid de l'utilisateur qui fait la requête -* `%(seid)s`, eid de l'état courant de l'objet - -Dans le script de création d'un workflow, penser à mettre `_()` autour des noms d'états et de transitions -pour que ceux si soient pris en compte par les scripts de gestion des catalogues i18n. - -General -------- - -A workflow can be defined in a `LAX` application thanks to the system -entities ``State`` and ``Transition``. Those are defined within all -LAX application and can be set-up through the main administrator interface. - -Once your schema is defined, you can start creating the set of states and -the required transitions for your applications entities. - -You first need to define the states and then the transitions between those -to complete your workflow. - -A ``State`` defines the status of an entity. While creating a new state, -you will be first given the option to select the entity type the state -can be applied to. By choosing ``Apply``, a new section will be displayed -in the editing screen to enable you to add relation to the state you are -creating. - -A ``Transition`` is also based on an entity type it can be applied to. -By choosing ``Apply``, a new section will be displayed in the editing -screen to enable you to add relation to the transition you are -creating. - -At the transition level you will also define the group of user which can -aplly this transition to an object. - - -Example of a simple workflow ----------------------------- - -Please see the tutorial to view and example of a simple workflow. - - -[Create a simple workflow for BlogDemo, to have a moderator approve new blog -entry to be published. This implies, specify a dedicated group of blog -moderator as well as hide the view of a blog entry to the user until -it reaches the state published] - -Set-up a workflow ------------------ - -Before starting, make sure you refresh your mind by reading [link to -definition_workflow chapter]. - -We want to create a workflow to control the quality of the BlogEntry -submitted on your application. When a BlogEntry is created by a user -its state should be `submitted`. To be visible to all, it needs to -be in the state `published`. To move from `submitted` to `published` -we need a transition that we can name `approve_blogentry`. - -We do not want every user to be allowed to change the state of a -BlogEntry. We need to define a group of user, `moderators`, and -this group will have appropriate permissions to approve BlogEntry -to be published and visible to all. - -There are two ways to create a workflow, form the user interface, -and also by defining it in ``migration/postcreate.py``. This script -is executed each time a new ``./bin/laxctl db-init`` is done. -If you create the states and transitions through the user interface -this means that next time you will need to initialize the database -you will have to re-create all the entities. -We strongly recommand you create the workflow in ``migration\postcreate.py`` -and we will now show you how. -The user interface would only be a reference for you to view the states -and transitions but is not the appropriate interface to define your -application workflow. - -Update the schema -~~~~~~~~~~~~~~~~~ -To enable a BlogEntry to have a State, we have to define a relation -``in_state`` in the schema of BlogEntry. Please do as follows, add -the line ``in_state (...)``:: - - class BlogEntry(EntityType): - title = String(maxsize=100, required=True) - publish_date = Date(default='TODAY') - text_format = String(meta=True, internationalizable=True, maxsize=50, - default='text/rest', constraints=[format_constraint]) - text = String(fulltextindexed=True) - category = String(vocabulary=('important','business')) - entry_of = SubjectRelation('Blog', cardinality='?*') - in_state = SubjectRelation('State', cardinality='1*') - -As you updated the schema, you will have re-execute ``./bin/laxctl db-init`` -to initialize the database and migrate your existing entities. -[WRITE ABOUT MIGRATION] - -Create states, transitions and group permissions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -At the time the ``postcreate.py`` script is executed, several methods -can be used. They are all defined in the ``class ServerMigrationHelper``. -We will only discuss the method we use to create a wrokflow here. - -To define our workflow for BlogDemo, please add the following lines -to ``migration/postcreate.py``:: - - _ = unicode - - moderators = add_entity('EGroup', name=u"moderators") - - submitted = add_state(_('submitted'), 'BlogEntry', initial=True) - published = add_state(_('published'), 'BlogEntry') - - add_transition(_('approve_blogentry'), 'BlogEntry', (submitted,), published, ('moderators', 'managers'),) - - checkpoint() - -``add_entity`` is used here to define the new group of users that we -need to define the transitions, `moderators`. -If this group required by the transition is not defined before the -transition is created, it will not create the relation `transition -require the group moderator`. - -``add_state`` expects as the first argument the name of the state you are -willing to create, then the entity type on which the state can be applied, -and an optionnal argument to set if the state is the initial state -of the entity type or not. - -``add_transition`` expects as the first argument the name of the -transition, then the entity type on which we can apply the transition, -then the list of possible initial states from which the transition -can be applied, the target state of the transition, and the permissions -(e.g. list of the groups of users who can apply the transition). - -.. image:: images/lax-book.03-transitions-view.en.png - -You can now notice that in the actions box of a BlogEntry, the state -is now listed as well as the possible transitions from this state -defined by the workflow. This transition, as defined in the workflow, -will only being displayed for the users belonging to the group -moderators of managers. - - diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/06-maintemplate.en.txt --- a/doc/book/en/06-maintemplate.en.txt Fri Nov 21 17:36:42 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,210 +0,0 @@ -.. -*- coding: utf-8 -*- - -Views & Templates -================= - - -Look at ``lax/skel/ginco/web/views/basetemplates.py`` and you will -find the base templates used to generate HTML for your application. - -A page is composed as indicated on the schema below: - -.. image:: images/lax-book.06-main-template-layout.en.png - -In this section we will go through a couple of the primary templates -you must be interested in, that is to say, the HTMLPageHeader, -the HTMLPageFooter and the TheMainTemplate. - - -HTMLPageHeader --------------- - -Let's use a different logo than the default one provided with LAX -and customize our header. - -Change logo -~~~~~~~~~~~ - -The easiest way to use a different logo is to replace the existing -``logo.png`` in ``myapp/data`` by your prefered icon and refresh. -By default all application will look for a ``logo.png`` to be -rendered in the logo section. - -.. image:: images/lax-book.06-main-template-logo.en.png - -[ADD] -customized external_resources in myapp/data cd crih for reference - -[WRITE ME] -ADD how to use external_resources variables used in ginco/web/webconfig.py - -Customize header -~~~~~~~~~~~~~~~~ - -Let's now move the search box in the header and remove the login form -from the header. We'll show how to move it to the left column of the application. - -Let's sat we do not want anymore the login menu to be in the header, but we -prefer it to be in the left column just below the logo. As the left column is -rendered by ``TheMainTemplate``, we will show how to do it in TheMainTemplate_. - -First, to remove the login menu, we just need to comment out the display of the -login component such as follows: :: - - class MyHTMLPageHeader(HTMLPageHeader): - - def main_header(self, view): - """build the top menu with authentification info and the rql box""" - self.w(u'\n') - self.w(u'\n') - # appliname and breadcrumbs - self.w(u'') - # logged user and help - #self.w(u'') - # lastcolumn - self.w(u'\n') - self.w(u'\n') - self.template('logform', rset=self.rset, id='popupLoginBox', klass='hidden', - title=False, message=False) - - - -.. image:: images/lax-book.06-header-no-login.en.png - -Let's now move the search box in the top-right header area. To do so, we will -first create a method to get the search box display and insert it in the header -table. - -:: - - from ginco.web.views.basetemplates import HTMLPageHeader - class MyHTMLPageHeader(HTMLPageHeader): - def main_header(self, view): - """build the top menu with authentification info and the rql box""" - self.w(u'\n') - self.w(u'\n') - # appliname and breadcrumbs - self.w(u'') - - # logged user and help - #self.w(u'') - - self.w(u'') - # lastcolumn - self.w(u'\n') - self.w(u'\n') - self.template('logform', rset=self.rset, id='popupLoginBox', klass='hidden', - title=False, message=False) - - def get_searchbox(self, view, context): - boxes = list(self.vreg.possible_vobjects('boxes', self.req, self.rset, - view=view, context=context)) - if boxes: - for box in boxes: - if box.id == 'search_box': - box.dispatch(w=self.w, view=view) - - - - -HTMLPageFooter --------------- - -If you want to change the footer for example, look -for HTMLPageFooter and override it in your views file as in: -:: - - form ginco.web.views.basetemplates import HTMLPageFooter - class MyHTMLPageFooter(HTMLPageFooter): - def call(self, **kwargs): - self.w(u'') - -Updating a view does not require any restart of the server. By reloading -the page you can see your new page footer. - - -TheMainTemplate ---------------- -.. _TheMainTemplate: - -The MainTemplate is a bit complex as it tries to accomodate many -different cases. We are now about to go through it and cutomize entirely -our application. - -TheMainTemplate is responsible for the general layout of the entire application. -It defines the template of ``id = main`` that is used by the application. Is -also defined in ``ginco/web/views/basetemplates.py`` another template that can -be used based on TheMainTemplate called SimpleMainTemplate which does not have -a top section. - -.. image:: images/lax-book.06-simple-main-template.en.png - -CSS changes ------------ - -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/``. - -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. - -1 -[TODO] -Add login menu in left column - - -[WRITE ME] - -* customize MainTemplate and show that everything in the user - interface can be changed - -[TODO] -Rajouter une section pour definir la terminologie utilisee. -Dans ginco-doc rajouter une section pour erudi-ctl shell ou -on liste les commandes dispos. diff -r 979dbe0cade3 -r 80c65c9f7c41 doc/book/en/07-00-data-as-objects.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/07-00-data-as-objects.en.txt Fri Nov 21 17:37:27 2008 +0100 @@ -0,0 +1,142 @@ +.. -*- coding: utf-8 -*- + + +Manipulation des données stockées +================================= + +Les classes `Entity` et `AnyEntity` +----------------------------------- +Pour fournir un comportement spécifique à un type d'entité, il suffit de définir +une classe héritant de la class `ginco.entities.AnyEntity`. En général il faut +définir ces classes dans un module du package `entities` d'une application pour +qu'elle soit disponible à la fois coté serveur et coté client. + +La classe `AnyEntity` est une classe chargée dynamiquement héritant de la classe +de base `Entity` (`ginco.common.entity`). On définit une sous-classe pour +ajouter des méthodes ou spécialiser les comportements d'un type d'entité donné. + +Des descripteurs sont ajoutés à l'enregistrement pour initialiser la classe en +fonction du schéma : + +* on peut accéder aux attributs définis dans le schéma via les attributs de même + nom sur les instances (valeur typée) + +* on peut accéder aux relations définies dans le schéma via les attributs de même + nom sur les instances (liste d'instances d'entité) + +Les méthodes définies sur la classe `AnyEntity` ou `Entity` sont les suivantes : + +* `has_eid()`, retourne vrai si l'entité à un eid affecté (i.e. pas en cours de + création) + +* `check_perm(action)`, vérifie que l'utilisateur à le droit d'effectuer + l'action demandée sur l'entité + +:Formattage et génération de la sortie: + + * `view(vid, **kwargs)`, applique la vue donnée à l'entité + + * `absolute_url(**kwargs)`, retourne une URL absolue permettant d'accéder à la + vue primaire d'une entité + + * `rest_path()`, renvoie une l'URL REST relative permettant d'obtenir l'entité + + * `format(attr)`, retourne le format (type MIME) du champ passé en argument + + * `printable_value(attr, value=_marker, attrtype=None, format='text/html')`, + retourne une chaine permettant l'affichage dans un format donné de la valeur + d'un attribut (la valeur est automatiquement récupérée au besoin) + + * `display_name(form='')`, retourne une chaîne pour afficher le type de + l'entité, en spécifiant éventuellement la forme désirée ('plural' pour la + forme plurielle) + +:Gestion de données: + + * `as_rset()`, transforme l'entité en un resultset équivalent simulant + le résultat de la requête `Any X WHERE X eid _eid_` + + * `complete(skip_bytes=True)`, effectue une requête permettant de récupérer d'un + coup toutes les valeurs d'attributs manquant sur l'entité + + * `get_value(name)`, récupere la valeur associée à l'attribut passé en argument + + * `related(rtype, x='subject', limit=None, entities=False)`, retourne une liste + des entités liées à l'entité courant par la relation donnée en argument + + * `unrelated(rtype, targettype, x='subject', limit=None)`, retourne un result set + des entités not liées à l'entité courante par la relation donnée en argument + et satisfaisants les contraintes de celle-ci + + * `set_attributes(**kwargs)`, met à jour la liste des attributs avec + les valeurs correspondantes passées sous forme d'arguments nommés + + * `copy_relations(ceid)`, copie les relations de l'entité ayant l'eid passé en + argument sur l'entité courante + + * `last_modified(view)`, retourne la date à laquelle on doit considérer + l'objet comme modifié (utiliser par la gestion de cache HTTP) + + * `delete()` permet de supprimer l'entité représentée + +:Meta-données standard (Dublin Core): + + * `dc_title()`, retourne une chaine unicode correspondant à la méta-donnée + 'Title' (utilise par défaut le premier attribut non 'meta' du schéma de + l'entité) + + * `dc_long_title()`, comme dc_title mais peut retourner un titre plus détaillé + + * `dc_description(format='text/plain')`, retourne une chaine unicode + correspondant à la méta-donnée 'Description' (cherche un attribut + 'description' par défaut) + + * `dc_authors()`, retourne une chaine unicode correspondant à la méta-donnée + 'Authors' (propriétaires par défaut) + + * `dc_date(date_format=None)`, retourne une chaine unicode + correspondant à la méta-donnée 'Date' (date de modification par défaut) + +:Contrôle du vocabulaire pour les relations: + + * `vocabulary(rtype, x='subject', limit=None)`, appelée notamment + par les vues d'édition d'erudi, elle renvoie une liste de couple + (label, eid) des entités qui pourraient être liées à l'entité + via la relation `rtype` + * `subject_relation_vocabulary(rtype, limit=None)`, appelée + en interne par `vocabulary` dans le cas d'une relation sujet + * `object_relation_vocabulary(rtype, limit=None)`, appelée + en interne par `vocabulary` dans le cas d'une relation objet + * `relation_vocabulary(rtype, targettype, x, limit=None)`, appelé + en interne par `subject_relation_vocabulary` et `object_relation_vocabulary` + + +Les *rtags* +----------- +Les *rtags* permettent de spécifier certains comportements propres aux relations +d'un type d'entité donné (voir plus loin). Ils sont définis sur la classe +d'entité via l'attribut `rtags` qui est un dictionnaire dont les clés sont un +triplet :: + + , , , ,