# HG changeset patch # User Aurelien Campeas # Date 1271254508 -7200 # Node ID 7ee07d18dc9588d59820b83a9c2d30120cc82894 # Parent 385c2351153ec54cda333d185e0d0aacafd0340e [doc/book] include sylvain great advanced tutorial, move tutorials in one proper section diff -r 385c2351153e -r 7ee07d18dc95 doc/book/en/intro/book-map.rst --- a/doc/book/en/intro/book-map.rst Wed Apr 14 10:31:09 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -.. -*- coding: utf-8 -*- - -Book map -======== - -[XXX WRITE ME] - -* explain how to use this book and what chapters to read in what order depending on the - objectives of the reader - diff -r 385c2351153e -r 7ee07d18dc95 doc/book/en/intro/tutorial/blog-in-five-minutes.rst --- a/doc/book/en/intro/tutorial/blog-in-five-minutes.rst Wed Apr 14 10:31:09 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -.. -*- coding: utf-8 -*- - -.. _BlogFiveMinutes: - -Get a blog running in five minutes! ------------------------------------ - -For Debian or Ubuntu users, first install the following packages (:ref:`DebianInstallation`):: - - cubicweb, cubicweb-dev, cubicweb-blog - -For Windows or Mac OS X users, you must install cubicweb from source (see :ref:`SourceInstallation` and :ref:`WindowsInstallation`). - -Then create and initialize your instance:: - - cubicweb-ctl create blog myblog - -And start it:: - - cubicweb-ctl start -D myblog - -The -D option is the debugging mode of cubicweb, removing it will lauch the instance in the background. - -Permission -~~~~~~~~~~ - -This command assumes that you have root access to the /etc/ path. In order to initialize your instance as a `user` (from scratch), please check your current PYTHONPATH then create the ~/etc/cubicweb.d directory. - -Instance parameters -~~~~~~~~~~~~~~~~~~~ - -If the database installation failed, you'd like to change some instance parameters, for example, the database host or the user name. These informations can be edited in the `source` file located in the /etc/cubicweb.d/myblog directory. - -Then relaunch the database creation: - - cubicweb-ctl db-create myblog - -Other paramaters, like web server or emails parameters, can be modified in the `all-in-one.conf` file. - -This is it. Your blog is running. Visit http://localhost:8080 and enjoy it! This blog is fully functionnal. The next section section will present the way to develop new cubes and customizing the look of your instance. - - diff -r 385c2351153e -r 7ee07d18dc95 doc/book/en/intro/tutorial/components.rst --- a/doc/book/en/intro/tutorial/components.rst Wed Apr 14 10:31:09 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ -.. -*- coding: utf-8 -*- - -.. _cubes: - -Cubes ------ - -Standard library -~~~~~~~~~~~~~~~~ - -A library of standard cubes are available from `CubicWeb Forge`_ -Cubes provide entities and views. - -The available application entities in standard cubes are: - -* addressbook: PhoneNumber and PostalAddress - -* basket: Basket (like a shopping cart) - -* blog: Blog (a *very* basic blog) - -* classfolder: Folder (to organize things but grouping them in folders) - -* classtags: Tag (to tag anything) - -* comment: Comment (to attach comment threads to entities) - -* file: File (to allow users to upload and store binary or text files) - -* link: Link (to collect links to web resources) - -* mailinglist: MailingList (to reference a mailing-list and the URLs - for its archives and its admin interface) - -* person: Person (easily mixed with addressbook) - -* task: Task (something to be done between start and stop date) - -* zone: Zone (to define places within larger places, for example a - city in a state in a country) - -.. _`CubicWeb Forge`: http://www.cubicweb.org/project/ - -Adding comments to BlogDemo -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To import a cube in your instance just change the line in the -``__pkginfo__.py`` file and verify that the cube you are planning -to use is listed by the command ``cubicweb-ctl list``. -For example:: - - __use__ = ('comment',) - -will make the ``Comment`` entity available in your ``BlogDemo`` -cube. - -Change the schema to add a relationship between ``BlogEntry`` and -``Comment`` and you are done. Since the comment cube defines the -``comments`` relationship, adding the line:: - - comments = ObjectRelation('Comment', cardinality='1*', composite='object') - -to the definition of a ``BlogEntry`` will be enough. - -Synchronize the data model -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Once you modified your data model, you need to synchronize the -database with your model. For this purpose, *CubicWeb* provides -a very useful command ``cubicweb-ctl shell blogdemo`` which -launches an interactive shell where you can enter migration -commands (see :ref:`cubicweb-ctl` for more details)). -As you added the cube named `comment`, you need to run: - -:: - - add_cube('comment') - -You can now start your instance and comment your blog entries. diff -r 385c2351153e -r 7ee07d18dc95 doc/book/en/intro/tutorial/conclusion.rst --- a/doc/book/en/intro/tutorial/conclusion.rst Wed Apr 14 10:31:09 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -.. -*- coding: utf-8 -*- - -What's next? ------------- - -We demonstrated how from a straight out of the box *CubicWeb* installation, you -can build your web application based on a data model. It's all already there: -views, templates, permissions, etc. The step forward is now for you to customize -according to your needs. - -Many features are available to extend your application, for example: RSS channel -integration (:ref:`XmlAndRss`), hooks (:ref:`hooks`), support of sources such as -Google App Engine (:ref:`GoogleAppEngineSource`) and lots of others to discover -through our book. - diff -r 385c2351153e -r 7ee07d18dc95 doc/book/en/intro/tutorial/create-cube.rst --- a/doc/book/en/intro/tutorial/create-cube.rst Wed Apr 14 10:31:09 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,430 +0,0 @@ -.. -*- coding: utf-8 -*- - - -.. _Steps: - -Steps for creating your cube ----------------------------- - -The following steps will help you to create and customize a new cube. - -1. :ref:`CreateYourCube` - -Create the directory to hold the code of your cube. The most important files that will be useful to customize your newly created cube are: - * schema.py: contains the data model - * views.py: contains your custom views - * entities.py: contains XXX - -The detailed structure of the code directory is described in :ref:`cubelayout`. - -2. :ref:`DefineDataModel` - -Define the data model of your application. - -3. :ref:`ExploreYourInstance` - -Create, run, and explore an instance of your cube. - -4. :ref:`DefineViews` - -Customize the views of your data: how and which part of your data are showed. - -Note: views don't concern the look'n'feel or design of the site. For that, you should use CSS instead, and default CSS or your new cube are located in 'blog/data/'. - - -5. :ref:`DefineEntities` - -Define your own entities to add useful functions when you manipulate your data, especially when you write view. - - -.. _CreateYourCube: - -Create your cube ----------------- - -The packages ``cubicweb`` and ``cubicweb-dev`` install a command line -tool for *CubicWeb* called ``cubicweb-ctl``. This tool provides a wide -range of commands described in details in :ref:`cubicweb-ctl`. - -Once your *CubicWeb* development environment is set up, you can create -a new cube:: - - cubicweb-ctl newcube blog - -This will create in the cubes directory (``/path/to/forest/cubes`` for Mercurial -installation, ``/usr/share/cubicweb/cubes`` for debian packages installation) -a directory named ``blog`` reflecting the structure described in :ref:`Concepts`. - - -For packages installation, you can still create new cubes in your home directory using the following configuration. Let's say you want to develop your new cubes in `~src/cubes`, then set the following environment variables: -:: - - CW_CUBES_PATH=~/src/cubes - CW_MODE=user - -and then create your new cube using: -:: - - cubicweb-ctl newcube --directory=~/src/cubes blog - - - - - - -.. _DefineDataModel: - -Define your data model ----------------------- - -The data model or schema is the core of your *CubicWeb* application. -It defines the type of content your application will handle. - -The data model of your cube ``blog`` is defined in the file ``schema.py``: - -.. sourcecode:: python - - from yams.buildobjs import EntityType, String, SubjectRelation, Date - - 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='?*') - -The first step is the import of the EntityType (generic class for entity and -attributes that will be used in both Blog and BlogEntry entities. - -A Blog has a title and a description. The title is a string that is -required and must be less than 50 characters. The -description is a string that is not constrained. - -A BlogEntry has a title, a publish_date and a content. The title is a -string that is required and must be less than 100 characters. The -publish_date is a Date with a default value of TODAY, meaning that -when a BlogEntry is created, its publish_date will be the current day -unless it is modified. The content is a string that will be indexed in -the database full-text index and has no constraint. - -A BlogEntry also has a relationship ``entry_of`` that links it to a -Blog. The cardinality ``?*`` means that a BlogEntry can be part of -zero or one Blog (``?`` means `zero or one`) and that a Blog can -have any number of BlogEntry (``*`` means `any number including -zero`). For completeness, remember that ``+`` means `one or more`. - - -.. _ExploreYourInstance: - -Create and explore your instance --------------------------------- -.. _CreateYourInstance: - -Create your instance -~~~~~~~~~~~~~~~~~~~~ - -To use this cube as an instance and create a new instance named ``blogdemo``, do:: - - cubicweb-ctl create blog blogdemo - -This command will create the corresponding database and initialize it. - - -.. _WelcomeToYourWebInstance: - -Welcome to your web instance -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Start your instance in debug mode with the following command: :: - - cubicweb-ctl start -D blogdemo - - -You can now access your web instance to create blogs and post messages -by visiting the URL http://localhost:8080/. - -A login form will appear. By default, the instance will not allow anonymous -users to enter the instance. To login, you need then use the admin account -you created at the time you initialized the database with ``cubicweb-ctl -create``. - -.. image:: ../../images/login-form.png - - -Once authenticated, you can start playing with your instance -and create entities. - -.. image:: ../../images/blog-demo-first-page.png - -Please notice that so far, the *CubicWeb* framework managed all aspects of -the web application based on the schema provided at the beginning. - -.. _AddEntities: - -Add entities -~~~~~~~~~~~~ - -We will now add entities in our web application. - -Add a Blog -********** - -Let us create a few of these entities. Click on the `[+]` at the left of the -link Blog on the home page. Call this new Blog ``Tech-blog`` and type in -``everything about technology`` as the description, then validate the form by -clicking on ``Validate``. - -.. image:: ../../images/cbw-create-blog.en.png - :alt: from to create blog - -Click on the logo at top left to get back to the home page, then -follow the Blog link that will list for you all the existing Blog. -You should be seeing a list with a single item ``Tech-blog`` you -just created. - -.. image:: ../../images/cbw-list-one-blog.en.png - :alt: displaying a list of a single blog - -Clicking on this item will get you to its detailed description except -that in this case, there is not much to display besides the name and -the phrase ``everything about technology``. - -Now get back to the home page by clicking on the top-left logo, then -create a new Blog called ``MyLife`` and get back to the home page -again to follow the Blog link for the second time. The list now -has two items. - -.. image:: ../../images/cbw-list-two-blog.en.png - :alt: displaying a list of two blogs - -Add a BlogEntry -*************** - -Get back to the home page and click on [+] at the left of the link -BlogEntry. Call this new entry ``Hello World`` and type in some text -before clicking on ``Validate``. You added a new blog entry without -saying to what blog it belongs. There is a box on the left entitled -``actions``, click on the menu item ``modify``. You are back to the form -to edit the blog entry you just created, except that the form now has -another section with a combobox titled ``add relation``. Chose -``entry_of`` in this menu and a second combobox appears where you pick -``MyLife``. - -You could also have, at the time you started to fill the form for a -new entity BlogEntry, hit ``Apply`` instead of ``Validate`` and the -combobox titled ``add relation`` would have showed up. - - -.. image:: ../../images/cbw-add-relation-entryof.en.png - :alt: editing a blog entry to add a relation to a blog - -Validate the changes by clicking ``Validate``. The entity BlogEntry -that is displayed now includes a link to the entity Blog named -``MyLife``. - -.. image:: ../../images/cbw-detail-one-blogentry.en.png - :alt: displaying the detailed view of a blogentry - -Note that all of this was handled by the framework and that the only input -that was provided so far is the schema. To get a graphical view of the schema, -point your browser to the URL http://localhost:8080/schema - -.. image:: ../../images/cbw-schema.en.png - :alt: graphical view of the schema (aka data-model) - - -.. _DefineViews: - -Define your entity views ------------------------- - -Each entity defined in a model is associated with default views -allowing different rendering of the data. You can redefine each of -them according to your needs and preferences. So let's see how the -views are defined. - - -The view selection principle -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A view is defined by a Python class which includes: - - - an identifier (all objects in *CubicWeb* are recorded in a - registry and this identifier will be used as a key) - - - a filter to select the result sets it can be applied to - -A view has a set of methods complying with the `View` class interface -(`cubicweb.common.view`). - -*CubicWeb* provides a lot of standard views for the type `EntityView`; -for a complete list, read the code in directory ``cubicweb/web/views/``. - -A view is applied on a `result set` which contains a set of entities -we are trying to display. *CubicWeb* uses a selector mechanism which -computes for each available view a score: the view with the highest -score is then used to display the given `result set`. The standard -library of selectors is in ``cubicweb.selector``. - -It is possible to define multiple views for the same identifier -and to associate selectors and filters to allow the application -to find the most appropriate way to render the data. - -For example, the view named ``primary`` is the one used to display a -single entity. We will now show you how to create a primary view for -BlogEntry. - - -Primary view customization -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you wish to modify the way a `BlogEntry` is rendered, you will have -to subclass the `primary` view, for instance in the module ``views`` -of the cube ``cubes/blog/views.py``. - -The standard primary view is the most sophisticated view of all. It -has more than a call() method. It is a template. Actually the entry -point calls the following sequence of (redefinable) methods: - - * render_entity_title - - * render_entity_metadata - - * render_entity_attributes - - * render_entity_relations - - * render_side_boxes - -Excepted side boxes, we can see all of them already in action in the -blog entry view. This is all described in more details in -:ref:`primary`. - -We can for example add in front of the publication date a prefix -specifying that the date we see is the publication date. - -To do so, please apply the following changes: - -.. sourcecode:: python - - from cubicweb.selectors import implements - from cubicweb.web.views import primary - - class BlogEntryPrimaryView(primary.PrimaryView): - __select__ = implements('BlogEntry') - - def render_entity_attributes(self, entity): - self.w(u'

published on %s

' % - entity.publish_date.strftime('%Y-%m-%d')) - super(BlogEntryPrimaryView, self).render_entity_attributes(entity) - -.. note:: - When a view is modified, it is not required to restart the instance - server. Save the Python file and reload the page in your web browser - to view the changes. - -You can now see that the publication date has a prefix. - -.. image:: ../../images/cbw-update-primary-view.en.png - :alt: modified primary view - - -The above source code defines a new primary view for ``BlogEntry``. - -Since views are applied to result sets and result sets can be tables of -data, we have to recover the entity from its (row,col)-coordinates. -The view has a ``self.w()`` method that is used to output data, in our -example HTML output. - -.. note:: - You can find more details about views and selectors in :ref:`Views`. - - -.. _DefineEntities: - -Write entities to add logic in your data ----------------------------------------- - -By default, CubicWeb provides a default entity for each data type defined in the schema. -A default entity mainly contains the attributes defined in the data model. - -You can redefine each entity to provide additional functions to help you write your views. - -.. sourcecode:: python - - from cubicweb.entities import AnyEntity - - class BlogEntry(AnyEntity): - """customized class for BlogEntry entities""" - __regid__ = 'BlogEntry' - __implements__ = AnyEntity.__implements__ - - def display_cw_logo(self): - if 'CW' in self.title: - return True - else: - return False - -Customizing an entity requires that your entity: - - inherits from ``cubicweb.entities`` or any subclass - - defines a ``__regid__`` linked to the corresponding data type of your schema - - implements the base class by explicitly using ``__implements__``. - -We implemented here a function ``display_cw_logo`` which tests if the blog entry title contains 'CW'. -This function can then be used when you customize your views. For instance, you can modify your previous ``views.py`` as follows: - -.. sourcecode:: python - - class BlogEntryPrimaryView(primary.PrimaryView): - __select__ = implements('BlogEntry') - - ... - - def render_entity_title(self, entity): - if entity.display_cw_logo(): - self.w(u'') - super(BlogEntryPrimaryView, self).render_entity_title(entity) - -Then each blog entry whose title contains 'CW' is shown with the CubicWeb logo in front of it. - -.. _UpdatingSchemaAndSynchronisingInstance: - -Updating the schema and synchronising the instance --------------------------------------------------- - -While developping your cube, you may want to update your data model. Let's say you -want to add a ``category`` attribute in the ``Blog`` data type. This is called a migration. - -The required steps are: -1. modify the file ``schema.py``. The ``Blog`` class looks now like this: - -.. sourcecode:: python - - class Blog(EntityType): - title = String(maxsize=50, required=True) - description = String() - category = String(required=True, vocabulary=(_('Professional'), _('Personal')), default='Personal') - -2. stop your ``blogdemo`` instance - -3. start the cubicweb shell for your instance by running the following command: - -.. sourcecode:: bash - - cubicweb-ctl shell blogdemo - -4. in the shell, execute: - -.. sourcecode:: python - - add_attribute('Blog', 'category') - -5. you can restart your instance, modify a blog entity and check that the new attribute -``category`` has been added. - -Of course, you may also want to add relations, entity types, ... See :ref:`migration` -for a list of all available migration commands. - diff -r 385c2351153e -r 7ee07d18dc95 doc/book/en/intro/tutorial/index.rst --- a/doc/book/en/intro/tutorial/index.rst Wed Apr 14 10:31:09 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -.. -*- coding: utf-8 -*- - -.. _Tutorial: - -Tutorial -======== - -*CubicWeb* is a semantic web application framework that favors reuse and -object-oriented design. - -A `cube` is a component that includes a model defining the data types and a set of -views to display the data. A cube can be built by assembling other cubes. - -An `instance` is a specific installation of a cube and includes configuration -files. - - -This tutorial will show how to create a `cube` and how to use it as an -application to run an `instance`. - -.. toctree:: - :maxdepth: 2 - - blog-in-five-minutes - create-cube - components - maintemplate - conclusion diff -r 385c2351153e -r 7ee07d18dc95 doc/book/en/intro/tutorial/maintemplate.rst --- a/doc/book/en/intro/tutorial/maintemplate.rst Wed Apr 14 10:31:09 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,131 +0,0 @@ -.. -*- coding: utf-8 -*- - -Templates ---------- - -Look at ``cubicweb/web/views/basetemplates.py`` and you will -find the base templates used to generate HTML for your application. - -A page is composed as indicated on the schema below: - -.. image:: ../../images/lax-book.06-main-template-layout.en.png - -In this section we will demonstrate a change in one of the main -interesting template from the three you will look for, -that is to say, the HTMLPageHeader, the HTMLPageFooter -and the TheMainTemplate. - - -Customize a template -~~~~~~~~~~~~~~~~~~~~ - -Based on the diagram below, each template can be overriden -by your customized template. To do so, we recommand you create -a Python module ``blog.views.templates`` to keep it organized. -In this module you will have to import the parent class you are -interested as follows: :: - - from cubicweb.web.views.basetemplates import HTMLPageHeader, \ - HTMLPageFooter, TheMainTemplate - -and then create your sub-class:: - - class MyBlogHTMLPageHeader(HTMLPageHeader): - ... - -Customize header -````````````````` - -Let's now move the search box in the header and remove the login form from the -header. We'll show how to move it to the left column of the user interface. - -Let's say we do not want anymore the login menu to be in the header - -First, to remove the login menu, we just need to comment out the display of the -login graphic component such as follows: - -.. sourcecode:: python - - class MyBlogHTMLPageHeader(HTMLPageHeader): - - def main_header(self, view): - """build the top menu with authentification info and the rql box""" - self.w(u'\n') - self.w(u'\n') - # appliname and breadcrumbs - self.w(u'') - # logged user and help - #self.w(u'') - # lastcolumn - self.w(u'\n') - self.w(u'\n') - self.template('logform', rset=self.cw_rset, id='popupLoginBox', klass='hidden', - title=False, message=False) - - - -.. image:: ../../images/lax-book.06-header-no-login.en.png - -Customize footer -```````````````` - -If you want to change the footer for example, look -for HTMLPageFooter and override it in your views file as in: - -.. sourcecode:: python - - from cubicweb.web.views.basetemplates import HTMLPageFooter - - class MyHTMLPageFooter(HTMLPageFooter): - - def call(self, **kwargs): - self.w(u'') - -Updating a view does not require any restart of the server. By reloading -the page you can see your new page footer. - - -TheMainTemplate -``````````````` - -.. _TheMainTemplate: - -The MainTemplate is a bit complex as it tries to accomodate many -different cases. We are now about to go through it and cutomize entirely -our application. - -TheMainTemplate is responsible for the general layout of the entire application. -It defines the template of ``__regid__ = main`` that is used by the application. Is -also defined in ``cubicweb/web/views/basetemplates.py`` another template that can -be used based on TheMainTemplate called SimpleMainTemplate which does not have -a top section. - -.. image:: ../../images/lax-book.06-simple-main-template.en.png - -XXX -[WRITE ME] - -* customize MainTemplate and show that everything in the user - interface can be changed - diff -r 385c2351153e -r 7ee07d18dc95 doc/book/en/tutorials/advanced/index.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/tutorials/advanced/index.rst Wed Apr 14 16:15:08 2010 +0200 @@ -0,0 +1,586 @@ +.. _advanced_tutorial: + +Building a photo gallery with CubicWeb +====================================== + +Desired features +---------------- + +* basically a photo gallery + +* photo stored onto the fs and displayed dynamically through a web interface + +* navigation through folder (album), tags, geographical zone, people on the + picture... using facets + +* advanced security (eg not everyone can see everything). More on this later. + + +Cube creation and schema definition +----------------------------------- + +.. _adv_tuto_create_new_cube: + +Step 1: creating a new cube for my web site +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +One note about my development environment: I wanted to use packaged +version of CubicWeb and cubes while keeping my cube in my user +directory, let's say `~src/cubes`. I achieve this by setting the +following environment variables:: + + CW_CUBES_PATH=~/src/cubes + CW_MODE=user + +I can now create the cube which will hold custom code for this web +site using:: + + c-c newcube --directory=~/src/cubes sytweb + + +.. _adv_tuto_assemble_cubes: + +Step 2: pick building blocks into existing cubes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Almost everything I want represent in my web-site is somewhat already modelized in +some cube that I'll extend for my need. So I'll pick the following cubes: + +* `folder`, containing `Folder` entity type, which will be used as + both 'album' and a way to map file system folders. Entities are + added to a given folder using the `filed_under` relation. + +* `file`, containing `File` and `Image` entity types, gallery view, + and a file system import utility. + +* `zone`, containing the `Zone` entity type for hierarchical geographical + zones. Entities (including sub-zones) are added to a given zone using the + `situated_in` relation. + +* `person`, containing the `Person` entity type plus some basic views. + +* `comment`, providing a full commenting system allowing one to comment entity types + supporting the `comments` relation by adding a `Comment` entity. + +* `tag`, providing a full tagging system as a easy and powerful way to classify + entities supporting the `tags` relation by linking the to `Tag` entities. This + will allows navigation into a large number of picture. + +Ok, now I'll tell my cube requires all this by editing cubes/sytweb/__pkginfo__.py: + + .. sourcecode:: python + + __depends_cubes__ = {'file': '>= 1.2.0', + 'folder': '>= 1.1.0', + 'person': '>= 1.2.0', + 'comment': '>= 1.2.0', + 'tag': '>= 1.2.0', + 'zone': None, + } + __depends__ = {'cubicweb': '>= 3.5.10', + } + for key,value in __depends_cubes__.items(): + __depends__['cubicweb-'+key] = value + __use__ = tuple(__depends_cubes__) + +Notice that you can express minimal version of the cube that should be used, +`None` meaning whatever version available. + +Step 3: glue everything together in my cube's schema +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. sourcecode:: python + + from yams.buildobjs import RelationDefinition + + class comments(RelationDefinition): + subject = 'Comment' + object = ('File', 'Image') + cardinality = '1*' + composite = 'object' + + class tags(RelationDefinition): + subject = 'Tag' + object = ('File', 'Image') + + class filed_under(RelationDefinition): + subject = ('File', 'Image') + object = 'Folder' + + class situated_in(RelationDefinition): + subject = 'Image' + object = 'Zone' + + class displayed_on(RelationDefinition): + subject = 'Person' + object = 'Image' + + +This schema: + +* allows to comment and tag on `File` and `Image` entity types by adding the + `comments` and `tags` relations. This should be all we've to do for this + feature since the related cubes provide 'pluggable section' which are + automatically displayed on the primary view of entity types supporting the + relation. + +* adds a `situated_in` relation definition so that image entities can be + geolocalized. + +* add a new relation `displayed_on` relation telling who can be seen on a + picture. + +This schema will probably have to evolve as time goes (for security handling at +least), but since the possibility to make schema evolving is one of CubicWeb +feature (and goal), we won't worry and see that later when needed. + + +Step 4: creating the instance +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now that I've a schema, I want to create an instance so I can start To +create an instance using this new 'sytweb' cube, I run:: + + c-c create sytweb sytweb_instance + +hint : if you get an error while the database is initialized, you can +avoid having to reanswer to questions by runing :: + + c-c db-create sytweb_instance + +This will use your already configured instance and start directly from the create +database step, thus skipping questions asked by the 'create' command. + +Once the instance and database are fully initialized, run :: + + c-c start sytweb_instance + +to start the instance, check you can connect on it, etc... + + +Security, testing and migration +------------------------------- + +This post will cover various topics: + +* configuring security +* migrating existing instance +* writing some unit tests + +Here is the ``read`` security model I want: + +* folders, files, images and comments should have one of the following visibility: + - ``public``, everyone can see it + - ``authenticated``, only authenticated users can see it + - ``restricted``, only a subset of authenticated users can see it +* managers (e.g. me) can see everything +* only authenticated user can see people +* everyone can see classifier entities, eg tag and zone + +Also, unless explicity specified, visibility of an image should be the same as +its parent folder, as well as visibility of a comment should be the same as the +commented entity. If there is no parent entity, the default visibility is +``authenticated``. + +Regarding write security, that's much easier: +* anonymous can't write anything +* authenticated users can only add comment +* managers will add the remaining stuff + +Now, let's implement that! + +Proper security in CubicWeb is done at the schema level, so you don't have to +bother with it in views: users will only see what they can see automatically. + +.. _adv_tuto_security: + +Step 1: configuring security into the schema +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In schema, you can grant access according to groups, or to some RQL expressions: +users get access it the expression return some results. To implements the read +security defined earlier, groups are not enough, we'll need RQL expression. Here +is the idea: + +* add a `visibility` attribute on folder, image and comment, which may be one of + the value explained above + +* add a `may_be_read_by` relation from folder, image and comment to users, + which will define who can see the entity + +* security propagation will be done in hook. + +So the first thing to do is to modify my cube'schema.py to define those +relations: + +.. sourcecode:: python + + from yams.constraints import StaticVocabularyConstraint + + class visibility(RelationDefinition): + subject = ('Folder', 'File', 'Image', 'Comment') + object = 'String' + constraints = [StaticVocabularyConstraint(('public', 'authenticated', + 'restricted', 'parent'))] + default = 'parent' + cardinality = '11' # required + + class may_be_read_by(RelationDefinition): + subject = ('Folder', 'File', 'Image', 'Comment',) + object = 'CWUser' + +We can note the following points: + +* we've added a new `visibility` attribute to folder, file, image and comment + using a `RelationDefinition` + +* `cardinality = '11'` means this attribute is required. This is usually hidden + under the `required` argument given to the `String` constructor, but we can + rely on this here (same thing for StaticVocabularyConstraint, which is usually + hidden by the `vocabulary` argument) + +* the `parent` possible value will be used for visibility propagation + +Now, we should be able to define security rules in the schema, based on these new +attribute and relation. Here is the code to add to *schema.py*: + +.. sourcecode:: python + + from cubicweb.schema import ERQLExpression + + VISIBILITY_PERMISSIONS = { + 'read': ('managers', + ERQLExpression('X visibility "public"'), + ERQLExpression('X may_be_read_by U')), + 'add': ('managers',), + 'update': ('managers', 'owners',), + 'delete': ('managers', 'owners'), + } + AUTH_ONLY_PERMISSIONS = { + 'read': ('managers', 'users'), + 'add': ('managers',), + 'update': ('managers', 'owners',), + 'delete': ('managers', 'owners'), + } + CLASSIFIERS_PERMISSIONS = { + 'read': ('managers', 'users', 'guests'), + 'add': ('managers',), + 'update': ('managers', 'owners',), + 'delete': ('managers', 'owners'), + } + + from cubes.folder.schema import Folder + from cubes.file.schema import File, Image + from cubes.comment.schema import Comment + from cubes.person.schema import Person + from cubes.zone.schema import Zone + from cubes.tag.schema import Tag + + Folder.__permissions__ = VISIBILITY_PERMISSIONS + File.__permissions__ = VISIBILITY_PERMISSIONS + Image.__permissions__ = VISIBILITY_PERMISSIONS + Comment.__permissions__ = VISIBILITY_PERMISSIONS.copy() + Comment.__permissions__['add'] = ('managers', 'users',) + Person.__permissions__ = AUTH_ONLY_PERMISSIONS + Zone.__permissions__ = CLASSIFIERS_PERMISSIONS + Tag.__permissions__ = CLASSIFIERS_PERMISSIONS + +What's important in there: + +* `VISIBILITY_PERMISSIONS` provides read access to managers group, if + `visibility` attribute's value is 'public', or if user (designed by the 'U' + variable in the expression) is linked to the entity (the 'X' variable) through + the `may_read` permission + +* we modify permissions of the entity types we use by importing them and + modifying their `__permissions__` attribute + +* notice the `.copy()`: we only want to modify 'add' permission for `Comment`, + not for all entity types using `VISIBILITY_PERMISSIONS`! + +* the remaining part of the security model is done using regular groups: + + - `users` is the group to which all authenticated users will belong + - `guests` is the group of anonymous users + + +.. _ adv_tuto_security_propagation: + +Step 2: security propagation in hooks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To fullfill the requirements, we have to implement:: + + Also, unless explicity specified, visibility of an image should be the same as + its parent folder, as well as visibility of a comment should be the same as the + commented entity. + +This kind of `active` rule will be done using CubicWeb's hook +system. Hooks are triggered on database event such as addition of new +entity or relation. + +The trick part of the requirement is in *unless explicitly specified*, notably +because when the entity addition hook is added, we don't know yet its 'parent' +entity (eg folder of an image, image commented by a comment). To handle such things, +CubicWeb provides `Operation`, which allow to schedule things to do at commit time. + +In our case we will: + +* on entity creation, schedule an operation that will set default visibility + +* when a "parent" relation is added, propagate parent's visibility unless the + child already has a visibility set + +Here is the code in cube's *hooks.py*: + +.. sourcecode:: python + + from cubicweb.selectors import implements + from cubicweb.server import hook + + class SetVisibilityOp(hook.Operation): + def precommit_event(self): + for eid in self.session.transaction_data.pop('pending_visibility'): + entity = self.session.entity_from_eid(eid) + if entity.visibility == 'parent': + entity.set_attributes(visibility=u'authenticated') + + class SetVisibilityHook(hook.Hook): + __regid__ = 'sytweb.setvisibility' + __select__ = hook.Hook.__select__ & implements('Folder', 'File', 'Image', 'Comment') + events = ('after_add_entity',) + def __call__(self): + hook.set_operation(self._cw, 'pending_visibility', self.entity.eid, + SetVisibilityOp) + + class SetParentVisibilityHook(hook.Hook): + __regid__ = 'sytweb.setparentvisibility' + __select__ = hook.Hook.__select__ & hook.match_rtype('filed_under', 'comments') + events = ('after_add_relation',) + + def __call__(self): + parent = self._cw.entity_from_eid(self.eidto) + child = self._cw.entity_from_eid(self.eidfrom) + if child.visibility == 'parent': + child.set_attributes(visibility=parent.visibility) + +Notice: + +* hooks are application objects, hence have selectors that should match entity or + relation types to which the hook applies. To match a relation type, we use the + hook specific `match_rtype` selector. + +* usage of `set_operation`: instead of adding an operation for each added entity, + set_operation allows to create a single one and to store entity's eids to be + processed in session's transaction data. This is a good pratice to avoid heavy + operations manipulation cost when creating a lot of entities in the same + transaction. + +* the `precommit_event` method of the operation will be called at transaction's + commit time. + +* in a hook, `self._cw` is the repository session, not a web request as usually + in views + +* according to hook's event, you have access to different attributes on the hook + instance. Here: + + - `self.entity` is the newly added entity on 'after_add_entity' events + + - `self.eidfrom` / `self.eidto` are the eid of the subject / object entity on + 'after_add_relatiohn' events (you may also get the relation type using + `self.rtype`) + +The `parent` visibility value is used to tell "propagate using parent security" +because we want that attribute to be required, so we can't use None value else +we'll get an error before we get any chance to propagate... + +Now, we also want to propagate the `may_be_read_by` relation. Fortunately, +CubicWeb provides some base hook classes for such things, so we only have to add +the following code to *hooks.py*: + +.. sourcecode:: python + + # relations where the "parent" entity is the subject + S_RELS = set() + # relations where the "parent" entity is the object + O_RELS = set(('filed_under', 'comments',)) + + class AddEntitySecurityPropagationHook(hook.PropagateSubjectRelationHook): + """propagate permissions when new entity are added""" + __regid__ = 'sytweb.addentity_security_propagation' + __select__ = (hook.PropagateSubjectRelationHook.__select__ + & hook.match_rtype_sets(S_RELS, O_RELS)) + main_rtype = 'may_be_read_by' + subject_relations = S_RELS + object_relations = O_RELS + + class AddPermissionSecurityPropagationHook(hook.PropagateSubjectRelationAddHook): + """propagate permissions when new entity are added""" + __regid__ = 'sytweb.addperm_security_propagation' + __select__ = (hook.PropagateSubjectRelationAddHook.__select__ + & hook.match_rtype('may_be_read_by',)) + subject_relations = S_RELS + object_relations = O_RELS + + class DelPermissionSecurityPropagationHook(hook.PropagateSubjectRelationDelHook): + __regid__ = 'sytweb.delperm_security_propagation' + __select__ = (hook.PropagateSubjectRelationDelHook.__select__ + & hook.match_rtype('may_be_read_by',)) + subject_relations = S_RELS + object_relations = O_RELS + +* the `AddEntitySecurityPropagationHook` will propagate the relation + when `filed_under` or `comments` relations are added + + - the `S_RELS` and `O_RELS` set as well as the `match_rtype_sets` selector are + used here so that if my cube is used by another one, it'll be able to + configure security propagation by simply adding relation to one of the two + sets. + +* the two others will propagate permissions changes on parent entities to + children entities + + +.. _adv_tuto_tesing_security: + +Step 3: testing our security +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Security is tricky. Writing some tests for it is a very good idea. You should +even write them first, as Test Driven Development recommends! + +Here is a small test case that will check the basis of our security +model, in *test/unittest_sytweb.py*: + +.. sourcecode:: python + + from cubicweb.devtools.testlib import CubicWebTC + from cubicweb import Binary + + class SecurityTC(CubicWebTC): + + def test_visibility_propagation(self): + # create a user for later security checks + toto = self.create_user('toto') + # init some data using the default manager connection + req = self.request() + folder = req.create_entity('Folder', + name=u'restricted', + visibility=u'restricted') + photo1 = req.create_entity('Image', + data_name=u'photo1.jpg', + data=Binary('xxx'), + filed_under=folder) + self.commit() + photo1.clear_all_caches() # good practice, avoid request cache effects + # visibility propagation + self.assertEquals(photo1.visibility, 'restricted') + # unless explicitly specified + photo2 = req.create_entity('Image', + data_name=u'photo2.jpg', + data=Binary('xxx'), + visibility=u'public', + filed_under=folder) + self.commit() + self.assertEquals(photo2.visibility, 'public') + # test security + self.login('toto') + req = self.request() + self.assertEquals(len(req.execute('Image X')), 1) # only the public one + self.assertEquals(len(req.execute('Folder X')), 0) # restricted... + # may_be_read_by propagation + self.restore_connection() + folder.set_relations(may_be_read_by=toto) + self.commit() + photo1.clear_all_caches() + self.failUnless(photo1.may_be_read_by) + # test security with permissions + self.login('toto') + req = self.request() + self.assertEquals(len(req.execute('Image X')), 2) # now toto has access to photo2 + self.assertEquals(len(req.execute('Folder X')), 1) # and to restricted folder + + if __name__ == '__main__': + from logilab.common.testlib import unittest_main + unittest_main() + +It's not complete, but show most things you'll want to do in tests: adding some +content, creating users and connecting as them in the test, etc... + +To run it type: :: + + [syt@scorpius test]$ pytest unittest_sytweb.py + ======================== unittest_sytweb.py ======================== + -> creating tables [....................] + -> inserting default user and default groups. + -> storing the schema in the database [....................] + -> database for instance data initialized. + . + ---------------------------------------------------------------------- + Ran 1 test in 22.547s + + OK + + +The first execution is taking time, since it creates a sqlite database for the +test instance. The second one will be much quicker: :: + + [syt@scorpius test]$ pytest unittest_sytweb.py + ======================== unittest_sytweb.py ======================== + . + ---------------------------------------------------------------------- + Ran 1 test in 2.662s + + OK + +If you do some changes in your schema, you'll have to force regeneration of that +database. You do that by removing the tmpdb files before running the test: :: + + [syt@scorpius test]$ rm tmpdb* + + +.. Note:: + pytest is a very convenient utilities to control test execution, from the `logilab-common`_ + package + +.. _`logilab-common`: http://www.logilab.org/project/logilab-common + +.. _adv_tuto_migration_script: + +Step 4: writing the migration script and migrating the instance +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Prior to those changes, Iv'e created an instance, feeded it with some data, so I +don't want to create a new one, but to migrate the existing one. Let's see how to +do that. + +Migration commands should be put in the cube's *migration* directory, in a +file named file:`_Any.py` ('Any' being there mostly for historical reason). + +Here I'll create a *migration/0.2.0_Any.py* file containing the following +instructions: + +.. sourcecode:: python + + add_relation_type('may_be_read_by') + add_relation_type('visibility') + sync_schema_props_perms() + +Then I update the version number in cube's *__pkginfo__.py* to 0.2.0. And +that's it! Those instructions will: + +* update the instance's schema by adding our two new relations and update the + underlying database tables accordingly (the two first instructions) + +* update schema's permissions definition (the later instruction) + + +To migrate my instance I simply type:: + + [syt@scorpius ~]$ cubicweb-ctl upgrade sytweb + +I'll then be asked some questions to do the migration step by step. You should say +YES when it asks if a backup of your database should be done, so you can get back +to initial state if anything goes wrong... + diff -r 385c2351153e -r 7ee07d18dc95 doc/book/en/tutorials/base/blog-in-five-minutes.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/tutorials/base/blog-in-five-minutes.rst Wed Apr 14 16:15:08 2010 +0200 @@ -0,0 +1,42 @@ +.. -*- coding: utf-8 -*- + +.. _BlogFiveMinutes: + +Get a blog running in five minutes! +----------------------------------- + +For Debian or Ubuntu users, first install the following packages (:ref:`DebianInstallation`):: + + cubicweb, cubicweb-dev, cubicweb-blog + +For Windows or Mac OS X users, you must install cubicweb from source (see :ref:`SourceInstallation` and :ref:`WindowsInstallation`). + +Then create and initialize your instance:: + + cubicweb-ctl create blog myblog + +And start it:: + + cubicweb-ctl start -D myblog + +The -D option is the debugging mode of cubicweb, removing it will lauch the instance in the background. + +Permission +~~~~~~~~~~ + +This command assumes that you have root access to the /etc/ path. In order to initialize your instance as a `user` (from scratch), please check your current PYTHONPATH then create the ~/etc/cubicweb.d directory. + +Instance parameters +~~~~~~~~~~~~~~~~~~~ + +If the database installation failed, you'd like to change some instance parameters, for example, the database host or the user name. These informations can be edited in the `source` file located in the /etc/cubicweb.d/myblog directory. + +Then relaunch the database creation: + + cubicweb-ctl db-create myblog + +Other paramaters, like web server or emails parameters, can be modified in the `all-in-one.conf` file. + +This is it. Your blog is running. Visit http://localhost:8080 and enjoy it! This blog is fully functionnal. The next section section will present the way to develop new cubes and customizing the look of your instance. + + diff -r 385c2351153e -r 7ee07d18dc95 doc/book/en/tutorials/base/components.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/tutorials/base/components.rst Wed Apr 14 16:15:08 2010 +0200 @@ -0,0 +1,79 @@ +.. -*- coding: utf-8 -*- + +.. _cubes: + +Cubes +----- + +Standard library +~~~~~~~~~~~~~~~~ + +A library of standard cubes are available from `CubicWeb Forge`_ +Cubes provide entities and views. + +The available application entities in standard cubes are: + +* addressbook: PhoneNumber and PostalAddress + +* basket: Basket (like a shopping cart) + +* blog: Blog (a *very* basic blog) + +* classfolder: Folder (to organize things but grouping them in folders) + +* classtags: Tag (to tag anything) + +* comment: Comment (to attach comment threads to entities) + +* file: File (to allow users to upload and store binary or text files) + +* link: Link (to collect links to web resources) + +* mailinglist: MailingList (to reference a mailing-list and the URLs + for its archives and its admin interface) + +* person: Person (easily mixed with addressbook) + +* task: Task (something to be done between start and stop date) + +* zone: Zone (to define places within larger places, for example a + city in a state in a country) + +.. _`CubicWeb Forge`: http://www.cubicweb.org/project/ + +Adding comments to BlogDemo +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To import a cube in your instance just change the line in the +``__pkginfo__.py`` file and verify that the cube you are planning +to use is listed by the command ``cubicweb-ctl list``. +For example:: + + __use__ = ('comment',) + +will make the ``Comment`` entity available in your ``BlogDemo`` +cube. + +Change the schema to add a relationship between ``BlogEntry`` and +``Comment`` and you are done. Since the comment cube defines the +``comments`` relationship, adding the line:: + + comments = ObjectRelation('Comment', cardinality='1*', composite='object') + +to the definition of a ``BlogEntry`` will be enough. + +Synchronize the data model +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Once you modified your data model, you need to synchronize the +database with your model. For this purpose, *CubicWeb* provides +a very useful command ``cubicweb-ctl shell blogdemo`` which +launches an interactive shell where you can enter migration +commands (see :ref:`cubicweb-ctl` for more details)). +As you added the cube named `comment`, you need to run: + +:: + + add_cube('comment') + +You can now start your instance and comment your blog entries. diff -r 385c2351153e -r 7ee07d18dc95 doc/book/en/tutorials/base/conclusion.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/tutorials/base/conclusion.rst Wed Apr 14 16:15:08 2010 +0200 @@ -0,0 +1,15 @@ +.. -*- coding: utf-8 -*- + +What's next? +------------ + +We demonstrated how from a straight out of the box *CubicWeb* installation, you +can build your web application based on a data model. It's all already there: +views, templates, permissions, etc. The step forward is now for you to customize +according to your needs. + +Many features are available to extend your application, for example: RSS channel +integration (:ref:`XmlAndRss`), hooks (:ref:`hooks`), support of sources such as +Google App Engine (:ref:`GoogleAppEngineSource`) and lots of others to discover +through our book. + diff -r 385c2351153e -r 7ee07d18dc95 doc/book/en/tutorials/base/create-cube.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/tutorials/base/create-cube.rst Wed Apr 14 16:15:08 2010 +0200 @@ -0,0 +1,431 @@ +.. -*- coding: utf-8 -*- + +.. _Steps: + +Steps for creating your cube +---------------------------- + +The following steps will help you to create and customize a new cube. + +1. :ref:`CreateYourCube` + +Create the directory to hold the code of your cube. The most important +files that will be useful to customize your newly created cube are: + + * schema.py: contains the data model + * views.py: contains your custom views + * entities.py: contains XXX + +The detailed structure of the code directory is described in :ref:`cubelayout`. + +2. :ref:`DefineDataModel` + +Define the data model of your application. + +3. :ref:`ExploreYourInstance` + +Create, run, and explore an instance of your cube. + +4. :ref:`DefineViews` + +Customize the views of your data: how and which part of your data are showed. + +Note: views don't concern the look'n'feel or design of the site. For that, you should use CSS instead, and default CSS or your new cube are located in 'blog/data/'. + + +5. :ref:`DefineEntities` + +Define your own entities to add useful functions when you manipulate your data, especially when you write view. + + +.. _CreateYourCube: + +Create your cube +---------------- + +The packages ``cubicweb`` and ``cubicweb-dev`` install a command line +tool for *CubicWeb* called ``cubicweb-ctl``. This tool provides a wide +range of commands described in details in :ref:`cubicweb-ctl`. + +Once your *CubicWeb* development environment is set up, you can create +a new cube:: + + cubicweb-ctl newcube blog + +This will create in the cubes directory (``/path/to/forest/cubes`` for Mercurial +installation, ``/usr/share/cubicweb/cubes`` for debian packages installation) +a directory named ``blog`` reflecting the structure described in :ref:`Concepts`. + + +For packages installation, you can still create new cubes in your home directory using the following configuration. Let's say you want to develop your new cubes in `~src/cubes`, then set the following environment variables: +:: + + CW_CUBES_PATH=~/src/cubes + CW_MODE=user + +and then create your new cube using: +:: + + cubicweb-ctl newcube --directory=~/src/cubes blog + + + + + + +.. _DefineDataModel: + +Define your data model +---------------------- + +The data model or schema is the core of your *CubicWeb* application. +It defines the type of content your application will handle. + +The data model of your cube ``blog`` is defined in the file ``schema.py``: + +.. sourcecode:: python + + from yams.buildobjs import EntityType, String, SubjectRelation, Date + + 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='?*') + +The first step is the import of the EntityType (generic class for entity and +attributes that will be used in both Blog and BlogEntry entities. + +A Blog has a title and a description. The title is a string that is +required and must be less than 50 characters. The +description is a string that is not constrained. + +A BlogEntry has a title, a publish_date and a content. The title is a +string that is required and must be less than 100 characters. The +publish_date is a Date with a default value of TODAY, meaning that +when a BlogEntry is created, its publish_date will be the current day +unless it is modified. The content is a string that will be indexed in +the database full-text index and has no constraint. + +A BlogEntry also has a relationship ``entry_of`` that links it to a +Blog. The cardinality ``?*`` means that a BlogEntry can be part of +zero or one Blog (``?`` means `zero or one`) and that a Blog can +have any number of BlogEntry (``*`` means `any number including +zero`). For completeness, remember that ``+`` means `one or more`. + + +.. _ExploreYourInstance: + +Create and explore your instance +-------------------------------- +.. _CreateYourInstance: + +Create your instance +~~~~~~~~~~~~~~~~~~~~ + +To use this cube as an instance and create a new instance named ``blogdemo``, do:: + + cubicweb-ctl create blog blogdemo + +This command will create the corresponding database and initialize it. + + +.. _WelcomeToYourWebInstance: + +Welcome to your web instance +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Start your instance in debug mode with the following command: :: + + cubicweb-ctl start -D blogdemo + + +You can now access your web instance to create blogs and post messages +by visiting the URL http://localhost:8080/. + +A login form will appear. By default, the instance will not allow anonymous +users to enter the instance. To login, you need then use the admin account +you created at the time you initialized the database with ``cubicweb-ctl +create``. + +.. image:: ../../images/login-form.png + + +Once authenticated, you can start playing with your instance +and create entities. + +.. image:: ../../images/blog-demo-first-page.png + +Please notice that so far, the *CubicWeb* framework managed all aspects of +the web application based on the schema provided at the beginning. + +.. _AddEntities: + +Add entities +~~~~~~~~~~~~ + +We will now add entities in our web application. + +Add a Blog +********** + +Let us create a few of these entities. Click on the `[+]` at the left of the +link Blog on the home page. Call this new Blog ``Tech-blog`` and type in +``everything about technology`` as the description, then validate the form by +clicking on ``Validate``. + +.. image:: ../../images/cbw-create-blog.en.png + :alt: from to create blog + +Click on the logo at top left to get back to the home page, then +follow the Blog link that will list for you all the existing Blog. +You should be seeing a list with a single item ``Tech-blog`` you +just created. + +.. image:: ../../images/cbw-list-one-blog.en.png + :alt: displaying a list of a single blog + +Clicking on this item will get you to its detailed description except +that in this case, there is not much to display besides the name and +the phrase ``everything about technology``. + +Now get back to the home page by clicking on the top-left logo, then +create a new Blog called ``MyLife`` and get back to the home page +again to follow the Blog link for the second time. The list now +has two items. + +.. image:: ../../images/cbw-list-two-blog.en.png + :alt: displaying a list of two blogs + +Add a BlogEntry +*************** + +Get back to the home page and click on [+] at the left of the link +BlogEntry. Call this new entry ``Hello World`` and type in some text +before clicking on ``Validate``. You added a new blog entry without +saying to what blog it belongs. There is a box on the left entitled +``actions``, click on the menu item ``modify``. You are back to the form +to edit the blog entry you just created, except that the form now has +another section with a combobox titled ``add relation``. Chose +``entry_of`` in this menu and a second combobox appears where you pick +``MyLife``. + +You could also have, at the time you started to fill the form for a +new entity BlogEntry, hit ``Apply`` instead of ``Validate`` and the +combobox titled ``add relation`` would have showed up. + + +.. image:: ../../images/cbw-add-relation-entryof.en.png + :alt: editing a blog entry to add a relation to a blog + +Validate the changes by clicking ``Validate``. The entity BlogEntry +that is displayed now includes a link to the entity Blog named +``MyLife``. + +.. image:: ../../images/cbw-detail-one-blogentry.en.png + :alt: displaying the detailed view of a blogentry + +Note that all of this was handled by the framework and that the only input +that was provided so far is the schema. To get a graphical view of the schema, +point your browser to the URL http://localhost:8080/schema + +.. image:: ../../images/cbw-schema.en.png + :alt: graphical view of the schema (aka data-model) + + +.. _DefineViews: + +Define your entity views +------------------------ + +Each entity defined in a model is associated with default views +allowing different renderings of the data. You can redefine each of +them according to your needs and preferences. So let's see how the +views are defined. + + +The view selection principle +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A view is defined by a Python class which includes: + + - an identifier (all objects in *CubicWeb* are recorded in a + registry and this identifier will be used as a key) + + - a filter to select the result sets it can be applied to + +A view has a set of methods complying with the `View` class interface +(`cubicweb.common.view`). + +*CubicWeb* provides a lot of standard views for the type `EntityView`; +for a complete list, read the code in directory ``cubicweb/web/views/``. + +A view is applied on a `result set` which contains a set of entities +we are trying to display. *CubicWeb* uses a selector mechanism which +computes for each available view a score: the view with the highest +score is then used to display the given `result set`. The standard +library of selectors is in ``cubicweb.selector``. + +It is possible to define multiple views for the same identifier +and to associate selectors and filters to allow the application +to find the most appropriate way to render the data. + +For example, the view named ``primary`` is the one used to display a +single entity. We will now show you how to create a primary view for +BlogEntry. + + +Primary view customization +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you wish to modify the way a `BlogEntry` is rendered, you will have +to subclass the `primary` view, for instance in the module ``views`` +of the cube ``cubes/blog/views.py``. + +The standard primary view is the most sophisticated view of all. It +has more than a call() method. It is a template. Actually the entry +point calls the following sequence of (redefinable) methods: + + * render_entity_title + + * render_entity_metadata + + * render_entity_attributes + + * render_entity_relations + + * render_side_boxes + +Excepted side boxes, we can see all of them already in action in the +blog entry view. This is all described in more details in +:ref:`primary`. + +We can for example add in front of the publication date a prefix +specifying that the date we see is the publication date. + +To do so, please apply the following changes: + +.. sourcecode:: python + + from cubicweb.selectors import implements + from cubicweb.web.views import primary + + class BlogEntryPrimaryView(primary.PrimaryView): + __select__ = implements('BlogEntry') + + def render_entity_attributes(self, entity): + self.w(u'

published on %s

' % + entity.publish_date.strftime('%Y-%m-%d')) + super(BlogEntryPrimaryView, self).render_entity_attributes(entity) + +.. note:: + When a view is modified, it is not required to restart the instance + server. Save the Python file and reload the page in your web browser + to view the changes. + +You can now see that the publication date has a prefix. + +.. image:: ../../images/cbw-update-primary-view.en.png + :alt: modified primary view + + +The above source code defines a new primary view for ``BlogEntry``. + +Since views are applied to result sets and result sets can be tables of +data, we have to recover the entity from its (row,col)-coordinates. +The view has a ``self.w()`` method that is used to output data, in our +example HTML output. + +.. note:: + You can find more details about views and selectors in :ref:`Views`. + + +.. _DefineEntities: + +Write entities to add logic in your data +---------------------------------------- + +By default, CubicWeb provides a default entity for each data type defined in the schema. +A default entity mainly contains the attributes defined in the data model. + +You can redefine each entity to provide additional functions to help you write your views. + +.. sourcecode:: python + + from cubicweb.entities import AnyEntity + + class BlogEntry(AnyEntity): + """customized class for BlogEntry entities""" + __regid__ = 'BlogEntry' + __implements__ = AnyEntity.__implements__ + + def display_cw_logo(self): + if 'CW' in self.title: + return True + else: + return False + +Customizing an entity requires that your entity: + - inherits from ``cubicweb.entities`` or any subclass + - defines a ``__regid__`` linked to the corresponding data type of your schema + - implements the base class by explicitly using ``__implements__``. + +We implemented here a function ``display_cw_logo`` which tests if the blog entry title contains 'CW'. +This function can then be used when you customize your views. For instance, you can modify your previous ``views.py`` as follows: + +.. sourcecode:: python + + class BlogEntryPrimaryView(primary.PrimaryView): + __select__ = implements('BlogEntry') + + ... + + def render_entity_title(self, entity): + if entity.display_cw_logo(): + self.w(u'') + super(BlogEntryPrimaryView, self).render_entity_title(entity) + +Then each blog entry whose title contains 'CW' is shown with the CubicWeb logo in front of it. + +.. _UpdatingSchemaAndSynchronisingInstance: + +Updating the schema and synchronising the instance +-------------------------------------------------- + +While developping your cube, you may want to update your data model. Let's say you +want to add a ``category`` attribute in the ``Blog`` data type. This is called a migration. + +The required steps are: +1. modify the file ``schema.py``. The ``Blog`` class looks now like this: + +.. sourcecode:: python + + class Blog(EntityType): + title = String(maxsize=50, required=True) + description = String() + category = String(required=True, vocabulary=(_('Professional'), _('Personal')), default='Personal') + +2. stop your ``blogdemo`` instance + +3. start the cubicweb shell for your instance by running the following command: + +.. sourcecode:: bash + + cubicweb-ctl shell blogdemo + +4. in the shell, execute: + +.. sourcecode:: python + + add_attribute('Blog', 'category') + +5. you can restart your instance, modify a blog entity and check that the new attribute +``category`` has been added. + +Of course, you may also want to add relations, entity types, ... See :ref:`migration` +for a list of all available migration commands. + diff -r 385c2351153e -r 7ee07d18dc95 doc/book/en/tutorials/base/index.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/tutorials/base/index.rst Wed Apr 14 16:15:08 2010 +0200 @@ -0,0 +1,29 @@ +.. -*- coding: utf-8 -*- + +.. _Tutorial: + + +Building a simple blog +====================== + +*CubicWeb* is a semantic web application framework that favors reuse and +object-oriented design. + +A `cube` is a component that includes a model defining the data types and a set of +views to display the data. A cube can be built by assembling other cubes. + +An `instance` is a specific installation of a cube and includes configuration +files. + + +This tutorial will show how to create a `cube` and how to use it as an +application to run an `instance`. + +.. toctree:: + :maxdepth: 2 + + blog-in-five-minutes + create-cube + components + maintemplate + conclusion diff -r 385c2351153e -r 7ee07d18dc95 doc/book/en/tutorials/base/maintemplate.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/tutorials/base/maintemplate.rst Wed Apr 14 16:15:08 2010 +0200 @@ -0,0 +1,131 @@ +.. -*- coding: utf-8 -*- + +Templates +--------- + +Look at ``cubicweb/web/views/basetemplates.py`` and you will +find the base templates used to generate HTML for your application. + +A page is composed as indicated on the schema below: + +.. image:: ../../images/lax-book.06-main-template-layout.en.png + +In this section we will demonstrate a change in one of the main +interesting template from the three you will look for, +that is to say, the HTMLPageHeader, the HTMLPageFooter +and the TheMainTemplate. + + +Customize a template +~~~~~~~~~~~~~~~~~~~~ + +Based on the diagram below, each template can be overriden +by your customized template. To do so, we recommand you create +a Python module ``blog.views.templates`` to keep it organized. +In this module you will have to import the parent class you are +interested as follows: :: + + from cubicweb.web.views.basetemplates import HTMLPageHeader, \ + HTMLPageFooter, TheMainTemplate + +and then create your sub-class:: + + class MyBlogHTMLPageHeader(HTMLPageHeader): + ... + +Customize header +````````````````` + +Let's now move the search box in the header and remove the login form from the +header. We'll show how to move it to the left column of the user interface. + +Let's say we do not want anymore the login menu to be in the header + +First, to remove the login menu, we just need to comment out the display of the +login graphic component such as follows: + +.. sourcecode:: python + + class MyBlogHTMLPageHeader(HTMLPageHeader): + + def main_header(self, view): + """build the top menu with authentification info and the rql box""" + self.w(u'\n') + self.w(u'\n') + # appliname and breadcrumbs + self.w(u'') + # logged user and help + #self.w(u'') + # lastcolumn + self.w(u'\n') + self.w(u'\n') + self.template('logform', rset=self.cw_rset, id='popupLoginBox', klass='hidden', + title=False, message=False) + + + +.. image:: ../../images/lax-book.06-header-no-login.en.png + +Customize footer +```````````````` + +If you want to change the footer for example, look +for HTMLPageFooter and override it in your views file as in: + +.. sourcecode:: python + + from cubicweb.web.views.basetemplates import HTMLPageFooter + + class MyHTMLPageFooter(HTMLPageFooter): + + def call(self, **kwargs): + self.w(u'') + +Updating a view does not require any restart of the server. By reloading +the page you can see your new page footer. + + +TheMainTemplate +``````````````` + +.. _TheMainTemplate: + +The MainTemplate is a bit complex as it tries to accomodate many +different cases. We are now about to go through it and cutomize entirely +our application. + +TheMainTemplate is responsible for the general layout of the entire application. +It defines the template of ``__regid__ = main`` that is used by the application. Is +also defined in ``cubicweb/web/views/basetemplates.py`` another template that can +be used based on TheMainTemplate called SimpleMainTemplate which does not have +a top section. + +.. image:: ../../images/lax-book.06-simple-main-template.en.png + +XXX +[WRITE ME] + +* customize MainTemplate and show that everything in the user + interface can be changed + diff -r 385c2351153e -r 7ee07d18dc95 doc/book/en/tutorials/index.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/tutorials/index.rst Wed Apr 14 16:15:08 2010 +0200 @@ -0,0 +1,19 @@ +.. _Tutorials: + +--------- +Tutorials +--------- + +We present two tutorials of different levels. The blog building +tutorial introduces one smoothly to the basic concepts. + +Then there is a photo gallery construction tutorial which highlights +more advanced concepts such as unit tests, security settings, +migration scripts. + +.. toctree:: + :maxdepth: 1 + :numbered: + + base/index + advanced/index