Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

rename bfgwiki to wiki

  • Loading branch information...
commit e53e13423685eac190676c4be32716c3a42603e4 1 parent ae8e4ad
@mcdonc mcdonc authored
Showing with 7,287 additions and 0 deletions.
  1. +300 −0 docs/tutorials/wiki/authorization.rst
  2. +18 −0 docs/tutorials/wiki/background.rst
  3. +137 −0 docs/tutorials/wiki/basiclayout.rst
  4. +162 −0 docs/tutorials/wiki/definingmodels.rst
  5. +416 −0 docs/tutorials/wiki/definingviews.rst
  6. +49 −0 docs/tutorials/wiki/distributing.rst
  7. +27 −0 docs/tutorials/wiki/index.rst
  8. +254 −0 docs/tutorials/wiki/installation.rst
  9. +5 −0 docs/tutorials/wiki/src/authorization/CHANGES.txt
  10. +4 −0 docs/tutorials/wiki/src/authorization/README.txt
  11. +28 −0 docs/tutorials/wiki/src/authorization/setup.cfg
  12. +43 −0 docs/tutorials/wiki/src/authorization/setup.py
  13. +21 −0 docs/tutorials/wiki/src/authorization/tutorial.ini
  14. +2 −0  docs/tutorials/wiki/src/authorization/tutorial/__init__.py
  15. +25 −0 docs/tutorials/wiki/src/authorization/tutorial/configure.zcml
  16. +44 −0 docs/tutorials/wiki/src/authorization/tutorial/login.py
  17. +27 −0 docs/tutorials/wiki/src/authorization/tutorial/models.py
  18. +23 −0 docs/tutorials/wiki/src/authorization/tutorial/run.py
  19. +8 −0 docs/tutorials/wiki/src/authorization/tutorial/security.py
  20. +34 −0 docs/tutorials/wiki/src/authorization/tutorial/templates/edit.pt
  21. +32 −0 docs/tutorials/wiki/src/authorization/tutorial/templates/login.pt
  22. +99 −0 docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt
  23. +380 −0 docs/tutorials/wiki/src/authorization/tutorial/templates/static/default.css
  24. BIN  docs/tutorials/wiki/src/authorization/tutorial/templates/static/images/img01.gif
  25. BIN  docs/tutorials/wiki/src/authorization/tutorial/templates/static/images/img02.gif
  26. BIN  docs/tutorials/wiki/src/authorization/tutorial/templates/static/images/img03.gif
  27. BIN  docs/tutorials/wiki/src/authorization/tutorial/templates/static/images/img04.gif
  28. BIN  docs/tutorials/wiki/src/authorization/tutorial/templates/static/images/spacer.gif
  29. +109 −0 docs/tutorials/wiki/src/authorization/tutorial/templates/static/style.css
  30. +243 −0 docs/tutorials/wiki/src/authorization/tutorial/templates/static/templatelicense.txt
  31. +31 −0 docs/tutorials/wiki/src/authorization/tutorial/templates/view.pt
  32. +123 −0 docs/tutorials/wiki/src/authorization/tutorial/tests.py
  33. +76 −0 docs/tutorials/wiki/src/authorization/tutorial/views.py
  34. +4 −0 docs/tutorials/wiki/src/basiclayout/CHANGES.txt
  35. +4 −0 docs/tutorials/wiki/src/basiclayout/README.txt
  36. +28 −0 docs/tutorials/wiki/src/basiclayout/setup.cfg
  37. +42 −0 docs/tutorials/wiki/src/basiclayout/setup.py
  38. +20 −0 docs/tutorials/wiki/src/basiclayout/tutorial.ini
  39. +2 −0  docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py
  40. +17 −0 docs/tutorials/wiki/src/basiclayout/tutorial/configure.zcml
  41. +12 −0 docs/tutorials/wiki/src/basiclayout/tutorial/models.py
  42. +22 −0 docs/tutorials/wiki/src/basiclayout/tutorial/run.py
  43. +99 −0 docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt
  44. +380 −0 docs/tutorials/wiki/src/basiclayout/tutorial/templates/static/default.css
  45. BIN  docs/tutorials/wiki/src/basiclayout/tutorial/templates/static/images/img01.gif
  46. BIN  docs/tutorials/wiki/src/basiclayout/tutorial/templates/static/images/img02.gif
  47. BIN  docs/tutorials/wiki/src/basiclayout/tutorial/templates/static/images/img03.gif
  48. BIN  docs/tutorials/wiki/src/basiclayout/tutorial/templates/static/images/img04.gif
  49. BIN  docs/tutorials/wiki/src/basiclayout/tutorial/templates/static/images/spacer.gif
  50. +243 −0 docs/tutorials/wiki/src/basiclayout/tutorial/templates/static/templatelicense.txt
  51. +19 −0 docs/tutorials/wiki/src/basiclayout/tutorial/tests.py
  52. +2 −0  docs/tutorials/wiki/src/basiclayout/tutorial/views.py
  53. +4 −0 docs/tutorials/wiki/src/models/CHANGES.txt
  54. +4 −0 docs/tutorials/wiki/src/models/README.txt
  55. +28 −0 docs/tutorials/wiki/src/models/setup.cfg
  56. +42 −0 docs/tutorials/wiki/src/models/setup.py
  57. +20 −0 docs/tutorials/wiki/src/models/tutorial.ini
  58. +2 −0  docs/tutorials/wiki/src/models/tutorial/__init__.py
  59. +17 −0 docs/tutorials/wiki/src/models/tutorial/configure.zcml
  60. +22 −0 docs/tutorials/wiki/src/models/tutorial/models.py
  61. +22 −0 docs/tutorials/wiki/src/models/tutorial/run.py
  62. +99 −0 docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt
  63. +380 −0 docs/tutorials/wiki/src/models/tutorial/templates/static/default.css
  64. BIN  docs/tutorials/wiki/src/models/tutorial/templates/static/images/img01.gif
  65. BIN  docs/tutorials/wiki/src/models/tutorial/templates/static/images/img02.gif
  66. BIN  docs/tutorials/wiki/src/models/tutorial/templates/static/images/img03.gif
  67. BIN  docs/tutorials/wiki/src/models/tutorial/templates/static/images/img04.gif
  68. BIN  docs/tutorials/wiki/src/models/tutorial/templates/static/images/spacer.gif
  69. +243 −0 docs/tutorials/wiki/src/models/tutorial/templates/static/templatelicense.txt
  70. +64 −0 docs/tutorials/wiki/src/models/tutorial/tests.py
  71. +2 −0  docs/tutorials/wiki/src/models/tutorial/views.py
  72. +4 −0 docs/tutorials/wiki/src/viewdecorators/CHANGES.txt
  73. +4 −0 docs/tutorials/wiki/src/viewdecorators/README.txt
  74. +28 −0 docs/tutorials/wiki/src/viewdecorators/setup.cfg
  75. +42 −0 docs/tutorials/wiki/src/viewdecorators/setup.py
  76. +21 −0 docs/tutorials/wiki/src/viewdecorators/tutorial.ini
  77. +2 −0  docs/tutorials/wiki/src/viewdecorators/tutorial/__init__.py
  78. +13 −0 docs/tutorials/wiki/src/viewdecorators/tutorial/configure.zcml
  79. +22 −0 docs/tutorials/wiki/src/viewdecorators/tutorial/models.py
  80. +22 −0 docs/tutorials/wiki/src/viewdecorators/tutorial/run.py
  81. +30 −0 docs/tutorials/wiki/src/viewdecorators/tutorial/templates/edit.pt
  82. +99 −0 docs/tutorials/wiki/src/viewdecorators/tutorial/templates/mytemplate.pt
  83. +380 −0 docs/tutorials/wiki/src/viewdecorators/tutorial/templates/static/default.css
  84. BIN  docs/tutorials/wiki/src/viewdecorators/tutorial/templates/static/images/img01.gif
  85. BIN  docs/tutorials/wiki/src/viewdecorators/tutorial/templates/static/images/img02.gif
  86. BIN  docs/tutorials/wiki/src/viewdecorators/tutorial/templates/static/images/img03.gif
  87. BIN  docs/tutorials/wiki/src/viewdecorators/tutorial/templates/static/images/img04.gif
  88. BIN  docs/tutorials/wiki/src/viewdecorators/tutorial/templates/static/images/spacer.gif
  89. +109 −0 docs/tutorials/wiki/src/viewdecorators/tutorial/templates/static/style.css
  90. +243 −0 docs/tutorials/wiki/src/viewdecorators/tutorial/templates/static/templatelicense.txt
  91. +26 −0 docs/tutorials/wiki/src/viewdecorators/tutorial/templates/view.pt
  92. +123 −0 docs/tutorials/wiki/src/viewdecorators/tutorial/tests.py
  93. +62 −0 docs/tutorials/wiki/src/viewdecorators/tutorial/views.py
  94. +3 −0  docs/tutorials/wiki/src/views/CHANGES.txt
  95. +4 −0 docs/tutorials/wiki/src/views/README.txt
  96. +28 −0 docs/tutorials/wiki/src/views/setup.cfg
  97. +42 −0 docs/tutorials/wiki/src/views/setup.py
  98. +21 −0 docs/tutorials/wiki/src/views/tutorial.ini
  99. +2 −0  docs/tutorials/wiki/src/views/tutorial/__init__.py
  100. +36 −0 docs/tutorials/wiki/src/views/tutorial/configure.zcml
  101. +22 −0 docs/tutorials/wiki/src/views/tutorial/models.py
  102. +22 −0 docs/tutorials/wiki/src/views/tutorial/run.py
  103. +32 −0 docs/tutorials/wiki/src/views/tutorial/templates/edit.pt
  104. +99 −0 docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt
  105. +380 −0 docs/tutorials/wiki/src/views/tutorial/templates/static/default.css
  106. BIN  docs/tutorials/wiki/src/views/tutorial/templates/static/images/img01.gif
  107. BIN  docs/tutorials/wiki/src/views/tutorial/templates/static/images/img02.gif
  108. BIN  docs/tutorials/wiki/src/views/tutorial/templates/static/images/img03.gif
  109. BIN  docs/tutorials/wiki/src/views/tutorial/templates/static/images/img04.gif
  110. BIN  docs/tutorials/wiki/src/views/tutorial/templates/static/images/spacer.gif
  111. +109 −0 docs/tutorials/wiki/src/views/tutorial/templates/static/style.css
  112. +243 −0 docs/tutorials/wiki/src/views/tutorial/templates/static/templatelicense.txt
  113. +29 −0 docs/tutorials/wiki/src/views/tutorial/templates/view.pt
  114. +127 −0 docs/tutorials/wiki/src/views/tutorial/tests.py
  115. +56 −0 docs/tutorials/wiki/src/views/tutorial/views.py
  116. +239 −0 docs/tutorials/wiki/viewdecorators.rst
View
300 docs/tutorials/wiki/authorization.rst
@@ -0,0 +1,300 @@
+====================
+Adding Authorization
+====================
+
+Our application currently allows anyone with access to the server to
+view, edit, and add pages to our wiki. For purposes of demonstration
+we'll change our application to allow people whom are members of a
+*group* named ``group:editors`` to add and edit wiki pages but we'll
+continue allowing anyone with access to the server to view pages.
+:mod:`pyramid` provides facilities for *authorization* and
+*authentication*. We'll make use of both features to provide security
+to our application.
+
+The source code for this tutorial stage can be browsed via
+`http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/authorization/
+<http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/authorization/>`_.
+
+
+The source code for this tutorial stage can be browsed at
+`docs.repoze.org <http://docs.repoze.org/bfgwiki-1.3/authorization>`_.
+
+Configuring a ``pyramid`` Authentication Policy
+--------------------------------------------------
+
+For any :mod:`pyramid` application to perform authorization, we
+need to add a ``security.py`` module and we'll need to change our
+:term:`application registry` to add an :term:`authentication policy`
+and a :term:`authorization policy`.
+
+Changing ``configure.zcml``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+We'll change our ``configure.zcml`` file to enable an
+``AuthTktAuthenticationPolicy`` and an ``ACLAuthorizationPolicy`` to
+enable declarative security checking. We'll also add a new view
+stanza, which specifies a :term:`forbidden view`. This configures our
+login view to show up when :mod:`pyramid` detects that a view
+invocation can not be authorized. When you're done, your
+``configure.zcml`` will look like so:
+
+.. literalinclude:: src/authorization/tutorial/configure.zcml
+ :linenos:
+ :language: xml
+
+Note that the ``authtktauthenticationpolicy`` tag has two attributes:
+``secret`` and ``callback``. ``secret`` is a string representing an
+encryption key used by the "authentication ticket" machinery
+represented by this policy: it is required. The ``callback`` is a
+string, representing a :term:`dotted Python name`, which points at the
+``groupfinder`` function in the current directory's ``security.py``
+file. We haven't added that module yet, but we're about to.
+
+Adding ``security.py``
+~~~~~~~~~~~~~~~~~~~~~~
+
+Add a ``security.py`` module within your package (in the same
+directory as ``run.py``, ``views.py``, etc) with the following
+content:
+
+.. literalinclude:: src/authorization/tutorial/security.py
+ :linenos:
+ :language: python
+
+The ``groupfinder`` function defined here is an authorization policy
+"callback"; it is a callable that accepts a userid and a request. If
+the userid exists in the set of users known by the system, the
+callback will return a sequence of group identifiers (or an empty
+sequence if the user isn't a member of any groups). If the userid
+*does not* exist in the system, the callback will return ``None``. In
+a production system this data will most often come from a database,
+but here we use "dummy" data to represent user and groups
+sources. Note that the ``editor`` user is a member of the
+``group:editors`` group in our dummy group data (the ``GROUPS`` data
+structure).
+
+Adding Login and Logout Views
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+We'll add a ``login`` view which renders a login form and processes
+the post from the login form, checking credentials.
+
+We'll also add a ``logout`` view to our application and provide a link
+to it. This view will clear the credentials of the logged in user and
+redirect back to the front page.
+
+We'll add a different file (for presentation convenience) to add login
+and logout views. Add a file named ``login.py`` to your application
+(in the same directory as ``views.py``) with the following content:
+
+.. literalinclude:: src/authorization/tutorial/login.py
+ :linenos:
+ :language: python
+
+Changing Existing Views
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Then we need to change each of our ``view_page``, ``edit_page`` and
+``add_page`` views in ``views.py`` to pass a "logged in" parameter
+into its template. We'll add something like this to each view body:
+
+.. ignore-next-block
+.. code-block:: python
+ :linenos:
+
+ from pyramid.security import authenticated_userid
+ logged_in = authenticated_userid(request)
+
+We'll then change the return value of each view that has an associated
+``renderer`` to pass the `resulting `logged_in`` value to the
+template. For example:
+
+.. ignore-next-block
+.. code-block:: python
+ :linenos:
+
+ return dict(page = context,
+ content = content,
+ logged_in = logged_in,
+ edit_url = edit_url)
+
+Adding the ``login.pt`` Template
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Add a ``login.pt`` template to your templates directory. It's
+referred to within the login view we just added to ``login.py``.
+
+.. literalinclude:: src/authorization/tutorial/templates/login.pt
+ :linenos:
+ :language: xml
+
+Change ``view.pt`` and ``edit.pt``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+We'll also need to change our ``edit.pt`` and ``view.pt`` templates to
+display a "Logout" link if someone is logged in. This link will
+invoke the logout view.
+
+To do so we'll add this to both templates within the ``<div
+class="main_content">`` div:
+
+.. code-block:: xml
+ :linenos:
+
+ <span tal:condition="logged_in">
+ <a href="${request.application_url}/logout">Logout</a>
+ </span>
+
+Giving Our Root Model Object an ACL
+-----------------------------------
+
+We need to give our root model object an :term:`ACL`. This ACL will
+be sufficient to provide enough information to the :mod:`pyramid`
+security machinery to challenge a user who doesn't have appropriate
+credentials when he attempts to invoke the ``add_page`` or
+``edit_page`` views.
+
+We need to perform some imports at module scope in our ``models.py``
+file:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.security import Allow
+ from pyramid.security import Everyone
+
+Our root model is a ``Wiki`` object. We'll add the following line at
+class scope to our ``Wiki`` class:
+
+.. code-block:: python
+ :linenos:
+
+ __acl__ = [ (Allow, Everyone, 'view'),
+ (Allow, 'group:editors', 'edit') ]
+
+It's only happenstance that we're assigning this ACL at class scope.
+An ACL can be attached to an object *instance* too; this is how "row
+level security" can be achieved in :mod:`pyramid` applications. We
+actually only need *one* ACL for the entire system, however, because
+our security requirements are simple, so this feature is not
+demonstrated.
+
+Our resulting ``models.py`` file will now look like so:
+
+.. literalinclude:: src/authorization/tutorial/models.py
+ :linenos:
+ :language: python
+
+Adding ``permission`` Declarations to our ``bfg_view`` Decorators
+-----------------------------------------------------------------
+
+To protect each of our views with a particular permission, we need to
+pass a ``permission`` argument to each of our
+:class:`pyramid.view.bfg_view` decorators. To do so, within
+``views.py``:
+
+- We add ``permission='view'`` to the decorator attached to the
+ ``view_wiki`` view function. This makes the assertion that only
+ users who possess the effective ``view`` permission at the time of
+ the request may invoke this view. We've granted
+ :data:`pyramid.security.Everyone` the view permission at the root
+ model via its ACL, so everyone will be able to invoke the
+ ``view_wiki`` view.
+
+- We add ``permission='view'`` to the decorator attached to the
+ ``view_page`` view function. This makes the assertion that only
+ users who possess the effective ``view`` permission at the time of
+ the request may invoke this view. We've granted
+ :data:`pyramid.security.Everyone` the view permission at the root
+ model via its ACL, so everyone will be able to invoke the
+ ``view_page`` view.
+
+- We add ``permission='edit'`` to the decorator attached to the
+ ``add_page`` view function. This makes the assertion that only
+ users who possess the effective ``edit`` permission at the time of
+ the request may invoke this view. We've granted the
+ ``group:editors`` principal the ``edit`` permission at the root
+ model via its ACL, so only the a user whom is a member of the group
+ named ``group:editors`` will able to invoke the ``add_page`` view.
+ We've likewise given the ``editor`` user membership to this group
+ via thes ``security.py`` file by mapping him to the
+ ``group:editors`` group in the ``GROUPS`` data structure (``GROUPS =
+ {'editor':['group:editors']}``); the ``groupfinder`` function
+ consults the ``GROUPS`` data structure. This means that the
+ ``editor`` user can add pages.
+
+- We add ``permission='edit'`` to the ``bfg_view`` decorator attached
+ to the ``edit_page`` view function. This makes the assertion that
+ only users who possess the effective ``edit`` permission at the time
+ of the request may invoke this view. We've granted the
+ ``group:editors`` principal the ``edit`` permission at the root
+ model via its ACL, so only the a user whom is a member of the group
+ named ``group:editors`` will able to invoke the ``edit_page`` view.
+ We've likewise given the ``editor`` user membership to this group
+ via thes ``security.py`` file by mapping him to the
+ ``group:editors`` group in the ``GROUPS`` data structure (``GROUPS =
+ {'editor':['group:editors']}``); the ``groupfinder`` function
+ consults the ``GROUPS`` data structure. This means that the
+ ``editor`` user can edit pages.
+
+Viewing the Application in a Browser
+------------------------------------
+
+We can finally examine our application in a browser. The views we'll
+try are as follows:
+
+- Visiting ``http://localhost:6543/`` in a browser invokes the
+ ``view_wiki`` view. This always redirects to the ``view_page`` view
+ of the FrontPage page object. It is executable by any user.
+
+- Visiting ``http://localhost:6543/FrontPage/`` in a browser invokes
+ the ``view_page`` view of the front page page object. This is
+ because it's the :term:`default view` (a view without a ``name``)
+ for ``Page`` objects. It is executable by any user.
+
+- Visiting ``http://localhost:6543/FrontPage/edit_page`` in a browser
+ invokes the edit view for the front page object. It is executable
+ by only the ``editor`` user. If a different user (or the anonymous
+ user) invokes it, a login form will be displayed. Supplying the
+ credentials with the username ``editor``, password ``editor`` will
+ show the edit page form being displayed.
+
+- Visiting ``http://localhost:6543/add_page/SomePageName`` in a
+ browser invokes the add view for a page. It is executable by only
+ the ``editor`` user. If a different user (or the anonymous user)
+ invokes it, a login form will be displayed. Supplying the
+ credentials with the username ``editor``, password ``editor`` will
+ show the edit page form being displayed.
+
+Seeing Our Changes To ``views.py`` and our Templates
+----------------------------------------------------
+
+Our ``views.py`` module will look something like this when we're done:
+
+.. literalinclude:: src/authorization/tutorial/views.py
+ :linenos:
+ :language: python
+
+Our ``edit.pt`` template will look something like this when we're done:
+
+.. literalinclude:: src/authorization/tutorial/templates/edit.pt
+ :linenos:
+ :language: xml
+
+Our ``view.pt`` template will look something like this when we're done:
+
+.. literalinclude:: src/authorization/tutorial/templates/view.pt
+ :linenos:
+ :language: xml
+
+Revisiting the Application
+---------------------------
+
+When we revisit the application in a browser, and log in (as a result
+of hitting an edit or add page and submitting the login form with the
+``editor`` credentials), we'll see a Logout link in the upper right
+hand corner. When we click it, we're logged out, and redirected back
+to the front page.
+
+
+
View
18 docs/tutorials/wiki/background.rst
@@ -0,0 +1,18 @@
+==========
+Background
+==========
+
+This version of the :mod:`pyramid` wiki tutorial presents a
+:mod:`pyramid` application that uses technologies which will be
+familiar to someone with :term:`Zope` experience. It uses
+:term:`ZODB` as a persistence mechanism and :term:`traversal` to map
+URLs to code. It can also be followed by people without any prior
+Python web framework experience.
+
+To code along with this tutorial, the developer will need a UNIX
+machine with development tools (Mac OS X with XCode, any Linux or BSD
+variant, etc) *or* he will need a Windows system of any kind.
+
+This tutorial targets :mod:`pyramid` version 1.3.
+
+Have fun!
View
137 docs/tutorials/wiki/basiclayout.rst
@@ -0,0 +1,137 @@
+============
+Basic Layout
+============
+
+The starter files generated by the ``pyramid_zodb`` template are basic,
+but they provide a good orientation for the high-level patterns common
+to most :term:`traversal` -based :mod:`pyramid` (and :term:`ZODB`
+based) projects.
+
+The source code for this tutorial stage can be browsed via
+`http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/basiclayout/
+<http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/basiclayout/>`_.
+
+``__init__.py``
+---------------
+
+A directory on disk can be turned into a Python :term:`package` by
+containing an ``__init__.py`` file. Even if empty, this marks a
+directory as a Python package.
+
+Configuration With ``configure.zcml``
+--------------------------------------
+
+The ``pyramid_zodb`` template uses :term:`ZCML` to perform system
+configuration. The ZCML file generated by the template looks like the
+following:
+
+ .. literalinclude:: src/basiclayout/tutorial/configure.zcml
+ :linenos:
+ :language: xml
+
+#. *Line 1*. The root ``<configure>`` element.
+
+#. *Line 4*. Boilerplate, the comment explains.
+
+#. *Lines 6-10*. Register a ``<view>`` that names a ``context`` type
+ that is a class. ``.views.my_view`` is a *function* we write
+ (generated by the ``pyramid_zodb`` template) that is given a
+ ``context`` object and a ``request`` and which returns a
+ dictionary. The ``renderer`` tag indicates that the
+ ``templates/mytemplate.pt`` template should be used to turn the
+ dictionary returned by the view into a response.
+ ``templates/mytemplate.pt`` is a *relative* path: it names the
+ ``mytemplate.pt`` file which lives in the ``templates``
+ subdirectory of the directory in which this ``configure.zcml``
+ lives in. In this case, it means it lives in the ``tutorial``
+ package's ``templates`` directory as ``mytemplate.pt``
+
+ Since this ``<view>`` doesn't have a ``name`` attribute, it is the
+ "default" view for that class.
+
+#. *Lines 12-15*. Register a ``static`` view which answers requests
+ which start with ``/static``. This is a view that will serve up
+ static resources for us, in this case, at
+ ``http://localhost:6543/static/`` and below. The ``path`` element
+ of this tag is a relative directory name, so it finds the resources
+ it should serve within the ``templates/static`` directory inside
+ the ``tutorial`` package.
+
+Content Models with ``models.py``
+---------------------------------
+
+:mod:`pyramid` often uses the word :term:`model` when talking about
+content resources arranged in the hierarchical *object graph*
+consulted by :term:`traversal`. The ``models.py`` file is where the
+``pyramid_zodb`` Paster template put the classes that implement our
+model objects.
+
+Here is the source for ``models.py``:
+
+ .. literalinclude:: src/basiclayout/tutorial/models.py
+ :linenos:
+ :language: py
+
+#. *Lines 3-4*. The ``MyModel`` class we referred to in the ZCML file
+ named ``configure.zcml`` is implemented here. Instances of this
+ class will be capable of being persisted in :term:`ZODB` because
+ the class inherits from the
+ :class:`persistent.mapping.PersistentMapping` class. The
+ ``__parent__`` and ``__name__`` are important parts of the
+ :term:`traversal` protocol. By default, have these as ``None``
+ indicating that this is the :term:`root` object.
+
+#. *Lines 6-12*. ``appmaker`` is used to return the *application
+ root* object. It is called on *every request* to the
+ :mod:`pyramid` application. It also performs bootstrapping by
+ *creating* an application root (inside the ZODB root object) if one
+ does not already exist.
+
+ We do so by first seeing if the database has the persistent
+ application root. If not, we make an instance, store it, and
+ commit the transaction. We then return the application root
+ object.
+
+App Startup with ``run.py``
+---------------------------
+
+When you run the application using the ``paster`` command using the
+``tutorial.ini`` generated config file, the application configuration
+points at an Setuptools *entry point* described as
+``egg:tutorial#app``. In our application, because the application's
+``setup.py`` file says so, this entry point happens to be the ``app``
+function within the file named ``run.py``:
+
+ .. literalinclude:: src/basiclayout/tutorial/run.py
+ :linenos:
+ :language: py
+
+#. *Lines 1-2*. Perform some dependency imports.
+
+#. *Line 12*. Get the ZODB configuration from the ``tutorial.ini``
+ file's ``[app:main]`` section represented by the ``settings``
+ dictionary passed to our ``app`` function. This will be a URI
+ (something like ``file:///path/to/Data.fs``).
+
+#. *Line 15*. We create a "finder" object using the
+ ``PersistentApplicationFinder`` helper class, passing it the ZODB
+ URI and the "appmaker" we've imported from ``models.py``.
+
+#. *Lines 16 - 17*. We create a :term:`root factory` which uses the
+ finder to return a ZODB root object.
+
+#. *Line 18*. We construct a :term:`Configurator` with a :term:`root
+ factory` and the settings keywords parsed by PasteDeploy. The root
+ factory is named ``get_root``.
+
+#. *Lines 19-21*. Begin configuration using the ``begin`` method of
+ the :meth:`pyramid.configuration.Configurator` class, load the
+ ``configure.zcml`` file from our package using the
+ :meth:`pyramid.configuration.Configurator.load_zcml` method, and
+ end configuration using the
+ :meth:`pyramid.configuration.Configurator.end` method.
+
+#. *Line 22*. Use the
+ :meth:`pyramid.configuration.Configurator.make_wsgi_app` method
+ to return a :term:`WSGI` application.
+
View
162 docs/tutorials/wiki/definingmodels.rst
@@ -0,0 +1,162 @@
+===============
+Defining Models
+===============
+
+The first change we'll make to our bone-stock ``paster`` -generated
+application will be to define a number of :term:`model` constructors.
+For this application, which will be a Wiki, we will need two kinds of
+model constructors: a "Wiki" model constructor, and a "Page" model
+constructor. Both our Page and Wiki constructors will be class
+objects. A single instance of the "Wiki" class will serve as a
+container for "Page" objects, which will be instances of the "Page"
+class.
+
+The source code for this tutorial stage can be browsed via
+`http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/models/
+<http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/models/>`_.
+
+Deleting the Database
+---------------------
+
+We're going to remove the ``MyModel`` Python model class from our
+``models.py`` file. Since this class is referred to within our
+persistent storage (represented on disk as a file named ``Data.fs``),
+we'll have strange things happen the next time we want to visit the
+application in a browser. Remove the ``Data.fs`` from the
+``tutorial`` directory before proceeding any further. It's always
+fine to do this as long as you don't care about the content of the
+database; the database itself will be recreated as necessary.
+
+Adding Model Classes
+--------------------
+
+The next thing we want to do is remove the ``MyModel`` class from the
+generated ``models.py`` file. The ``MyModel`` class is only a sample
+and we're not going to use it.
+
+.. note::
+
+ There is nothing automagically special about the filename
+ ``models.py``. A project may have many models throughout its
+ codebase in arbitrarily-named files. Files implementing models
+ often have ``model`` in their filenames (or they may live in a
+ Python subpackage of your application package named ``models``) ,
+ but this is only by convention.
+
+Then, we'll add a ``Wiki`` class. Because this is a ZODB application,
+this class should inherit from
+:class:`persistent.mapping.PersistentMapping`. We want it to inherit
+from the :class:`persistent.mapping.PersistentMapping` class because
+our Wiki class will be a mapping of wiki page names to ``Page``
+objects. The :class:`persistent.mapping.PersistentMapping` class
+provides our class with mapping behavior, and makes sure that our Wiki
+page is stored as a "first-class" persistent object in our ZODB
+database.
+
+Our ``Wiki`` class should also have a ``__name__`` attribute set to
+``None`` at class scope, and should have a ``__parent__`` attribute
+set to ``None`` at class scope as well. If a model has a
+``__parent__`` attribute of ``None`` in a traversal-based
+:mod:`pyramid` application, it means that it's the :term:`root`
+model. The ``__name__`` of the root model is also always ``None``.
+
+Then we'll add a ``Page`` class. This class should inherit from the
+:class:`persistent.Persistent` class. We'll also give it an
+``__init__`` method that accepts a single parameter named ``data``.
+This parameter will contain the :term:`ReStructuredText` body
+representing the wiki page content. Note that ``Page`` objects don't
+have an initial ``__name__`` or ``__parent__`` attribute. All objects
+in a traversal graph must have a ``__name__`` and a ``__parent__``
+attribute. We don't specify these here because both ``__name__`` and
+``__parent__`` will be set by by a :term:`view` function when a Page
+is added to our Wiki mapping.
+
+Add an Appmaker
+---------------
+
+We're using a mini-framework callable named
+``PersistentApplicationFinder`` in our application (see ``run.py``).
+A ``PersistentApplicationFinder`` accepts a ZODB URL as well as an
+"appmaker" callback. This callback typically lives in the
+``models.py`` file.
+
+We want to change the appmaker function in our ``models.py`` file so
+that our application root is a Wiki instance, and we'll also slot a
+single page object (the front page) into the wiki.
+
+Looking at the Result of Our Edits to ``models.py``
+---------------------------------------------------
+
+The result of all of our edits to ``models.py`` will end up looking
+something like this:
+
+.. literalinclude:: src/models/tutorial/models.py
+ :linenos:
+ :language: python
+
+Testing the Models
+------------------
+
+To make sure the code we just wrote works, we write tests for the
+model classes and the appmaker. Changing ``tests.py``, we'll write a
+separate test class for each model class, and we'll write a test class
+for the ``appmaker``.
+
+To do so, we'll retain the ``tutorial.tests.ViewTests`` class provided
+as a result of the ``pyramid_zodb`` project generator. We'll add
+three test classes: one for the ``Page`` model named
+``PageModelTests``, one for the ``Wiki`` model named
+``WikiModelTests``, and one for the appmaker named ``AppmakerTests``.
+
+When we're done changing ``tests.py``, it will look something like so:
+
+.. literalinclude:: src/models/tutorial/tests.py
+ :linenos:
+ :language: python
+
+Running the Tests
+-----------------
+
+We can run these tests by using ``setup.py test`` in the same way we
+did in :ref:`running_tests`. Assuming our shell's current working
+directory is the "tutorial" distribution directory:
+
+On UNIX:
+
+.. code-block:: text
+
+ $ ../bin/python setup.py test -q
+
+On Windows:
+
+.. code-block:: text
+
+ c:\bigfntut\tutorial> ..\Scripts\python setup.py test -q
+
+The expected output is something like this:
+
+.. code-block:: text
+
+ .....
+ ----------------------------------------------------------------------
+ Ran 5 tests in 0.008s
+
+ OK
+
+Declaring Dependencies in Our ``setup.py`` File
+-----------------------------------------------
+
+Our application depends on packages which are not dependencies of the
+original "tutorial" application as it was generated by the ``paster
+create`` command. We'll add these dependencies to our ``tutorial``
+package's ``setup.py`` file by assigning these dependencies to both
+the ``install_requires`` and the ``tests_require`` parameters to the
+``setup`` function. In particular, we require the ``docutils``
+package.
+
+Our resulting ``setup.py`` should look like so:
+
+.. literalinclude:: src/models/setup.py
+ :linenos:
+ :language: python
+
View
416 docs/tutorials/wiki/definingviews.rst
@@ -0,0 +1,416 @@
+==============
+Defining Views
+==============
+
+A :term:`view callable` in a traversal-based :mod:`pyramid`
+application is typically a simple Python function that accepts two
+parameters: :term:`context`, and :term:`request`. A view callable is
+assumed to return a :term:`response` object.
+
+.. note:: A :mod:`pyramid` view can also be defined as callable
+ which accepts *one* arguments: a :term:`request`. You'll see this
+ one-argument pattern used in other :mod:`pyramid` tutorials and
+ applications. Either calling convention will work in any
+ :mod:`pyramid` application; the calling conventions can be used
+ interchangeably as necessary. In :term:`traversal` based
+ applications, such as this tutorial, the context is used frequently
+ within the body of a view method, so it makes sense to use the
+ two-argument syntax in this application. However, in :term:`url
+ dispatch` based applications, the context object is rarely used in
+ the view body itself, so within code that uses URL-dispatch-only,
+ it's common to define views as callables that accept only a request
+ to avoid the visual "noise".
+
+We're going to define several :term:`view callable` functions then
+wire them into :mod:`pyramid` using some :term:`view
+configuration` via :term:`ZCML`.
+
+The source code for this tutorial stage can be browsed via
+`http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/views/
+<http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/views/>`_.
+
+Adding View Functions
+=====================
+
+We're going to add four :term:`view callable` functions to our
+``views.py`` module. One view (named ``view_wiki``) will display the
+wiki itself (it will answer on the root URL), another named
+``view_page`` will display an individual page, another named
+``add_page`` will allow a page to be added, and a final view named
+``edit_page`` will allow a page to be edited.
+
+.. note::
+
+ There is nothing automagically special about the filename
+ ``views.py``. A project may have many views throughout its codebase
+ in arbitrarily-named files. Files implementing views often have
+ ``view`` in their filenames (or may live in a Python subpackage of
+ your application package named ``views``), but this is only by
+ convention.
+
+The ``view_wiki`` view function
+-------------------------------
+
+The ``view_wiki`` function will be configured to respond as the
+default view of a ``Wiki`` model object. It always redirects to the
+``Page`` object named "FrontPage". It returns an instance of the
+:class:`webob.exc.HTTPFound` class (instances of which implement the
+WebOb :term:`response` interface), and the
+:func:`pyramid.url.model_url` API.
+:func:`pyramid.url.model_url` constructs a URL to the ``FrontPage``
+page (e.g. ``http://localhost:6543/FrontPage``), and uses it as the
+"location" of the HTTPFound response, forming an HTTP redirect.
+
+The ``view_page`` view function
+-------------------------------
+
+The ``view_page`` function will be configured to respond as the
+default view of a ``Page`` object. The ``view_page`` function renders
+the :term:`ReStructuredText` body of a page (stored as the ``data``
+attribute of the context passed to the view; the context will be a
+Page object) as HTML. Then it substitutes an HTML anchor for each
+*WikiWord* reference in the rendered HTML using a compiled regular
+expression.
+
+The curried function named ``check`` is used as the first argument to
+``wikiwords.sub``, indicating that it should be called to provide a
+value for each WikiWord match found in the content. If the wiki (our
+page's ``__parent__``) already contains a page with the matched
+WikiWord name, the ``check`` function generates a view link to be used
+as the substitution value and returns it. If the wiki does not
+already contain a page with with the matched WikiWord name, the
+function generates an "add" link as the substitution value and returns
+it.
+
+As a result, the ``content`` variable is now a fully formed bit of
+HTML containing various view and add links for WikiWords based on the
+content of our current page object.
+
+We then generate an edit URL (because it's easier to do here than in
+the template), and we wrap up a number of arguments in a dictionary
+and return it.
+
+The arguments we wrap into a dictionary include ``page``, ``content``,
+and ``edit_url``. As a result, the *template* associated with this
+view callable will be able to use these names to perform various
+rendering tasks. The template associated with this view callable will
+be a template which lives in ``templates/view.pt``, which we'll
+associate with this view via the :term:`view configuration` which
+lives in the ``configure.zcml`` file.
+
+Note the contrast between this view callable and the ``view_wiki``
+view callable. In the ``view_wiki`` view callable, we return a
+:term:`response` object. In the ``view_page`` view callable, we
+return a *dictionary*. It is *always* fine to return a
+:term:`response` object from a :mod:`pyramid` view. Returning a
+dictionary is allowed only when there is a :term:`renderer` associated
+with the view callable in the view configuration.
+
+The ``add_page`` view function
+------------------------------
+
+The ``add_page`` function will be invoked when a user clicks on a
+WikiWord which isn't yet represented as a page in the system. The
+``check`` function within the ``view_page`` view generates URLs to
+this view. It also acts as a handler for the form that is generated
+when we want to add a page object. The ``context`` of the
+``add_page`` view is always a Wiki object (*not* a Page object).
+
+The request :term:`subpath` in :mod:`pyramid` is the sequence of
+names that are found *after* the view name in the URL segments given
+in the ``PATH_INFO`` of the WSGI request as the result of
+:term:`traversal`. If our add view is invoked via,
+e.g. ``http://localhost:6543/add_page/SomeName``, the :term:`subpath`
+will be a tuple: ``('SomeName',)``.
+
+The add view takes the zeroth element of the subpath (the wiki page
+name), and aliases it to the name attribute in order to know the name
+of the page we're trying to add.
+
+If the view rendering is *not* a result of a form submission (if the
+expression ``'form.submitted' in request.params`` is ``False``), the
+view renders a template. To do so, it generates a "save url" which
+the template use as the form post URL during rendering. We're lazy
+here, so we're trying to use the same template (``templates/edit.pt``)
+for the add view as well as the page edit view. To do so, we create a
+dummy Page object in order to satisfy the edit form's desire to have
+*some* page object exposed as ``page``, and we'll render the template
+to a response.
+
+If the view rendering *is* a result of a form submission (if the
+expression ``'form.submitted' in request.params`` is ``True``), we
+scrape the page body from the form data, create a Page object using
+the name in the subpath and the page body, and save it into "our
+context" (the wiki) using the ``__setitem__`` method of the
+context. We then redirect back to the ``view_page`` view (the default
+view for a page) for the newly created page.
+
+The ``edit_page`` view function
+-------------------------------
+
+The ``edit_page`` function will be invoked when a user clicks the
+"Edit this Page" button on the view form. It renders an edit form but
+it also acts as the handler for the form it renders. The ``context``
+of the ``edit_page`` view will *always* be a Page object (never a Wiki
+object).
+
+If the view execution is *not* a result of a form submission (if the
+expression ``'form.submitted' in request.params`` is ``False``), the
+view simply renders the edit form, passing the request, the page
+object, and a save_url which will be used as the action of the
+generated form.
+
+If the view execution *is* a result of a form submission (if the
+expression ``'form.submitted' in request.params`` is ``True``), the
+view grabs the ``body`` element of the request parameter and sets it
+as the ``data`` attribute of the page context. It then redirects to
+the default view of the context (the page), which will always be the
+``view_page`` view.
+
+Viewing the Result of Our Edits to ``views.py``
+===============================================
+
+The result of all of our edits to ``views.py`` will leave it looking
+like this:
+
+.. literalinclude:: src/views/tutorial/views.py
+ :linenos:
+ :language: python
+
+Adding Templates
+================
+
+Most view callables we've added expected to be rendered via a
+:term:`template`. Each template is a :term:`Chameleon` template. The
+default templating system in :mod:`pyramid` is a variant of
+:term:`ZPT` provided by Chameleon. These templates will live in the
+``templates`` directory of our tutorial package.
+
+The ``view.pt`` Template
+------------------------
+
+The ``view.pt`` template is used for viewing a single wiki page. It
+is used by the ``view_page`` view function. It should have a div that
+is "structure replaced" with the ``content`` value provided by the
+view. It should also have a link on the rendered page that points at
+the "edit" URL (the URL which invokes the ``edit_page`` view for the
+page being viewed).
+
+Once we're done with the ``view.pt`` template, it will look a lot like
+the below:
+
+.. literalinclude:: src/views/tutorial/templates/view.pt
+ :linenos:
+ :language: xml
+
+.. note:: The names available for our use in a template are always
+ those that are present in the dictionary returned by the view
+ callable. But our templates make use of a ``request`` object that
+ none of our tutorial views return in their dictionary. This value
+ appears as if "by magic". However, ``request`` is one of several
+ names that are available "by default" in a template when a template
+ renderer is used. See :ref:`chameleon_template_renderers` for more
+ information about other names that are available by default in a
+ template when a Chameleon template is used as a renderer.
+
+The ``edit.pt`` Template
+------------------------
+
+The ``edit.pt`` template is used for adding and editing a wiki page.
+It is used by the ``add_page`` and ``edit_page`` view functions. It
+should display a page containing a form that POSTs back to the
+"save_url" argument supplied by the view. The form should have a
+"body" textarea field (the page data), and a submit button that has
+the name "form.submitted". The textarea in the form should be filled
+with any existing page data when it is rendered.
+
+Once we're done with the ``edit.pt`` template, it will look a lot like
+the below:
+
+.. literalinclude:: src/views/tutorial/templates/edit.pt
+ :linenos:
+ :language: xml
+
+Static Resources
+----------------
+
+Our templates name a single static resource named ``style.css``. We
+need to create this and place it in a file named ``style.css`` within
+our package's ``templates/static`` directory. This file is a little
+too long to replicate within the body of this guide, however it is
+available `online
+<http://github.com/Pylons/pyramid/blob/master/docs/tutorials/wiki/src/views/tutorial/templates/static/default.css>`_.
+
+This CSS file will be accessed via
+e.g. ``http://localhost:6543/static/style.css`` by virtue of the
+``static`` directive we've defined in the ``configure.zcml`` file.
+Any number and type of static resources can be placed in this
+directory (or subdirectories) and are just referred to by URL within
+templates.
+
+Testing the Views
+=================
+
+We'll modify our ``tests.py`` file, adding tests for each view
+function we added above. As a result, we'll *delete* the
+``ViewTests`` test in the file, and add four other test classes:
+``ViewWikiTests``, ``ViewPageTests``, ``AddPageTests``, and
+``EditPageTests``. These test the ``view_wiki``, ``view_page``,
+``add_page``, and ``edit_page`` views respectively.
+
+Once we're done with the ``tests.py`` module, it will look a lot like
+the below:
+
+.. literalinclude:: src/views/tutorial/tests.py
+ :linenos:
+ :language: python
+
+Running the Tests
+=================
+
+We can run these tests by using ``setup.py test`` in the same way we
+did in :ref:`running_tests`. Assuming our shell's current working
+directory is the "tutorial" distribution directory:
+
+On UNIX:
+
+.. code-block:: text
+
+ $ ../bin/python setup.py test -q
+
+On Windows:
+
+.. code-block:: text
+
+ c:\bigfntut\tutorial> ..\Scripts\python setup.py test -q
+
+The expected result looks something like:
+
+.. code-block:: text
+
+ .........
+ ----------------------------------------------------------------------
+ Ran 9 tests in 0.203s
+
+ OK
+
+Mapping Views to URLs in ``configure.zcml``
+===========================================
+
+The ``configure.zcml`` file contains ``view`` declarations which serve
+to map URLs (via :term:`traversal`) to view functions. This is also
+known as :term:`view configuration`. You'll need to add four ``view``
+declarations to ``configure.zcml``.
+
+#. Add a declaration which maps the "Wiki" class in our ``models.py``
+ file to the view named ``view_wiki`` in our ``views.py`` file with
+ no view name. This is the default view for a Wiki. It does not
+ use a ``renderer`` because the ``view_wiki`` view callable always
+ returns a *response* object rather than a dictionary.
+
+#. Add a declaration which maps the "Wiki" class in our ``models.py``
+ file to the view named ``add_page`` in our ``views.py`` file with
+ the view name ``add_page``. Associate this view with the
+ ``templates/edit.pt`` template file via the ``renderer`` attribute.
+ This view will use the :term:`Chameleon` ZPT renderer configured
+ with the ``templates/edit.pt`` template to render non-*response*
+ return values from the ``add_page`` view. This is the add view for
+ a new Page.
+
+#. Add a declaration which maps the "Page" class in our ``models.py``
+ file to the view named ``view_page`` in our ``views.py`` file with
+ no view name. Associate this view with the ``templates/view.pt``
+ template file via the ``renderer`` attribute. This view will use
+ the :term:`Chameleon` ZPT renderer configured with the
+ ``templates/view.pt`` template to render non-*response* return
+ values from the ``view_page`` view. This is the default view for a
+ Page.
+
+#. Add a declaration which maps the "Page" class in our ``models.py``
+ file to the view named ``edit_page`` in our ``views.py`` file with
+ the view name ``edit_page``. Associate this view with the
+ ``templates/edit.pt`` template file via the ``renderer`` attribute.
+ This view will use the :term:`Chameleon` ZPT renderer configured
+ with the ``templates/edit.pt`` template to render non-*response*
+ return values from the ``edit_page`` view. This is the edit view
+ for a page.
+
+As a result of our edits, the ``configure.zcml`` file should look
+something like so:
+
+.. literalinclude:: src/views/tutorial/configure.zcml
+ :linenos:
+ :language: xml
+
+Examining ``tutorial.ini``
+==========================
+
+Let's take a look at our ``tutorial.ini`` file. The contents of the
+file are as follows:
+
+.. literalinclude:: src/models/tutorial.ini
+ :linenos:
+ :language: ini
+
+The WSGI Pipeline
+-----------------
+
+Within ``tutorial.ini``, note the existence of a ``[pipeline:main]``
+section which specifies our WSGI pipeline. This "pipeline" will be
+served up as our WSGI application. As far as the WSGI server is
+concerned the pipeline *is* our application. Simpler configurations
+don't use a pipeline: instead they expose a single WSGI application as
+"main". Our setup is more complicated, so we use a pipeline.
+
+``egg:repoze.zodbconn#closer`` is at the "top" of the pipeline. This
+is a piece of middleware which closes the ZODB connection opened by
+the PersistentApplicationFinder at the end of the request.
+
+``egg:repoze.tm#tm`` is the second piece of middleware in the
+pipeline. This commits a transaction near the end of the request
+unless there's an exception raised.
+
+Adding an Element to the Pipeline
+---------------------------------
+
+Let's add a piece of middleware to the WSGI pipeline:
+``egg:Paste#evalerror`` middleware which displays debuggable errors in
+the browser while you're developing (not recommended for deployment).
+Let's insert evalerror into the pipeline right below
+"egg:repoze.zodbconn#closer", making our resulting ``tutorial.ini``
+file look like so:
+
+.. literalinclude:: src/views/tutorial.ini
+ :linenos:
+ :language: ini
+
+Viewing the Application in a Browser
+====================================
+
+Once we've set up the WSGI pipeline properly, we can finally examine
+our application in a browser. The views we'll try are as follows:
+
+- Visiting ``http://localhost:6543/`` in a browser invokes the
+ ``view_wiki`` view. This always redirects to the ``view_page`` view
+ of the FrontPage page object.
+
+- Visiting ``http://localhost:6543/FrontPage/`` in a browser invokes
+ the ``view_page`` view of the front page page object. This is
+ because it's the *default view* (a view without a ``name``) for Page
+ objects.
+
+- Visiting ``http://localhost:6543/FrontPage/edit_page`` in a browser
+ invokes the edit view for the front page object.
+
+- Visiting ``http://localhost:6543/add_page/SomePageName`` in a
+ browser invokes the add view for a page.
+
+- To generate an error, visit ``http://localhost:6543/add_page`` which
+ will generate an ``IndexError`` for the expression
+ ``request.subpath[0]``. You'll see an interactive traceback
+ facility provided by evalerror.
+
+
+
+
+
View
49 docs/tutorials/wiki/distributing.rst
@@ -0,0 +1,49 @@
+=============================
+Distributing Your Application
+=============================
+
+Once your application works properly, you can create a "tarball" from
+it by using the ``setup.py sdist`` command. The following commands
+assume your current working directory is the ``tutorial`` package
+we've created and that the parent directory of the ``tutorial``
+package is a virtualenv representing a :mod:`pyramid` environment.
+
+On UNIX:
+
+.. code-block:: text
+
+ $ ../bin/python setup.py sdist
+
+On Windows:
+
+.. code-block:: text
+
+ c:\bigfntut> ..\Scripts\python setup.py sdist
+
+.. warning:: If your project files are not checked in to a version
+ control repository (such as Subversion), the dist tarball will
+ *not* contain all the files it needs to. In particular, it will
+ not contain non-Python-source files (such as templates and static
+ files). To ensure that these are included, check your files into a
+ version control repository before running ``setup.py sdist``.
+
+The output of such a command will be something like:
+
+.. code-block:: text
+
+ running sdist
+ # .. more output ..
+ creating dist
+ tar -cf dist/tutorial-0.1.tar tutorial-0.1
+ gzip -f9 dist/tutorial-0.1.tar
+ removing 'tutorial-0.1' (and everything under it)
+
+Note that this command creates a tarball in the "dist" subdirectory
+named ``tutorial-0.1.tar.gz``. You can send this file to your friends
+to show them your cool new application. They should be able to
+install it by pointing the ``easy_install`` command directly at it.
+Or you can upload it to `PyPI <http://pypi.python.org>`_ and share it
+with the rest of the world, where it can be downloaded via
+``easy_install`` remotely like any other package people download from
+PyPI.
+
View
27 docs/tutorials/wiki/index.rst
@@ -0,0 +1,27 @@
+.. _bfg_wiki_tutorial:
+
+ZODB + Traversal Wiki Tutorial
+==============================
+
+This tutorial introduces a :term:`traversal` -based :mod:`pyramid`
+application to a developer familiar with Python. When we're done with
+the tutorial, the developer will have created a basic Wiki application
+with authentication.
+
+For cut and paste purposes, the source code for all stages of this
+tutorial can be browsed at
+`http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki
+<http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki>`_.
+
+.. toctree::
+ :maxdepth: 2
+
+ background
+ installation
+ basiclayout
+ definingmodels
+ definingviews
+ viewdecorators
+ authorization
+ distributing
+
View
254 docs/tutorials/wiki/installation.rst
@@ -0,0 +1,254 @@
+============
+Installation
+============
+
+For the most part, the installation process for this tutorial
+duplicates the steps described in :ref:`installing_chapter` and
+:ref:`project_narr`, however it also explains how to install
+additional libraries for tutorial purposes.
+
+Preparation
+========================
+
+Please take the following steps to prepare for the tutorial. The
+steps to prepare for the tutorial are slightly different depending on
+whether you're using UNIX or Windows.
+
+Preparation, UNIX
+-----------------
+
+#. If you don't already have a Python 2.6 interpreter installed on
+ your system, obtain, install, or find `Python 2.6
+ <http://python.org/download/releases/2.6.6/>`_ for your system.
+
+#. Install the latest `setuptools` into the Python you
+ obtained/installed/found in the step above: download `ez_setup.py
+ <http://peak.telecommunity.com/dist/ez_setup.py>`_ and run it using
+ the ``python`` interpreter of your Python 2.6 installation:
+
+ .. code-block:: bash
+
+ $ /path/to/my/Python-2.6/bin/python ez_setup.py
+
+#. Use that Python's `bin/easy_install` to install `virtualenv`:
+
+ .. code-block:: bash
+
+ $ /path/to/my/Python-2.6/bin/easy_install virtualenv
+
+#. Use that Python's virtualenv to make a workspace:
+
+ .. code-block:: bash
+
+ $ path/to/my/Python-2.6/bin/virtualenv --no-site-packages \
+ bigfntut
+
+#. Switch to the ``bigfntut`` directory:
+
+ .. code-block:: bash
+
+ $ cd bigfntut
+
+#. (Optional) Consider using ``source bin/activate`` to make your
+ shell environment wired to use the virtualenv.
+
+#. Use ``easy_install`` to get :mod:`pyramid` and its direct
+ dependencies installed:
+
+ .. code-block:: bash
+
+ $ bin/easy_install pyramid
+
+#. Use ``easy_install`` to install ``docutils``, ``repoze.tm``,
+ ``repoze.zodbconn``, ``nose`` and ``coverage``:
+
+ .. code-block:: bash
+
+ $ bin/easy_install docutils repoze.tm repoze.zodbconn \
+ nose coverage
+
+Preparation, Windows
+--------------------
+
+#. Install, or find `Python 2.6
+ <http://python.org/download/releases/2.6.6/>`_ for your system.
+
+#. Install the latest `setuptools` into the Python you
+ obtained/installed/found in the step above: download `ez_setup.py
+ <http://peak.telecommunity.com/dist/ez_setup.py>`_ and run it using
+ the ``python`` interpreter of your Python 2.6 installation using a
+ command prompt:
+
+ .. code-block:: bat
+
+ c:\> c:\Python26\python ez_setup.py
+
+#. Use that Python's `bin/easy_install` to install `virtualenv`:
+
+ .. code-block:: bat
+
+ c:\> c:\Python26\Scripts\easy_install virtualenv
+
+#. Use that Python's virtualenv to make a workspace:
+
+ .. code-block:: bat
+
+ c:\> c:\Python26\Scripts\virtualenv --no-site-packages bigfntut
+
+#. Switch to the ``bigfntut`` directory:
+
+ .. code-block:: bat
+
+ c:\> cd bigfntut
+
+#. (Optional) Consider using ``bin\activate.bat`` to make your shell
+ environment wired to use the virtualenv.
+
+#. Use ``easy_install`` to get :mod:`pyramid` and its direct
+ dependencies installed:
+
+ .. code-block:: bat
+
+ c:\bigfntut> Scripts\easy_install pyramid
+
+#. Use ``easy_install`` to install ``docutils``, ``repoze.tm``,
+ ``repoze.zodbconn``, ``nose`` and ``coverage``:
+
+ .. code-block:: bat
+
+ c:\bigfntut> Scripts\easy_install docutils repoze.tm \
+ repoze.zodbconn nose coverage
+
+.. _making_a_project:
+
+Making a Project
+================
+
+Your next step is to create a project. :mod:`pyramid` supplies a
+variety of templates to generate sample projects. For this tutorial,
+we will use the :term:`ZODB` -oriented template named ``pyramid_zodb``.
+
+The below instructions assume your current working directory is the
+"virtualenv" named "bigfntut".
+
+On UNIX:
+
+.. code-block:: bash
+
+ $ bin/paster create -t pyramid_zodb tutorial
+
+On Windows:
+
+.. code-block:: bat
+
+ c:\bigfntut> Scripts\paster create -t pyramid_zodb tutorial
+
+.. note:: If you are using Windows, the ``pyramid_zodb`` Paster template
+ doesn't currently deal gracefully with installation into a location
+ that contains spaces in the path. If you experience startup
+ problems, try putting both the virtualenv and the project into
+ directories that do not contain spaces in their paths.
+
+Installing the Project in "Development Mode"
+============================================
+
+In order to do development on the project easily, you must "register"
+the project as a development egg in your workspace using the
+``setup.py develop`` command. In order to do so, cd to the "tutorial"
+directory you created in :ref:`making_a_project`, and run the
+"setup.py develop" command using virtualenv Python interpreter.
+
+On UNIX:
+
+.. code-block:: bash
+
+ $ cd tutorial
+ $ ../bin/python setup.py develop
+
+On Windows:
+
+.. code-block:: bat
+
+ C:\bigfntut> cd tutorial
+ C:\bigfntut\tutorial> ..\Scripts\python setup.py develop
+
+.. _running_tests:
+
+Running the Tests
+=================
+
+After you've installed the project in development mode, you may run
+the tests for the project.
+
+On UNIX:
+
+.. code-block:: bash
+
+ $ ../bin/python setup.py test -q
+
+On Windows:
+
+.. code-block:: bat
+
+ c:\bigfntut\tutorial> ..\Scripts\python setup.py test -q
+
+Starting the Application
+========================
+
+Start the application.
+
+On UNIX:
+
+.. code-block:: bash
+
+ $ ../bin/paster serve tutorial.ini --reload
+
+On Windows:
+
+.. code-block:: bat
+
+ c:\bifgfntut\tutorial> ..\Scripts\paster serve tutorial.ini --reload
+
+Exposing Test Coverage Information
+==================================
+
+You can run the ``nosetests`` command to see test coverage
+information. This runs the tests in the same way that ``setup.py
+test`` does but provides additional "coverage" information, exposing
+which lines of your project are "covered" (or not covered) by the
+tests.
+
+On UNIX:
+
+.. code-block:: bash
+
+ $ ../bin/nosetests --cover-package=tutorial --cover-erase --with-coverage
+
+On Windows:
+
+.. code-block:: bat
+
+ c:\bigfntut\tutorial> ..\Scripts\nosetests --cover-package=tutorial \
+ --cover-erase --with-coverage
+
+Looks like the code in the ``pyramid_zodb`` template for ZODB projects is
+missing some test coverage, particularly in the file named
+``models.py``.
+
+Visit the Application in a Browser
+==================================
+
+In a browser, visit `http://localhost:6543/ <http://localhost:6543>`_.
+You will see the generated application's default page.
+
+Decisions the ``pyramid_zodb`` Template Has Made For You
+========================================================
+
+Creating a project using the ``pyramid_zodb`` template makes the
+assumption that you are willing to use :term:`ZODB` as persistent
+storage and :term:`traversal` to map URLs to code. :mod:`pyramid`
+supports any persistent storage mechanism (e.g. a SQL database or
+filesystem files, etc). It also supports an additional mechanism to
+map URLs to code (:term:`URL dispatch`). However, for the purposes of
+this tutorial, we'll only be using traversal and ZODB.
+
View
5 docs/tutorials/wiki/src/authorization/CHANGES.txt
@@ -0,0 +1,5 @@
+0.0
+---
+
+- Initial version
+
View
4 docs/tutorials/wiki/src/authorization/README.txt
@@ -0,0 +1,4 @@
+tutorial README
+
+
+
View
28 docs/tutorials/wiki/src/authorization/setup.cfg
@@ -0,0 +1,28 @@
+[nosetests]
+match=^test
+nocapture=1
+cover-package=tutorial
+with-coverage=1
+cover-erase=1
+
+[compile_catalog]
+directory = tutorial/locale
+domain = tutorial
+statistics = true
+
+[extract_messages]
+add_comments = TRANSLATORS:
+output_file = tutorial/locale/tutorial.pot
+width = 80
+
+[init_catalog]
+domain = tutorial
+input_file = tutorial/locale/tutorial.pot
+output_dir = tutorial/locale
+
+[update_catalog]
+domain = tutorial
+input_file = tutorial/locale/tutorial.pot
+output_dir = tutorial/locale
+previous = true
+
View
43 docs/tutorials/wiki/src/authorization/setup.py
@@ -0,0 +1,43 @@
+import os
+
+from setuptools import setup, find_packages
+
+here = os.path.abspath(os.path.dirname(__file__))
+README = open(os.path.join(here, 'README.txt')).read()
+CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
+
+requires = [
+ 'pyramid',
+ 'docutils',
+ 'ZODB3',
+ 'repoze.zodbconn',
+ 'repoze.tm',
+ ]
+
+setup(name='tutorial',
+ version='0.0',
+ description='tutorial',
+ long_description=README + '\n\n' + CHANGES,
+ classifiers=[
+ "Intended Audience :: Developers",
+ "Framework :: Pylons",
+ "Programming Language :: Python",
+ "Topic :: Internet :: WWW/HTTP",
+ "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
+ ],
+ author='',
+ author_email='',
+ url='',
+ keywords='web wsgi pylons pyramid bfg',
+ packages=find_packages(),
+ include_package_data=True,
+ zip_safe=False,
+ install_requires=requires,
+ tests_require=requires,
+ test_suite="tutorial",
+ entry_points = """\
+ [paste.app_factory]
+ app = tutorial.run:app
+ """
+ )
+
View
21 docs/tutorials/wiki/src/authorization/tutorial.ini
@@ -0,0 +1,21 @@
+[DEFAULT]
+debug = true
+
+[app:zodb]
+use = egg:tutorial#app
+reload_templates = true
+debug_authorization = false
+debug_notfound = false
+zodb_uri = file://%(here)s/Data.fs?connection_cache_size=20000
+
+[pipeline:main]
+pipeline =
+ egg:repoze.zodbconn#closer
+ egg:Paste#evalerror
+ egg:repoze.tm#tm
+ zodb
+
+[server:main]
+use = egg:Paste#http
+host = 0.0.0.0
+port = 6543
View
2  docs/tutorials/wiki/src/authorization/tutorial/__init__.py
@@ -0,0 +1,2 @@
+# A package
+
View
25 docs/tutorials/wiki/src/authorization/tutorial/configure.zcml
@@ -0,0 +1,25 @@
+<configure xmlns="http://pylonshq.com/pyramid">
+
+ <!-- this must be included for the view declarations to work -->
+ <include package="pyramid.includes" />
+
+ <scan package="."/>
+
+ <view
+ view=".login.login"
+ renderer="templates/login.pt"
+ context="pyramid.exceptions.Forbidden"/>
+
+ <authtktauthenticationpolicy
+ secret="sosecret"
+ callback=".security.groupfinder"
+ />
+
+ <aclauthorizationpolicy/>
+
+ <static
+ name="static"
+ path="templates/static"
+ />
+
+</configure>
View
44 docs/tutorials/wiki/src/authorization/tutorial/login.py
@@ -0,0 +1,44 @@
+from webob.exc import HTTPFound
+
+from pyramid.view import bfg_view
+from pyramid.url import model_url
+
+from pyramid.security import remember
+from pyramid.security import forget
+
+from tutorial.models import Wiki
+from tutorial.security import USERS
+
+@bfg_view(context=Wiki, name='login', renderer='templates/login.pt')
+def login(request):
+ login_url = model_url(request.context, request, 'login')
+ referrer = request.url
+ if referrer == login_url:
+ referrer = '/' # never use the login form itself as came_from
+ came_from = request.params.get('came_from', referrer)
+ message = ''
+ login = ''
+ password = ''
+ if 'form.submitted' in request.params:
+ login = request.params['login']
+ password = request.params['password']
+ if USERS.get(login) == password:
+ headers = remember(request, login)
+ return HTTPFound(location = came_from,
+ headers = headers)
+ message = 'Failed login'
+
+ return dict(
+ message = message,
+ url = request.application_url + '/login',
+ came_from = came_from,
+ login = login,
+ password = password,
+ )
+
+@bfg_view(context=Wiki, name='logout')
+def logout(request):
+ headers = forget(request)
+ return HTTPFound(location = model_url(request.context, request),
+ headers = headers)
+
View
27 docs/tutorials/wiki/src/authorization/tutorial/models.py
@@ -0,0 +1,27 @@
+from persistent import Persistent
+from persistent.mapping import PersistentMapping
+
+from pyramid.security import Allow
+from pyramid.security import Everyone
+
+class Wiki(PersistentMapping):
+ __name__ = None
+ __parent__ = None
+ __acl__ = [ (Allow, Everyone, 'view'),
+ (Allow, 'group:editors', 'edit') ]
+
+class Page(Persistent):
+ def __init__(self, data):
+ self.data = data
+
+def appmaker(zodb_root):
+ if not 'app_root' in zodb_root:
+ app_root = Wiki()
+ frontpage = Page('This is the front page')
+ app_root['FrontPage'] = frontpage
+ frontpage.__name__ = 'FrontPage'
+ frontpage.__parent__ = app_root
+ zodb_root['app_root'] = app_root
+ import transaction
+ transaction.commit()
+ return zodb_root['app_root']
View
23 docs/tutorials/wiki/src/authorization/tutorial/run.py
@@ -0,0 +1,23 @@
+from pyramid.configuration import Configurator
+from repoze.zodbconn.finder import PersistentApplicationFinder
+
+from tutorial.models import appmaker
+
+def app(global_config, **settings):
+ """ This function returns a WSGI application.
+
+ It is usually called by the PasteDeploy framework during
+ ``paster serve``.
+ """
+ zodb_uri = settings.get('zodb_uri')
+ if zodb_uri is None:
+ raise ValueError("No 'zodb_uri' in application configuration.")
+ finder = PersistentApplicationFinder(zodb_uri, appmaker)
+ def get_root(request):
+ return finder(request.environ)
+ config = Configurator(root_factory=get_root, settings=settings)
+ config.begin()
+ config.load_zcml('configure.zcml')
+ config.end()
+ return config.make_wsgi_app()
+
View
8 docs/tutorials/wiki/src/authorization/tutorial/security.py
@@ -0,0 +1,8 @@
+USERS = {'editor':'editor',
+ 'viewer':'viewer'}
+GROUPS = {'editor':['group:editors']}
+
+def groupfinder(userid, request):
+ if userid in USERS:
+ return GROUPS.get(userid, [])
+
View
34 docs/tutorials/wiki/src/authorization/tutorial/templates/edit.pt
@@ -0,0 +1,34 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal">
+
+<head>
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
+ <title>Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)
+ Editing: ${page.__name__}</title>
+ <link rel="stylesheet" type="text/css"
+ href="${request.application_url}/static/style.css" />
+</head>
+
+<body>
+
+<div class="main_content">
+ <div style="float:right; width: 10em;"> Viewing
+ <span tal:replace="page.__name__">Page Name Goes Here</span> <br/>
+ You can return to the <a href="${request.application_url}"
+ >FrontPage</a>.
+ <span tal:condition="logged_in"><a
+ href="${request.application_url}/logout">Logout</a></span>
+ </div>
+
+ <div>
+ <form action="${save_url}" method="post">
+ <textarea name="body" tal:content="page.data" rows="10" cols="60"/>
+ <input type="submit" name="form.submitted" value="Save"/>
+ </form>
+ </div>
+</div>
+</body>
+</html>
View
32 docs/tutorials/wiki/src/authorization/tutorial/templates/login.pt
@@ -0,0 +1,32 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal">
+
+<head>
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
+ <title>Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title>
+ <link rel="stylesheet" type="text/css"
+ href="${request.application_url}/static/style.css" />
+</head>
+
+<body>
+
+<h1>Log In</h1>
+
+<div tal:replace="message"/>
+
+<div class="main_content">
+ <form action="${url}" method="post">
+ <input type="hidden" name="came_from" value="${came_from}"/>
+ <input type="text" name="login" value="${login}"/>
+ <br/>
+ <input type="password" name="password" value="${password}"/>
+ <br/>
+ <input type="submit" name="form.submitted" value="Log In"/>
+ </form>
+</div>
+
+</body>
+</html>
View
99 docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt
@@ -0,0 +1,99 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<title>${project} Application</title>
+<meta name="keywords" content="python web application" />
+<meta name="description" content="pyramid web application" />
+<link href="${request.application_url}/static/default.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+<!-- start header -->
+<div id="logo">
+ <h2><code>${project}</code>, a <code>Pyramid</code> application</h2>
+</div>
+<div id="header">
+ <div id="menu">
+ </div>
+</div>
+<!-- end header -->
+<div id="wrapper">
+ <!-- start page -->
+ <div id="page">
+ <!-- start content -->
+ <div id="content">
+ <div class="post">
+ <h1 class="title">Welcome to <code>${project}</code>, an
+ application generated by the <a
+ href="http://pylonshq.com/pyramid">Pyramid</a> web
+ application framework.</h1>
+ </div>
+ </div>
+ <!-- end content -->
+ <!-- start sidebar -->
+ <div id="sidebar">
+ <ul>
+ <li id="search">
+ <h2>Search<br/> <code>Pyramid</code> Documentation</h2>
+ <form method="get"
+ action="http://pylonshq.com/docs/pyramid/current/searchresults">
+ <fieldset>
+ <input type="text" id="q" name="text" value="" />
+ <input type="submit" id="x" value="Search" />
+ </fieldset>
+ </form>
+ </li>
+ <li>
+ <h2><code>Pyramid</code> links</h2>
+ <ul>
+ <li><a
+ href="http://pylonshq.com/docs/pyramid/current/#narrative-documentation">Narrative
+ Documentation</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#api-documentation">API
+ Documentation</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#tutorials">Tutorials</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#change-history">Change
+ History</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#sample-applications">Sample
+ Applications</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#support-and-development">Support
+ and Development</a>
+ </li>
+ <li>
+ <a
+ href="irc://irc.freenode.net#pylons">IRC Channel</a>
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+ <!-- end sidebar -->
+ <div style="clear: both;">&nbsp;</div>
+ </div>
+</div>
+<!-- end page -->
+<!-- start footer -->
+<div id="footer">
+ <p id="legal">( c ) 2008. All Rights Reserved. Template design
+ by <a href="http://www.freecsstemplates.org/">Free CSS
+ Templates</a>.</p>
+</div>
+<!-- end footer -->
+</body>
+</html>
View
380 docs/tutorials/wiki/src/authorization/tutorial/templates/static/default.css
@@ -0,0 +1,380 @@
+/*
+Design by Free CSS Templates
+http://www.freecsstemplates.org
+Released for free under a Creative Commons Attribution 2.5 License
+*/
+
+body {
+ margin: 0;
+ padding: 0;
+ background: url(images/img01.gif) repeat-x left top;
+ font-size: 13px;
+ font-family: "Trebuchet MS", Georgia, "Times New Roman", Times, serif;
+ text-align: justify;
+ color: #FFFFFF;
+}
+
+h1, h2, h3 {
+ margin: 0;
+ text-transform: lowercase;
+ font-weight: normal;
+ color: #FFFFFF;
+}
+
+h1 {
+ letter-spacing: -1px;
+ font-size: 32px;
+}
+
+h2 {
+ font-size: 23px;
+}
+
+p, ul, ol {
+ margin: 0 0 2em 0;
+ text-align: justify;
+ line-height: 26px;
+}
+
+a:link {
+ color: #8BD80E;
+}
+
+a:hover, a:active {
+ text-decoration: none;
+ color: #8BD80E;
+}
+
+a:visited {
+ color: #8BD80E;
+}
+
+img {
+ border: none;
+}
+
+img.left {
+ float: left;
+ margin-right: 15px;
+}
+
+img.right {
+ float: right;
+ margin-left: 15px;
+}
+
+/* Form */
+
+form {
+ margin: 0;
+ padding: 0;
+}
+
+fieldset {
+ margin: 0;
+ padding: 0;
+ border: none;
+}
+
+legend {
+ display: none;
+}
+
+input, textarea, select {
+ font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
+ font-size: 13px;
+ color: #333333;
+}
+
+#wrapper {
+ margin: 0;
+ padding: 0;
+ background: #000000;
+}
+
+/* Header */
+
+#header {
+ width: 713px;
+ margin: 0 auto;
+ height: 42px;
+}
+
+/* Menu */
+
+#menu {
+ float: left;
+ width: 713px;
+ height: 50px;
+ background: url(images/img02.gif) no-repeat left top;
+}
+
+#menu ul {
+ margin: 0;
+ padding: 0px 0 0 10px;
+ list-style: none;
+ line-height: normal;
+}
+
+#menu li {
+ display: block;
+ float: left;
+}
+
+#menu a {
+ display: block;
+ float: left;
+ background: url(images/img04.gif) no-repeat right 55%;
+ margin-top: 5px;
+ margin-right: 3px;
+ padding: 8px 17px;
+ text-decoration: none;
+ font-size: 13px;
+ color: #000000;
+}
+
+#menu a:hover {
+ color: #000000;
+}
+
+#menu .current_page_item a {
+ color: #000000;
+}
+
+/** LOGO */
+
+#logo {
+ width: 713px;
+ height: 80px;
+ margin: 0 auto;
+}
+
+#logo h1, #logo h2 {
+ float: left;
+ margin: 0;
+ padding: 30px 0 0 0px;
+ line-height: normal;
+}
+
+#logo h1 {
+ font-family: Georgia, "Times New Roman", Times, serif;
+ font-size:40px;
+}
+
+#logo h1 a {
+ text-decoration: none;
+ color: #4C4C4C;
+}
+
+#logo h1 a:hover { text-decoration: underline; }
+
+#logo h2 {
+ float: left;
+ padding: 45px 0 0 18px;
+ font: 18px Georgia, "Times New Roman", Times, serif;
+ color: #8BD80E;
+}
+
+#logo p a {
+ text-decoration: none;
+ color: #8BD80E;
+}
+
+#logo p a:hover { text-decoration: underline; }
+
+
+
+/* Page */
+
+#page {
+ width: 663px;
+ margin: 0 auto;
+ background: #4C4C4C url(images/img03.gif) no-repeat left bottom;
+ padding: 0 25px;
+}
+
+/* Content */
+
+#content {
+ float: left;
+ width: 410px;
+
+}
+
+/* Post */
+
+.post {
+ padding: 15px 0px;
+ margin-bottom: 20px;
+}
+
+.post .title {
+ margin-bottom: 20px;
+ padding-bottom: 5px;
+}
+
+.post h1 {
+ padding: 0px 0 0 0px;
+ background: url(images/img08.jpg) no-repeat left top;
+ font-size: 24px;
+ color: #FFFFFF;
+}
+
+.post h2 {
+ padding: 0px 0 0 0px;
+ font-size: 22px;
+ color: #FFFFFF;
+}
+
+.post .entry {
+}
+
+.post .meta {
+ padding: 15px 15px 30px 0px;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 11px;
+}
+
+.post .meta p {
+ margin: 0;
+ padding-top: 15px;
+ line-height: normal;
+ color: #FFFFFF;
+}
+
+.post .meta .byline {
+ float: left;
+}
+
+.post .meta .links {
+ float: right;
+}
+
+.post .meta .more {
+ padding: 0 10px 0 18px;
+}
+
+.post .meta .comments {
+}
+
+.post .meta b {
+ display: none;
+}
+
+
+/* Sidebar */
+
+#sidebar {
+ width: 210px;
+ float: right;
+ margin: 0;
+ padding: 0;
+}
+
+#sidebar ul {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+#sidebar li {
+ margin-bottom: 40px;
+}
+
+#sidebar li ul {
+}
+
+#sidebar li li {
+ margin: 0;
+}
+
+#sidebar h2 {
+ width: 250px;
+ padding: 8px 0 0 0px;
+ margin-bottom: 10px;
+ background: url(images/img07.jpg) no-repeat left top;
+ font-size: 20px;
+ color: #FFFFFF;
+}
+
+/* Search */
+
+#search {
+
+}
+
+#search h2 {
+ margin-bottom: 20px;
+}
+
+#s {
+ width: 140px;
+ margin-right: 5px;
+ padding: 3px;
+ border: 1px solid #BED99C;
+}
+
+#x {
+ padding: 3px;
+ border: none;
+ background: #8BD80E;
+ text-transform: lowercase;
+ font-size: 11px;
+ color: #FFFFFF;
+}
+
+/* Boxes */
+
+.box1 {
+ padding: 20px;
+}
+
+.box2 {
+ color: #BABABA;
+}
+
+.box2 h2 {
+ margin-bottom: 15px;
+ font-size: 16px;
+ color: #FFFFFF;
+}
+
+.box2 ul {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+.box2 a:link, .box2 a:hover, .box2 a:active, .box2 a:visited {
+ color: #EDEDED;
+}
+
+/* Footer */
+#footer-wrap {
+}
+
+#footer {
+ margin: 0 auto;
+ padding: 20px 0 10px 0;
+ background: #000000;
+}
+
+html>body #footer {
+ height: auto;
+}
+
+#footer p {
+ font-size: 11px;
+}
+
+#legal {
+ clear: both;
+ padding-top: 17px;
+ text-align: center;
+ color: #FFFFFF;
+}
+
+#legal a {
+ font-weight: normal;
+ color: #FFFFFF;
+}
View
BIN  docs/tutorials/wiki/src/authorization/tutorial/templates/static/images/img01.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  docs/tutorials/wiki/src/authorization/tutorial/templates/static/images/img02.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  docs/tutorials/wiki/src/authorization/tutorial/templates/static/images/img03.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  docs/tutorials/wiki/src/authorization/tutorial/templates/static/images/img04.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  docs/tutorials/wiki/src/authorization/tutorial/templates/static/images/spacer.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
109 docs/tutorials/wiki/src/authorization/tutorial/templates/static/style.css
@@ -0,0 +1,109 @@
+html, body {
+ color: black;
+ background-color: #ddd;
+ font: x-small "Lucida Grande", "Lucida Sans Unicode", geneva, sans-serif;
+ margin: 0;
+ padding: 0;
+}
+
+td, th {padding:3px;border:none;}
+tr th {text-align:left;background-color:#f0f0f0;color:#333;}
+tr.odd td {background-color:#edf3fe;}
+tr.even td {background-color:#fff;}
+
+#header {
+ height: 80px;
+ width: 777px;
+ background: blue URL('../images/header_inner.png') no-repeat;
+ border-left: 1px solid #aaa;
+ border-right: 1px solid #aaa;
+ margin: 0 auto 0 auto;
+}
+
+a.link, a, a.active {
+ color: #369;
+}
+
+
+#main_content {
+ color: black;
+ font-size: 127%;
+ background-color: white;
+ width: 757px;
+ margin: 0 auto 0 auto;
+ border-left: 1px solid #aaa;
+ border-right: 1px solid #aaa;
+ padding: 10px;
+}
+
+#sidebar {
+ border: 1px solid #aaa;
+ background-color: #eee;
+ margin: 0.5em;
+ padding: 1em;
+ float: right;
+ width: 200px;
+ font-size: 88%;
+}
+
+#sidebar h2 {
+ margin-top: 0;
+}
+
+#sidebar ul {
+ margin-left: 1.5em;
+ padding-left: 0;
+}
+
+h1,h2,h3,h4,h5,h6,#getting_started_steps {
+ font-family: "Century Schoolbook L", Georgia, serif;
+ font-weight: bold;