# HG changeset patch # User Sylvain Thénault # Date 1295425875 -3600 # Node ID 117b53c10d7903edb2898dd47622ef0229ed3fc8 # Parent 026581ea2b16faa3dceab71c21e8fd6a24fa1e36# Parent f04df13fc85141fbfb9abc028064afca765d31f4 backport stable diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/devrepo/testing.rst --- a/doc/book/en/devrepo/testing.rst Fri Jan 14 08:10:41 2011 +0100 +++ b/doc/book/en/devrepo/testing.rst Wed Jan 19 09:31:15 2011 +0100 @@ -196,13 +196,13 @@ blog_entry_2 = req.create_entity('BlogEntry', title=u'yes', content=u'cubicweb yes') blog_entry_2.set_relations(entry_of=cubicweb_blog) - self.assertEquals(len(MAILBOX), 0) + self.assertEqual(len(MAILBOX), 0) self.commit() - self.assertEquals(len(MAILBOX), 2) + self.assertEqual(len(MAILBOX), 2) mail = MAILBOX[0] - self.assertEquals(mail.subject, '[data] hop') + self.assertEqual(mail.subject, '[data] hop') mail = MAILBOX[1] - self.assertEquals(mail.subject, '[data] yes') + self.assertEqual(mail.subject, '[data] yes') Visible actions tests ````````````````````` @@ -229,7 +229,7 @@ def test_admin(self): req = self.request() rset = req.execute('Any C WHERE C is Conference') - self.assertListEquals(self.pactions(req, rset), + self.assertListEqual(self.pactions(req, rset), [('workflow', workflow.WorkflowActions), ('edit', confactions.ModifyAction), ('managepermission', actions.ManagePermissionsAction), @@ -238,7 +238,7 @@ ('generate_badge_action', badges.GenerateBadgeAction), ('addtalkinconf', confactions.AddTalkInConferenceAction) ]) - self.assertListEquals(self.action_submenu(req, rset, 'addrelated'), + self.assertListEqual(self.action_submenu(req, rset, 'addrelated'), [(u'add Track in_conf Conference object', u'http://testing.fr/cubicweb/add/Track' u'?__linkto=in_conf%%3A%(conf)s%%3Asubject&' diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/devweb/request.rst --- a/doc/book/en/devweb/request.rst Fri Jan 14 08:10:41 2011 +0100 +++ b/doc/book/en/devweb/request.rst Wed Jan 19 09:31:15 2011 +0100 @@ -1,5 +1,5 @@ -The `Request` class (`cubicweb.web`) ------------------------------------- +The `Request` class (`cubicweb.web.request`) +-------------------------------------------- Overview ```````` @@ -7,7 +7,8 @@ A request instance is created when an HTTP request is sent to the web server. It contains informations such as form parameters, authenticated user, etc. It is a very prevalent object and is used -throughout all of the framework and applications. +throughout all of the framework and applications, as you'll access to +almost every resources through it. **A request represents a user query, either through HTTP or not (we also talk about RQL queries on the server side for example).** @@ -24,8 +25,8 @@ * `User and identification`: - * `user`, instance of `cubicweb.common.utils.User` corresponding to - the authenticated user + * `user`, instance of `cubicweb.entities.authobjs.CWUser` corresponding to the + authenticated user * `Session data handling` diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/devweb/views/views.rst --- a/doc/book/en/devweb/views/views.rst Fri Jan 14 08:10:41 2011 +0100 +++ b/doc/book/en/devweb/views/views.rst Wed Jan 19 09:31:15 2011 +0100 @@ -88,7 +88,7 @@ Other basic view classes ```````````````````````` -Here are some of the subclasses of `View` defined in `cubicweb.common.view` +Here are some of the subclasses of `View` defined in `cubicweb.view` that are more concrete as they relate to data rendering within the application: * `EntityView`, view applying to lines or cell containing an entity (e.g. an eid) diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/blog-demo-first-page.png Binary file doc/book/en/images/blog-demo-first-page.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/cbw-add-relation-entryof_en.png Binary file doc/book/en/images/cbw-add-relation-entryof_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/cbw-create-blog_en.png Binary file doc/book/en/images/cbw-create-blog_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/cbw-detail-one-blogentry_en.png Binary file doc/book/en/images/cbw-detail-one-blogentry_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/cbw-list-one-blog_en.png Binary file doc/book/en/images/cbw-list-one-blog_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/cbw-list-two-blog_en.png Binary file doc/book/en/images/cbw-list-two-blog_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/cbw-schema_en.png Binary file doc/book/en/images/cbw-schema_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/cbw-update-primary-view_en.png Binary file doc/book/en/images/cbw-update-primary-view_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/lax-book_06-header-no-login_en.png Binary file doc/book/en/images/lax-book_06-header-no-login_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/lax-book_06-main-template-layout_en.png Binary file doc/book/en/images/lax-book_06-main-template-layout_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/lax-book_06-simple-main-template_en.png Binary file doc/book/en/images/lax-book_06-simple-main-template_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/login-form.png Binary file doc/book/en/images/login-form.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/tutos-base_blog-form_en.png Binary file doc/book/en/images/tutos-base_blog-form_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/tutos-base_blog-primary-after-post-creation_en.png Binary file doc/book/en/images/tutos-base_blog-primary-after-post-creation_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/tutos-base_blog-primary_en.png Binary file doc/book/en/images/tutos-base_blog-primary_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/tutos-base_blogs-list_en.png Binary file doc/book/en/images/tutos-base_blogs-list_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/tutos-base_form-generic-relations_en.png Binary file doc/book/en/images/tutos-base_form-generic-relations_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/tutos-base_index_en.png Binary file doc/book/en/images/tutos-base_index_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/tutos-base_login-form_en.png Binary file doc/book/en/images/tutos-base_login-form_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/tutos-base_myblog-blogentry-taggable-commentable-primary_en.png Binary file doc/book/en/images/tutos-base_myblog-blogentry-taggable-commentable-primary_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/tutos-base_myblog-community-custom-primary_en.png Binary file doc/book/en/images/tutos-base_myblog-community-custom-primary_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/tutos-base_myblog-community-default-primary_en.png Binary file doc/book/en/images/tutos-base_myblog-community-default-primary_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/tutos-base_myblog-community-taggable-primary_en.png Binary file doc/book/en/images/tutos-base_myblog-community-taggable-primary_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/tutos-base_myblog-custom-footer_en.png Binary file doc/book/en/images/tutos-base_myblog-custom-footer_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/tutos-base_myblog-schema_en.png Binary file doc/book/en/images/tutos-base_myblog-schema_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/tutos-base_myblog-siteinfo_en.png Binary file doc/book/en/images/tutos-base_myblog-siteinfo_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/tutos-base_schema_en.png Binary file doc/book/en/images/tutos-base_schema_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/tutos-base_siteconfig_en.png Binary file doc/book/en/images/tutos-base_siteconfig_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/images/tutos-base_user-menu_en.png Binary file doc/book/en/images/tutos-base_user-menu_en.png has changed diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/tutorials/advanced/index.rst --- a/doc/book/en/tutorials/advanced/index.rst Fri Jan 14 08:10:41 2011 +0100 +++ b/doc/book/en/tutorials/advanced/index.rst Wed Jan 19 09:31:15 2011 +0100 @@ -66,25 +66,35 @@ 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: +Ok, now I'll tell my cube requires all this by editing :file:`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__) + __depends__ = {'cubicweb': '>= 3.8.0', + 'cubicweb-file': '>= 1.2.0', + 'cubicweb-folder': '>= 1.1.0', + 'cubicweb-person': '>= 1.2.0', + 'cubicweb-comment': '>= 1.2.0', + 'cubicweb-tag': '>= 1.2.0', + 'cubicweb-zone': None} Notice that you can express minimal version of the cube that should be used, -`None` meaning whatever version available. +`None` meaning whatever version available. All packages starting with 'cubicweb-' +will be recognized as being cube, not bare python packages. You can still specify +this explicitly using instead the `__depends_cubes__` dictionary which should +contains cube's name without the prefix. So the example below would be written +as: + + .. sourcecode:: python + + __depends__ = {'cubicweb': '>= 3.8.0'} + __depends_cubes__ = {'file': '>= 1.2.0', + 'folder': '>= 1.1.0', + 'person': '>= 1.2.0', + 'comment': '>= 1.2.0', + 'tag': '>= 1.2.0', + 'zone': None} + Step 3: glue everything together in my cube's schema ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -170,9 +180,11 @@ 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 users can see people * everyone can see classifier entities, such as tag and zone @@ -226,6 +238,12 @@ cardinality = '11' # required class may_be_read_by(RelationDefinition): + __permissions__ = { + 'read': ('managers', 'users'), + 'add': ('managers',), + 'delete': ('managers',), + } + subject = ('Folder', 'File', 'Image', 'Comment',) object = 'CWUser' @@ -241,6 +259,9 @@ * the `parent` possible value will be used for visibility propagation +* think to secure the `may_be_read_by` permissions, else any user can add/delte it + by default, which somewhat breaks our security model... + 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*: @@ -541,7 +562,7 @@ 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: :: - $ rm tmpdb* + $ rm data/tmpdb* .. Note:: diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/tutorials/base/blog-in-five-minutes.rst --- a/doc/book/en/tutorials/base/blog-in-five-minutes.rst Fri Jan 14 08:10:41 2011 +0100 +++ b/doc/book/en/tutorials/base/blog-in-five-minutes.rst Wed Jan 19 09:31:15 2011 +0100 @@ -1,42 +1,70 @@ .. -*- coding: utf-8 -*- -.. _BlogFiveMinutes: +.. _TutosBaseBlogFiveMinutes: Get a blog running in five minutes! ----------------------------------- -For Debian or Ubuntu users, first install the following packages (:ref:`DebianInstallation`):: +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`). +Windows or Mac OS X users 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:: +You'll be asked a few questions, and you can keep the default answer for most of +them. The one question you'll have to think about is the database you'll want to +use for that instance. For a quick test, if you don't have `postgresql` installed +and configured (see :ref:`PostgresqlConfiguration`), it's highly recommended to +choose `sqlite` when asked for which database driver to use, since it has a much +simple setup (no database server needed). + +One the process is completed (including database initialisation), you can start +your instance by using: :: cubicweb-ctl start -D myblog -The -D option is the debugging mode of cubicweb, removing it will lauch the instance in the background. +The `-D` option activates the debugging mode. Removing it will launch the instance +as a daemon in the background, and ``cubicweb-ctl stop myblog`` will stop +it in that case. + + +About file system permissions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Permission -~~~~~~~~~~ +Unless you installed from sources, the above commands assume that you have root +access to the :file:`/etc/` directory. In order to initialize your instance as a +regular user, within your home directory, you can use the :envvar:`CW_MODE` +environment variable: :: -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. + export CW_MODE=user + +then create a :file:`~/etc/cubicweb.d` directory that will hold your instances. + +More information about how to configure your own environment is +available in :ref:`ResourceMode`. + Instance parameters ~~~~~~~~~~~~~~~~~~~ -If you would like to change some instance parameters, for example, the database host or the user name, edit the `source` file located in the /etc/cubicweb.d/myblog directory. +If you would like to change database parameters such as the database host or the +user name used to connect to the database, edit the `sources` file located in the +:file:`/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. +Other parameters, like web server or emails parameters, can be modified in the +:file:`/etc/cubicweb.d/myblog/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. +You'll have to restart the instance after modification in one of those files. +This is it. Your blog is functional and running. Visit http://localhost:8080 and enjoy it! diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/tutorials/base/components.rst --- a/doc/book/en/tutorials/base/components.rst Fri Jan 14 08:10:41 2011 +0100 +++ /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 026581ea2b16 -r 117b53c10d79 doc/book/en/tutorials/base/conclusion.rst --- a/doc/book/en/tutorials/base/conclusion.rst Fri Jan 14 08:10:41 2011 +0100 +++ b/doc/book/en/tutorials/base/conclusion.rst Wed Jan 19 09:31:15 2011 +0100 @@ -3,11 +3,16 @@ What's next? ------------ -In this chapter, we have seen have you can, right after the installation of *CubicWeb*, build a web application in five minutes by defining a data model. Everything is there already: views, templates, permissions, etc. +In this tutorial, we have seen have you can, right after the installation of +|cubicweb|, build a web application in a few minutes by defining a data model as +assembling cubes. You get a working application that you can then customize there +and there while keeping something that works. This is important in agile +development practices, you can right from the start of the project show things +to customer and so take the right decision early in the process. -The next step is to change the design and learn about the many features available to customize and extend your application: RSS channels (:ref:`XmlAndRss`), events (:ref:`hooks`), support of sources such as -Google App Engine (:ref:`GoogleAppEngineSource`), etc. - -You will find more `tutorials and howtos`_ in the blog published on the CubicWeb.org website. +The next steps will be to discover hooks, security, data sources, digging deeper +into view writing and interface customisation... Yet a lot of fun stuff to +discover! You will find more `tutorials and howtos`_ in the blog published on the +CubicWeb.org website. .. _`tutorials and howtos`: http://www.cubicweb.org/view?rql=Any+X+ORDERBY+D+DESC+WHERE+X+is+BlogEntry%2C+T+tags+X%2C+T+name+IN+%28%22tutorial%22%2C+%22howto%22%29%2C+X+creation_date+D diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/tutorials/base/create-cube.rst --- a/doc/book/en/tutorials/base/create-cube.rst Fri Jan 14 08:10:41 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,437 +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 do not define the look'n'feel and the design of your application. For that, you will use CSS and the files located 'blog/data/'. - - -5. :ref:`DefineEntities` - -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_view`. - -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 is_instance - from cubicweb.web.views import primary - - class BlogEntryPrimaryView(primary.PrimaryView): - __select__ = is_instance('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' - - 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__ = is_instance('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: - -.. sourcecode:: bash - - cubicweb-ctl stop blogdemo - -3. start the cubicweb shell for your instance by running the following command: - -.. sourcecode:: bash - - cubicweb-ctl shell blogdemo - -4. at the cubicweb shell prompt, execute: - -.. sourcecode:: python - - add_attribute('Blog', 'category') - -5. restart your instance: - -.. sourcecode:: bash - - cubicweb-ctl start blogdemo - -6. modify a blog entity and check that the new attribute -``category`` has been added. - -Of course, you may also want to add relations, entity types, etc. See :ref:`migration` -for a list of all available migration commands. - diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/tutorials/base/customizing-the-application.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/tutorials/base/customizing-the-application.rst Wed Jan 19 09:31:15 2011 +0100 @@ -0,0 +1,536 @@ +.. -*- coding: utf-8 -*- + +.. _TutosBaseCustomizingTheApplication: + +Customizing your application +---------------------------- + +So far so good. The point is that usually, you won't get enough by assembling +cubes out-of-the-box. You will want to customize them, have a personal look and +feel, add your own data model and so on. Or maybe start from scratch? + +So let's get a bit deeper and start coding our own cube. In our case, we want +to customize the blog we created to add more features to it. + + +Create your own cube +~~~~~~~~~~~~~~~~~~~~ + +First, notice that if you've installed |cubicweb| using Debian packages, you will +need the additional ``cubicweb-dev`` package to get the commands necessary to +|cubicweb| development. All `cubicweb-ctl` commands are described in details in +:ref:`cubicweb-ctl`. + +Once your |cubicweb| development environment is set up, you can create a new +cube:: + + cubicweb-ctl newcube myblog + +This will create in the cubes directory (:file:`/path/to/forest/cubes` for source +installation, :file:`/usr/share/cubicweb/cubes` for Debian packages installation) +a directory named :file:`blog` reflecting the structure described in +:ref:`cubelayout`. + +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 + +and then create your new cube using: :: + + cubicweb-ctl newcube --directory=~/src/cubes myblog + +.. Note: + + We previously used `myblog` as the name of our *instance*. We're now creating + a *cube* with the same name. Both are different things. We'll now try to + specify when we talk about one or another, but keep in mind this difference. + + +Cube metadata +~~~~~~~~~~~~~ + +A simple set of metadata about your cube are stored in the :file:`__pkginfo__.py` +file. In our case, we want to extend the blog cube, so we have to tell that our +cube depends on this cube, by modifying the ``__depends__`` dictionary in that +file: + +.. sourcecode:: python + + __depends__ = {'cubicweb': '>= 3.10.7', + 'cubicweb-blog': None} + +where the ``None`` means we do not depends on a particular version of the cube. + + +Extending the data model +~~~~~~~~~~~~~~~~~~~~~~~~ + +The data model or schema is the core of your |cubicweb| application. It defines +the type of content your application will handle. It is defined in the file +:file:`schema.py` of the cube. + + +Defining our model +****************** + +For the sake of example, let's say we want a new entity type named `Community` +with a name, a description. A `Community` will hold several blogs. + +.. sourcecode:: python + + from yams.buildobjs import EntityType, RelationDefinition, String, RichString + + class Community(EntityType): + name = String(maxsize=50, required=True) + description = RichString() + + class community_blog(RelationDefinition): + subject = 'Community' + object = 'Blog' + cardinality = '*?' + composite = 'subject' + +The first step is the import from the :mod:`yams` package necessary classes to build +the schema. + +This file defines the following: + +* a `Community` has a title and a description as attributes + + - the name is a string that is required and can't be longer than 50 characters + + - the description is a string that is not constrained and may contains rich + content such as HTML or Restructured text. + +* a `Community` may be linked to a `Blog` using the `community_blog` relation + + - ``*`` means a community may be linked to 0 to N blog, ``?`` means a blog may + be linked to 0 to 1 community. For completeness, remember that you can also + use ``+`` for 1 to N, and ``1`` for single, mandatory relation (e.g. one to one); + + - this is a composite relation where `Community` (e.g. the subject of the + relation) is the composite. That means that if you delete a community, its + blog will be deleted as well. + +Of course, there are a lot of other data types and things such as constraints, +permissions, etc, that may be defined in the schema, but those won't be covered +in this tutorial. + +Notice that our schema refers to the `Blog` entity type which is not defined +here. But we know this type is available since we depend on the `blog` cube +which is defining it. + + +Applying changes to the model into our instance +*********************************************** + +Now the problem is that we created an instance using the `blog` cube, not our +`myblog` cube, so if we don't do anything there is no way that we'll see anything +changing in the instance. + +One easy way, as we've no really valuable data in the instance would be to trash and recreated it:: + + cubicweb-ctl stop myblog # or Ctrl-C in the terminal running the server in debug mode + cubicweb-ctl delete myblog + cubicweb-ctl create myblog + cubicweb-ctl start -D myblog + +Another way is to add our cube to the instance using the cubicweb-ctl shell +facility. It's a python shell connected to the instance with some special +commands available to manipulate it (the same as you'll have in migration +scripts, which are not covered in this tutorial). In that case, we're interested +in the `add_cube` command: :: + + $ cubicweb-ctl stop myblog # or Ctrl-C in the terminal running the server in debug mode + $ cubicweb-ctl shell myblog + entering the migration python shell + just type migration commands or arbitrary python code and type ENTER to execute it + type "exit" or Ctrl-D to quit the shell and resume operation + >>> add_cube('myblog') + >>> + $ cubicweb-ctl start -D myblog + +The `add_cube` command is enough since it automatically updates our +application to the cube's schema. There are plenty of other migration +commands of a more finer grain. They are described in :ref:`migration` + +As explained, leave the shell by typing Ctrl-D. If you restart the instance and +take another look at the schema, you'll see that changes to the data model have +actually been applied (meaning database schema updates and all necessary stuff +has been done). + +.. image:: ../../images/tutos-base_myblog-schema_en.png + :alt: the instance schema after adding our cube + +If you follow the 'info' link in the user pop-up menu, you'll also see that the +instance is using blog and myblog cubes. + +.. image:: ../../images/tutos-base_myblog-siteinfo_en.png + :alt: the instance schema after adding our cube + +You can now add some communities, link them to blog, etc... You'll see that the +framework provides default views for this entity type (we have not yet defined any +view for it!), and also that the blog primary view will show the community it's +linked to if any. All this thanks to the model driven interface provided by the +framework. + +You'll then be able to redefine each of them according to your needs +and preferences. We'll now see how to do such thing. + +Defining your views +~~~~~~~~~~~~~~~~~~~ + +|cubicweb| provides a lot of standard views in directory +:file:`cubicweb/web/views/`. We already talked about 'primary' and 'list' views, +which are views which apply to one ore more entities. + +A view is defined by a python class which includes: + + - an identifier: all objects used to build the user interface in |cubicweb| are + recorded in a registry and this identifier will be used as a key in that + registry. There may be multiple views for the same identifier. + + - a *selector*, which is a kind of filter telling how well a view suit to a + particular context. When looking for a particular view (e.g. given an + identifier), |cubicweb| computes for each available view with that identifier + a score which is returned by the selector. Then the view with the highest + score is used. The standard library of selectors is in + :mod:`cubicweb.selector`. + +A view has a set of methods inherited from the :class:`cubicweb.view.View` class, +though you usually don't derive directly from this class but from one of its more +specific child class. + +Last but not least, |cubicweb| provides a set of default views accepting any kind +of entities. + +Want a proof? Create a community as you've already done for other entity types +through the index page, you'll then see something like that: + +.. image:: ../../images/tutos-base_myblog-community-default-primary_en.png + :alt: the default primary view for our community entity type + + +If you notice the weird messages that appear in the page: those are messages +generated for the new data model, which have no translation yet. To fix that, +we'll have to use dedicated `cubicweb-ctl` commands: + +.. sourcecode: bash + + cubicweb-ctl i18ncube myblog # build/update cube's message catalogs + # then add translation into .po file into the cube's i18n directory + cubicweb-ctl i18ninstance myblog # recompile instance's message catalogs + cubicweb-ctl restart -D myblog # instance has to be restarted to consider new catalogs + +You'll then be able to redefine each of them according to your needs and +preferences. So let's see how to do such thing. + +Changing the layout of the application +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The layout is the general organization of the pages in the site. Views that generate +the layout are sometimes referred to as 'templates'. They are implemented in the +framework in the module :mod:`cubicweb.web.views.basetemplates`. By overriding +classes in this module, you can customize whatever part you wish of the default +layout. + +But notice that |cubicweb| provides many other ways to customize the +interface, thanks to actions and components (which you can individually +(de)activate, control their location, customize their look...) as well as +"simple" CSS customization. You should first try to achieve your goal using such +fine grained parametrization rather then overriding a whole template, which usually +embeds customisation access points that you may loose in the process. + +But for the sake of example, let's say we want to change the generic page +footer... We can simply add to the module ``views`` of our cube, +e.g. :file:`cubes/myblog/views.py`, the code below: + +.. sourcecode:: python + + from cubicweb.web.views import basetemplates + + class MyHTMLPageFooter(basetemplates.HTMLPageFooter): + + def footer_content(self): + self.w(u'This website has been created with CubicWeb.') + + def registration_callback(vreg): + vreg.register_all(globals().values(), __name__, (MyHTMLPageFooter,)) + vreg.register_and_replace(MyHTMLPageFooter, basetemplates.HTMLPageFooter) + + +* Our class inherits from the default page footer to ease getting things right, + but this is not mandatory. + +* When we want to write something to the output stream, we simply call `self.w`, + with *must be passed an unicode string*. + +* The latest function is the most exotic stuff. The point is that without it, you + would get an error at display time because the framework wouldn't be able to + choose which footer to use between :class:`HTMLPageFooter` and + :class:`MyHTMLPageFooter`, since both have the same selector, hence the same + score... In this case, we want our footer to replace the default one, so we have + to define a :func:`registration_callback` function to control object + registration: the first instruction tells to register everything in the module + but the :class:`MyHTMLPageFooter` class, then the second to register it instead + of :class:`HTMLPageFooter`. Without this function, everything in the module is + registered blindly. + +.. Note:: + + When a view is modified while running in debug mode, 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. + +We will now have this simple footer on every page of the site. + + +Primary view customization +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The 'primary' view (i.e. any view with the identifier set to 'primary') is the one used to +display all the information about a single entity. The standard primary view is one +of the most sophisticated views of all. It has several customisation points, but +its power comes with `uicfg`, allowing you to control it without having to +subclass it. + +However this is a bit off-topic for this first tutorial. Let's say we simply want a +custom primary view for my `Community` entity type, using directly the view +interface without trying to benefit from the default implementation (you should +do that though if you're rewriting reusable cubes; everything is described in more +details in :ref:`primary_view`). + + +So... Some code! That we'll put again in the module ``views`` of our cube. + +.. sourcecode:: python + + from cubicweb.selectors import is_instance + from cubicweb.web.views import primary + + class CommunityPrimaryView(primary.PrimaryView): + __select__ = is_instance('Community') + + def cell_call(self, row, col): + entity = self.cw_rset.get_entity(row, col) + self.w(u'

Welcome to the "%s" community

' % entity.printable_value('name')) + if entity.description: + self.w(u'

%s

' % entity.printable_value('description')) + +What's going on here? + +* Our class inherits from the default primary view, here mainly to get the correct + view identifier, since we don't use any of its features. + +* We set on it a selector telling that it only applies when trying to display + some entity of the `Community` type. This is enough to get an higher score than + the default view for entities of this type. + +* View applying to entities usually have to define `cell_call` as entry point, + and are given `row` and `col` arguments tell to which entity in the result set + the view is applied. We can then get this entity from the result set + (`self.cw_rset`) by using the `get_entity` method. + +* To ease thing, we access our entity's attribute for display using its + printable_value method, which will handle formatting and escaping when + necessary. As you can see, you can also access attributes by their name on the + entity to get the raw value. + + +You can now reload the page of the community we just created and see the changes. + +.. image:: ../../images/tutos-base_myblog-community-custom-primary_en.png + :alt: the custom primary view for our community entity type + +We've seen here a lot of thing you'll have to deal with to write views in +|cubicweb|. The good news is that this is almost everything that is used to +build higher level layers. + +.. Note:: + + As things get complicated and the volume of code in your cube increases, you can + of course still split your views module into a python package with subpackages. + +You can find more details about views and selectors in :ref:`Views`. + + +Write entities to add logic in your data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +|cubicweb| provides an ORM to easily programmaticaly manipulate +entities (just like the one we have fetched earlier by calling +`get_entity` on a result set). By default, entity +types are instances of the :class:`AnyEntity` class, which holds a set of +predefined methods as well as property automatically generated for +attributes/relations of the type it represents. + +You can redefine each entity to provide additional methods or whatever you want +to help you write your application. Customizing an entity requires that your +entity: + +- inherits from :class:`cubicweb.entities.AnyEntity` or any subclass + +- defines a :attr:`__regid__` linked to the corresponding data type of your schema + +You may then want to add your own methods, override default implementation of some +method, etc... + +.. sourcecode:: python + + from cubicweb.entities import AnyEntity, fetch_config + + + class Community(AnyEntity): + """customized class for Community entities""" + __regid__ = 'Community' + + fetch_attrs, fetch_order = fetch_config(['name']) + + def dc_title(self): + return self.name + + def display_cw_logo(self): + return 'CubicWeb' in self.description + +In this example: + +* we used convenience :func:`fetch_config` function to tell which attributes + should be prefetched by the ORM when looking for some related entities of this + type, and how they should be ordered + +* we overrode the standard `dc_title` method, used in various place in the interface + to display the entity (though in this case the default implementation would + have had the same result) + +* we implemented here a method :meth:`display_cw_logo` which tests if the blog + entry title contains 'CW'. It can then be used when you're writing code + involving 'Community' entities in your views, hooks, etc. For instance, you can + modify your previous views as follows: + +.. sourcecode:: python + + + class CommunityPrimaryView(primary.PrimaryView): + __select__ = is_instance('Community') + + def cell_call(self, row, col): + entity = self.cw_rset.get_entity(row, col) + self.w(u'

Welcome to the "%s" community

' % entity.printable_value('name')) + if entity.display_cw_logo(): + self.w(u'') + if entity.description: + self.w(u'

%s

' % entity.printable_value('description')) + +Then each community whose description contains 'CW' is shown with the |cubicweb| +logo in front of it. + +.. Note:: + + As for view, you don't have to restart your instance when modifying some entity + classes while your server is running in debug mode, the code will be + automatically reloaded. + + +Extending the application by using more cubes! +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +One of the goal of the |cubicweb| framework was to have truly reusable +components. To do so, they must both behave nicely when plugged into the +application and be easily customisable, from the data model to the user +interface. And I think the result is pretty successful, thanks to system such as +the selection mechanism and the choice to write views as python code which allows +to build our page using true object oriented programming techniques, that no +template language provides. + + +A library of standard cubes is available from `CubicWeb Forge`_, to address a +lot of common concerns such has manipulating people, files, things to do, etc. In +our community blog case, we could be interested for instance in functionalities +provided by the `comment` and `tag` cubes. The former provides threaded +discussion functionalities, the latter a simple tag mechanism to classify content. +Let's say we want to try those. We will first modify our cube's :file:`__pkginfo__.py` +file: + +.. sourcecode:: python + + __depends__ = {'cubicweb': '>= 3.10.7', + 'cubicweb-blog': None, + 'cubicweb-comment': None, + 'cubicweb-tag': None} + +Now, we'll simply tell on which entity types we want to activate the 'comment' +and 'tag' facilities by adding respectively the 'comments' and 'tags' relations on +them in our schema (:file:`schema.py`). + +.. sourcecode:: python + + class comments(RelationDefinition): + subject = 'Comment' + object = 'BlogEntry' + cardinality = '1*' + composite = 'object' + + class tags(RelationDefinition): + subject = 'Tag' + object = ('Community', 'BlogEntry') + + +So in the case above we activated comments on `BlogEntry` entities and tags on +both `Community` and `BlogEntry`. Various views from both `comment` and `tag` +cubes will then be automatically displayed when one of those relations is +supported. + +Let's synchronize the data model as we've done earlier: :: + + + $ cubicweb-ctl stop myblog + $ cubicweb-ctl shell myblog + entering the migration python shell + just type migration commands or arbitrary python code and type ENTER to execute it + type "exit" or Ctrl-D to quit the shell and resume operation + >>> add_cubes('comment', 'tag') + >>> + +Then restart the instance. Let's look at a blog entry: + +.. image:: ../../images/tutos-base_myblog-blogentry-taggable-commentable-primary_en.png + :alt: the primary view for a blog entry with comments and tags activated + +As you can see, we now have a box displaying tags and a section proposing to add +a comment and displaying existing one below the post. All this without changing +anything in our views, thanks to the design of generic views provided by the +framework. Though if we take a look at a community, we won't see the tags box! +That's because by default this box try to locate itself in the left column within +the white frame, and this column is handled by the primary view we +hijacked. Let's change our view to make it more extensible, by keeping both our +custom rendering but also extension points provided by the default +implementation. + + +.. sourcecode:: python + + class CommunityPrimaryView(primary.PrimaryView): + __select__ = is_instance('Community') + + def render_entity_title(self, entity): + self.w(u'

Welcome to the "%s" community

' % entity.printable_value('name')) + + def render_entity_attributes(self, entity): + if entity.display_cw_logo(): + self.w(u'') + if entity.description: + self.w(u'

%s

' % entity.printable_value('description')) + +It appears now properly: + +.. image:: ../../images/tutos-base_myblog-community-taggable-primary_en.png + :alt: the custom primary view for a community entry with tags activated + +You can control part of the interface independently from each others, piece by +piece. Really. + + + +.. _`CubicWeb Forge`: http://www.cubicweb.org/project diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/tutorials/base/discovering-the-ui.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/tutorials/base/discovering-the-ui.rst Wed Jan 19 09:31:15 2011 +0100 @@ -0,0 +1,161 @@ + +.. _TutosBaseDiscoveringTheUI: + +Discovering the web interface +----------------------------- + +You can now access your web instance to create blogs and post messages +by visiting the URL http://localhost:8080/. + +By default, anonymous access is disabled, so a login form will appear. If you +asked to allow anonymous access when initializing the instance, click on the +'login' link in the top right hand corner. To login, you need then use the admin +account you specified at the time you initialized the database with +``cubicweb-ctl create``. + +.. image:: ../../images/tutos-base_login-form_en.png + :alt: the login form + + +Once authenticated, you can start playing with your instance. The default index +page looks like the following: + +.. image:: ../../images/tutos-base_index_en.png + :alt: the index page + + +Minimal configuration +~~~~~~~~~~~~~~~~~~~~~ + +Before creating entities, let's change that 'unset title' thing that appears +here and there. This comes from a |cubicweb| system properties. To set it, +click on the 'site configuration link' in the pop-up menu behind your login name +in the upper left-hand corner + +.. image:: ../../images/tutos-base_user-menu_en.png + :alt: the user pop-up menu + +The site title is in the 'Ui' section. Simply set it to the desired value and +click the 'validate' button. + +.. image:: ../../images/tutos-base_siteconfig_en.png + :alt: the site configuration form + +You should see a 'changes applied' message. You can now go back to the +index page by clicking on the |cubicweb| logo in the upper left-hand corner. + +You will much likely still see 'unset title' at this point. This is because by +default the index page is cached. Force a refresh of the page (by typing Ctrl-R +in Firefox for instance) and you should now see the title you entered. + + +Adding entities +~~~~~~~~~~~~~~~ + +The ``blog`` cube defines several entity types, among them ``Blog`` which is a +container for ``BlogEntry`` (i.e. posts) on a particular topic. We can get a +graphical view of the schema by clicking on the 'site schema' link in the user +pop-up menu we've already seen: + +.. image:: ../../images/tutos-base_schema_en.png + :alt: graphical view of the schema (aka data-model) + +Nice isn't it? Notice that this, as most other stuff we'll see in this tutorial, +is generated by the framework according to the model of the application. In our +case, the model defined by the ``blog`` cube. + +Now let us create a few of these entities. + + +Add a blog +********** + +Clicking on the `[+]` at the left of the 'Blog' link on the index page will lead +you to an HTML form to create a blog. + +.. image:: ../../images/tutos-base_blog-form_en.png + :alt: the blog creation form + +For instance, call this new blog 'Tech-blog' and type in 'everything about +technology' as the description , then validate the form by clicking on +'Validate'. You will be redirected to the `primary` view of the newly created blog. + +.. image:: ../../images/tutos-base_blog-primary_en.png + :alt: the blog primary view + + +Add a blog post +*************** + +There are several ways to add a blog entry. The simplest is to click on the 'add +blog entry' link in the actions box on viewing the blog you have just created. +You will then see a form to create a post, with a 'blog entry of' field preset +to the blog we're coming from. Enter a title, some content, click the 'validate' +button and you're done. You will be redirected to the blog primary view, though you +now see that it contains the blog post you've just created. + +.. image:: ../../images/tutos-base_blog-primary-after-post-creation_en.png + :alt: the blog primary view after creation of a post + +Notice there are some new boxes that appears in the left column. + +You can achieve the same thing by following the same path as we did for the blog +creation, e.g. by clicking on the `[+]` at the left of the 'Blog entry' link on +the index page. The diffidence being that since there is no context information, +the 'blog entry of' selector won't be preset to the blog. + + +If you click on the 'modify' link of the action box, you are back to +the form to edit the entity you just created, except that the form now +has another section with a combo-box entitled 'add relation'. It +provisos a generic way to edit relations which don't appears in the +above form. Choose the relation you want to add and a second combo box +appears where you can pick existing entities. If there are too many +of them, you will be offered to navigate to the target entity, that is +go away from the form and go back to it later, once you've selected +the entity you want to link with. + +.. image:: ../../images/tutos-base_form-generic-relations_en.png + :alt: the generic relations combo box + +This combo box can't appear until the entity is actually created. That's why you +haven't seen it at creation time. You could also have hit 'Apply' instead of +'validate' and it would have showed up. + + +About ui auto-adaptation +~~~~~~~~~~~~~~~~~~~~~~~~ + +One of the things that make |cubicweb| different of other frameworks is +its automatic user interface that adapts itself according to the data being +displayed. Let's see an example. + +If you go back to the home page an click on the 'Blog' link, you will be redirected +to the primary view of the blog, the same we've seen earlier. Now, add another +blog, go back to the index page, and click again on this link. You will see +a very different view (namely the 'list' view). + +.. image:: ../../images/tutos-base_blogs-list_en.png + :alt: the list view when there are more than one blog to display + +This is because in the first case, the framework chose to use the 'primary' +view since there was only one entity in the data to be displayed. Now that there +are two entities, the 'list' view is more appropriate and hence is being used. + +There are various other places where |cubicweb| adapts to display data in the best +way, the main being provided by the view *selection* mechanism that will be detailed +later. + + +Digging deeper +~~~~~~~~~~~~~~ + +By following principles explained below, you should now be able to +create new users for your application, to configure with a finer +grain, etc... You will notice that the index page lists a lot of types +you don't know about. Most are built-in types provided by the framework +to make the whole system work. You may ignore them in a first time and +discover them as time goes. + +One thing that is worth playing with is the search box. It may be used in various +way, from simple full text search to advanced queries using the :ref:`RQL` . diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/tutorials/base/index.rst --- a/doc/book/en/tutorials/base/index.rst Fri Jan 14 08:10:41 2011 +0100 +++ b/doc/book/en/tutorials/base/index.rst Wed Jan 19 09:31:15 2011 +0100 @@ -1,30 +1,48 @@ .. -*- coding: utf-8 -*- -.. _Tutorial: - -.. _tuto_blog: +.. _TutosBase: -Building a simple blog -====================== +Building a simple blog with |cubicweb| +====================================== -*CubicWeb* is a semantic web application framework that favors reuse and +|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. + +This tutorial is designed to help in your very first steps to start with +|cubicweb|. We will tour through basic concepts such as: -An `instance` is a specific installation of a cube and includes configuration -files. +* getting an application running by using existing components +* discovering the default user interface +* basic extending and customizing the look and feel of that application + +More advanced concepts are covered in :ref:`advanced_tutorial`. -This tutorial will show how to create a `cube` and how to use it as an -application to run an `instance`. +.. _TutosBaseVocab: + +Some vocabulary +--------------- + +|cubicweb| comes with a few words of vocabulary that you should know to +understand what we're talking about. To follow this tutorial, you should at least +know that: + +* a `cube` is a component that usually includes a model defining some data types + and a set of views to display them. A cube can be built by assembling other + cubes; + +* an `instance` is a specific installation of one or more cubes and includes + configuration files, a web server and a database. + +Reading :ref:`Concepts` for more vocabulary will be required at some point. + +Now, let's start the hot stuff! .. toctree:: :maxdepth: 2 blog-in-five-minutes - create-cube - components - maintemplate + discovering-the-ui + customizing-the-application conclusion diff -r 026581ea2b16 -r 117b53c10d79 doc/book/en/tutorials/base/maintemplate.rst --- a/doc/book/en/tutorials/base/maintemplate.rst Fri Jan 14 08:10:41 2011 +0100 +++ /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 026581ea2b16 -r 117b53c10d79 etwist/twconfig.py --- a/etwist/twconfig.py Fri Jan 14 08:10:41 2011 +0100 +++ b/etwist/twconfig.py Wed Jan 19 09:31:15 2011 +0100 @@ -49,7 +49,7 @@ {'type' : 'string', 'default': "", 'help': 'http server address on which to listen (default to everywhere)', - 'group': 'web', 'level': 0, + 'group': 'web', 'level': 1, }), ('max-post-length', {'type' : 'bytes', diff -r 026581ea2b16 -r 117b53c10d79 hooks/integrity.py --- a/hooks/integrity.py Fri Jan 14 08:10:41 2011 +0100 +++ b/hooks/integrity.py Wed Jan 19 09:31:15 2011 +0100 @@ -79,8 +79,6 @@ session = self.session pendingeids = session.transaction_data.get('pendingeids', ()) pendingrtypes = session.transaction_data.get('pendingrtypes', ()) - # poping key is not optional: if further operation trigger new deletion - # of relation, we'll need a new operation for eid, rtype in self.get_data(): # recheck pending eids / relation types if eid in pendingeids: @@ -301,8 +299,6 @@ session = self.session pendingeids = session.transaction_data.get('pendingeids', ()) neweids = session.transaction_data.get('neweids', ()) - # poping key is not optional: if further operation trigger new deletion - # of composite relation, we'll need a new operation for eid, rtype in self.get_data(): # don't do anything if the entity is being created or deleted if not (eid in pendingeids or eid in neweids): diff -r 026581ea2b16 -r 117b53c10d79 i18n/en.po --- a/i18n/en.po Fri Jan 14 08:10:41 2011 +0100 +++ b/i18n/en.po Wed Jan 19 09:31:15 2011 +0100 @@ -328,22 +328,22 @@ msgstr "Relations" msgid "CWSource" -msgstr "" +msgstr "Data source" msgid "CWSourceHostConfig" -msgstr "" +msgstr "Host configuration" msgid "CWSourceHostConfig_plural" -msgstr "" +msgstr "Host configurations" msgid "CWSource_plural" -msgstr "" +msgstr "Data sources" msgid "CWUniqueTogetherConstraint" -msgstr "" +msgstr "Unicity constraint" msgid "CWUniqueTogetherConstraint_plural" -msgstr "" +msgstr "Unicity constraints" msgid "CWUser" msgstr "User" @@ -525,13 +525,13 @@ msgstr "New relation" msgid "New CWSource" -msgstr "" +msgstr "New source" msgid "New CWSourceHostConfig" -msgstr "" +msgstr "New host configuration" msgid "New CWUniqueTogetherConstraint" -msgstr "" +msgstr "New unicity constraint" msgid "New CWUser" msgstr "New user" @@ -733,13 +733,13 @@ msgstr "This relation" msgid "This CWSource" -msgstr "" +msgstr "This data source" msgid "This CWSourceHostConfig" -msgstr "" +msgstr "This host configuration" msgid "This CWUniqueTogetherConstraint" -msgstr "" +msgstr "This unicity constraint" msgid "This CWUser" msgstr "This user" @@ -933,10 +933,10 @@ msgstr "relation definition" msgid "add CWSourceHostConfig cw_host_config_of CWSource object" -msgstr "" +msgstr "host configuration" msgid "add CWUniqueTogetherConstraint constraint_of CWEType object" -msgstr "" +msgstr "unicity constraint" msgid "add CWUser in_group CWGroup object" msgstr "user" @@ -1433,18 +1433,18 @@ msgstr "" msgid "constraint_of" -msgstr "" +msgstr "constraint of" msgctxt "CWUniqueTogetherConstraint" msgid "constraint_of" -msgstr "" +msgstr "constraint of" msgctxt "CWEType" msgid "constraint_of_object" -msgstr "" +msgstr "constrained by" msgid "constraint_of_object" -msgstr "" +msgstr "constrained by" msgid "constraints" msgstr "" @@ -1540,7 +1540,7 @@ msgid "" "creating CWUniqueTogetherConstraint (CWUniqueTogetherConstraint " "constraint_of CWEType %(linkto)s)" -msgstr "creating unique together constrain for entity type %(linkto)s" +msgstr "creating unique together constraint for entity type %(linkto)s" msgid "creating CWUser (CWUser in_group CWGroup %(linkto)s)" msgstr "creating a new user in group %(linkto)s" @@ -3315,14 +3315,14 @@ msgctxt "CWAttribute" msgid "relations_object" -msgstr "" - -msgctxt "CWRelation" +msgstr "constrained by" + +msgctxt "CWRType" msgid "relations_object" -msgstr "" +msgstr "constrained by" msgid "relations_object" -msgstr "" +msgstr "constrained by" msgid "relative url of the bookmarked page" msgstr "" diff -r 026581ea2b16 -r 117b53c10d79 i18n/fr.po --- a/i18n/fr.po Fri Jan 14 08:10:41 2011 +0100 +++ b/i18n/fr.po Wed Jan 19 09:31:15 2011 +0100 @@ -347,10 +347,10 @@ msgstr "Source de données" msgid "CWUniqueTogetherConstraint" -msgstr "Contrainte unique_together" +msgstr "Contrainte d'unicité" msgid "CWUniqueTogetherConstraint_plural" -msgstr "Contraintes unique_together" +msgstr "Contraintes d'unicité" msgid "CWUser" msgstr "Utilisateur" diff -r 026581ea2b16 -r 117b53c10d79 schema.py --- a/schema.py Fri Jan 14 08:10:41 2011 +0100 +++ b/schema.py Wed Jan 19 09:31:15 2011 +0100 @@ -83,7 +83,7 @@ 'SubWorkflowExitPoint')) INTERNAL_TYPES = set(('CWProperty', 'CWPermission', 'CWCache', 'ExternalUri', - 'CWSource', 'CWSourceAlias', + 'CWSource', 'CWSourceHostConfig', )) diff -r 026581ea2b16 -r 117b53c10d79 server/hook.py --- a/server/hook.py Fri Jan 14 08:10:41 2011 +0100 +++ b/server/hook.py Wed Jan 19 09:31:15 2011 +0100 @@ -225,8 +225,9 @@ Nothing precludes one to invent new categories and use the -:class:`~cubicweb.server.session.hooks_control` context manager to filter them -in or out. +:class:`~cubicweb.server.session.hooks_control` context manager to +filter them in or out. Note that ending the transaction with commit() +or rollback() will restore the hooks. Hooks specific selector @@ -299,6 +300,7 @@ key=lambda x: x.order) with security_enabled(session, write=False): for hook in hooks: + #print hook.category, hook.__regid__ hook() class HooksManager(object): @@ -371,8 +373,23 @@ class match_rtype_sets(ExpectedValueSelector): - """accept if parameters specified as initializer arguments are specified - in named arguments given to the selector + """accept if the relation type is in one of the sets given as initializer + argument. The goal of this selector is that it keeps reference to original sets, + so modification to thoses sets are considered by the selector. For instance + + MYSET = set() + + class Hook1(Hook): + __regid__ = 'hook1' + __select__ = Hook.__select__ & match_rtype_sets(MYSET) + ... + + class Hook2(Hook): + __regid__ = 'hook2' + __select__ = Hook.__select__ & match_rtype_sets(MYSET) + + Client code can now change `MYSET`, this will changes the selection criteria + of :class:`Hook1` and :class:`Hook1`. """ def __init__(self, *expected): diff -r 026581ea2b16 -r 117b53c10d79 setup.py --- a/setup.py Fri Jan 14 08:10:41 2011 +0100 +++ b/setup.py Wed Jan 19 09:31:15 2011 +0100 @@ -194,9 +194,10 @@ old_ok = DS._ok def _ok(self, path): """Return True if ``path`` can be written during installation.""" - out = old_ok(self, path) + out = old_ok(self, path) # here for side effect from setuptools realpath = os.path.normcase(os.path.realpath(path)) - if realpath.startswith(sys.prefix): + allowed_path = os.path.normcase(sys.prefix) + if realpath.startswith(allowed_path): out = True return out DS._ok = _ok diff -r 026581ea2b16 -r 117b53c10d79 skeleton/__pkginfo__.py.tmpl --- a/skeleton/__pkginfo__.py.tmpl Fri Jan 14 08:10:41 2011 +0100 +++ b/skeleton/__pkginfo__.py.tmpl Wed Jan 19 09:31:15 2011 +0100 @@ -8,13 +8,13 @@ version = '.'.join(str(num) for num in numversion) license = '%(license)s' - author = '%(author)s' author_email = '%(author-email)s' +description = '%(shortdesc)s' +web = 'http://www.cubicweb.org/project/%%s' %% distname -description = '%(shortdesc)s' - -web = 'http://www.cubicweb.org/project/%%s' %% distname +__depends__ = %(dependencies)s +__recommends__ = {} from os import listdir as _listdir @@ -40,6 +40,3 @@ # Note: here, you'll need to add subdirectories if you want # them to be included in the debian package -__depends__ = %(dependencies)s -__recommends__ = {} - diff -r 026581ea2b16 -r 117b53c10d79 skeleton/setup.py --- a/skeleton/setup.py Fri Jan 14 08:10:41 2011 +0100 +++ b/skeleton/setup.py Wed Jan 19 09:31:15 2011 +0100 @@ -1,8 +1,25 @@ #!/usr/bin/env python -# pylint: disable=W0404,W0622,W0704,W0613 +# pylint: disable=W0142,W0403,W0404,W0613,W0622,W0622,W0704,R0904,C0103,E0611 +# # copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr - +# +# This file is part of CubicWeb tag cube. +# +# CubicWeb is free software: you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 2.1 of the License, or (at your option) +# any later version. +# +# CubicWeb is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with CubicWeb. If not, see . +"""Generic Setup script, takes package info from __pkginfo__.py file +""" __docformat__ = "restructuredtext en" import os @@ -12,49 +29,50 @@ try: if os.environ.get('NO_SETUPTOOLS'): - raise ImportError() + raise ImportError() # do as there is no setuptools from setuptools import setup from setuptools.command import install_lib - USE_SETUPTOOLS = 1 + USE_SETUPTOOLS = True except ImportError: from distutils.core import setup from distutils.command import install_lib - USE_SETUPTOOLS = 0 + USE_SETUPTOOLS = False +from distutils.command import install_data - -sys.modules.pop('__pkginfo__', None) # import required features -from __pkginfo__ import modname, version, license, description, \ - web, author, author_email -# import optional features -import __pkginfo__ -distname = getattr(__pkginfo__, 'distname', modname) -scripts = getattr(__pkginfo__, 'scripts', []) -data_files = getattr(__pkginfo__, 'data_files', None) -include_dirs = getattr(__pkginfo__, 'include_dirs', []) -ext_modules = getattr(__pkginfo__, 'ext_modules', None) -dependency_links = getattr(__pkginfo__, 'dependency_links', []) - -STD_BLACKLIST = ('CVS', '.svn', '.hg', 'debian', 'dist', 'build') - -IGNORED_EXTENSIONS = ('.pyc', '.pyo', '.elc', '~') +from __pkginfo__ import modname, version, license, description, web, \ + author, author_email if exists('README'): long_description = file('README').read() else: long_description = '' + +# import optional features +import __pkginfo__ if USE_SETUPTOOLS: - requires = {} - for entry in ("__depends__", "__recommends__"): - requires.update(getattr(__pkginfo__, entry, {})) - install_requires = [("%s %s" % (d, v and v or "")).strip() + requires = {} + for entry in ("__depends__",): # "__recommends__"): + requires.update(getattr(__pkginfo__, entry, {})) + install_requires = [("%s %s" % (d, v and v or "")).strip() for d, v in requires.iteritems()] else: - install_requires = [] + install_requires = [] + +distname = getattr(__pkginfo__, 'distname', modname) +scripts = getattr(__pkginfo__, 'scripts', ()) +include_dirs = getattr(__pkginfo__, 'include_dirs', ()) +data_files = getattr(__pkginfo__, 'data_files', None) +ext_modules = getattr(__pkginfo__, 'ext_modules', None) +dependency_links = getattr(__pkginfo__, 'dependency_links', ()) + +BASE_BLACKLIST = ('CVS', '.svn', '.hg', 'debian', 'dist', 'build') +IGNORED_EXTENSIONS = ('.pyc', '.pyo', '.elc', '~') def ensure_scripts(linux_scripts): - """Creates the proper script names required for each platform + """ + Creates the proper script names required for each platform (taken from 4Suite) """ from distutils import util @@ -64,23 +82,8 @@ scripts_ = linux_scripts return scripts_ -def get_packages(directory, prefix): - """return a list of subpackages for the given directory""" - result = [] - for package in os.listdir(directory): - absfile = join(directory, package) - if isdir(absfile): - if exists(join(absfile, '__init__.py')) or \ - package in ('test', 'tests'): - if prefix: - result.append('%s.%s' % (prefix, package)) - else: - result.append(package) - result += get_packages(absfile, result[-1]) - return result - def export(from_dir, to_dir, - blacklist=STD_BLACKLIST, + blacklist=BASE_BLACKLIST, ignore_ext=IGNORED_EXTENSIONS, verbose=True): """make a mirror of from_dir in to_dir, omitting directories and files @@ -134,6 +137,35 @@ dest = join(self.install_dir, base, directory) export(directory, dest, verbose=False) +# re-enable copying data files in sys.prefix +old_install_data = install_data.install_data +if USE_SETUPTOOLS: + # overwrite InstallData to use sys.prefix instead of the egg directory + class MyInstallData(old_install_data): + """A class that manages data files installation""" + def run(self): + _old_install_dir = self.install_dir + if self.install_dir.endswith('egg'): + self.install_dir = sys.prefix + old_install_data.run(self) + self.install_dir = _old_install_dir + try: + import setuptools.command.easy_install # only if easy_install avaible + # monkey patch: Crack SandboxViolation verification + from setuptools.sandbox import DirectorySandbox as DS + old_ok = DS._ok + def _ok(self, path): + """Return True if ``path`` can be written during installation.""" + out = old_ok(self, path) # here for side effect from setuptools + realpath = os.path.normcase(os.path.realpath(path)) + allowed_path = os.path.normcase(sys.prefix) + if realpath.startswith(allowed_path): + out = True + return out + DS._ok = _ok + except ImportError: + pass + def install(**kwargs): """setup entry point""" if USE_SETUPTOOLS: @@ -142,9 +174,13 @@ # install-layout option was introduced in 2.5.3-1~exp1 elif sys.version_info < (2, 5, 4) and '--install-layout=deb' in sys.argv: sys.argv.remove('--install-layout=deb') - if USE_SETUPTOOLS and install_requires: + cmdclass = {'install_lib': MyInstallLib} + if USE_SETUPTOOLS: kwargs['install_requires'] = install_requires kwargs['dependency_links'] = dependency_links + kwargs['zip_safe'] = False + cmdclass['install_data'] = MyInstallData + return setup(name = distname, version = version, license = license, @@ -156,7 +192,7 @@ scripts = ensure_scripts(scripts), data_files = data_files, ext_modules = ext_modules, - cmdclass = {'install_lib': MyInstallLib}, + cmdclass = cmdclass, **kwargs ) diff -r 026581ea2b16 -r 117b53c10d79 web/uicfg.py --- a/web/uicfg.py Fri Jan 14 08:10:41 2011 +0100 +++ b/web/uicfg.py Wed Jan 19 09:31:15 2011 +0100 @@ -128,6 +128,7 @@ indexview_etype_section = InitializableDict( EmailAddress='subobject', # entity types in the 'system' table by default (managers only) + CWSource='system', CWUser='system', CWGroup='system', CWPermission='system', CWCache='system', diff -r 026581ea2b16 -r 117b53c10d79 web/views/schema.py --- a/web/views/schema.py Fri Jan 14 08:10:41 2011 +0100 +++ b/web/views/schema.py Wed Jan 19 09:31:15 2011 +0100 @@ -45,7 +45,7 @@ ALWAYS_SKIP_TYPES = BASE_TYPES | SCHEMA_TYPES SKIP_TYPES = (ALWAYS_SKIP_TYPES | META_RTYPES | SYSTEM_RTYPES | WORKFLOW_TYPES | INTERNAL_TYPES) -SKIP_TYPES.update(set(('CWUser', 'CWGroup'))) +SKIP_TYPES.update(set(('CWUser', 'CWGroup', 'EmailAddress', 'Bookmark'))) def skip_types(req): if int(req.form.get('skipmeta', True)): diff -r 026581ea2b16 -r 117b53c10d79 web/views/sparql.py --- a/web/views/sparql.py Fri Jan 14 08:10:41 2011 +0100 +++ b/web/views/sparql.py Wed Jan 19 09:31:15 2011 +0100 @@ -52,7 +52,7 @@ __regid__ = 'sparql' def call(self): form = self._cw.vreg['forms'].select('sparql', self._cw) - self.w(form.render()) + form.render(w=self.w) sparql = self._cw.form.get('sparql') vid = self._cw.form.get('resultvid', 'table') if sparql: @@ -67,7 +67,7 @@ else: rql, args = qinfo.finalize() if vid == 'sparqlxml': - url = self._cw.build_url('view', rql=(rql,args), vid=vid) + url = self._cw.build_url('view', rql=rql % args, vid=vid) raise Redirect(url) rset = self._cw.execute(rql, args) self.wview(vid, rset, 'null')