# HG changeset patch # User Nicolas Chauvat # Date 1226967390 -3600 # Node ID 9c919a47e1408823f9be6cd77b0708c4464401c3 # Parent 30f19b9768575ac6f834d009d6c414a7b3d4147e [doc] total file reorganisation - phase 1 complete diff -r 30f19b976857 -r 9c919a47e140 doc/book/en/.static/cubicweb.png Binary file doc/book/en/.static/cubicweb.png has changed diff -r 30f19b976857 -r 9c919a47e140 doc/book/en/.static/logilab.png Binary file doc/book/en/.static/logilab.png has changed diff -r 30f19b976857 -r 9c919a47e140 doc/book/en/.static/sphinx-default.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/.static/sphinx-default.css Tue Nov 18 01:16:30 2008 +0100 @@ -0,0 +1,860 @@ +/** + * Sphinx Doc Design + */ + +html, body { + background: white; +} + +body { + font-family: Verdana, sans-serif; + font-size: 100%; + background-color: white; + color: black; + margin: 0; + padding: 0; +} + +/* :::: LAYOUT :::: */ + +div.logilablogo { + padding: 10px 10px 10px 10px; + height:75; +} + + +div.document { + background-color: white; +} + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 230px; +} + +div.body { + background-color: white; + padding: 0 20px 30px 20px; + border-left:solid; + border-left-color:#e2e2e2; + border-left-width:thin; +} + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; +} + +div.clearer { + clear: both; +} + +div.footer { + color: #ff4500; + width: 100%; + padding: 9px 0 9px 0; + text-align: center; + font-size: 75%; +} + +div.footer a { + color: #ff4500; + text-decoration: underline; +} + +div.related { + background-color: #ff7700; + color: white; + width: 100%; + height: 30px; + line-height: 30px; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +div.related a { + color: white; + font-weight:bold; +} + +/* ::: TOC :::: */ + +div.sphinxsidebar { + border-style:solid; + border-color: white; +/* background-color:#e2e2e2;*/ + padding-bottom:5px; +} + +div.sphinxsidebar h3 { + font-family: 'Verdanda', sans-serif; + color: black; + font-size: 1.2em; + font-weight: normal; + margin: 0; + padding: 0; + font-weight:bold; + font-style:italic; +} + +div.sphinxsidebar h4 { + font-family: 'Verdana', sans-serif; + color: black; + font-size: 1.1em; + font-weight: normal; + margin: 5px 0 0 0; + padding: 0; + font-weight:bold; + font-style:italic; +} + +div.sphinxsidebar p { + color: black; +} + +div.sphinxsidebar p.topless { + margin: 5px 10px 10px 10px; +} + +div.sphinxsidebar ul { + margin: 10px; + padding: 0; + list-style: none; + color: black; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar a { + color: black; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #e2e2e2; + font-family: sans-serif; + font-size: 1em; + padding-bottom: 5px; +} + +/* :::: MODULE CLOUD :::: */ +div.modulecloud { + margin: -5px 10px 5px 10px; + padding: 10px; + line-height: 160%; + border: 1px solid #cbe7e5; + background-color: #f2fbfd; +} + +div.modulecloud a { + padding: 0 5px 0 5px; +} + +/* :::: SEARCH :::: */ +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li div.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* :::: COMMON FORM STYLES :::: */ + +div.actions { + padding: 5px 10px 5px 10px; + border-top: 1px solid #cbe7e5; + border-bottom: 1px solid #cbe7e5; + background-color: #e0f6f4; +} + +form dl { + color: #333; +} + +form dt { + clear: both; + float: left; + min-width: 110px; + margin-right: 10px; + padding-top: 2px; +} + +input#homepage { + display: none; +} + +div.error { + margin: 5px 20px 0 0; + padding: 5px; + border: 1px solid #d00; + font-weight: bold; +} + +/* :::: INLINE COMMENTS :::: */ + +div.inlinecomments { + position: absolute; + right: 20px; +} + +div.inlinecomments a.bubble { + display: block; + float: right; + background-image: url(style/comment.png); + background-repeat: no-repeat; + width: 25px; + height: 25px; + text-align: center; + padding-top: 3px; + font-size: 0.9em; + line-height: 14px; + font-weight: bold; + color: black; +} + +div.inlinecomments a.bubble span { + display: none; +} + +div.inlinecomments a.emptybubble { + background-image: url(style/nocomment.png); +} + +div.inlinecomments a.bubble:hover { + background-image: url(style/hovercomment.png); + text-decoration: none; + color: #3ca0a4; +} + +div.inlinecomments div.comments { + float: right; + margin: 25px 5px 0 0; + max-width: 50em; + min-width: 30em; + border: 1px solid #2eabb0; + background-color: #f2fbfd; + z-index: 150; +} + +div#comments { + border: 1px solid #2eabb0; + margin-top: 20px; +} + +div#comments div.nocomments { + padding: 10px; + font-weight: bold; +} + +div.inlinecomments div.comments h3, +div#comments h3 { + margin: 0; + padding: 0; + background-color: #2eabb0; + color: white; + border: none; + padding: 3px; +} + +div.inlinecomments div.comments div.actions { + padding: 4px; + margin: 0; + border-top: none; +} + +div#comments div.comment { + margin: 10px; + border: 1px solid #2eabb0; +} + +div.inlinecomments div.comment h4, +div.commentwindow div.comment h4, +div#comments div.comment h4 { + margin: 10px 0 0 0; + background-color: #2eabb0; + color: white; + border: none; + padding: 1px 4px 1px 4px; +} + +div#comments div.comment h4 { + margin: 0; +} + +div#comments div.comment h4 a { + color: #d5f4f4; +} + +div.inlinecomments div.comment div.text, +div.commentwindow div.comment div.text, +div#comments div.comment div.text { + margin: -5px 0 -5px 0; + padding: 0 10px 0 10px; +} + +div.inlinecomments div.comment div.meta, +div.commentwindow div.comment div.meta, +div#comments div.comment div.meta { + text-align: right; + padding: 2px 10px 2px 0; + font-size: 95%; + color: #538893; + border-top: 1px solid #cbe7e5; + background-color: #e0f6f4; +} + +div.commentwindow { + position: absolute; + width: 500px; + border: 1px solid #cbe7e5; + background-color: #f2fbfd; + display: none; + z-index: 130; +} + +div.commentwindow h3 { + margin: 0; + background-color: #2eabb0; + color: white; + border: none; + padding: 5px; + font-size: 1.5em; + cursor: pointer; +} + +div.commentwindow div.actions { + margin: 10px -10px 0 -10px; + padding: 4px 10px 4px 10px; + color: #538893; +} + +div.commentwindow div.actions input { + border: 1px solid #2eabb0; + background-color: white; + color: #135355; + cursor: pointer; +} + +div.commentwindow div.form { + padding: 0 10px 0 10px; +} + +div.commentwindow div.form input, +div.commentwindow div.form textarea { + border: 1px solid #3c9ea2; + background-color: white; + color: black; +} + +div.commentwindow div.error { + margin: 10px 5px 10px 5px; + background-color: #fbe5dc; + display: none; +} + +div.commentwindow div.form textarea { + width: 99%; +} + +div.commentwindow div.preview { + margin: 10px 0 10px 0; + background-color: #70d0d4; + padding: 0 1px 1px 25px; +} + +div.commentwindow div.preview h4 { + margin: 0 0 -5px -20px; + padding: 4px 0 0 4px; + color: white; + font-size: 1.3em; +} + +div.commentwindow div.preview div.comment { + background-color: #f2fbfd; +} + +div.commentwindow div.preview div.comment h4 { + margin: 10px 0 0 0!important; + padding: 1px 4px 1px 4px!important; + font-size: 1.2em; +} + +/* :::: SUGGEST CHANGES :::: */ +div#suggest-changes-box input, div#suggest-changes-box textarea { + border: 1px solid #ccc; + background-color: white; + color: black; +} + +div#suggest-changes-box textarea { + width: 99%; + height: 400px; +} + + +/* :::: PREVIEW :::: */ +div.preview { + background-image: url(style/preview.png); + padding: 0 20px 20px 20px; + margin-bottom: 30px; +} + + +/* :::: INDEX PAGE :::: */ + +table.contentstable { + width: 90%; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* :::: INDEX STYLES :::: */ + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable dl, table.indextable dd { + margin-top: 0; + margin-bottom: 0; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +form.pfform { + margin: 10px 0 20px 0; +} + +/* :::: GLOBAL STYLES :::: */ + +.docwarning { + background-color: #ffe4e4; + padding: 10px; + margin: 0 -20px 0 -20px; + border-bottom: 1px solid #f66; +} + +p.subhead { + font-weight: bold; + margin-top: 20px; +} + +a { + color: black; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: 'Verdana', sans-serif; + background-color: white; + font-weight: bold; + color: black; + border-bottom: 1px solid #ccc; + margin: 20px -20px 10px -20px; + padding: 3px 0 3px 10px; +} + +div.body h1 { margin-top: 0; font-size: 150%; } +div.body h2 { font-size: 120%; } +div.body h3 { font-size: 100%; } +div.body h4 { font-size: 80%; } +div.body h5 { font-size: 600%; } +div.body h6 { font-size: 40%; } + +a.headerlink { + color: #c60f0f; + font-size: 0.8em; + padding: 0 4px 0 4px; + text-decoration: none; + visibility: hidden; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink { + visibility: visible; +} + +a.headerlink:hover { + background-color: #c60f0f; + color: white; +} + +div.body p, div.body dd, div.body li { + text-align: justify; + line-height: 130%; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +ul.fakelist { + list-style: none; + margin: 10px 0 10px 20px; + padding: 0; +} + +.field-list ul { + padding-left: 1em; +} + +.first { + margin-top: 0 !important; +} + +/* "Footnotes" heading */ +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +/* "Topics" */ + +div.topic { + background-color: #eee; + border: 1px solid #ccc; + padding: 0 7px 0 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* Admonitions */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +div.admonition dl { + margin-bottom: 0; +} + +div.admonition p { + display: inline; +} + +div.seealso { + background-color: #ffc; + border: 1px solid #ff6; +} + +div.warning { + background-color: #ffe4e4; + border: 1px solid #f66; +} + +div.note { + background-color: #eee; + border: 1px solid #ccc; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +table.docutils { + border: 0; +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 0; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +table.field-list td, table.field-list th { + border: 0 !important; +} + +table.footnote td, table.footnote th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +dl { + margin-bottom: 15px; + clear: both; +} + +dd p { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.refcount { + color: #060; +} + +dt:target, +.highlight { + background-color: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +th { + text-align: left; + padding-right: 5px; +} + +pre { + padding: 5px; + background-color: #efc; + color: #333; + border: 1px solid #ac9; + border-left: none; + border-right: none; + overflow: auto; +} + +td.linenos pre { + padding: 5px 0px; + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + margin-left: 0.5em; +} + +table.highlighttable td { + padding: 0 0.5em 0 0.5em; +} + +tt { + background-color: #ecf0f3; + padding: 0 1px 0 1px; + font-size: 0.95em; +} + +tt.descname { + background-color: transparent; + font-weight: bold; + font-size: 1.2em; +} + +tt.descclassname { + background-color: transparent; +} + +tt.xref, a tt { + background-color: transparent; + font-weight: bold; +} + +.footnote:target { background-color: #ffa } + +h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.versionmodified { + font-style: italic; +} + +form.comment { + margin: 0; + padding: 10px 30px 10px 30px; + background-color: #eee; +} + +form.comment h3 { + background-color: #326591; + color: white; + margin: -10px -30px 10px -30px; + padding: 5px; + font-size: 1.4em; +} + +form.comment input, +form.comment textarea { + border: 1px solid #ccc; + padding: 2px; + font-family: sans-serif; + font-size: 100%; +} + +form.comment input[type="text"] { + width: 240px; +} + +form.comment textarea { + width: 100%; + height: 200px; + margin-bottom: 10px; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +/* :::: PRINT :::: */ +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0; + width : 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + div#comments div.new-comment-box, + #top-link { + display: none; + } +} diff -r 30f19b976857 -r 9c919a47e140 doc/book/en/.templates/layout.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/.templates/layout.html Tue Nov 18 01:16:30 2008 +0100 @@ -0,0 +1,195 @@ +{%- block doctype -%} + +{%- endblock %} +{%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %} +{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %} +{%- macro relbar %} + +{%- endmacro %} +{%- macro sidebar %} + {%- if builder != 'htmlhelp' %} +
+
+ {%- block sidebarlogo %} + {%- if logo %} + + {%- endif %} + {%- endblock %} + {%- block sidebartoc %} + {%- if display_toc %} +

Table Of Contents

+ {{ toc }} + {%- endif %} + {%- endblock %} + {%- block sidebarrel %} + {%- if prev %} +

Previous topic

+

{{ prev.title }}

+ {%- endif %} + {%- if next %} +

Next topic

+

{{ next.title }}

+ {%- endif %} + {%- endblock %} + {%- if sourcename %} +

This Page

+ + {%- endif %} + {%- if customsidebar %} + {{ rendertemplate(customsidebar) }} + {%- endif %} + {%- block sidebarsearch %} + {%- if pagename != "search" %} +

{{ builder == 'web' and 'Keyword' or 'Quick' }} search

+ + {%- if builder == 'web' %} +

Enter a module, class or function name.

+ {%- endif %} + {%- endif %} + {%- endblock %} +
+
+ {%- endif %} +{%- endmacro -%} + + + + + {%- if builder != 'htmlhelp' %} + {%- set titlesuffix = " — " + docstitle %} + {%- endif %} + {{ title|striptags }}{{ titlesuffix }} + {%- if builder == 'web' %} + + {%- for link, type, title in page_links %} + + {%- endfor %} + {%- else %} + + + {%- endif %} + {%- if builder != 'htmlhelp' %} + + + + + {%- if use_opensearch %} + + {%- endif %} + {%- if favicon %} + + {%- endif %} + {%- endif %} +{%- block rellinks %} + {%- if hasdoc('about') %} + + {%- endif %} + + + + {%- if hasdoc('copyright') %} + + {%- endif %} + + {%- if parents %} + + {%- endif %} + {%- if next %} + + {%- endif %} + {%- if prev %} + + {%- endif %} +{%- endblock %} +{%- block extrahead %}{% endblock %} + + + +{% block logilablogo %} + +{% endblock %} + +{%- block relbar1 %}{{ relbar() }}{% endblock %} + +{%- block sidebar1 %}{# possible location for sidebar #}{% endblock %} + +{%- block document %} +
+
+ {%- if builder != 'htmlhelp' %} +
+ {%- endif %} +
+ {% block body %}{% endblock %} +
+ {%- if builder != 'htmlhelp' %} +
+ {%- endif %} +
+{%- endblock %} + +{%- block sidebar2 %}{{ sidebar() }}{% endblock %} +
+
+ +{%- block relbar2 %}{{ relbar() }}{% endblock %} + +{%- block footer %} + +{%- endblock %} + + diff -r 30f19b976857 -r 9c919a47e140 doc/book/en/01-introduction.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/01-introduction.en.txt Tue Nov 18 01:16:30 2008 +0100 @@ -0,0 +1,343 @@ +.. -*- coding: utf-8 -*- + +.. _Overview: + +Aperçu rapide de `CubicWeb` +=========================== + +`CubicWeb` nous permet de développer des instances d'applications web +basées sur un ou plusieurs `cube`. + +Ce à quoi nous réferrons en parlant de `cube` est un modèle définissant +des types de données et des vues. Un `cube` est un composant re-utilisable +regroupé avec d'autres cubes sur le système de fichiers. + +Un `instance` réferre à une installation spécifique d'un ou plusieurs cubes +où sont regroupés tous les fichiers de configuration de l'application web finale. + +Dans ce document, nous allons vous montrer comment créer un cube et l'utiliser +dans une instance pour votre application web. + +Créez votre cube +---------------- + +Après avoir installé votre environement de développement `CubicWeb`, vous pouvez +commencer à construire votre premier cube: :: + + cubicweb-ctl newcube blog + +Cela va créer dans ``/path/to/forest/cubes`` une répertoire contenant :: + + 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 + +Toute modification apportée à votre modele de données devra +etre effectué dans ce répertoire. + + + +Définissez votre schéma de données +---------------------------------- + +Le modèle de données ou schéma est au coeur d'une application `CubicWeb`. +C'est là où vous allez devoir définir le type de contenu que votre application +devra gérer. + +Votre modele de données est défini dans le fichier ``schema.py`` de votre cube +``blog`` comme suit. + +:: + + 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='?*') + +Un ``Blog`` a un titre et une description. Le titre est une chaîne +de caractères requise par la classe parente EntityType et ne doit +pas excéder 50 caractères. La description est une chaîne de +caractères sans contraintes. + +Une ``BlogEntry`` a un titre, une date de publication et du texte +étant son contenu. Le titre est une chaîne de caractères qui ne +doit pas excéder 100 caractères. La date de publication est de type Date et a +pour valeur par défaut TODAY, ce qui signifie que lorsqu'une +``BlogEntry`` sera créée, sa date de publication sera la date +courante a moins de modifier ce champ. Le texte est une chaîne de +caractères qui sera indexée en plein texte et sans contraintes. + +Une ``BlogEntry`` a aussi une relation nommée ``entry_of`` qui la +relie à un ``Blog``. La cardinalité ``?*`` signifie que BlogEntry +peut faire partie de zero a un Blog (``?`` signifie `zero ou un`) et +qu'un Blog peut avoir une infinité de BlogEntry (``*`` signifie +`n'importe quel nombre incluant zero`). +Par soucis de complétude, nous rappellerons que ``+`` signifie +`un ou plus`. + + +Créez votre instance +-------------------- + +:: + + cubicweb-ctl create blog blogdemo + +Cette commande va créer un répertoire ``~/etc/cubicweb.d/blogdemo`` +contenant tous les fichiers de configuration nécessaire au lancement +de votre application web. + +L'instance ``blogdemo`` est construite sur le cube ``blog``. + +Bienvenue dans votre application web +------------------------------------ + +Lancez votre application en exécutant : :: + + cubicweb-ctl start -D blogdemo + + +Vous pouvez à présent accéder à votre application web vous permettant de +créer des blogs et d'y poster des messages en visitant l'URL http://localhost:8080/. +Un premier formulaire d'authentification va vous être proposé. Par défaut, +l'application n'autorisera pas d'utilisateur anonyme à accéder a votre +application. Vous devrez donc utiliser l'utilisateur administrateur que +vous aurez crée lors de l'initialisation de votre base de données via +``cubicweb-ctl create``. + +.. image:: images/login-form.png + + +Une fois authentifié, vous pouvez commencer à jouer avec votre +application et créer des entités. Bravo ! + +.. image:: images/blog-demo-first-page.png + + +Rappelez-vous que pour le moment, tout a été géré par la plate-forme +`CubicWeb` et que la seule chose qui a été fournie est le schéma de +données. + +Créons des entités +------------------ + +Nous allons maintenant créer quelques entités dans notre application. + +Créez un Blog +~~~~~~~~~~~~~ + +Créons à présent quelques entités. Cliquez sur `[+]` sur la +droite du lien Blog. Appelez cette nouvelle entité Blog ``Tech-Blog`` +et tapez pour la description ``everything about technology``, +puis validez le formulaire d'édition en cliquant sur le bouton +``Validate``. + +.. image:: images/cbw-create-blog.en.png + :alt: from to create blog + +En cliquant sur le logo situé dans le coin gauche de la fenêtre, +vous allez être redirigé vers la page d'accueil. Ensuite, si vous allez +sur le lien Blog, vous devriez voir la liste des entités Blog, en particulier +celui que vous venez juste de créer ``Tech-Blog``. + +.. image:: images/cbw-list-one-blog.en.png + :alt: displaying a list of a single blog + +Si vous cliquez sur ``Tech-Blog`` vous devriez obtenir une description +détaillée, ce qui dans notre cas, n'est rien de plus que le titre +et la phrase ``everything about technology`` + +Maintenant retournons sur la page d'accueil et créons un nouveau +Blog ``MyLife`` et retournons sur la page d'accueil, puis suivons +le lien Blog et nous constatons qu'à présent deux blogs sont listés. + +.. image:: images/cbw-list-two-blog.en.png + :alt: displaying a list of two blogs + +Créons un article +~~~~~~~~~~~~~~~~~ + +Revenons sur la page d'accueil et cliquons sur `[+]` à droite du lien +`articles`. Appellons cette nouvelle entité ``Hello World`` et introduisons +un peut de texte avant de ``Valider``. Vous venez d'ajouter un article +sans avoir précisé à quel Blog il appartenait. Dans la colonne de gauche +se trouve une boite intitulé ``actions``, cliquez sur le menu ``modifier``. +Vous êtes de retour sur le formulaire d'édition de l'article que vous +venez de créer, à ceci près que ce formulaire a maintenant une nouvelle +section intitulée ``ajouter relation``. Choisissez ``entry_of`` dans ce menu, +cela va faire apparaitre une deuxième menu déroulant dans lequel vous +allez pouvoir séléctionner le Blog ``MyLife``. + +Vous auriez pu aussi, au moment où vous avez crée votre article, sélectionner +``appliquer`` au lieu de ``valider`` et le menu ``ajouter relation`` serait apparu. + +.. image:: images/cbw-add-relation-entryof.en.png + :alt: editing a blog entry to add a relation to a blog + +Validez vos modifications en cliquant sur ``Valider``. L'entité article +qui est listée contient maintenant un lien vers le Blog auquel il +appartient, ``MyLife``. + +.. image:: images/cbw-detail-one-blogentry.en.png + :alt: displaying the detailed view of a blogentry + +Rappelez-vous que pour le moment, tout a été géré par la plate-forme +`CubicWeb` et que la seule chose qui a été fournie est le schéma de +données. D'ailleurs pour obtenir une vue graphique du schéma, visitez +le lien `Application schema`` a l'URL suivante : +http://localhost:8080/view?vid=schema + +.. image:: images/cbw-schema.en.png + :alt: graphical view of the schema (aka data-model) + + +Définissez les vues de vos données +---------------------------------- + +Le principe de sélection des vues +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Une vue est une classe Python qui inclut: + + - un identifiant (tous les objets dans `CubicWeb` sont listés + dans un registre et cet identifiant est utilisé comme la clé) + + - un filtre de sélection de `result sets` auxquels il + peut s'appliquer + +`CubicWeb` fournit un ensemble de vues standard pour le type d'objet +`EntityView`. vous poubez les trouver listées dans ``cubicweb/web/views``. + +Une vue est appliquée sur un `result set` qui représente l'ensemble +des entités que nous cherchons à appliquer. `CubicWeb` utilise un +sélecteur qui permet de calculer un score et d'identifier la vue +la plus adaptée au `result set` que nous voulons afficher. La librarie +standard des sélecteurs se trouve dans ``cubicweb.common.selector`` +et une librairie des méthodes utilisées pour calculer les scores +est dans ``cubicweb.vregistry.vreq``. + + +Il est possible de définir plusieurs vues ayant le meme identifiant +et d'y associer des sélecteurs et des filtres afin de permettre à +l'application de s'adapter au mieux aux données que nous avons +besoin d'afficher. Nous verrons cela plus en détails dans :ref:`DefinitionVues`. + +On peut citer l'exemple de la vue nommée ``primary`` qui est celle utilisée +pour visualiser une entité seule. Nous allons vous montrer comment modifier +cette vue. + +Modification des vues +~~~~~~~~~~~~~~~~~~~~~ +Si vous souhaitez modifier la manière dont est rendue un article (`Blogentry`), +vous devez surcharger la vue ``primary`` définie dans le module ``views`` de +votre cube, ``cubes/blog/views.py``. + +Nous pourrions par exemple ajouter devant la date de publication un préfixe +indiquant que la date visualisée est la date de publication. + +Pour cela appliquez les modifications suivantes: + +:: + + 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:: + Lors qu'une vue est modifiée il n'est pas nécessaire de relancer + l'application. Sauvez juste le fichier Python et rechargez la page + dans votre navigateur afin de visualiser les modifications. + + +Nous pouvons voir que la date de publication est préfixée comme souhaitée. + + +.. image:: images/cbw-update-primary-view.en.png + :alt: modified primary view + + + +Le code que nous avons modifié définit une vue primaire pour une entité de +type `BlogEntry`. + +Etant donné que les vues sont appliquées sur des `result sets` et que +les `result sets` peuvent être des tableaux de données, il est indispensable +de récupérer l'entité selon ses coordonnées (row,col). + +La méthode ``self.w()`` est utilisée pour afficher des données. En particulier +dans notre exemple, nous l'utilisons pour afficher des tags HTML et des valeurs +des attributs de notre entité. + + diff -r 30f19b976857 -r 9c919a47e140 doc/book/en/02-foundation.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/02-foundation.en.txt Tue Nov 18 01:16:30 2008 +0100 @@ -0,0 +1,431 @@ +.. -*- coding: utf-8 -*- + +Fondements `CubicWeb` +===================== +Un peu d'histoire... +-------------------- + +`CubicWeb` est une plate-forme logicielle de développement d'application web +qui est développée par Logilab_ depuis 2001. + + +Entièrement développée en Python, `CubicWeb` publie des données provenant +de plusieurs sources telles que des bases de données SQL, des répertoire +LDAP et des systèmes de gestion de versions tels que subversion. + +L'interface utilisateur de `CubicWeb` a été spécialement conçue pour laisser +à l'utilisateur final toute latitude pour sélectionner puis présenter les données. +Elle permet d'explorer aisément la base de connaissances et d'afficher les +résultats avec la présentation la mieux adaptée à la tâche en cours. +La flexibilité de cette interface redonne à l'utilisateur le contrôle de +paramètres d'affichage et de présentation qui sont habituellement réservés +aux développeurs. + +Parmi les applications déjà réalisées, on dénombre un annuaire en ligne pour +le grand public (voir http://www.118000.fr/), un système de gestion d'études +numériques et de simulations pour un bureau d'études, un service de garde +partagée d'enfants (voir http://garde-partagee.atoukontact.fr/), la gestion +du développement de projets logiciels (voir http://www.logilab.org), etc. + +En 2008, `CubicWeb` a été porté pour un nouveau type de source: le datastore +de GoogleAppEngine_. + +.. _GoogleAppEngine: http://code.google.com/appengine/ + + +Architecture globale +-------------------- +.. image:: images/archi_globale.png + +**Note**: en pratique la partie cliente et la partie serveur sont +généralement intégrées dans le même processus et communiquent donc +directement, sans nécessiter des appels distants via Pyro. Il est +cependant important de retenir que ces deux parties sont disjointes +et qu'il est même possible d'en exécuter plusieurs exemplaires dans +des processus distincts pour répartir la charge globale d'un site +sur une ou plusieurs machines. + +Concepts et vocabulaire +----------------------- + +*schéma* + le schéma définit le modèle de données d'une application sous forme + d'entités et de relations, grâce à la bibliothèque `yams`_. C'est + l'élément central d'une application. Il est initialement défini sur + le système de fichiers et est stocké dans la base de données lors de + la création d'une instance. `CubicWeb` fournit un certain nombres de + types d'entités inclus systématiquement car nécessaire au noyau + `CubicWeb` et une librairie de cubes devant être inclus + explicitement le cas échéant. + +*type d'entité* + une entité est un ensemble d'attributs ; l'attribut de + base de toute entité, qui est sa clef, est l'eid + +*type de relation* + les entités sont liées entre elles par des relations. Dans `CubicWeb` + les relations sont binaires : par convention on nomme le premier terme + d'une relation son 'sujet' et le second son 'objet'. + +*type d'entité final* + les types finaux correspondent aux types de bases comme les chaînes + de caractères, les nombres entiers... Une propriété de ces types est + qu'ils ne peuvent être utilisés qu'uniquement comme objet d'une + relation. Les attributs d'une entité (non finale) sont des entités + (finales). + +*type de relation finale* + une relation est dite finale si son objet est un type final. Cela revient à + un attribut d'une entité. + +*entrepôt* + ou *repository*, c'est la partie serveur RQL de `CubicWeb`. Attention à ne pas + confondre avec un entrepôt mercurial ou encore un entrepôt debian. + +*source* + une source de données est un conteneur de données quelquonque (SGBD, annuaire + LDAP...) intégré par l'entrepôt `CubicWeb`. Un entrepôt possède au moins une source + dite "system" contenant le schéma de l'application, l'index plein-texte et + d'autres informations vitales au système. + +*configuration* + il est possible de créer différentes configurations pour une instance : + + - ``repository`` : entrepôt uniquement, accessible pour les clients via Pyro + - ``twisted`` : interface web uniquement, communiquant avec un entrepôt via Pyro + - ``all-in-one`` : interface web et entrepôt dans un seul processus. L'entrepôt + peut ou non être accessible via Pyro + +*cube* + un cube est un modèle regroupant un ou plusieurs types de données et/ou + des vues afin de fournir une fonctionalité précise, ou une application `CubicWeb` + complète utilisant éventuellement d'autres cube. Les différents + cubes disponibles sur une machine sont installés dans + `/path/to/forest/cubicweb/cubes` + +*instance* + une instance est une installation spécifique d'un cube. Sont regroupes + dans une instance tous les fichiers de configurations necessaires au bon + fonctionnement de votre application web. Elle referrera au(x) cube(s) sur + le(s)quel(s) votre application se base. + Par exemple intranet/jpl et logilab.org sont deux instances du cube jpl que + nous avons developpes en interne. + Les instances sont définies dans le répertoire `~/etc/cubicweb.d`. + +*application* + le mot application est utilisé parfois pour parler d'une instance et parfois + d'un composant, en fonction du contexte... Mieux vaut donc éviter de + l'utiliser et utiliser plutôt *cube* et *instance*. + +*result set* + objet encaspulant les résultats d'une requête RQL et des informations sur + cette requête. + +*Pyro* + `Python Remote Object`_, système d'objets distribués pur Python similaire à + Java's RMI (Remote Method Invocation), pouvant être utilisé pour la + communication entre la partie web du framework et l'entrepôt RQL. + +.. _`Python Remote Object`: http://pyro.sourceforge.net/ +.. _`yams`: http://www.logilab.org/project/name/yams/ + + +Moteur `CubicWeb` +----------------- + +Le moteur web de `CubicWeb` consiste en quelques classes gérant un ensemble d'objets +chargés dynamiquement au lancement de `CubicWeb`. Ce sont ces objets dynamiques, issus +du modèle ou de la librairie, qui construisent le site web final. Les différents +composants dynamiques sont par exemple : + +* coté client et serveur + + - les définitions d'entités, contenant la logique permettant la manipulation des + données de l'application + +* coté client + + - les *vues* , ou encore plus spécifiquement + + - les boites + - l'en-tête et le pied de page + - les formulaires + - les gabarits de pages + + - les *actions* + - les *controleurs* + +* coté serveur + + - les crochets de notification + - les vues de notification + +Les différents composants du moteur sont : + +* un frontal web (seul twisted disponible pour le moment), transparent du point + de vue des objets dynamiques +* un objet encapsulant la configuration +* un `vregistry` (`cubicweb.cwvreg`) contenant les objets chargés dynamiquements + +Détail de la procédure d'enregistrement +--------------------------------------- +Au démarage le `vregistry` ou base de registres inspecte un certain nombre de +répertoires à la recherche de définition de classes "compatible". Après une +procédure d'enregistrement les objets sont affectés dans différents registres +afin d'être ensuite séléctionné dynamiquement pendant le fonctionnement de +l'application. + +La classe de base de tout ces objets est la classe `AppRsetObject` (module +`cubicweb.common.appobject`). + + +API Python/RQL +-------------- + +Inspiré de la db-api standard, avec un object Connection possédant les méthodes +cursor, rollback et commit principalement. La méthode importante est la méthode +`execute` du curseur : + +`execute(rqlstring, args=None, eid_key=None, build_descr=True)` + +:rqlstring: la requête rql à éxécuter (unicode) +:args: si la requête contient des substitutions, un dictionnaire contenant les + valeurs à utiliser +:eid_key: + un détail d'implémentation du cache de requêtes RQL fait que si une substitution est + utilisée pour introduire un eid *levant des ambiguités dans la résolution de + type de la requête*, il faut spécifier par cet argument la clé correspondante + dans le dictionnaire + +C'est l'objet Connection qui possède les méthodes classiques `commit` et +`rollback`. Vous ne *devriez jamais avoir à les utiliser* lors du développement +d'interface web sur la base du framework `CubicWeb` étant donné que la fin de la +transaction est déterminée par celui-ci en fonction du succès d'éxécution de la +requête. + +NOTE : lors de l'éxécution de requêtes de modification (SET,INSERT,DELETE), si une +requête génère une erreur liée à la sécurité, un rollback est systématiquement +effectuée sur la transaction courante. + + +La classe `Request` (`cubicweb.web`) +------------------------------------ +Une instance de requête est créée lorsque une requête HTTP est transmise au +serveur web. Elle contient des informations telles que les paramètres de +formulaires, l'utilisateur connecté, etc. + +**De manière plus générale une requête représente une demande d'un utilisateur, +que se soit par HTTP ou non (on parle également de requête rql coté serveur par +exemple)** + +Une instance de la classe `Request` possède les attributs : + +* `user`, instance de`cubicweb.common.utils.User` correspondant à l'utilisateur + connecté +* `form`, dictionaire contenant les valeurs de formulaire web +* `encoding`, l'encodage de caractère à utiliser dans la réponse + +Mais encore : + +:Gestion des données de session: + * `session_data()`, retourne un dictionaire contenant l'intégralité des + données de la session + * `get_session_data(key, default=None)`, retourne la valeur associée à + la clé ou la valeur `default` si la clé n'est pas définie + * `set_session_data(key, value)`, associe une valeur à une clé + * `del_session_data(key)`, supprime la valeur associé à une clé + + +:Gestion de cookie: + * `get_cookie()`, retourne un dictionnaire contenant la valeur de l'entête + HTTP 'Cookie' + * `set_cookie(cookie, key, maxage=300)`, ajoute un en-tête HTTP `Set-Cookie`, + avec une durée de vie 5 minutes par défault (`maxage` = None donne un cooke + *de session"* expirant quand l'utilisateur ferme son navigateur + * `remove_cookie(cookie, key)`, fait expirer une valeur + +:Gestion d'URL: + * `url()`, retourne l'url complète de la requête HTTP + * `base_url()`, retourne l'url de la racine de l'application + * `relative_path()`, retourne chemin relatif de la requête + +:Et encore...: + * `set_content_type(content_type, filename=None)`, place l'en-tête HTTP + 'Content-Type' + * `get_header(header)`, retourne la valeur associé à un en-tête HTTP + arbitraire de la requête + * `set_header(header, value)`, ajoute un en-tête HTTP arbitraire dans la + réponse + * `cursor()` retourne un curseur RQL sur la session + * `execute(*args, **kwargs)`, raccourci vers .cursor().execute() + * `property_value(key)`, gestion des propriétés (`EProperty`) + * le dictionaire `data` pour stocker des données pour partager de + l'information entre les composants *durant l'éxécution de la requête*. + +A noter que cette classe est en réalité abstraite et qu'une implémentation +concrète sera fournie par le *frontend* web utilisé (en l'occurent *twisted* +aujourd'hui). Enfin pour les vues ou autres qui sont éxécutés coté serveur, +la majeure partie de l'interface de `Request` est définie sur la session +associée au client. + + +La classe `AppObject` +--------------------- + +En général : + +* on n'hérite pas directement des cette classe mais plutôt d'une classe + plus spécifique comme par exemple `AnyEntity`, `EntityView`, `AnyRsetView`, + `Action`... + +* pour être enregistrable, un classe fille doit définir son registre (attribut + `__registry__`) et son identifiant (attribut `id`). Généralement on n'a pas à + s'occuper du registre, uniquement de l'identifiant `id` :) + +On trouve un certain nombre d'attributs et de méthodes définis dans cette classe +et donc commune à tous les objets de l'application : + +A l'enregistrement, les attributs suivants sont ajoutés dynamiquement aux +*classes* filles: + +* `vreg`, le `vregistry` de l'application +* `schema`, le schéma de l'application +* `config`, la configuration de l'application + +On trouve également sur les instances les attributs : + +* `req`, instance de `Request` +* `rset`, le "result set" associé à l'objet le cas échéant +* `cursor`, curseur rql sur la session + + +:Gestion d'URL: + * `build_url(method=None, **kwargs)`, retourne une URL absolue construites à + partir des arguments donnés. Le *controleur* devant gérer la réponse + peut-être spécifié via l'argument spécial `method` (le branchement est + théoriquement bien effectué automatiquement :). + + * `datadir_url()`, retourne l'url du répertoire de données de l'application + (contenant les fichiers statiques tels que les images, css, js...) + + * `base_url()`, raccourci sur `req.base_url()` + + * `url_quote(value)`, version *unicode safe* de de la fonction `urllib.quote` + +:Manipulation de données: + + * `etype_rset(etype, size=1)`, raccourci vers `vreg.etype_rset()` + + * `eid_rset(eid, rql=None, descr=True)`, retourne un objet result set pour + l'eid donné + * `entity(row, col=0)`, retourne l'entité correspondant à la position données + du "result set" associé à l'objet + + * `complete_entity(row, col=0, skip_bytes=True)`, équivalent à `entity` mais + appelle également la méthode `complete()` sur l'entité avant de la retourner + +:Formattage de données: + * `format_date(date, date_format=None, time=False)` + * `format_time(time)`, + +:Et encore...: + + * `external_resource(rid, default=_MARKER)`, accède à une valeur définie dans + le fichier de configuration `external_resource` + + * `tal_render(template, variables)`, + + +**NOTE IMPORTANTE** +Lorsqu'on hérite d'`AppObject` (même indirectement), il faut **toujours** +utiliser **super()** pour récupérer les méthodes et attributs des classes +parentes, et pas passer par l'identifiant de classe parente directement. +(sous peine de tomber sur des bugs bizarres lors du rechargement automatique +des vues). Par exemple, plutôt que d'écrire:: + + class Truc(PrimaryView): + def f(self, arg1): + PrimaryView.f(self, arg1) + +Il faut écrire:: + + class Truc(PrimaryView): + def f(self, arg1): + super(Truc, self).f(arg1) + + + + + + +Structure standard d'un cube +---------------------------- + +Un cube complexe est structuré selon le modèle suivant : + +:: + + moncube/ + | + |-- schema.py + | + |-- entities/ + | + |-- sobjects/ + | + |-- views/ + | + |-- test/ + | + |-- i18n/ + | + |-- data/ + | + |-- migration/ + | |- postcreate.py + | \- depends.map + | + |-- debian/ + | + \-- __pkginfo__.py + +On peut utiliser de simple module python plutôt que des répertoires (packages), +par ex.: + +:: + + moncube/ + | + |-- entities.py + |-- hooks.py + \-- views.py + + +où : + +* ``schema`` contient la définition du schéma (coté serveur uniquement) +* ``entities`` contient les définitions d'entités (coté serveur et interface web) +* ``sobjects`` contient les crochets et/ou vues de notification (coté serveur + uniquement) +* ``views`` contient les différents composants de l'interface web (coté interface + web uniquement) +* ``test`` contient les tests spécifiques à l'application (non installé) +* ``i18n`` contient les catalogues de messages pour les langues supportées (coté + serveur et interface web) +* ``data`` contient des fichiers de données arbitraires servis statiquement + (images, css, fichiers javascripts)... (coté interface web uniquement) +* ``migration`` contient le fichier d'initialisation de nouvelles instances + (``postcreate.py``) et générallement un fichier donnant les dépendances `CubicWeb` du + composant en fonction de la version de celui-ci (``depends.map``) +* ``debian`` contient les fichiers contrôlant le packaging debian (vous y + trouverez les fichiers classiques ``control``, ``rules``, ``changelog``... (non + installé) +* le fichier ``__pkginfo__.py`` donne un certain nombre de méta-données sur le + composant, notamment le nom de la distribution et la version courante (coté + serveur et interface web) ou encore les sous-cubes utilisés par ce + cube. + +Le strict minimum étant : + +* le fichier ``__pkginfo__.py`` +* la définition du schéma diff -r 30f19b976857 -r 9c919a47e140 doc/book/en/03-01-bis-installation.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/03-01-bis-installation.en.txt Tue Nov 18 01:16:30 2008 +0100 @@ -0,0 +1,79 @@ +.. -*- coding: utf-8 -*- + +.. _installationGAE: + +Installation de `CubicWeb` pour GoogleAppEngine +=============================================== + +Qu'est-ce que `LAX` ? +======================= + +`LAX` (Logilab Appengine eXtension) est un framework d'application +web basé sur `Google AppEngine`. + +`LAX` est un portage de la partie web de la plate-forme applicative +développée par Logilab depuis 2001. Cette plate-forme publie des +données tirées de bases SQL, d'annuaires LDAP et de systèmes de +gestion de version. En avril 2008, elle a été portée pour fonctionner +sur le "datastore" de `Google AppEngine`. + +XXX: faire un parallèle entre Django/GAE et LAX/GAE + + +Téléchargement des sources +========================== + +- Les sources de `Google AppEngine` peuvent être récupérées à l'adresse + suivante : http://code.google.com/appengine/downloads.html + +- Les sources de `LAX` se trouvent à l'adresse suivante : + http://lax.logilab.org/ + + +Installation +============ + +Une fois décompactée, l'archive `lax-0.1.0-alpha.tar.gz`, on obtient +l'arborescence suivante:: + + . + |-- app.yaml + |-- custom.py + |-- data + |-- ginco/ + |-- i18n/ + |-- logilab/ + |-- main.py + |-- mx/ + |-- rql/ + |-- schema.py + |-- simplejson/ + |-- tools/ + | |-- generate_schema_img.py + | `-- i18ncompile.py + |-- views.py + |-- yams/ + `-- yapps/ + + +On retrouve le squelette d'une application web de `Google AppEngine` +(fichiers ``app.yaml``, ``main.py`` en particulier) avec les dépendances +supplémentaires nécessaires à l'utilisation du framework `LAX` + + +Lancement de l'application de base +================================== + +Plusieurs répertoires doivent être accessibles via la variable +d'environnement ``PYTHONPATH`` :: + + $ export PYTHONPATH=/path/to/google_appengine:/path/to/google_appengine/lib/yaml/lib:/path/to/myapp/ + +Le répertoire yaml n'est nécessaire que pour le lancement des scripts +qui se trouvent dans lax/tools et pour l'exécution des tests unitaires. + +Pour démarrer:: + + $ python /path/to/google_appengine/dev_appserver.py /path/to/lax + + diff -r 30f19b976857 -r 9c919a47e140 doc/book/en/03-01-installation.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/03-01-installation.en.txt Tue Nov 18 01:16:30 2008 +0100 @@ -0,0 +1,150 @@ +.. -*- coding: utf-8 -*- + +Installation +============ + +Installation de Cubicweb et de ses dépendances +---------------------------------------------- + +`CubicWeb` est disponible via un entrepôt Mercurial utilisant l'extension forest. +Vous devez donc dans un premier temps vous assurer que Mercurial est bien installé +et que vous avez l'extension forest. + +Installation de Mercurial +````````````````````````` +Veuillez vous référer a la documentation en ligne du projet Mercurial_. + +.. _Mercurial: http://www.selenic.com/mercurial/wiki/ + +Installation de l'extension forest +`````````````````````````````````` +Dans un premier temps, récupérez une copie des sources via: http://hg.akoha.org/hgforest/. +Ensuite, ajoutez a votre ``~/.hgrc`` les lignes suivantes :: + + [extensions] + hgext.forest= + # or, if forest.py is not in the hgext dir: + # forest=/path/to/forest.py + +Récupération des sources +```````````````````````` +Clonez la foret dans votre répertoire de travail. + +:: + + hg fclone http://www.logilab.org/hg/forests/cubicweb + +.. note:: + Nous vous recommandons de créer un lien symbolique vers l'outil ``cubicweb-ctl`` + que vous allez etre amené a utiliser. + + :: + + $ ln -s /path/to/forest/cubicweb/bin/cubicweb-ctl ~/bin + +Installation de Postgres +```````````````````````` +Veuillez vous référer a la documentation en ligne du projet Postgres_. + +.. _Postgres: http://www.postgresql.org/ + +Vous allez devoir installer les trois paquets suivants: `postgres-8.3`, +`postgres-contrib-8.3` and `postgresql-plpython-8.3`. + + +On pourra ensuite installer les paquets suivants : + +* `pyro` si vous voulez que l'entrepôt soit accessible via Pyro ou si le client + et le serveur ne sont pas sur la même machine (auquel cas il faut installer ce + paquet sur les machines clientes et serveur) + +* `python-ldap` si vous voulez utiliser une source ldap sur le serveur + + +.. _ConfigurationEnv: + +Configuration de l'environnement +-------------------------------- + +[FIXME] +Ces variables ne sont plus requises pour le bon fonctionnement de `CubicWeb`, non? +A part rajouter la foret dans le PYTHONPATH, rien de plus ne doit etre fait? + +Mettez à jour votre variable d'environemment PYTHONPATH afin d'y ajouter +le chemin d'acces a votre foret ``cubicweb``. + +Ajouter les lignes suivantes à son `.bashrc` ou `.bash_profile` pour configurer +votre environnement de développement :: + + export PYTHONPATH=/full/path/to/cubicweb-forest + + export ERUDI_REGISTRY=~/etc/erudi.d/ + export ERUDI_TEMPLATES=~/hg/ + export ERUDI_RUNTIME=/tmp/ + +Cela suppose que le composant erudi que vous développez est dans un +sous-répertoire de *~/hg/* et que vous avez créé le répertoire *~/etc/erudi.d/* +pour que `cubicweb-ctl` y place vos instances de test. + +.. _ConfigurationPostgres: + +Configuration Postgres +---------------------- +* Tout d'abord vous devez initialiser votre base de données Postgres via la + commande ``initidb``. + :: + + $ initdb -D /path/to/pgsql + + Une fois ces paquets installés vous pouvez lancer votre server de base de + données Postgres avec la commande suivante: :: + + $ postgres -D /path/to/psql + + Si vous ne pouvez exécuter cette commande pour des raisons de permissions + assurez-vous que votre utilisateur a droit d'écriture sur les la base de données. + + :: + + $ chown username /path/to/pgsql + +* Création d'un super utilisateur pour la création d'instance (**root**) :: + + createuser -s username + + Initialisez le mot de passe de ce superutilisateur ``username`` via + ``su - postgres`` puis ``psql``. + + Un mot de passe de connection pour cet utilisateur vous sera demandé. Il + faudra utiliser ce login / mot de passe à la création d'instance via + `cubicweb-ctl` + +[XXX] +Est-ce que ces etapes sont vraiment necessaires? +sand : lors de l'installation de ma bdd cela n'a pas ete fait +et il semble que tout aille bien. Doit etre verifie avec les experts. + +* installation des extensions pour l'index plein texte :: + + cat /usr/share/postgresql/8.1/contrib/tsearch2.sql | psql -U pgadmin template1 + +* installation du langage plpythonu par défaut :: + + createlang -U pgadmin plpythonu template1 + + +Configuration Pyro +------------------ +Si vous utilisez Pyro, il est nécessaire d'avoir un serveur de noms Pyro +tournant sur votre réseau (par défaut celui-ci est repéré par une requête +broadcast). Pour cela il faut soit : + +* le lancer à la main avant le démarrage de erudi avec la commande `pyro-ns` + +* le lancer à la main avant le démarrage de erudi sous forme d'un serveur avec + la commande `pyro-nsd start` + +* éditer le fichier */etc/default/pyro-nsd* pour que le serveur de nom pyro soit + lancé automatiquement au démarrage de la machine + + diff -r 30f19b976857 -r 9c919a47e140 doc/book/en/03-02-create-instance.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/03-02-create-instance.en.txt Tue Nov 18 01:16:30 2008 +0100 @@ -0,0 +1,127 @@ +.. -*- coding: utf-8 -*- + +=================================== +Creation de votre premiere instance +=================================== + + +Qu'est-ce qu'une instance? +========================== + +Une instance CubicWeb consiste en un dossier situe dans ``~/etc/cubicweb.d`` +qui permettra de lancer une application web. Une instance est cree a partir +d'un ou plusieurs cubes. + +Nous recommandons de ne pas definir de schema, entites ou vues dans l'instance +meme si cela est possible dans un but de re-utilisabilite des entities et de leurs +vues. Nous conseillons plutot de developper des cubes qui pourront par la suite +etre utilises dans d'autres instances (approche modulaire). + +L'instance n'est qu'un conteneur referrant a des cubes et a des parametres +des configuration de l'application web. + +Qu'est-ce qu'un cube? +===================== + +Un cube definit des entities, leur vues, leur schemas et leur workflow +dans un repertoire independant situe dans ``/path/to/forest/cubicweb/cubes/``. + +Lors de la creation d'une instance, vous avez la possibilite de lister +le ou les cubes que votre instance va utiliser. Utiliser un cube signifie +avoir a disposition dans votre instance les entites definies dans le schema +de votre cube ainsi que les vues et les workflows. + + +.. note:: + Les commandes utilisees ci-apres sont detaillees dans la section + dediee a :ref:`cubicweb-ctl`. + + +Création d'un cube +================== + +Commençons par créer un squelette qui nous servira de base au développement de +notre cube ou application :: + + cd ~/hg + + cubicweb-ctl newtemplate moncube + + # répondre aux questions + hg init moncube + cd moncube + hg add . + hg ci + +A partir de là si tout va bien, votre cube devrait être affiché par +`cubicweb-ctl list` dans la section *Available components*, si ce n'est pas le cas +revoir la section :ref:`ConfigurationEnv`. + + +Pour utiliser un cube, il faut le mentionner dans la variable +__use__ du fichier __pkginfo__ de l'application. Cette variable +contrôle à la fois le packaging de l'application (dépendances gérées +par les utilitaires système comme les outils APT) et les composants +effectivement utilisables lors de la création de la base +(import_erschema('Moncomposant') ne fonctionne pas sinon). + +Création d'une instance de développement +======================================== + +Maintenant que nous avons notre squelette de modèle, on peut en créer une +instance afin de voir ce que tout ça donne dans un simple navigateur web. +Nous allons utiliser une configuration `all-in-one` afin de simplifier les +choses :: + + cubicweb-ctl create -c all-in-one moncube moninstance + +Une série de questions vont être posées, la réponse par défaut est généralement +suffisante. Vous pourrez de toute façon modifier la configuration par la suite +en éditant les fichiers générés. Lorsqu'un login/mot de passe d'accès au sgbd +vous est demandé, il est recommandé d'utiliser l'utilisateur créé lors de la +:ref:`ConfigurationPostgres`. + +Il est important de distinguer ici l'utilisateur utilisé pour accéder au sgbd, +et l'utilisateur utilisé pour s'authentifier dans l'application cubicweb. Lorsque +l'application cubicweb démarre, elle utilise le login/mot de passe sgdb pour +récupérer le schéma et gérer les transactions bas-niveau. En revanche, lorsque +`cubicweb-ctl create` vous demande un login/mot de passe `manager` pour cubicweb, il +s'agit d'un utilisateur qui sera créé dans l'application `cubicweb` pour pouvoir +s'y connecter dans un premier temps et l'administrer. Il sera par la suite possible +de créer des utilisateurs différents pour l'application. + +A l'issue de cette commande, la définition de votre instance se trouve dans +*~/etc/cubicweb.d/moninstance/*. Pour la lancer, il suffit de taper :: + + cubicweb-ctl start -D moninstance + +L'option `-D` indique le *debug mode* : l'instance ne passe pas en mode serveur +et ne se déconnecte pas du terminal, ce qui simplifie le dépannage en cas de non +démarrage de l'instance. Vous pouvez ensuite allez voir ce que ça donne en +pointant votre navigateur sur l'url `http://localhost:8080` (le n° de port +dépend de votre configuration). Pour vous authentifier vous pouvez utiliser le +login/mot de passe administrateur que vous avez spécifié lors de la création de +l'instance. + +Pour arrêter l'instance, un Ctrl-C dans la fenêtre où vous l'avez lancé +suffit. Si l'option `-D` a été omise, il faut taper :: + + cubicweb-ctl stop moninstance + +Voilà, tout est en place pour démarrer le développement du modèle... + + +Utilisation de cubicweb-liveserver +---------------------------------- + +Afin de tester rapidement un nouveau cube, on peut également +utiliser le script `cubicweb-liveserver` qui permet de créer une +application en mémoire (utilisant une base de données SQLite par +défaut) et la rendre accessible via un serveur web:: + + cubicweb-ctl live-server moncomposant + +ou bien, pour utiliser une base de données existante (SQLite ou postgres):: + + cubicweb-ctl live-server -s monfichier_sources moncomposant + diff -r 30f19b976857 -r 9c919a47e140 doc/book/en/03-03-cubicweb-ctl.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/03-03-cubicweb-ctl.en.txt Tue Nov 18 01:16:30 2008 +0100 @@ -0,0 +1,121 @@ +.. -*- coding: utf-8 -*- + +.. _cubicweb-ctl: + +L'outil `cubicweb-ctl` +====================== +`cubicweb-ctl` est le couteau suisse pour la gestion d'instances CubicWeb. +La syntaxe générale est :: + + cubicweb-ctl [options commande] + +Pour voir les commandes disponibles :: + + cubicweb-ctl + cubicweb-ctl --help + +A noter que les commandes disponibles varient en fonction des parties d'CubicWeb +qui sont installées. + +Pour voir l'aide pour une commande spécifiques :: + + cubicweb-ctl --help + +Commandes pour la création d'un cube +------------------------------------ +* ``newcube``, crée un nouveau cube sur le système de fichiers + à partir du nom passé en paramètre. Cette commande crée le cube à partir + d'une squelette d'application, incluant également les fichiers pour le + packaging debian) + +Commandes pour la création d'une instance +----------------------------------------- +* ``create``, crée les fichiers de configuration d'une instance +* ``db-create``, crée la base de données système d'une instance (tables et + extensions uniquement) +* ``db-init``, initialise la base de données système d'une instance (schéma, + groupes, utilisateurs, workflows...) + +Par défaut ces trois commandes sont enchainées. + +Commande pour la création d'une instance pour Google App Engine +--------------------------------------------------------------- +* ``newgapp``, crée les fichiers de configuration d'une instance + +Cette commande doit être suivie de l'exécution de commandes +permettant l'initialisation de la base de données spécifique à +Google App Engine, appellée ``datastore``. + +Pour plus de détails veuillez vous référer à `LAX <>`_ + + +Commandes pour le lancement des instances +----------------------------------------- +* ``start``, démarre une, plusieurs, ou toutes les instances +* ``stop``, arrêt une, plusieurs, ou toutes les instances +* ``restart``, redémarre une, plusieurs, ou toutes les instances +* ``status``, donne l'état des instances + +Commandes pour la maintenance des instances +------------------------------------------- +* ``upgrade``, lance la migration d'instance(s) existante(s) lorsqu'une nouvelle + version d'CubicWeb ou du composant est installée +* ``shell``, ouvre un shell de migration pour la maintenance manuelle d'une instance +* ``db-dump``, crée un dump de la base de données système +* ``db-restore``, restore un dump de la base de données système +* ``db-check``, vérifie l'intégrité des données d'une instance. Si la correction + automatique est activée, il est conseillé de faire un dump avant cette + opération +* ``schema-sync``, , synchronise le schéma persistent d'une instance avec le schéma + de l'application. Il est conseillé de faire un dump avant cette opération + +Commandes pour la maintenance des catalogues i18n +------------------------------------------------- +* ``i18nlibupdate``, regénère les catalogues de messages de la librairie CubicWeb +* ``i18nupdate``, regénère les catalogues de messages d'un composant +* ``i18ncompile``, recompile les catalogues de messages d'une instance. Cela est + effectué automatiquement lors d'une upgrade + +Cf :ref:`Internationalisation`. + +Autres commandes +---------------- +* ``list``, donne la liste des configurations, des composants et des instances + disponibles +* ``delete``, supprime une instance (fichiers de configuration et base de données) + + + +Exemples +-------- + +Creation d'une instance a partir de cube existant +````````````````````````````````````````````````` + +Afin de creer une instance a partir d'un cube existant, executez la commande +suivant :: + + cubicweb-ctl create + +Cette commande va creer les fichiers de configuration d'une instance dans +``~/etc/cubicweb.d/``. +L'outil ``cubicweb-ctl`` va vous autoriser a executer au sein de ``create`` +les commandes ``db-create`` et ``db-init`` afin de completer la creation de +votre instance en une seule commande. + +Si vous decidez de ne pas le faire lorsque ``cubicweb-ctl create`` vous le +propose, alors n'oubliez pas de lancer ces commandes (``cubicweb-ctl db-create``, +``cubicweb-ctl db-init`` ) par la suite, sinon +votre installation ne sera pas complete. + + +Creation d'une instance a partir d'une nouveau cube +``````````````````````````````````````````````````` + +Creez avant tout votre nouveau cube :: + + cubicweb-ctl newcube + +Cette commande va creer un nouveau cube dans ``/path/to/forest/cubicweb/cubes/``. + + diff -r 30f19b976857 -r 9c919a47e140 doc/book/en/03-04-mercurial.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/03-04-mercurial.en.txt Tue Nov 18 01:16:30 2008 +0100 @@ -0,0 +1,112 @@ +.. -*- coding: utf-8 -*- + +Présentation de Mercurial +------------------------- + +Introduction +```````````` +Mercurial_ gère un ensemble distribué d'entrepôts contenant des arbres de +révisions (chaque révision indique les changements à effectuer pour obtenir la +version suivante, et ainsi de suite). Localement, on dispose d'un entrepôt +contenant un arbre de révisions, et d'un répertoire de travail. Il est possible +de mettre dans son répertoire de travail, une des versions issue de son entrepôt +local, de la modifier puis de la verser dans son entrepôt. Il est également +possible de récuprer dans son entrepôt local des révisions venant d'un autre +entrepôt, ou d'exporter ses propres révisions depuis son entrepôt local vers un +autre entrepôt. + +A noter que contrairement à CVS/Subversion, on crée généralement un entrepôt par +projet à gérer. + +Lors d'un développement collaboratif, on crée généralement un entrepôt central +accessible à tout les développeurs du projet. Ces entrepôts centraux servent de +référence. Selon ses besoins, chacun peut ensuite disposer d'un entrepôt local, +qu'il faudra penser à synchroniser avec l'entrepôt central de temps à autre. + + +Principales commandes +````````````````````` +* Créer un entrepôt local :: + + hg clone ssh://orion//home/src/prive/rep + +* Voir le contenu de l'entrepôt local (outil graphique en Tk) :: + + hg view + +* Ajouter un sous-répertoire ou un fichier dans le répertoire courant :: + + hg add rep + +* Placer dans son répertoire de travail une révision spécifique (ou la dernière + revision) issue de l'entrepôt local :: + + hg update [identifiant-revision] + hg up [identifiant-revision] + +* Récupérer dans son entrepôt local, l'arbre de révisions contenu dans un + entrepôt distant (cette opération ne modifie pas le répertoire local) :: + + hg pull ssh://orion//home/src/prive/rep + hg pull -u ssh://orion//home/src/prive/rep # équivalent à pull + update + +* Voir quelles sont les têtes de branches de l'entrepôt local si un `pull` a + tiré une nouvelle branche :: + + hg heads + +* Verser le répertoire de travail dans l'entrepôt local (et créer une nouvelle + révision) :: + + hg commit + hg ci + +* Fusionner, avec la révision mère du répertoire local, une autre révision issue + de l'entrepôt local (la nouvelle révision qui en résultera aura alors deux + révisions mères) :: + + hg merge identifiant-revision + +* Exporter dans un entrepôt distant, l'arbre de révisions contenu dans son + entrepôt local (cette opération ne modifie pas le répertoire local) :: + + hg push ssh://orion//home/src/prive/rep + +* Voir quelle sont les révisions locales non présentes dans un autre entrepôt :: + + hg outgoing ssh://orion//home/src/prive/rep + +* Voir quelle sont les révisions d'un autre entrepôt non présentes localement :: + + hg incoming ssh://orion//home/src/prive/rep + +* Voir quelle est la révision issue de l'entrepôt local qui a été sortie dans le + répertoire de travail et modifiée :: + + hg parent + +* Voir les différences entre le répertoire de travail et la révision mère de + l'entrepôt local, éventuellement permettant de les verser dans l'entrepôt + local :: + + hg diff + hg commit-tool + hg ct + + +Bonnes pratiques +```````````````` +* penser à faire un `hg pull -u` régulièrement et particulièrement avant de + faire un `hg commit` + +* penser à faire un `hg push` lorsque votre entrepôt contient une version + relativement stable de vos modifications + +* si un `hg pull -u` a créé une nouvelle tête de branche : + + 1. identifier l'identifiant de celle-ci avec `hg head` + 2. fusionner avec `hg merge` + 3. `hg ci` + 4. `hg push` + +.. _Mercurial: http://www.selenic.com/mercurial/ diff -r 30f19b976857 -r 9c919a47e140 doc/book/en/03-setup.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/03-setup.en.txt Tue Nov 18 01:16:30 2008 +0100 @@ -0,0 +1,15 @@ +.. -*- coding: utf-8 -*- + +.. _MiseEnPlaceEnv: + +Installation et mise en place d'un environnement `CubicWeb` +=========================================================== + +.. toctree:: + :maxdepth: 1 + + 03-01-installation.en.txt + 03-02-create-instance.en.txt + 03-03-cubicweb-ctl.en.txt + 03-04-mercurial.en.txt + diff -r 30f19b976857 -r 9c919a47e140 doc/book/en/04-01-schema-stdlib.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/04-01-schema-stdlib.en.txt Tue Nov 18 01:16:30 2008 +0100 @@ -0,0 +1,70 @@ +.. -*- coding: utf-8 -*- + +Schémas prédéfinies dans la librairie +------------------------------------- + +La librairie définit un certain nombre de schémas d'entités nécessaires +au système ou bien couramment utilisées dans les application `cubicweb`. +Vous pouvez bien entendu étendre ces schémas au besoin. + + +Schémas "systèmes" +`````````````````` + +* `EUser`, utilisateurs du système +* `EGroup`, groupes d'utilisateurs +* `EEType`, types d'entité +* `ERType`, types de relation + +* `State`, état d'un workflow +* `Transition`, transition d'un workflow +* `TrInfo`, enregistrement d'un passage de transition pour une entité + +* `EmailAddress`, adresse électronique, utilisé par le système de notification + pour les utilisateurs et par d'autres schéma optionnels + +* `EProperty`, utilisé pour configurer l'application +* `EPermission`, utilisé pour configurer la sécurité de l'application + +* `Card`, fiche documentaire générique +* `Bookmark`, un type d'entité utilisé pour permetter à un utilisateur de + personnaliser ses liens de navigation dans l'application. + + +Composants de la librairie +`````````````````````````` +Une application est construite sur la base de plusieurs composants de base. +Parmi les composants de base disponible, on trouve par exemple : + +* `ecomment`, fournit le type d'entité `Comment` permettant de commenter les + entités du site + +* `emailinglist`, fournit le type d'entité `Mailinglist` regroupant des + informations sur une liste de discussion + +* `efile`, fournit les types d'entités `File` et `Image` utilisés pour + représenter des fichiers (texte ou binaire) avec quelques données + supplémentaires comme le type MIME ou l'encodage le cas échéant (). + +* `elink`, fournit le type d'entité lien internet (`Link`) + +* `eblog`, fournit le type d'entité weblog (`Blog`) + +* `eperson`, fournit le type d'entité personne physique (`Person`) + +* `eaddressbook`, fournit les types d'entités utilisés pour représenter des n° + de téléphone (`PhoneNumber`) et des adresses postales (`PostalAddress`) + +* `eclasstags`, système de classfication à base d'étiquettes (`Tag`) + +* `eclassfolders`, système de classification à base de dossiers hiérarchiques + destinés à créer des rubriques de navigation (`Folder`) + +* `eemail`, gestion d'archives de courriers électroniques (`Email`, `Emailpart`, + `Emailthread`) + +* `ebasket`, gestion de paniers (`Basket`) permettant de regrouper des entités + +Pour déclarer l'utilisation d'un composant, une fois celui-ci installé, ajoutez +le nom du composant à la variable `__use__` du fichier `__pkginfo__.py` de +votre propre composant. diff -r 30f19b976857 -r 9c919a47e140 doc/book/en/04-02-schema-definition.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/04-02-schema-definition.en.txt Tue Nov 18 01:16:30 2008 +0100 @@ -0,0 +1,348 @@ +.. -*- coding: utf-8 -*- + +Définition d'un type d'entité +----------------------------- + +Un type d'entité est définit par une classe python héritant de `EntityType`. Le +nom de la classe correspond au nom du type. Ensuite le corps de la classe +contient la description des attributs et des relations pour ce type d'entité, +par exemple :: + + class Personne(EntityType): + """une personne avec les propriétés et relations nécessaires à mon + application""" + + nom = String(required=True, fulltextindexed=True) + prenom = String(required=True, fulltextindexed=True) + civilite = String(vocabulary=('M', 'Mme', 'Mlle')) + date_naiss = Date() + travaille_pour = SubjectRelation('Company', cardinality='?*') + +* le nom de l'attribut python correspond au nom de l'attribut ou de la relation + dans cubicweb. + +* tout les types de bases sont disponibles nativement : `String`, `Int`, `Float`, + `Boolean`, `Date`, `Datetime`, `Time`, `Byte`. + +* Chaque type d'entité a au moins les méta-relations suivantes : + + - `eid` (`Int`) + + - `creation_date` (`Datetime`) + + - `modification_date` (`Datetime`) + + - `created_by` (`EUser`) (quel utilisateur a créé l'entité) + + - `owned_by` (`EUser`) (à qui appartient l'entité, par défaut le + créateur mais pas forcément et il peut exister plusieurs propriétaires) + + - `is` (`EEType`) + + +* il est également possible de définir des relations dont le type d'entité est + l'objet en utilisant `ObjectRelation` plutôt que `SubjectRelation` + +* le premier argument de `SubjectRelation` et `ObjectRelation` donne + respectivement le type d'entité objet /sujet de la relation. Cela + peut être : + + * une chaine de caractères correspondant à un type d'entité + + * un tuple de chaines de caractères correspondant à plusieurs types d'entité + + * les chaînes de caractères spéciales suivantes : + + - "**" : tout les types d'entité + - "*" : tout les types d'entité non méta + - "@" : tout les types d'entité méta mais non "système" (i.e. servant à la + description du schema en base) + +* il est possible d'utiliser l'attribut possible `meta` pour marquer un type + d'entité comme étant "méta" (i.e. servant à décrire / classifier d'autre + entités) + +* propriétés optionnelles des attributs et relations : + + - `description` : chaine de caractères décrivant un attribut ou une + relation. Par défaut cette chaine sera utilisée dans le formulaire de saisie + de l'entité, elle est donc destinée à aider l'utilisateur final et doit être + marquée par la fonction `_` pour être correctement internationalisée. + + - `constraints` : liste de contraintes devant être respecté par la relation + (c.f. `Contraintes`_) + + - `cardinality` : chaine de 2 caractères spécifiant la cardinalité de la + relation. Le premier caractère donne la cardinalité de la relation sur le + sujet, le 2eme sur l'objet. Quand une relation possède plusieurs sujets ou + objets possibles, la cardinalité s'applique sur l'ensemble et non un à un (et + doit donc à priori être cohérente...). Les valeurs possibles sont inspirées + des expressions régulières : + + * `1`: 1..1 + * `?`: 0..1 + * `+`: 1..n + * `*`: 0..n + + - `meta` : booléen indiquant que la relation est une méta relation (faux par + défaut) + +* propriétés optionnelles des attributs : + + - `required` : booléen indiquant si l'attribut est obligatoire (faux par + défaut) + + - `unique` : booléen indiquant si la valeur de l'attribut doit être unique + parmi toutes les entités de ce type (faux par défaut) + + - `indexed` : booléen indiquant si un index doit être créé dans la base de + données sur cette attribut (faux par défaut). C'est utile uniquement si vous + savez que vous allez faire de nombreuses recherche sur la valeur de cet + attribut. + + - `default` : valeur par défaut de l'attribut. A noter que dans le cas des + types date, les chaines de caractères correspondant aux mots-clés RQL + `TODAY` et `NOW` sont utilisables. + + - `vocabulary` : spécifie statiquement les valeurs possibles d'un attribut + +* propriétés optionnelles des attributs de type `String` : + + - `fulltextindexed` : booléen indiquant si l'attribut participe à l'index plein + texte (faux par défaut) (*valable également sur le type `Byte`*) + + - `internationalizable` : booléen indiquant si la valeur de cet attribut est + internationalisable (faux par défaut) + + - `maxsize` : entier donnant la taille maximum de la chaine (pas de limite par + défaut) + +* propriétés optionnelles des relations : + + - `composite` : chaîne indiquant que le sujet (composite == 'subject') est + composé de ou des objets de la relation. Pour le cas opposé (l'objet est + composé de ou des sujets de la relation, il suffit de mettre 'object' comme + valeur. La composition implique que quand la relation est supprimé (et donc + aussi quand le composite est supprimé), le ou les composés le sont + également. + +Contraintes +``````````` +Par défaut les types de contraintes suivant sont disponibles : + +* `SizeConstraint` : permet de spécifier une taille minimale et/ou maximale sur + les chaines de caractères (cas générique de `maxsize`) + +* `BoundConstraint` : permet de spécifier une valeur minimale et/ou maximale sur + les types numériques + +* `UniqueConstraint` : identique à "unique=True" + +* `StaticVocabularyConstraint` : identique à "vocabulary=(...)" + +* `RQLConstraint` : permet de spécifier une requête RQL devant être satisfaite + par le sujet et/ou l'objet de la relation. Dans cette requête les variables `S` + et `O` sont préféfinies respectivement comme l'entité sujet et objet de la + relation + +* `RQLVocabularyConstraint` : similaire à la précédente, mais exprimant une + contrainte "faible", i.e. servant uniquement à limiter les valeurs apparaissant + dans la liste déroulantes du formulaire d'édition, mais n'empêchant pas une + autre entité d'être séléctionnée + + +Définition d'un type de relation +-------------------------------- + +Un type de relation est définit par une classe python héritant de `RelationType`. Le +nom de la classe correspond au nom du type. Ensuite le corps de la classe +contient la description des propriétés de ce type de relation, ainsi +qu'éventuellement une chaine pour le sujet et une autre pour l'objet permettant +de créer des définitions de relations associées (auquel cas il est possibles de +donner sur la classe les propriétés de définition de relation explicitées +ci-dessus), par exemple :: + + class verrouille_par(RelationType): + """relation sur toutes les entités applicatives indiquant que celles-ci sont vérouillées + inlined = True + cardinality = '?*' + subject = '*' + object = 'EUser' + +En plus des permissions, les propriétés propres aux types de relation (et donc +partagés par toutes les définitions de relation de ce type) sont : + +* `inlined` : booléen contrôlant l'optimisation physique consistant à stocker la + relation dans la table de l'entité sujet au lieu de créer une table spécifique + à la relation. Cela se limite donc aux relations dont la cardinalité + sujet->relation->objet vaut 0..1 ('?') ou 1..1 ('1') + +* `symetric` : booléen indiquant que la relation est symétrique. i.e. + `X relation Y` implique `Y relation X` + +Dans le cas de définitions de relations simultanée, `sujet` et `object` peuvent +tout deux valoir la même chose que décrite pour le 1er argument de +`SubjectRelation` et `ObjectRelation`. + +A partir du moment où une relation n'est ni mise en ligne, ni symétrique, et +ne nécessite pas de permissions particulières, sa définition (en utilisant +`SubjectRelation` ou `ObjectRelation`) est suffisante. + + +Définition des permissions +-------------------------- + +La définition des permissions se fait à l'aide de l'attribut `permissions` des +types d'entité ou de relation. Celui-ci est un dictionnaire dont les clés sont +les types d'accès (action), et les valeurs les groupes ou expressions autorisées. + +Pour un type d'entité, les actions possibles sont `read`, `add`, `update` et +`delete`. + +Pour un type de relation, les actions possibles sont `read`, `add`, et `delete`. + +Pour chaque type d'accès, un tuple indique le nom des groupes autorisés et/ou +une ou plusieurs expressions RQL devant être vérifiées pour obtenir +l'accès. L'accès est donné à partir du moment où l'utilisateur fait parti d'un +des groupes requis ou dès qu'une expression RQL est vérifiée. + +Les groupes standards sont : + +* `guests` + +* `users` + +* `managers` + +* `owners` : groupe virtuel correspondant au propriétaire d'une entité. Celui-ci + ne peut être utilisé que pour les actions `update` et `delete` d'un type + d'entité. + +Il est également possible d'utiliser des groupes spécifiques devant être pour +cela créés dans le precreate de l'application (`migration/precreate.py`). + +Utilisation d'expression RQL sur les droits en écriture +``````````````````````````````````````````````````````` +Il est possible de définir des expressions RQL donnant des droits de +modification (`add`, `delete`, `update`) sur les types d'entité et de relation. + +Expression RQL pour les permissions sur un type d'entité : + +* il faut utiliser la classe `ERQLExpression` + +* l'expression utilisée correspond à la clause WHERE d'une requête RQL + +* dans cette expression, les variables X et U sont des références prédéfinies + respectivement sur l'entité courante (sur laquelle l'action est vérifiée) et + sur l'utilisateur ayant effectué la requête + +* il est possible d'utiliser dans cette expression les relations spéciales + "has__permission" dont le sujet est l'utilisateur et l'objet une + variable quelquonque, signifiant ainsi que l'utilisateur doit avoir la + permission d'effectuer l'action sur la ou les entités liées cette + variable + +Pour les expressions RQL sur un type de relation, les principes sont les mêmes +avec les différences suivantes : + +* il faut utiliser la classe `RRQLExpression` dans le cas d'une relation non + finale + +* dans cette expression, les variables S, O et U sont des références + prédéfinies respectivement sur le sujet et l'objet de la relation + courante (sur laquelle l'action est vérifiée) et sur l'utilisateur + ayant effectué la requête + +* On peut aussi définir des droits sur les attributs d'une entité (relation non + finale), sachant les points suivants : + + - pour définir des expressions rql, il faut utiliser la classe `ERQLExpression` + dans laquelle X représentera l'entité auquel appartient l'attribut + + - les permissions 'add' et 'delete' sont équivalentes. En pratique seul + 'add'/'read' son pris en considération + + +En plus de cela, le type d'entité `EPermission` de la librairie standard permet +de construire des modèles de sécurités très complexes et dynamiques. Le schéma +de ce type d'entité est le suivant : :: + + + 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.")) + + +Exemple de configuration extrait de *jpl* :: + + ... + + 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 + +Cette configuration suppose indique qu'une entité `EPermission` de nom +"add_version" peut-être associée à un projet et donner le droit de créer des +versions sur ce projet à des groupes spécifiques. Il est important de noter les +points suivants : + +* dans ce cas il faut protéger à la fois le type d'entité "Version" et la + relation liant une version à un projet ("version_of") + +* du fait de la généricité du type d'entité `EPermission`, il faut effectuer + l'unification avec les groupes et / ou les états le cas échéant dans + l'expression ("U in_group G, P require_group G" dans l'exemple ci-dessus) + + +Utilisation d'expression RQL sur les droits en lecture +`````````````````````````````````````````````````````` +Les principes sont les mêmes mais avec les restrictions suivantes : + +* on ne peut de `RRQLExpression` sur les types de relation en lecture + +* les relations spéciales "has__permission" ne sont pas utilisables + + +Note sur l'utilisation d'expression RQL sur la permission 'add' +``````````````````````````````````````````````````````````````` +L'utilisation d'expression RQL sur l'ajout d'entité ou de relation pose +potentiellement un problème pour l'interface utilisateur car si l'expression +utilise l'entité ou la relation à créer, on est pas capable de vérifier les +droits avant d'avoir effectué l'ajout (noter que cela n'est pas un problème coté +serveur rql car la vérification des droits est effectuée après l'ajout +effectif). Dans ce cas les méthodes de vérification des droits (check_perm, +has_perm) peuvent inidquer qu'un utilisateur n'a pas le droit d'ajout alors +qu'il pourrait effectivement l'obtenir. Pour palier à ce soucis il est en général +nécessaire dans tel cas d'utiliser une action reflétant les droits du schéma +mais permettant de faire la vérification correctement afin qu'elle apparaisse +bien le cas échéant. diff -r 30f19b976857 -r 9c919a47e140 doc/book/en/04-define-schema.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/04-define-schema.en.txt Tue Nov 18 01:16:30 2008 +0100 @@ -0,0 +1,21 @@ +.. -*- coding: utf-8 -*- + +Définition du modèle de données (*schéma*) +========================================== + +Le schéma est l'élément central d'une application d'CubicWeb, définissant le modèle +de données manipulé. Il est généralement défini à partir de type d'entités +existants dans la librairie et d'autres spécifiques, généralement décrites dans +un ou plusieurs fichiers python dans le sous-répertoire `schema` du modèle. + +A ce niveau il est important de noter la différence entre type de relation et +définition de relation : un type de relation est uniquement un nom de relation +avec éventuellement quelques propriétés supplémentaires (voir plus bas), alors +qu'une définition de relation est un triplet complet " + ". Eventuellement un type de relation +sera créé implicitement si aucun n'est associé à une définition de relation du +schema. + +.. include:: 04-01-schema-stdlib.en.txt +.. include:: 04-02-schema-definition.en.txt + diff -r 30f19b976857 -r 9c919a47e140 doc/book/en/05-01-views-stdlib.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/05-01-views-stdlib.en.txt Tue Nov 18 01:16:30 2008 +0100 @@ -0,0 +1,65 @@ +.. -*- coding: utf-8 -*- + +Vues prédéfinies dans la librairie +---------------------------------- +Un certain nombre de vues sont utilisées pour construire l'interface web, qui +s'appliquent à une ou plusieurs entités. On les distingue par leur identifiant, +et les principales sont : + +:primary: + vue principale pour une entité, elle est appelée par défaut lorsqu'il n'y a + qu'un seul élément correspondant à la recherche. Cette vue est censée + afficher le maximum d'informations à propos de l'objet. +:secondary: + vue secondaire d'une entité. Par défaut, Elle affiche les deux premiers + attributs de l'entité sous la forme d'un lien cliquable amenant sur la vue + primaire. +:oneline: + similaire à la vue `secondary`, mais appelée dans des cas où l'on désire que + la vue tient sur une ligne, ou de manière générale juste avoir une vue plus + abbrégée. Par défaut, cette vue utilise le paramètre de configuration + `MAX_LINE_CHAR` pour contrôler la taille du résultat. +:text: + similaire à la vue `oneline`, mais ne devant pas contenir de html. +:incontext, outofcontext: + similaire à la vue `secondary`, mais appelé si l'entité est considérée comme + en dehors ou dans son contexte. Par défault renvoie respectivement le + résultat de `textincontext` et `textoutofcontext` entouré par un lien + permettant d'accéder à la vue primaire de l'entité +:textincontext, textoutofcontext: + similaire à la vue `text`, mais appelé si l'entité est considérée comme + en dehors ou dans son contexte. Par défault renvoie respectivement le + résultat des méthodes `.dc_title` et `.dc_long_title` de l'entité +:list: + crée une liste html (
    ) et appelle la vue `listitem` pour chaque entité +:listitem: + redirige par défaut vers la vue `outofcontext` +:rss: + crée unvue RSS/XML et appelle la vue `rssitem` pour chaque entité +:rssitem: + crée unvue RSS/XML pour une entité à partir des résultats renvoyés par les + méthodes dublin core de l'objet (`dc_*`) + +Vues de départ : + +:index: + page d'acceuil +:schema: + affiche le schéma de l'application + +Vues particulières : + +:noresult: + appelé si le result set est vide +:finall: + affiche la valeur de la cellule sans transformation (dans le cas d'une + entité non finale, on voit son eid). Appelable sur n'importe quel result + set. +:table: + crée une table html () et appelle la vue `cell` pour chaque cellule + du résultat. Appelable sur n'importe quel result set. +:cell: + par défaut redirige sur la vue `final` si c'est une entité finale + ou sur la vue `outofcontext` sinon +:null: + vue toujours appelable et ne retournant rien diff -r 30f19b976857 -r 9c919a47e140 doc/book/en/05-define-views.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/05-define-views.en.txt Tue Nov 18 01:16:30 2008 +0100 @@ -0,0 +1,247 @@ +.. -*- coding: utf-8 -*- + +.. _DefinitionVues: + +Définition de vues +================== + +Les classes de base des vues +---------------------------- + +La class `View` (`cubicweb.common.view`) +```````````````````````````````````````` +Un vue écrit dans son flux de sortie via son attribut `w` (`UStreamIO`). + +L'interface de base des vues est la suivante : + +* `dispatch(**context)`, appelle ("rend") la vue en appellent `call` ou + `cell_call` en fonction des arguments passé +* `call(**kwargs)`, appelle la vue pour un result set complet ou nul +* `cell_call(row, col, **kwargs)`, appelle la vue pour une cellule donnée d'un + result set +* `url()`, retourne l'url permettant d'obtenir cette vue avec le result set en + cours +* `view(__vid, rset, __fallback_vid=None, **kwargs)`, appelle la vue + d'identificant `__vid` sur le result set donné. Il est possible de données un + identificant de vue de "fallback" qui sera utilisé si la vue demandée n'est + pas applicable au result set + +* `wview(__vid, rset, __fallback_vid=None, **kwargs)`, pareil que `view` mais + passe automatiquement le flux en argument + +* `html_headers()`, retourne une liste d'en-tête HTML à placer par le template + principal + +* `page_title()`, retourne le titre à utiliser dans l'en tête HTML `title` + +* `creator(eid)`, retourne l'eid et le login du créateur de l'entité ayant + l'eid passé en argument + +Autres classes de base : + +* `EntityView`, vue s'appliquant à aux lignes ou cellule contenant une entité + (eg un eid) +* `StartupView`, vue de départ n'ayant pas besoin de result set +* `AnyRsetView`, vue s'appliquant à n'importe quelle result set + +Le mecanisme de selection de vues +--------------------------------- + +Pour un identifiant de vue donne, plusieurs vues peuvent etre definies. +`CubicWeb` utilise un selecteur qui permet de calculer un score et d'identifier +la vue la plus appropriee a appliquer dans le contexte. La librairie du selecteur +se trouve dans ``cubicweb.common.selector`` et une librairie des methodes utilisees +pour calculer les scores est dans ``cubicweb.vregistry.vreq``. + +[FROM-LAX-BOOK] + +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. + +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 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 `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. +[FILLME] + +Les templates ou patron +----------------------- + +Les patrons (ou *template*) sont des cas particulier de vue ne dépendant a +priori pas d'un result set. La classe de base `Template` (`cubicweb.common.view`) +est une classe dérivée de la classe `View`. + +Pour construire une page HTML, un *template principal* est utilisé. Généralement +celui possédant l'identifiant 'main' est utilisé (ce n'est pas le cas lors +d'erreur dans celui-ci ou pour le formulaire de login par exemple). Ce patron +utilise d'autres patrons en plus des vues dépendants du contenu pour générer la +page à renvoyer. + +C'est ce template qui est chargé : + +1. d'éxécuter la requête RQL des données à afficher le cas échéant +2. éventuellement de déterminer la vue à utiliser pour l'afficher si non + spécifiée +3. de composer la page à retourner + + +Le patron principal par défaut (`cubicweb.web.views.basetemplates.TheMainTemplate`) +----------------------------------------------------------------------------------- + +Le template principal par défaut construit la page selon la décomposition +suivante : + +.. image:: images/main_template_layout.png + +Le rectancle contenant le `view.dispatch()` représente l'emplacement où est +inséré la vue de contenu à afficher. Les autres représentent des sous-templates +appelé pour construire la page. Les implémentations par défaut de tout ces +templates sont dans le module `cubicweb.web.views.basetemplates`. Vous pouvez +évidemment surcharger l'un des sous-templates pour modifier l'aspect visuel +d'une partie désirée de la page. + +On peut également contrôler certains comportements du template principal à +l'aide des paramètres de formulaire suivante : + +* `__notemplate`, si présente (quelque soit la valeur associée), seule la vue de + contenu est renvoyée +* `__force_display`, si présente et contient une valeur non nulle, pas de + navigation quelque soit le nombre d'entités à afficher +* `__method`, si le result set à afficher ne contient qu'une entité et que ce + paramètre est spécifié, celui-ci désigne une méthode à appeler sur l'entité + en lui donnant en argument le dictionnaire des paramètres de formulaire, avant + de reprendre le comportement classique (s'insère entre les étapes 1. et + 2. décrites ci-dessus) + + +.. include:: 05-01-views-stdlib.en.txt + + +Vues xml, binaires... +--------------------- +Pour les vues générants autre que du html (une image générée dynamiquement par +exemple), et qui ne peuvent donc généralement pas être incluse dans la page +HTML générée par le template principal (voir ci-dessus), il faut : + +* placer l'attribut `templatable` de la classe à `False` +* indiquer via l'attribut `content_type` de la classe le type MIME généré par la + vue 'application/octet-stream' + +Pour les vues générants un contenu binaire (une image générée dynamiquement par +exemple), il faut également placer l'attribut `binary` de la classe à `True` (ce +qui implique `templatable == False` afin que l'attribut `w` de la vue soit +remplacé par un flux binaire plutôt que unicode. + + +Quelques trucs (X)HTML à respecter +---------------------------------- +Certains navigateurs (dont firefox) n'aime pas les `
    ` vides (par vide +j'entend sans contenu dans la balise, il peut y avoir des attributs), faut +toujours mettre `
    ` même s'il n'y a rien dedans, et non `
    `. diff -r 30f19b976857 -r 9c919a47e140 doc/book/en/06-define-workflows.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/06-define-workflows.en.txt Tue Nov 18 01:16:30 2008 +0100 @@ -0,0 +1,154 @@ +.. -*- 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 30f19b976857 -r 9c919a47e140 doc/book/en/07-01-define-entities.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/07-01-define-entities.en.txt Tue Nov 18 01:16:30 2008 +0100 @@ -0,0 +1,168 @@ +.. -*- coding: utf-8 -*- + +Paramétrages et extensions spécifiques +-------------------------------------- + +Valeurs par défaut dynamiques +````````````````````````````` +Il est possible de définir dans le schéma des valeurs par défaut *statiques*. +Il est également possible de définir des valeurs par défaut *dynamiques* en +définissant sur la classe d'entité une méthode `default_` pour +un attribut donnée. + + +Contrôle des attributs chargés et du tri par défaut +``````````````````````````````````````````````````` +* l'attribut de classe `fetch_attrs` permet de définir sur une classe d'entité + la liste des noms des attributs ou relations devant être chargés + automatiquement lors de la récupération d'entité(s) de ce type. Dans le cas + des relations, on est limité aux relations *sujets de cardinalité `?` ou `1`*. + +* la méthode de classe `fetch_order(attr, var)` prend en argument un nom + d'attribut (ou de relation) et un nom de variable et doit retourner une chaine + à utiliser dans la close "ORDERBY" d'une requête RQL pour trier + automatiquement les listes d'entités de ce type selon cet attribut, ou `None` + si l'on ne veut pas de tri sur l'attribut passé en argument. Par défaut les + entités sont triées selon leur date de création + +* la méthode de classe `fetch_unrelated_order(attr, var)` est similaire à la + méthode `fetch_order` mais est utilisée essentiellement pour contrôler le tri + des listes déroulantes permettant de créer des relations dans la vue d'édition + d'une entité + +La fonction `fetch_config(fetchattrs, mainattr=None)` permet de simplifier la +définition des attributs à précharger et du tri en retournant une liste des +attributs à précharger (en considérant ceux de la classe `AnyEntity` +automatiquement) et une fonction de tri sur l'attribut "principal" (le 2eme +argument si spécifié ou sinon le premier attribut de la liste `fetchattrs`). +Cette fonction est définie dans le package `ginco.entities`. + +Par exemple : :: + + class Transition(AnyEntity): + """...""" + id = 'Transition' + fetch_attrs, fetch_order = fetch_config(['name']) + +Indique que pour le type d'entité "Transition" il faut précharger l'attribut +"name" et trier par défaut selon cet attribut. + + +Contrôle des formulaires d'édition +`````````````````````````````````` +Il est possible de contrôler les attributs/relations dans la vue d'édition +simple ou multiple à l'aide des *rtags* suivants : + +* `primary`, indique qu'un attribut ou une relation doit être incorporé dans + les formulaires d'édition simple et multiple. Dans le cas d'une relation, + le formulaire d'édition de l'entité liée sera inclus dans le formulaire + +* `secondary`, indique qu'un attribut ou une relation doit être incorporé dans + le formulaire d'édition simple uniquement. Dans le cas d'une relation, + le formulaire d'édition de l'entité liée sera inclus dans le formulaire + +* `generic`, indique qu'une relation doit être incorporé dans le formulaire + d'édition simple dans la boite générique d'ajout de relation + +* `generated`, indique qu'un attribut est caculé dynamiquement ou autre, et + qu'il ne doit donc pas être présent dans les formulaires d'édition + +Au besoin il est possible de surcharger la méthode +`relation_category(rtype, x='subject')` pour calculer dynamiquement la catégorie +d'édition d'une relation. + + +Contrôle de la boîte "add_related" +`````````````````````````````````` +La boite `add related` est une boite automatique proposant de créer une entité +qui sera automatiquement liée à l'entité de départ (le contexte dans lequel +s'affiche la boite). Par défaut, les liens présents dans cette boite sont +calculés en fonction des propriétés du schéma de l'entité visualisée, mais il +est possible de les spécifier explicitement à l'aide des *rtags* suivants : + +* `link`, indique qu'une relation est généralement créée vers une entité + existante et qu'il ne faut donc pas faire apparaitre de lien pour cette + relation + +* `create`, indique qu'une relation est généralement créée vers de nouvelles + entités et qu'il faut donc faire apparaitre un lien pour créer une nouvelle + entité et la lier automatiquement + +Au besoin il est possible de surcharger la méthode +`relation_mode(rtype, targettype, x='subject')` pour caculer dynamiquement la +catégorie de création d'une relation. + +A noter également que si au moins une action dans la catégorie "addrelated" est +trouvée pour le contexte courant, le fonctionnement automatique est désactivé +en faveur du fonctionnement explicite (i.e. affichage des actions de la +catégorie "addrelated" uniquement). + +Contrôle des formulaires de filtrage de table +````````````````````````````````````````````` +La vue "table" par défaut gère dynamiquement un formulaire de filtrage du +contenu de celle-ci. L'algorithme est le suivant : + +1. on considère que la première colonne contient les entités à restreindre +2. on recupère la première entité de la table (ligne 0) pour "représenter" + toutes les autres +3. pour toutes les autres variables définies dans la requête originale : + + 1. si la variable est liée à la variable principale par au moins une + n'importe quelle relation + 2. on appelle la méthode `filterform_vocabulary(rtype, x)` sur l'entité + et si rien est retourné (ou plus exactement un tuple de valeur `None`, + voir ci-dessous) on passe à la variable suivante, sinon un élément de + formulaire de filtrage sera créé avec les valeurs de vocabulaire + retournées + +4. il n'y a pas d'autres limitations sur le rql, il peut comporter des clauses + de tris, de groupes... Des fonctions javascripts sont utilisées pour + regénérer une requête à partir de la requête de départ et des valeurs + séléctionnées dans les filtres de formulaire. + + +La méthode `filterform_vocabulary(rtype, x, var, rqlst, args, cachekey)` prend +en argument le nom d'une relation et la "cible", qui indique si l'entité sur +laquelle la méthode est appellée est sujet ou objet de la relation. Elle doit +retourner : + +* un 2-uple de None si elle ne sait pas gérer cette relation + +* un type et une liste contenant le vocabulaire + + * la liste doit contenir des couples (valeur, label) + * le type indique si la valeur désigne un nombre entier (`type == 'int'`), une + chaîne de caractères (`type == 'string'`) ou une entité non finale (`type + == 'eid'`) + +Par exemple dans notre application de gestion de tickets, on veut pouvoir +filtrés ceux-ci par : + +* type +* priorité +* état (in_state) +* étiquette (tags) +* version (done_in) + +On définit donc la méthode suivante : :: + + + class Ticket(AnyEntity): + + ... + + def filterform_vocabulary(self, rtype, x, var, rqlst, args, cachekey): + _ = self.req._ + if rtype == 'type': + return 'string', [(x, _(x)) for x in ('bug', 'story')] + if rtype == 'priority': + return 'string', [(x, _(x)) for x in ('minor', 'normal', 'important')] + if rtype == 'done_in': + rql = insert_attr_select_relation(rqlst, var, rtype, 'num') + return 'eid', self.req.execute(rql, args, cachekey) + return super(Ticket, self).filterform_vocabulary(rtype, x, var, rqlst, + args, cachekey) + + +NOTE: Le support du filtrage sur les étiquettes et l'état est installé +automatiquement, pas besoin de le gérer ici. diff -r 30f19b976857 -r 9c919a47e140 doc/book/en/07-data-as-objects.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/07-data-as-objects.en.txt Tue Nov 18 01:16:30 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 :: + + , ,
    ) et appelle la vue `cell` pour chaque cellule + du résultat. Appelable sur n'importe quel result set. +:cell: + par défaut redirige sur la vue `final` si c'est une entité finale + ou sur la vue `outofcontext` sinon +:null: + vue toujours appelable et ne retournant rien diff -r 30f19b976857 -r 9c919a47e140 doc/book/fr/05-define-views.fr.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/fr/05-define-views.fr.txt Tue Nov 18 01:16:30 2008 +0100 @@ -0,0 +1,247 @@ +.. -*- coding: utf-8 -*- + +.. _DefinitionVues: + +Définition de vues +================== + +Les classes de base des vues +---------------------------- + +La class `View` (`cubicweb.common.view`) +```````````````````````````````````````` +Un vue écrit dans son flux de sortie via son attribut `w` (`UStreamIO`). + +L'interface de base des vues est la suivante : + +* `dispatch(**context)`, appelle ("rend") la vue en appellent `call` ou + `cell_call` en fonction des arguments passé +* `call(**kwargs)`, appelle la vue pour un result set complet ou nul +* `cell_call(row, col, **kwargs)`, appelle la vue pour une cellule donnée d'un + result set +* `url()`, retourne l'url permettant d'obtenir cette vue avec le result set en + cours +* `view(__vid, rset, __fallback_vid=None, **kwargs)`, appelle la vue + d'identificant `__vid` sur le result set donné. Il est possible de données un + identificant de vue de "fallback" qui sera utilisé si la vue demandée n'est + pas applicable au result set + +* `wview(__vid, rset, __fallback_vid=None, **kwargs)`, pareil que `view` mais + passe automatiquement le flux en argument + +* `html_headers()`, retourne une liste d'en-tête HTML à placer par le template + principal + +* `page_title()`, retourne le titre à utiliser dans l'en tête HTML `title` + +* `creator(eid)`, retourne l'eid et le login du créateur de l'entité ayant + l'eid passé en argument + +Autres classes de base : + +* `EntityView`, vue s'appliquant à aux lignes ou cellule contenant une entité + (eg un eid) +* `StartupView`, vue de départ n'ayant pas besoin de result set +* `AnyRsetView`, vue s'appliquant à n'importe quelle result set + +Le mecanisme de selection de vues +--------------------------------- + +Pour un identifiant de vue donne, plusieurs vues peuvent etre definies. +`CubicWeb` utilise un selecteur qui permet de calculer un score et d'identifier +la vue la plus appropriee a appliquer dans le contexte. La librairie du selecteur +se trouve dans ``cubicweb.common.selector`` et une librairie des methodes utilisees +pour calculer les scores est dans ``cubicweb.vregistry.vreq``. + +[FROM-LAX-BOOK] + +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. + +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 entry as before, you will notice that the +page is now looking much nicer. + +.. image:: images/lax-book.09-new-view-blogentry.fr.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.fr.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. +[FILLME] + +Les templates ou patron +----------------------- + +Les patrons (ou *template*) sont des cas particulier de vue ne dépendant a +priori pas d'un result set. La classe de base `Template` (`cubicweb.common.view`) +est une classe dérivée de la classe `View`. + +Pour construire une page HTML, un *template principal* est utilisé. Généralement +celui possédant l'identifiant 'main' est utilisé (ce n'est pas le cas lors +d'erreur dans celui-ci ou pour le formulaire de login par exemple). Ce patron +utilise d'autres patrons en plus des vues dépendants du contenu pour générer la +page à renvoyer. + +C'est ce template qui est chargé : + +1. d'éxécuter la requête RQL des données à afficher le cas échéant +2. éventuellement de déterminer la vue à utiliser pour l'afficher si non + spécifiée +3. de composer la page à retourner + + +Le patron principal par défaut (`cubicweb.web.views.basetemplates.TheMainTemplate`) +----------------------------------------------------------------------------------- + +Le template principal par défaut construit la page selon la décomposition +suivante : + +.. image:: images/main_template_layout.png + +Le rectancle contenant le `view.dispatch()` représente l'emplacement où est +inséré la vue de contenu à afficher. Les autres représentent des sous-templates +appelé pour construire la page. Les implémentations par défaut de tout ces +templates sont dans le module `cubicweb.web.views.basetemplates`. Vous pouvez +évidemment surcharger l'un des sous-templates pour modifier l'aspect visuel +d'une partie désirée de la page. + +On peut également contrôler certains comportements du template principal à +l'aide des paramètres de formulaire suivante : + +* `__notemplate`, si présente (quelque soit la valeur associée), seule la vue de + contenu est renvoyée +* `__force_display`, si présente et contient une valeur non nulle, pas de + navigation quelque soit le nombre d'entités à afficher +* `__method`, si le result set à afficher ne contient qu'une entité et que ce + paramètre est spécifié, celui-ci désigne une méthode à appeler sur l'entité + en lui donnant en argument le dictionnaire des paramètres de formulaire, avant + de reprendre le comportement classique (s'insère entre les étapes 1. et + 2. décrites ci-dessus) + + +.. include:: 05-01-views-stdlib.fr.txt + + +Vues xml, binaires... +--------------------- +Pour les vues générants autre que du html (une image générée dynamiquement par +exemple), et qui ne peuvent donc généralement pas être incluse dans la page +HTML générée par le template principal (voir ci-dessus), il faut : + +* placer l'attribut `templatable` de la classe à `False` +* indiquer via l'attribut `content_type` de la classe le type MIME généré par la + vue 'application/octet-stream' + +Pour les vues générants un contenu binaire (une image générée dynamiquement par +exemple), il faut également placer l'attribut `binary` de la classe à `True` (ce +qui implique `templatable == False` afin que l'attribut `w` de la vue soit +remplacé par un flux binaire plutôt que unicode. + + +Quelques trucs (X)HTML à respecter +---------------------------------- +Certains navigateurs (dont firefox) n'aime pas les `
    ` vides (par vide +j'entend sans contenu dans la balise, il peut y avoir des attributs), faut +toujours mettre `
    ` même s'il n'y a rien dedans, et non `
    `. diff -r 30f19b976857 -r 9c919a47e140 doc/book/fr/06-define-workflows.fr.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/fr/06-define-workflows.fr.txt Tue Nov 18 01:16:30 2008 +0100 @@ -0,0 +1,154 @@ +.. -*- 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.fr.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 30f19b976857 -r 9c919a47e140 doc/book/fr/07-01-define-entities.fr.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/fr/07-01-define-entities.fr.txt Tue Nov 18 01:16:30 2008 +0100 @@ -0,0 +1,168 @@ +.. -*- coding: utf-8 -*- + +Paramétrages et extensions spécifiques +-------------------------------------- + +Valeurs par défaut dynamiques +````````````````````````````` +Il est possible de définir dans le schéma des valeurs par défaut *statiques*. +Il est également possible de définir des valeurs par défaut *dynamiques* en +définissant sur la classe d'entité une méthode `default_` pour +un attribut donnée. + + +Contrôle des attributs chargés et du tri par défaut +``````````````````````````````````````````````````` +* l'attribut de classe `fetch_attrs` permet de définir sur une classe d'entité + la liste des noms des attributs ou relations devant être chargés + automatiquement lors de la récupération d'entité(s) de ce type. Dans le cas + des relations, on est limité aux relations *sujets de cardinalité `?` ou `1`*. + +* la méthode de classe `fetch_order(attr, var)` prend en argument un nom + d'attribut (ou de relation) et un nom de variable et doit retourner une chaine + à utiliser dans la close "ORDERBY" d'une requête RQL pour trier + automatiquement les listes d'entités de ce type selon cet attribut, ou `None` + si l'on ne veut pas de tri sur l'attribut passé en argument. Par défaut les + entités sont triées selon leur date de création + +* la méthode de classe `fetch_unrelated_order(attr, var)` est similaire à la + méthode `fetch_order` mais est utilisée essentiellement pour contrôler le tri + des listes déroulantes permettant de créer des relations dans la vue d'édition + d'une entité + +La fonction `fetch_config(fetchattrs, mainattr=None)` permet de simplifier la +définition des attributs à précharger et du tri en retournant une liste des +attributs à précharger (en considérant ceux de la classe `AnyEntity` +automatiquement) et une fonction de tri sur l'attribut "principal" (le 2eme +argument si spécifié ou sinon le premier attribut de la liste `fetchattrs`). +Cette fonction est définie dans le package `ginco.entities`. + +Par exemple : :: + + class Transition(AnyEntity): + """...""" + id = 'Transition' + fetch_attrs, fetch_order = fetch_config(['name']) + +Indique que pour le type d'entité "Transition" il faut précharger l'attribut +"name" et trier par défaut selon cet attribut. + + +Contrôle des formulaires d'édition +`````````````````````````````````` +Il est possible de contrôler les attributs/relations dans la vue d'édition +simple ou multiple à l'aide des *rtags* suivants : + +* `primary`, indique qu'un attribut ou une relation doit être incorporé dans + les formulaires d'édition simple et multiple. Dans le cas d'une relation, + le formulaire d'édition de l'entité liée sera inclus dans le formulaire + +* `secondary`, indique qu'un attribut ou une relation doit être incorporé dans + le formulaire d'édition simple uniquement. Dans le cas d'une relation, + le formulaire d'édition de l'entité liée sera inclus dans le formulaire + +* `generic`, indique qu'une relation doit être incorporé dans le formulaire + d'édition simple dans la boite générique d'ajout de relation + +* `generated`, indique qu'un attribut est caculé dynamiquement ou autre, et + qu'il ne doit donc pas être présent dans les formulaires d'édition + +Au besoin il est possible de surcharger la méthode +`relation_category(rtype, x='subject')` pour calculer dynamiquement la catégorie +d'édition d'une relation. + + +Contrôle de la boîte "add_related" +`````````````````````````````````` +La boite `add related` est une boite automatique proposant de créer une entité +qui sera automatiquement liée à l'entité de départ (le contexte dans lequel +s'affiche la boite). Par défaut, les liens présents dans cette boite sont +calculés en fonction des propriétés du schéma de l'entité visualisée, mais il +est possible de les spécifier explicitement à l'aide des *rtags* suivants : + +* `link`, indique qu'une relation est généralement créée vers une entité + existante et qu'il ne faut donc pas faire apparaitre de lien pour cette + relation + +* `create`, indique qu'une relation est généralement créée vers de nouvelles + entités et qu'il faut donc faire apparaitre un lien pour créer une nouvelle + entité et la lier automatiquement + +Au besoin il est possible de surcharger la méthode +`relation_mode(rtype, targettype, x='subject')` pour caculer dynamiquement la +catégorie de création d'une relation. + +A noter également que si au moins une action dans la catégorie "addrelated" est +trouvée pour le contexte courant, le fonctionnement automatique est désactivé +en faveur du fonctionnement explicite (i.e. affichage des actions de la +catégorie "addrelated" uniquement). + +Contrôle des formulaires de filtrage de table +````````````````````````````````````````````` +La vue "table" par défaut gère dynamiquement un formulaire de filtrage du +contenu de celle-ci. L'algorithme est le suivant : + +1. on considère que la première colonne contient les entités à restreindre +2. on recupère la première entité de la table (ligne 0) pour "représenter" + toutes les autres +3. pour toutes les autres variables définies dans la requête originale : + + 1. si la variable est liée à la variable principale par au moins une + n'importe quelle relation + 2. on appelle la méthode `filterform_vocabulary(rtype, x)` sur l'entité + et si rien est retourné (ou plus exactement un tuple de valeur `None`, + voir ci-dessous) on passe à la variable suivante, sinon un élément de + formulaire de filtrage sera créé avec les valeurs de vocabulaire + retournées + +4. il n'y a pas d'autres limitations sur le rql, il peut comporter des clauses + de tris, de groupes... Des fonctions javascripts sont utilisées pour + regénérer une requête à partir de la requête de départ et des valeurs + séléctionnées dans les filtres de formulaire. + + +La méthode `filterform_vocabulary(rtype, x, var, rqlst, args, cachekey)` prend +en argument le nom d'une relation et la "cible", qui indique si l'entité sur +laquelle la méthode est appellée est sujet ou objet de la relation. Elle doit +retourner : + +* un 2-uple de None si elle ne sait pas gérer cette relation + +* un type et une liste contenant le vocabulaire + + * la liste doit contenir des couples (valeur, label) + * le type indique si la valeur désigne un nombre entier (`type == 'int'`), une + chaîne de caractères (`type == 'string'`) ou une entité non finale (`type + == 'eid'`) + +Par exemple dans notre application de gestion de tickets, on veut pouvoir +filtrés ceux-ci par : + +* type +* priorité +* état (in_state) +* étiquette (tags) +* version (done_in) + +On définit donc la méthode suivante : :: + + + class Ticket(AnyEntity): + + ... + + def filterform_vocabulary(self, rtype, x, var, rqlst, args, cachekey): + _ = self.req._ + if rtype == 'type': + return 'string', [(x, _(x)) for x in ('bug', 'story')] + if rtype == 'priority': + return 'string', [(x, _(x)) for x in ('minor', 'normal', 'important')] + if rtype == 'done_in': + rql = insert_attr_select_relation(rqlst, var, rtype, 'num') + return 'eid', self.req.execute(rql, args, cachekey) + return super(Ticket, self).filterform_vocabulary(rtype, x, var, rqlst, + args, cachekey) + + +NOTE: Le support du filtrage sur les étiquettes et l'état est installé +automatiquement, pas besoin de le gérer ici. diff -r 30f19b976857 -r 9c919a47e140 doc/book/fr/07-data-as-objects.fr.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/fr/07-data-as-objects.fr.txt Tue Nov 18 01:16:30 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 :: + + , ,
    ) et appelle la vue `cell` pour chaque cellule - du résultat. Appelable sur n'importe quel result set. -:cell: - par défaut redirige sur la vue `final` si c'est une entité finale - ou sur la vue `outofcontext` sinon -:null: - vue toujours appelable et ne retournant rien diff -r 30f19b976857 -r 9c919a47e140 doc/book/fr/tut-create-app.fr.txt --- a/doc/book/fr/tut-create-app.fr.txt Mon Nov 17 14:38:30 2008 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,394 +0,0 @@ -.. -*- coding: utf-8 -*- - - -Tutoriel : créer votre première application web pour Postgres -============================================================= - - -[TRANSLATE ME TO FRENCH] - -Ce tutoriel va vous guider pas à pas a construire une apllication web -de gestion de Blog afin de vous faire découvrir les fonctionnalités de -`CubicWeb`. - -Nous supposons que vous avec déjà suivi le guide :ref:`MiseEnPlaceEnv` - - -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 :ref:`installation` guidelines and that both the `AppEngine SDK` and the -`LAX` framework are setup on your computer. - -Creating a new application --------------------------- - -We choosed in this tutorial to develop a blog as an example of web application -and will go through each required steps/actions to have it running with `LAX`. -When you installed `LAX`, you saw a directory named ``skel``. Make a copy of -this directory and call it ``BlogDemo``. - -The location of this directory does not matter. But once decided, make sure your ``PYTHONPATH`` is properly set (:ref:`installation`). - - -Defining a schema ------------------ - -With `LAX`, the schema/datamodel is the core of the application. This is where -you will define the type of content you have to hanlde in your 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 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 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`. - -Running 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.fr.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.fr.png - :alt: home page - -Creating system entities ------------------------- -You can only create new users if you decided not to use google authentication. - - -[WRITE ME : create users manages permissions etc] - - - -Creating application entites ----------------------------- - -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/lax-book.02-create-blog.fr.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/lax-book.03-list-one-blog.fr.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.fr.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.fr.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/lax-book.06-add-relation-entryof.fr.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/lax-book.07-detail-one-blogentry.fr.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.fr.png - :alt: graphical view of the schema (aka data-model) - -Site configuration ------------------- - -.. image:: images/lax-book.03-site-config-panel.fr.png - -This panel allows you to configure the appearance of your application site. -Six menus are available and we will go through each of them to explain how -to use them. - -Navigation -~~~~~~~~~~ -This menu provides you a way to adjust some navigation options depending on -your needs, such as the number of entities to display by page of results. -Follows the detailled list of available options : - -* navigation.combobox-limit : maximum number of entities to display in related - combo box (sample format: 23) -* navigation.page-size : maximum number of objects displayed by page of results - (sample format: 23) -* navigation.related-limit : maximum number of related entities to display in - the primary view (sample format: 23) -* navigation.short-line-size : maximum number of characters in short description - (sample format: 23) - -UI -~~ -This menu provides you a way to customize the user interface settings such as -date format or encoding in the produced html. -Follows the detailled list of available options : - -* ui.date-format : how to format date in the ui ("man strftime" for format description) -* ui.datetime-format : how to format date and time in the ui ("man strftime" for format - description) -* ui.default-text-format : default text format for rich text fields. -* ui.encoding : user interface encoding -* ui.fckeditor :should html fields being edited using fckeditor (a HTML WYSIWYG editor). - You should also select text/html as default text format to actually get fckeditor. -* ui.float-format : how to format float numbers in the ui -* ui.language : language of the user interface -* ui.main-template : id of main template used to render pages -* ui.site-title : site title, which is displayed right next to the logo in the header -* ui.time-format : how to format time in the ui ("man strftime" for format description) - - -Actions -~~~~~~~ -This menu provides a way to configure the context in which you expect the actions -to be displayed to the user and if you want the action to be visible or not. -You must have notice that when you view a list of entities, an action box is -available on the left column which display some actions as well as a drop-down -menu for more actions. - -The context available are : - -* mainactions : actions listed in the left box -* moreactions : actions listed in the `more` menu of the left box -* addrelated : add actions listed in the left box -* useractions : actions listed in the first section of drop-down menu - accessible from the right corner user login link -* siteactions : actions listed in the second section of drop-down menu - accessible from the right corner user login link -* hidden : select this to hide the specific action - -Boxes -~~~~~ -The application has already a pre-defined set of boxes you can use right away. -This configuration section allows you to place those boxes where you want in the -application interface to customize it. - -The available boxes are : - -* actions box : box listing the applicable actions on the displayed data - -* boxes_blog_archives_box : box listing the blog archives - -* possible views box : box listing the possible views for the displayed data - -* rss box : RSS icon to get displayed data as a RSS thread - -* search box : search box - -* startup views box : box listing the configuration options available for - the application site, such as `Preferences` and `Site Configuration` - -Components -~~~~~~~~~~ -[WRITE ME] - -Contextual components -~~~~~~~~~~~~~~~~~~~~~ -[WRITE ME] - -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.fr.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. - -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 30f19b976857 -r 9c919a47e140 doc/book/fr/tut-create-gae-app.fr.txt --- a/doc/book/fr/tut-create-gae-app.fr.txt Mon Nov 17 14:38:30 2008 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,218 +0,0 @@ -.. -*- coding: utf-8 -*- - -.. _tutorielGAE: - -Tutoriel : créer votre première application web pour Google AppEngine -===================================================================== - -Ce tutoriel va vous guider pas à pas a construire une apllication web -de gestion de Blog afin de vous faire découvrir les fonctionnalités de -`CubicWeb`. - -Nous supposons que vous avec déjà suivi le guide :ref:`installationGAE`. - - -Créez une nouvelle application ------------------------------- - -Nous choisissons dans ce tutoriel de développer un blog comme un exemple -d'application web et nous allons expliciter toutes les étapes nécessaires -à sa réalisation. - -:: - - cubicweb-ctl newgapp blogdemo - -`newgapp` est la commande permettant de créer une instance `CubicWeb` pour -le datastore. - -Assurez-vous que votre variable d'environnement ``PYTHONPATH`` est correctement -initialisée (:ref:`installationGAE`) - -Définissez un schéma --------------------- - -Le modèle de données ou schéma est au coeur d'une application `CubicWeb`. -C'est là où vous allez devoir définir le type de contenu que votre application -devra gérer. - -Commençons par un schéma simple que nous améliorerons progressivemment. - -Une fois votre instance ``blogdemo`` crée, vous trouverez un fichier ``schema.py`` -contenant la définition des entités suivantes : ``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='?*') - - -Un ``Blog`` a un titre et une description. Le titre est une chaîne -de caractères requise par la classe parente EntityType and ne doit -pas excéder 50 caractères. La description est une chaîne de -caractères sans contraintes. - -Une ``BlogEntry`` a un titre, une date de publication et du texte -étant son contenu. Le titre est une chaîne de caractères qui ne -doit pas excéder 100 caractères. La date de publication est de type Date et a -pour valeur par défaut TODAY, ce qui signifie que lorsqu'une -``BlogEntry`` sera créée, sa date de publication sera la date -courante a moins de modifier ce champ. Le texte est une chaîne de -caractères qui sera indexée en plein texte et sans contraintes. - -Une ``BlogEntry`` a aussi une relation nommée ``entry_of`` qui la -relie à un ``Blog``. La cardinalité ``?*`` signifie que BlogEntry -peut faire partie de zero a un Blog (``?`` signifie `zero ou un`) et -qu'un Blog peut avoir une infinité de BlogEntry (``*`` signifie -`n'importe quel nombre incluant zero`). -Par soucis de complétude, nous rappellerons que ``+`` signifie -`un ou plus`. - -Lancez l'application --------------------- - -Définir ce simple schéma est suffisant pour commencer. Assurez-vous -que vous avez suivi les étapes décrites dans la section installation -(en particulier visitez http://localhost:8080/_load en tant qu'administrateur -afin d'initialiser le datastore), puis lancez votre application avec la commande :: - - python dev_appserver.py BlogDemo - -puis dirigez vous vers http://localhost:8080/ (ou si c'est plus facile -vous pouvez utiliser la démo en ligne http://lax.appspot.com/). -[FIXME] -- changer la demo en ligne en quelque chose qui marche (!) - -.. image:: images/lax-book.00-login.fr.png - :alt: login screen - -Après vous être authentifié, vous arrivez sur la page d'accueil de votre -application. Cette page liste les types d'entités accessibles dans votre -application, en l'occurrence : Blog et Articles. Si vous lisez ``blog_plural`` -et ``blogentry_plural`` cela signifie que l'internationalisation (i18n) -n'a pas encore fonctionné. Ignorez cela pour le moment. - -.. image:: images/lax-book.01-start.fr.png - :alt: home page - -Créez des entités système -------------------------- - -Vous ne pourrez créer de nouveaux utilisateurs que dans le cas où vous -avez choisi de ne pas utiliser l'authentification Google. - - -[WRITE ME : create users manages permissions etc] - - - -Créez des entités applicatives ------------------------------- - -Créez un Blog -~~~~~~~~~~~~~ - -Créons à présent quelques entités. Cliquez sur `[+]` sur la -droite du lien Blog. Appelez cette nouvelle entité Blog ``Tech-Blog`` -et tapez pour la description ``everything about technology``, -puis validez le formulaire d'édition en cliquant sur le bouton -``Validate``. - - -.. image:: images/lax-book.02-create-blog.fr.png - :alt: from to create blog - -En cliquant sur le logo situé dans le coin gauche de la fenêtre, -vous allez être redirigé vers la page d'accueil. Ensuite, si vous allez -sur le lien Blog, vous devriez voir la liste des entités Blog, en particulier -celui que vous venez juste de créer ``Tech-Blog``. - -.. image:: images/lax-book.03-list-one-blog.fr.png - :alt: displaying a list of a single blog - -Si vous cliquez sur ``Tech-Blog`` vous devriez obtenir une description -détaillée, ce qui dans notre cas, n'est rien de plus que le titre -et la phrase ``everything about technology`` - - -.. image:: images/lax-book.04-detail-one-blog.fr.png - :alt: displaying the detailed view of a blog - -Maintenant retournons sur la page d'accueil et créons un nouveau -Blog ``MyLife`` et retournons sur la page d'accueil, puis suivons -le lien Blog et nous constatons qu'à présent deux blogs sont listés. - -.. image:: images/lax-book.05-list-two-blog.fr.png - :alt: displaying a list of two blogs - -Créons un article -~~~~~~~~~~~~~~~~~ - -Revenons sur la page d'accueil et cliquons sur `[+]` à droite du lien -`articles`. Appellons cette nouvelle entité ``Hello World`` et introduisons -un peut de texte avant de ``Valider``. Vous venez d'ajouter un article -sans avoir précisé à quel Blog il appartenait. Dans la colonne de gauche -se trouve une boite intitulé ``actions``, cliquez sur le menu ``modifier``. -Vous êtes de retour sur le formulaire d'édition de l'article que vous -venez de créer, à ceci près que ce formulaire a maintenant une nouvelle -section intitulée ``ajouter relation``. Choisissez ``entry_of`` dans ce menu, -cela va faire apparaitre une deuxième menu déroulant dans lequel vous -allez pouvoir séléctionner le Blog ``MyLife``. - -Vous auriez pu aussi, au moment où vous avez crée votre article, sélectionner -``appliquer`` au lieu de ``valider`` et le menu ``ajouter relation`` serait apparu. - -.. image:: images/lax-book.06-add-relation-entryof.fr.png - :alt: editing a blog entry to add a relation to a blog - -Validez vos modifications en cliquant sur ``Valider``. L'entité article -qui est listée contient maintenant un lien vers le Blog auquel il -appartient, ``MyLife``. - -.. image:: images/lax-book.07-detail-one-blogentry.fr.png - :alt: displaying the detailed view of a blogentry - -Rappelez-vous que pour le moment, tout a été géré par la plate-forme -`CubicWeb` et que la seule chose qui a été fournie est le schéma de -données. D'ailleurs pour obtenir une vue graphique du schéma, exécutez -la commande ``laxctl genschema blogdemo`` et vous pourrez visualiser -votre schéma a l'URL suivante : http://localhost:8080/schema - -.. image:: images/lax-book.08-schema.fr.png - :alt: graphical view of the schema (aka data-model) - - -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. - -