summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--CHANGES.txt151
-rw-r--r--CONTRIBUTORS.txt7
-rw-r--r--COPYRIGHT.txt2
-rw-r--r--TODO.txt26
-rw-r--r--docs/Makefile2
-rw-r--r--docs/api/authentication.rst10
-rw-r--r--docs/api/interfaces.rst4
-rw-r--r--docs/api/security.rst2
-rw-r--r--docs/api/url.rst2
-rw-r--r--docs/conf.py17
-rw-r--r--docs/copyright.rst15
-rw-r--r--docs/designdefense.rst16
-rw-r--r--docs/glossary.rst31
-rw-r--r--docs/index.rst7
-rw-r--r--docs/latexindex.rst9
-rw-r--r--docs/narr/assets.rst4
-rw-r--r--docs/narr/csrf.rst63
-rw-r--r--docs/narr/firstapp.rst4
-rw-r--r--docs/narr/flash.rst107
-rw-r--r--docs/narr/forms.rst126
-rw-r--r--docs/narr/hooks.rst90
-rw-r--r--docs/narr/introduction.rst2
-rw-r--r--docs/narr/muchadoabouttraversal.rst312
-rw-r--r--docs/narr/project.rst10
-rw-r--r--docs/narr/renderers.rst17
-rw-r--r--docs/narr/resourcelocation.rst103
-rw-r--r--docs/narr/resources.rst44
-rw-r--r--docs/narr/router.rst2
-rw-r--r--docs/narr/security.rst3
-rw-r--r--docs/narr/sessions.rst190
-rw-r--r--docs/narr/templates.rst2
-rw-r--r--docs/narr/traversal.rst201
-rw-r--r--docs/narr/urldispatch.rst52
-rw-r--r--docs/narr/viewconfig.rst986
-rw-r--r--docs/narr/views.rst1156
-rw-r--r--docs/tutorials/cmf/actions.rst28
-rw-r--r--docs/tutorials/cmf/catalog.rst73
-rw-r--r--docs/tutorials/cmf/content.rst67
-rw-r--r--docs/tutorials/cmf/index.rst38
-rw-r--r--docs/tutorials/cmf/missing.rst22
-rw-r--r--docs/tutorials/cmf/skins.rst23
-rw-r--r--docs/tutorials/cmf/workflow.rst14
-rw-r--r--docs/tutorials/wiki/authorization.rst6
-rw-r--r--docs/tutorials/wiki/basiclayout.rst1
-rw-r--r--docs/tutorials/wiki/definingmodels.rst2
-rw-r--r--docs/tutorials/wiki/definingviews.rst20
-rw-r--r--docs/tutorials/wiki/distributing.rst2
-rw-r--r--docs/tutorials/wiki/installation.rst74
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/ie6.css8
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/logo.pngbin6641 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css50
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/pyramid-small.pngbin0 -> 7044 bytes
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/pyramid.pngbin0 -> 33055 bytes
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/style.css109
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/templates/edit.pt77
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/templates/login.pt73
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt107
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/templates/view.pt80
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/ie6.css8
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/logo.pngbin6641 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css50
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid-small.pngbin0 -> 7044 bytes
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid.pngbin0 -> 33055 bytes
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt31
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/ie6.css8
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/logo.pngbin6641 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/pylons.css50
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/pyramid-small.pngbin0 -> 7044 bytes
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/pyramid.pngbin0 -> 33055 bytes
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt107
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/ie6.css8
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/logo.pngbin6641 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/pylons.css50
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/pyramid-small.pngbin0 -> 7044 bytes
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/pyramid.pngbin0 -> 33055 bytes
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/style.css109
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/templates/edit.pt71
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt107
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/templates/view.pt74
-rw-r--r--docs/tutorials/wiki2/authorization.rst8
-rw-r--r--docs/tutorials/wiki2/definingviews.rst28
-rw-r--r--docs/tutorials/wiki2/distributing.rst2
-rw-r--r--docs/tutorials/wiki2/installation.rst32
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/ie6.css8
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/logo.pngbin6641 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css50
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid-small.pngbin0 -> 7044 bytes
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid.pngbin0 -> 33055 bytes
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/style.css109
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt78
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt73
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt31
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt78
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/ie6.css8
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/logo.pngbin6641 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css50
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid-small.pngbin0 -> 7044 bytes
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid.pngbin0 -> 33055 bytes
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt31
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/ie6.css8
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/logo.pngbin6641 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/pylons.css50
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/pyramid-small.pngbin0 -> 7044 bytes
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/pyramid.pngbin0 -> 33055 bytes
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt31
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/__init__.py2
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/ie6.css8
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/logo.pngbin6641 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/pylons.css50
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/pyramid-small.pngbin0 -> 7044 bytes
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/pyramid.pngbin0 -> 33055 bytes
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/style.css109
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/edit.pt71
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt31
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/view.pt71
-rw-r--r--docs/zcml/handler.rst2
-rw-r--r--docs/zcml/view.rst14
-rw-r--r--pyramid/authentication.py27
-rw-r--r--pyramid/chameleon_text.py33
-rw-r--r--pyramid/config.py773
-rw-r--r--pyramid/httpexceptions.py1
-rw-r--r--pyramid/interfaces.py49
-rw-r--r--pyramid/paster.py21
-rw-r--r--pyramid/paster_templates/alchemy/+package+/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--pyramid/paster_templates/alchemy/+package+/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--pyramid/paster_templates/alchemy/+package+/static/ie6.css8
-rw-r--r--pyramid/paster_templates/alchemy/+package+/static/logo.pngbin6641 -> 0 bytes
-rw-r--r--pyramid/paster_templates/alchemy/+package+/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--pyramid/paster_templates/alchemy/+package+/static/pylons.css50
-rw-r--r--pyramid/paster_templates/alchemy/+package+/static/pyramid-small.pngbin0 -> 7044 bytes
-rw-r--r--pyramid/paster_templates/alchemy/+package+/static/pyramid.pngbin0 -> 33055 bytes
-rw-r--r--pyramid/paster_templates/alchemy/+package+/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--pyramid/paster_templates/alchemy/+package+/templates/model.pt_tmpl (renamed from pyramid/paster_templates/alchemy/+package+/templates/model.pt)31
-rw-r--r--pyramid/paster_templates/alchemy/+package+/templates/root.pt_tmpl (renamed from pyramid/paster_templates/alchemy/+package+/templates/root.pt)31
-rw-r--r--pyramid/paster_templates/pylons_basic/+package+/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--pyramid/paster_templates/pylons_basic/+package+/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--pyramid/paster_templates/pylons_basic/+package+/static/ie6.css8
-rw-r--r--pyramid/paster_templates/pylons_basic/+package+/static/logo.pngbin6641 -> 0 bytes
-rw-r--r--pyramid/paster_templates/pylons_basic/+package+/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--pyramid/paster_templates/pylons_basic/+package+/static/pylons.css50
-rw-r--r--pyramid/paster_templates/pylons_basic/+package+/static/pyramid-small.pngbin0 -> 7044 bytes
-rw-r--r--pyramid/paster_templates/pylons_basic/+package+/static/pyramid.pngbin0 -> 33055 bytes
-rw-r--r--pyramid/paster_templates/pylons_basic/+package+/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--pyramid/paster_templates/pylons_basic/+package+/templates/mytemplate.mako_tmpl (renamed from pyramid/paster_templates/starter/+package+/templates/mytemplate.pt)12
-rw-r--r--pyramid/paster_templates/pylons_minimal/+package+/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--pyramid/paster_templates/pylons_minimal/+package+/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--pyramid/paster_templates/pylons_minimal/+package+/static/ie6.css8
-rw-r--r--pyramid/paster_templates/pylons_minimal/+package+/static/logo.pngbin6641 -> 0 bytes
-rw-r--r--pyramid/paster_templates/pylons_minimal/+package+/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--pyramid/paster_templates/pylons_minimal/+package+/static/pylons.css50
-rw-r--r--pyramid/paster_templates/pylons_minimal/+package+/static/pyramid-small.pngbin0 -> 7044 bytes
-rw-r--r--pyramid/paster_templates/pylons_minimal/+package+/static/pyramid.pngbin0 -> 33055 bytes
-rw-r--r--pyramid/paster_templates/pylons_minimal/+package+/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--pyramid/paster_templates/pylons_minimal/+package+/templates/mytemplate.mako_tmpl (renamed from pyramid/paster_templates/pylons_minimal/+package+/templates/mytemplate.mako)31
-rw-r--r--pyramid/paster_templates/pylons_sqla/+package+/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--pyramid/paster_templates/pylons_sqla/+package+/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--pyramid/paster_templates/pylons_sqla/+package+/static/ie6.css8
-rw-r--r--pyramid/paster_templates/pylons_sqla/+package+/static/logo.pngbin6641 -> 0 bytes
-rw-r--r--pyramid/paster_templates/pylons_sqla/+package+/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--pyramid/paster_templates/pylons_sqla/+package+/static/pylons.css50
-rw-r--r--pyramid/paster_templates/pylons_sqla/+package+/static/pyramid-small.pngbin0 -> 7044 bytes
-rw-r--r--pyramid/paster_templates/pylons_sqla/+package+/static/pyramid.pngbin0 -> 33055 bytes
-rw-r--r--pyramid/paster_templates/pylons_sqla/+package+/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--pyramid/paster_templates/pylons_sqla/+package+/templates/mytemplate.mako_tmpl (renamed from pyramid/paster_templates/pylons_sqla/+package+/templates/mytemplate.mako)31
-rw-r--r--pyramid/paster_templates/routesalchemy/+package+/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--pyramid/paster_templates/routesalchemy/+package+/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--pyramid/paster_templates/routesalchemy/+package+/static/ie6.css8
-rw-r--r--pyramid/paster_templates/routesalchemy/+package+/static/logo.pngbin6641 -> 0 bytes
-rw-r--r--pyramid/paster_templates/routesalchemy/+package+/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--pyramid/paster_templates/routesalchemy/+package+/static/pylons.css50
-rw-r--r--pyramid/paster_templates/routesalchemy/+package+/static/pyramid-small.pngbin0 -> 7044 bytes
-rw-r--r--pyramid/paster_templates/routesalchemy/+package+/static/pyramid.pngbin0 -> 33055 bytes
-rw-r--r--pyramid/paster_templates/routesalchemy/+package+/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--pyramid/paster_templates/routesalchemy/+package+/templates/mytemplate.pt79
-rw-r--r--pyramid/paster_templates/routesalchemy/+package+/templates/mytemplate.pt_tmpl (renamed from pyramid/paster_templates/pylons_basic/+package+/templates/mytemplate.mako)31
-rw-r--r--pyramid/paster_templates/starter/+package+/static/pylons.css11
-rw-r--r--pyramid/paster_templates/starter/+package+/static/pyramid-small.pngbin0 -> 7044 bytes
-rw-r--r--pyramid/paster_templates/starter/+package+/templates/mytemplate.pt_tmpl76
-rw-r--r--pyramid/paster_templates/starter_zcml/+package+/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--pyramid/paster_templates/starter_zcml/+package+/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--pyramid/paster_templates/starter_zcml/+package+/static/ie6.css8
-rw-r--r--pyramid/paster_templates/starter_zcml/+package+/static/logo.pngbin6641 -> 0 bytes
-rw-r--r--pyramid/paster_templates/starter_zcml/+package+/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--pyramid/paster_templates/starter_zcml/+package+/static/pylons.css50
-rw-r--r--pyramid/paster_templates/starter_zcml/+package+/static/pyramid-small.pngbin0 -> 7044 bytes
-rw-r--r--pyramid/paster_templates/starter_zcml/+package+/static/pyramid.pngbin0 -> 33055 bytes
-rw-r--r--pyramid/paster_templates/starter_zcml/+package+/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--pyramid/paster_templates/starter_zcml/+package+/templates/mytemplate.pt79
-rw-r--r--pyramid/paster_templates/starter_zcml/+package+/templates/mytemplate.pt_tmpl76
-rw-r--r--pyramid/paster_templates/zodb/+package+/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--pyramid/paster_templates/zodb/+package+/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--pyramid/paster_templates/zodb/+package+/static/ie6.css8
-rw-r--r--pyramid/paster_templates/zodb/+package+/static/logo.pngbin6641 -> 0 bytes
-rw-r--r--pyramid/paster_templates/zodb/+package+/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--pyramid/paster_templates/zodb/+package+/static/pylons.css50
-rw-r--r--pyramid/paster_templates/zodb/+package+/static/pyramid-small.pngbin0 -> 7044 bytes
-rw-r--r--pyramid/paster_templates/zodb/+package+/static/pyramid.pngbin0 -> 33055 bytes
-rw-r--r--pyramid/paster_templates/zodb/+package+/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--pyramid/paster_templates/zodb/+package+/templates/mytemplate.pt79
-rw-r--r--pyramid/paster_templates/zodb/+package+/templates/mytemplate.pt_tmpl76
-rw-r--r--pyramid/renderers.py12
-rw-r--r--pyramid/request.py2
-rw-r--r--pyramid/security.py18
-rw-r--r--pyramid/session.py5
-rw-r--r--pyramid/testing.py50
-rw-r--r--pyramid/tests/test_authentication.py33
-rw-r--r--pyramid/tests/test_chameleon_text.py25
-rw-r--r--pyramid/tests/test_chameleon_zpt.py17
-rw-r--r--pyramid/tests/test_config.py1499
-rw-r--r--pyramid/tests/test_paster.py35
-rw-r--r--pyramid/tests/test_security.py33
-rw-r--r--pyramid/tests/test_session.py7
-rw-r--r--pyramid/tests/test_testing.py88
-rw-r--r--pyramid/tests/test_url.py42
-rw-r--r--pyramid/tests/test_view.py16
-rw-r--r--pyramid/traversal.py3
-rw-r--r--pyramid/url.py60
-rw-r--r--pyramid/view.py19
-rw-r--r--pyramid/zcml.py18
-rw-r--r--setup.py2
253 files changed, 5544 insertions, 4749 deletions
diff --git a/.gitignore b/.gitignore
index ae0b17b8e..562abec68 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@
.coverage
tutorial.db
env26/
+env26-debug/
env24/
env27/
jyenv/
diff --git a/CHANGES.txt b/CHANGES.txt
index ebf3bf4e2..c06961428 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,154 @@
+Next release
+============
+
+- ...
+
+1.0a9 (2011-01-08)
+==================
+
+Bug Fixes
+---------
+
+- The ``proutes`` command tried too hard to resolve the view for printing,
+ resulting in exceptions when an exceptional root factory was encountered.
+ Instead of trying to resolve the view, if it cannot, it will now just print
+ ``<unknown>``.
+
+- The `self` argument was included in new methods of the ``ISession`` interface
+ signature, causing ``pyramid_beaker`` tests to fail.
+
+- Readd ``pyramid.traversal.model_path_tuple`` as an alias for
+ ``pyramid.traversal.resource_path_tuple`` for backwards compatibility.
+
+Features
+--------
+
+- Add a new API ``pyramid.url.current_route_url``, which computes a URL based
+ on the "current" route (if any) and its matchdict values.
+
+- ``config.add_view`` now accepts a ``decorator`` keyword argument, a callable
+ which will decorate the view callable before it is added to the registry.
+
+- If a handler class provides an ``__action_decorator__`` attribute (usually
+ a classmethod or staticmethod), use that as the decorator for each view
+ registration for that handler.
+
+- The ``pyramid.interfaces.IAuthenticationPolicy`` interface now specifies an
+ ``unauthenticated_userid`` method. This method supports an important
+ optimization required by people who are using persistent storages which do
+ not support object caching and whom want to create a "user object" as a
+ request attribute.
+
+- A new API has been added to the ``pyramid.security`` module named
+ ``unauthenticated_userid``. This API function calls the
+ ``unauthenticated_userid`` method of the effective security policy.
+
+- An ``unauthenticated_userid`` method has been added to the dummy
+ authentication policy returned by
+ ``pyramid.config.Configurator.testing_securitypolicy``. It returns the
+ same thing as that the dummy authentication policy's
+ ``authenticated_userid`` method.
+
+- The class ``pyramid.authentication.AuthTktCookieHelper`` is now an API.
+ This class can be used by third-party authentication policy developers to
+ help in the mechanics of authentication cookie-setting.
+
+- New constructor argument to Configurator: ``default_view_mapper``. Useful
+ to create systems that have alternate view calling conventions. A view
+ mapper allows objects that are meant to be used as view callables to have
+ an arbitrary argument list and an arbitrary result. The object passed as
+ ``default_view_mapper`` should implement the
+ ``pyramid.interfaces.IViewMapperFactory`` interface.
+
+- add a ``set_view_mapper`` API to Configurator. Has
+ the same result as passing ``default_view_mapper`` to the Configurator
+ constructor.
+
+- ``config.add_view`` now accepts a ``mapper`` keyword argument, which should
+ either be ``None``, a string representing a Python dotted name, or an
+ object which is an ``IViewMapperFactory``. This feature is not useful for
+ "civilians", only for extension writers.
+
+- Allow static renderer provided during view registration to be overridden at
+ request time via a request attribute named ``override_renderer``, which
+ should be the name of a previously registered renderer. Useful to provide
+ "omnipresent" RPC using existing rendered views.
+
+- Instances of ``pyramid.testing.DummyRequest`` now have a ``session``
+ object, which is mostly a dictionary, but also implements the other session
+ API methods for flash and CSRF.
+
+Backwards Incompatibilities
+---------------------------
+
+- Since the ``pyramid.interfaces.IAuthenticationPolicy`` interface now
+ specifies that a policy implementation must implement an
+ ``unauthenticated_userid`` method, all third-party custom authentication
+ policies now must implement this method. It, however, will only be called
+ when the global function named ``pyramid.security.unauthenticated_userid``
+ is invoked, so if you're not invoking that, you will not notice any issues.
+
+- ``pyramid.interfaces.ISession.get_csrf_token`` now mandates that an
+ implementation should return a *new* token if one doesn't already exist in
+ the session (previously it would return None). The internal sessioning
+ implementation has been changed.
+
+Documentation
+-------------
+
+- The (weak) "Converting a CMF Application to Pyramid" tutorial has been
+ removed from the tutorials section. It was moved to the
+ ``pyramid_tutorials`` Github repository.
+
+- The "Resource Location and View Lookup" chapter has been replaced with a
+ variant of Rob Miller's "Much Ado About Traversal" (originally published at
+ http://blog.nonsequitarian.org/2010/much-ado-about-traversal/).
+
+- Many minor wording tweaks and refactorings (merged Casey Duncan's docs
+ fork, in which he is working on general editing).
+
+- Added (weak) description of new view mapper feature to Hooks narrative
+ chapter.
+
+- Split views chapter into 2: View Callables and View Configuration.
+
+- Reorder Renderers and Templates chapters after View Callables but before
+ View Configuration.
+
+- Merge Session Objects, Cross-Site Request Forgery, and Flash Messaging
+ chapter into a single Sessions chapter.
+
+- The Wiki and Wiki2 tutorials now have much nicer CSS and graphics.
+
+Internals
+---------
+
+- The "view derivation" code is now factored into a set of classes rather
+ than a large number of standalone functions (a side effect of the
+ view mapper refactoring).
+
+- The ``pyramid.renderer.RendererHelper`` class has grown a ``render_view``
+ method, which is used by the default view mapper (a side effect of the
+ view mapper refactoring).
+
+- The object passed as ``renderer`` to the "view deriver" is now an instance
+ of ``pyramid.renderers.RendererHelper`` rather than a dictionary (a side
+ effect of view mapper refactoring).
+
+- The class used as the "page template" in ``pyramid.chameleon_text`` was
+ removed, in preference to using a Chameleon-inbuilt version.
+
+- A view callable wrapper registered in the registry now contains an
+ ``__original_view__`` attribute which references the original view callable
+ (or class).
+
+- The (non-API) method of all internal authentication policy implementations
+ previously named ``_get_userid`` is now named ``unauthenticated_userid``,
+ promoted to an API method. If you were overriding this method, you'll now
+ need to override it as ``unauthenticated_userid`` instead.
+
+- Remove (non-API) function of config.py named _map_view.
+
1.0a8 (2010-12-27)
==================
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index b48e769a1..ec9042f08 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -90,7 +90,7 @@ Licensing Exceptions
Code committed within the ``docs/`` subdirectory of the Pyramid source
control repository and "docstrings" which appear in the documentation
-generated by runnning "make" within this directory is licensed under the
+generated by running "make" within this directory is licensed under the
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States
License (http://creativecommons.org/licenses/by-nc-sa/3.0/us/).
@@ -117,3 +117,8 @@ Contributors
- Casey Duncan, 2010/12/27
+- Rob Miller, 2010/12/28
+
+- Marius Gedminas, 2010/12/31
+
+- Marcin Lulek, 2011/01/02 \ No newline at end of file
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index aa870dc84..51c76455e 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -1,4 +1,4 @@
-Copyright (c) 2008-2010 Agendaless Consulting and Contributors.
+Copyright (c) 2008-2011 Agendaless Consulting and Contributors.
(http://www.agendaless.com), All Rights Reserved
Portions (c) Django Project (http://djangoproject.com/).
diff --git a/TODO.txt b/TODO.txt
index f81c78387..3615d9c1d 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -8,9 +8,32 @@ Must-Have (before 1.0)
- Consider deprecations for ``model`` and ``resource`` APIs.
+- Re-make testing.setUp() and testing.tearDown() the canonical APIs for test
+ configuration.
+
+- API docs for ``pyramid.views.action``.
+
+- Use a commit veto when configuring repoze.tm2 in paster templates for
+ non-1X, 2X, or 3X responses.
+
+- Consider passing two callables to CallbackAuthenticationPolicy: one for
+ validating/obtaining the userid, the other for returning groups.
+
+- Add docs for ``__action_decorator__`` attr of handler.
+
+- Document the ``request.override_renderer`` attribute.
+
+- Figure out how to slim the herd of paster templates.
+
Should-Have
-----------
+- Add a session attribute to DummyRequest.
+
+- Convert paster template and tutorial HTML templates to use
+ ``request.static_url('{{package}}:static/foo.css')`` rather than
+ ``${request.application_url}/static/foo.css``.
+
- Add notes about renderer response attrs to request docs.
- Add an example of using a cascade to serve static assets from the root.
@@ -26,9 +49,6 @@ Should-Have
- Change "Cleaning up After a Request" in the urldispatch chapter to
use ``request.add_response_callback``.
-- ``decorator=`` parameter to view_config. This would replace the existing
- _map_view "decorator" if it existed.
-
- Provide a response_set_cookie method on the request for rendered responses
that can be used as input to response.set_cookie?
diff --git a/docs/Makefile b/docs/Makefile
index 3d706d17e..1d032cf45 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -25,7 +25,7 @@ help:
clean:
-rm -rf _build/*
-html: _themes/
+html: _themes
mkdir -p _build/html _build/doctrees
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html
@echo
diff --git a/docs/api/authentication.rst b/docs/api/authentication.rst
index 54db77417..a6d4c1e18 100644
--- a/docs/api/authentication.rst
+++ b/docs/api/authentication.rst
@@ -3,6 +3,9 @@
:mod:`pyramid.authentication`
--------------------------------
+Authentication Policies
+~~~~~~~~~~~~~~~~~~~~~~~
+
.. automodule:: pyramid.authentication
.. autoclass:: AuthTktAuthenticationPolicy
@@ -11,3 +14,10 @@
.. autoclass:: RemoteUserAuthenticationPolicy
+Helper Classes
+~~~~~~~~~~~~~~
+
+ .. autoclass:: AuthTktCookieHelper
+
+
+
diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst
index b3c14e5f7..3ce926230 100644
--- a/docs/api/interfaces.rst
+++ b/docs/api/interfaces.rst
@@ -35,3 +35,7 @@ Other Interfaces
.. autointerface:: ITemplateRenderer
+ .. autointerface:: IViewMapperFactory
+
+ .. autointerface:: IViewMapper
+
diff --git a/docs/api/security.rst b/docs/api/security.rst
index 4acf5fe4d..de249355d 100644
--- a/docs/api/security.rst
+++ b/docs/api/security.rst
@@ -10,6 +10,8 @@ Authentication API Functions
.. autofunction:: authenticated_userid
+.. autofunction:: unauthenticated_userid
+
.. autofunction:: effective_principals
.. autofunction:: forget
diff --git a/docs/api/url.rst b/docs/api/url.rst
index 1aa3082b7..01be76283 100644
--- a/docs/api/url.rst
+++ b/docs/api/url.rst
@@ -9,6 +9,8 @@
.. autofunction:: route_url
+ .. autofunction:: current_route_url
+
.. autofunction:: route_path
.. autofunction:: static_url
diff --git a/docs/conf.py b/docs/conf.py
index 8c238cecd..aa7cb7d1e 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -13,6 +13,7 @@
import sys, os
import datetime
+import inspect
import warnings
warnings.simplefilter('ignore', DeprecationWarning)
@@ -76,7 +77,7 @@ copyright = '%s, Agendaless Consulting' % datetime.datetime.now().year
# other places throughout the built documents.
#
# The short X.Y version.
-version = '1.0a8'
+version = '1.0a9'
# The full version, including alpha/beta/rc tags.
release = version
@@ -407,6 +408,18 @@ def setup(app):
app.add_directive('frontmatter', frontmatter, 1, (0, 0, 0))
app.add_directive('mainmatter', mainmatter, 1, (0, 0, 0))
app.add_directive('backmatter', backmatter, 1, (0, 0, 0))
+ app.connect('autodoc-process-signature', resig)
+
+def resig(app, what, name, obj, options, signature, return_annotation):
+ """ Allow for preservation of ``@action_method`` decorated methods
+ in configurator """
+ docobj = getattr(obj, '__docobj__', None)
+ if docobj is not None:
+ argspec = inspect.getargspec(docobj)
+ if argspec[0] and argspec[0][0] in ('cls', 'self'):
+ del argspec[0][0]
+ signature = inspect.formatargspec(*argspec)
+ return signature, return_annotation
# turn off all line numbers in latex formatting
@@ -426,7 +439,7 @@ def setup(app):
epub_title = 'The Pyramid Web Application Development Framework, Version 1.0'
epub_author = 'Chris McDonough'
epub_publisher = 'Agendaless Consulting'
-epub_copyright = '2008-2010'
+epub_copyright = '2008-2011'
# The language of the text. It defaults to the language option
# or en if the language is not set.
diff --git a/docs/copyright.rst b/docs/copyright.rst
index fa564a785..9ef093d0c 100644
--- a/docs/copyright.rst
+++ b/docs/copyright.rst
@@ -7,7 +7,7 @@ by Chris McDonough
.. |copy| unicode:: U+000A9 .. COPYRIGHT SIGN
-Copyright |copy| 2008-2010, Agendaless Consulting.
+Copyright |copy| 2008-2011, Agendaless Consulting.
.. ISBN-10: 0615345379
@@ -48,9 +48,15 @@ with respect to the use of the information contained herein.
Attributions
------------
+Editor:
+ Casey Duncan
+
Contributors:
- Ben Bangert, Blaise Laflamme, Carlos de la Guardia, Paul Everitt,
- Marius Gedminas
+
+ Ben Bangert, Blaise Laflamme, Rob Miller, Mike Orr, Carlos de la Guardia,
+ Paul Everitt, Tres Seaver, Marius Gedminas, Chris Rossi, Joachim Krebs,
+ Xavier Spriet, Reed O'Brien, William Chambers, Charlie Choiniere, Jamaludin
+ Ahmad.
.. Cover Designer:
.. Nat Hardwick of `Electrosoup <http://www.electrosoup.co.uk>`_.
@@ -59,6 +65,9 @@ Used with permission:
The :ref:`webob_chapter` chapter is adapted, with permission, from
documentation originally written by Ian Bicking.
+ The :ref:`much_ado_about_traversal_chapter` chapter is adapted,
+ with permission, from an article written by Rob Miller.
+
.. Print Production
.. ----------------
diff --git a/docs/designdefense.rst b/docs/designdefense.rst
index 53b95b9d0..df14fb440 100644
--- a/docs/designdefense.rst
+++ b/docs/designdefense.rst
@@ -895,9 +895,9 @@ Pyramid Applications are Extensible; I Don't Believe In Application Extensibilit
Any :app:`Pyramid` application written obeying certain constraints is
*extensible*. This feature is discussed in the :app:`Pyramid` documentation
-chapters named :ref:`extending_chapter` and :ref:`advconf_narr`. It is made
-possible by the use of the :term:`Zope Component Architecture` and within
-:app:`Pyramid`.
+chapters named :ref:`extending_chapter` and :ref:`advconfig_narr`. It is
+made possible by the use of the :term:`Zope Component Architecture` and
+within :app:`Pyramid`.
"Extensible", in this context, means:
@@ -1018,7 +1018,7 @@ Challenge
:app:`Pyramid` performs automatic authorization checks only at :term:`view`
execution time. Zope 3 wraps context objects with a `security proxy
-<http://wiki.zope.org/zope3/WhatAreSecurityProxies>`, which causes Zope 3 to
+<http://wiki.zope.org/zope3/WhatAreSecurityProxies>`_, which causes Zope 3 to
do also security checks during attribute access. I like this, because it
means:
@@ -1604,10 +1604,10 @@ If you can understand this hello world program, you can use Pyramid:
app = config.make_wsgi_app()
serve(app)
-Pyramid has ~ 650 of documentation (printed), covering topics from the very
-basic to the most advanced. *Nothing* is left undocumented, quite literally.
-It also has an *awesome*, very helpful community. Visit the #repoze and/or
-#pylons IRC channels on freenode.net and see.
+Pyramid has ~ 650 pages of documentation (printed), covering topics from the
+very basic to the most advanced. *Nothing* is left undocumented, quite
+literally. It also has an *awesome*, very helpful community. Visit the
+#repoze and/or #pylons IRC channels on freenode.net and see.
Hate Zope
+++++++++
diff --git a/docs/glossary.rst b/docs/glossary.rst
index 49d273197..a7e3a7884 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -112,14 +112,14 @@ Glossary
about :app:`Pyramid` view callables.
view configuration
- View configuration is the act of associating a :term:`view
- callable` with configuration information. This configuration
- information helps map a given :term:`request` to a particular view
- callable and it can influence the response of a view callable.
- :app:`Pyramid` views can be configured via :term:`imperative
- configuration`, :term:`ZCML` or by a special ``@view_config``
- decorator coupled with a :term:`scan`. See :ref:`views_chapter`
- for more information about view configuration.
+ View configuration is the act of associating a :term:`view callable`
+ with configuration information. This configuration information helps
+ map a given :term:`request` to a particular view callable and it can
+ influence the response of a view callable. :app:`Pyramid` views can be
+ configured via :term:`imperative configuration`, :term:`ZCML` or by a
+ special ``@view_config`` decorator coupled with a :term:`scan`. See
+ :ref:`view_config_chapter` for more information about view
+ configuration.
view name
The "URL name" of a view, e.g ``index.html``. If a view is
@@ -834,7 +834,7 @@ Glossary
:meth:`pyramid.config.Configurator.add_route` and
:meth:`pyramid.config.Configurator.add_view` to make it more
convenient to register a collection of views as a single class when
- using :term:`url dispatch`. See also :ref:`handlers_chapter`.
+ using :term:`url dispatch`. See also :ref:`views_chapter`.
Deployment settings
Deployment settings are settings passed to the :term:`Configurator` as a
@@ -850,6 +850,15 @@ Glossary
WSGI middleware which can display debuggable traceback information in
the browser when an exception is raised by a Pyramid application. See
http://pypi.python.org/pypi/WebError .
-
-
+ view mapper
+ A view mapper is a class which implements the
+ :class:`pyramid.interfaces.IViewMapperFactory` interface, which performs
+ view argument and return value mapping. This is a plug point for
+ extension builders, not normally used by "civilians".
+
+ matchdict
+ The dictionary attached to the :term:`request` object as
+ ``request.matchdict`` when a :term:`URL dispatch` route has been matched.
+ Its keys are names as identified within the route pattern; its values are
+ the values matched by each pattern name.
diff --git a/docs/index.rst b/docs/index.rst
index 23ffb3b1b..7b1c46b70 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -38,18 +38,17 @@ Narrative documentation in chapter form explaining how to use
narr/firstapp
narr/project
narr/startup
- narr/resourcelocation
narr/urldispatch
+ narr/muchadoabouttraversal
narr/traversal
narr/views
narr/renderers
narr/templates
+ narr/viewconfig
narr/resources
narr/assets
narr/webob
narr/sessions
- narr/flash
- narr/csrf
narr/security
narr/hybrid
narr/i18n
@@ -64,6 +63,7 @@ Narrative documentation in chapter form explaining how to use
narr/router
narr/threadlocals
narr/zca
+ narr/forms
Tutorials
=========
@@ -78,7 +78,6 @@ applications to various platforms.
tutorials/wiki/index.rst
tutorials/wiki2/index.rst
tutorials/bfg/index.rst
- tutorials/cmf/index.rst
tutorials/gae/index.rst
tutorials/modwsgi/index.rst
tutorials/zeo/index.rst
diff --git a/docs/latexindex.rst b/docs/latexindex.rst
index 25e6791d0..4ff3bbfe7 100644
--- a/docs/latexindex.rst
+++ b/docs/latexindex.rst
@@ -31,18 +31,17 @@ Narrative Documentation
narr/configuration
narr/firstapp
narr/project
- narr/resourcelocation
narr/urldispatch
+ narr/muchadoabouttraversal
narr/traversal
narr/views
narr/renderers
narr/templates
+ narr/viewconfig
narr/resources
narr/assets
narr/webob
narr/sessions
- narr/flash
- narr/csrf
narr/security
narr/hybrid
narr/i18n
@@ -58,6 +57,7 @@ Narrative Documentation
narr/startup
narr/threadlocals
narr/zca
+ narr/forms
.. _tutorials:
@@ -125,7 +125,9 @@ ZCML Directive Reference
zcml/configure
zcml/default_permission
zcml/forbidden
+ zcml/handler
zcml/include
+ zcml/localenegotiator
zcml/notfound
zcml/remoteuserauthenticationpolicy
zcml/renderer
@@ -134,6 +136,7 @@ ZCML Directive Reference
zcml/scan
zcml/static
zcml/subscriber
+ zcml/translationdir
zcml/utility
zcml/view
diff --git a/docs/narr/assets.rst b/docs/narr/assets.rst
index f147426ce..f73ff231a 100644
--- a/docs/narr/assets.rst
+++ b/docs/narr/assets.rst
@@ -98,7 +98,7 @@ directory on a filesystem to an application user's browser. Use the
mechanism makes a directory of static files available at a name relative to
the application root URL, e.g. ``/static`` or as an external URL.
-.. note:: `~pyramid.config.Configurator.add_static_view` cannot serve a
+.. note:: :meth:`~pyramid.config.Configurator.add_static_view` cannot serve a
single file, nor can it serve a directory of static files directly
relative to the root URL of a :app:`Pyramid` application. For these
features, see :ref:`advanced_static`.
@@ -282,7 +282,7 @@ create such a circumstance, we suggest using the
in the application ``.ini`` file named ``media_location``. Then set the
value of ``media_location`` to either a prefix or a URL depending on whether
the application is being run in development or in production (use a different
-`.ini`` file for production than you do for development). This is just a
+``.ini`` file for production than you do for development). This is just a
suggestion for a pattern; any setting name other than ``media_location``
could be used.
diff --git a/docs/narr/csrf.rst b/docs/narr/csrf.rst
deleted file mode 100644
index 7586b0ed7..000000000
--- a/docs/narr/csrf.rst
+++ /dev/null
@@ -1,63 +0,0 @@
-.. _csrf_chapter:
-
-Preventing Cross-Site Request Forgery Attacks
-=============================================
-
-`Cross-site request forgery
-<http://en.wikipedia.org/wiki/Cross-site_request_forgery>`_ attacks are a
-phenomenon whereby a user with an identity on your website might click on a
-URL or button on another website which unwittingly redirects the user to your
-application to perform some command that requires elevated privileges.
-
-You can avoid most of these attacks by making sure that a the correct *CSRF
-token* has been set in an :app:`Pyramid` session object before performing any
-actions in code which requires elevated privileges and is invoked via a form
-post. To use CSRF token support, you must enable a :term:`session factory`
-as described in :ref:`using_the_default_session_factory` or
-:ref:`using_alternate_session_factories`.
-
-Using the ``session.new_csrf_token`` Method
--------------------------------------------
-
-To add a CSRF token to the session, use the ``session.new_csrf_token`` method.
-
-.. code-block:: python
- :linenos:
-
- token = request.session.new_csrf_token()
-
-The ``.new_csrf_token`` method accepts no arguments. It returns a *token*
-string, which will be opaque and randomized. This token will also be set
-into the session, awaiting pickup by the ``session.get_csrf_token`` method.
-You can subsequently use the returned token as the value of a hidden field in
-a form that posts to a method that requires elevated privileges. The handler
-for the form post should use ``session.get_csrf_token`` (explained below) to
-obtain the current CSRF token related to the user from the session, and
-compare it to the value of the hidden form field.
-
-Using the ``session.get_csrf_token`` Method
--------------------------------------------
-
-To get the current CSRF token from the session, use the
-``session.get_csrf_token`` method.
-
-.. code-block:: python
- :linenos:
-
- token = request.session.get_csrf_token()
-
-The ``get_csrf_token`` method accepts no arguments. It returns the "current"
-*token* string (as per the last call to ``session.new_csrf_token``). You can
-then use it to compare against the token provided within form post hidden
-value data. For example, if your form rendering included the CSRF token
-obtained via ``session.new_csrf_token`` as a hidden input field named
-``csrf_token``:
-
-.. code-block:: python
- :linenos:
-
- token = request.session.get_csrf_token()
- if token != request.POST['csrf_token']:
- raise ValueError('CSRF token did not match')
-
-
diff --git a/docs/narr/firstapp.rst b/docs/narr/firstapp.rst
index cb1e54b19..3f1098da4 100644
--- a/docs/narr/firstapp.rst
+++ b/docs/narr/firstapp.rst
@@ -10,7 +10,7 @@ more detail how it works.
.. note::
If you're a "theory-first" kind of person, you might choose to read
- :ref:`resourcelocation_chapter` and :ref:`views_chapter` before diving into
+ :ref:`urldispatch_chapter` and :ref:`views_chapter` before diving into
the code that follows, but it's not necessary if -- like many programmers
-- you're willing to "go with the flow".
@@ -323,7 +323,7 @@ For more information about the API of a :term:`Configurator` object,
see :class:`pyramid.config.Configurator` .
For more information about :term:`view configuration`, see
-:ref:`views_chapter`.
+:ref:`view_config_chapter`.
An example of using *declarative* configuration (:term:`ZCML`) instead of
imperative configuration to create a similar "hello world" is available
diff --git a/docs/narr/flash.rst b/docs/narr/flash.rst
deleted file mode 100644
index d41c2cdaf..000000000
--- a/docs/narr/flash.rst
+++ /dev/null
@@ -1,107 +0,0 @@
-.. _flash_chapter:
-
-Flash Messages
-==============
-
-"Flash messages" are simply a queue of message strings stored in the
-:term:`session`. To use flash messaging, you must enable a :term:`session
-factory` as described in :ref:`using_the_default_session_factory` or
-:ref:`using_alternate_session_factories`.
-
-Flash messaging has two main uses: to display a status message only once to
-the user after performing an internal redirect, and to allow generic code to
-log messages for single-time display without having direct access to an HTML
-template. The user interface consists of a number of methods of the
-:term:`session` object.
-
-Using the ``session.flash`` Method
-----------------------------------
-
-To add a message to a flash message queue, use a session object's ``flash``
-method:
-
-.. code-block:: python
- :linenos:
-
- request.session.flash('mymessage')
-
-The ``.flash`` method appends a message to a flash queue, creating the queue
-if necessary.
-
-``.flash`` accepts three arguments:
-
-.. method:: flash(message, queue='', allow_duplicate=True)
-
-The ``message`` argument is required. It represents a message you wish to
-later display to a user. It is usually a string but the ``message`` you
-provide is not modified in any way.
-
-The ``queue`` argument allows you to choose a queue to which to append the
-message you provide. This can be used to push different kinds of messages
-into flash storage for later display in different places on a page. You cam
-pass any name for your queue, but it must be a string. The default value is
-the empty string, which chooses the default queue. Each queue is independent,
-and can be popped by ``pop_flash`` or examined via ``peek_flash`` separately.
-``queue`` defaults to the empty string. The empty string represents the
-default flash message queue.
-
-.. code-block:: python
-
- request.session.flash(msg, 'myappsqueue')
-
-The ``allow_duplicate`` argument, which defaults to ``True``. If this is
-``False``, if you attempt to add a message to a queue which is already
-present in the queue, it will not be added.
-
-Using the ``session.pop_flash`` Method
---------------------------------------
-
-Once one or more messages has been added to a flash queue by the
-``session.flash`` API, the ``session.pop_flash`` API can be used to pop that
-queue and return it for use.
-
-To pop a particular queue of messages from the flash object, use the session
-object's ``pop_flash`` method.
-
-.. code-block:: python
- :linenos:
-
- >>> request.session.flash('info message')
- >>> request.session.pop_flash()
- ['info message']
-
-Calling ``session.pop_flash()`` again like above without a corresponding call
-to ``session.flash`` will return an empty list, because the queue has already
-been popped.
-
-.. code-block:: python
- :linenos:
-
- >>> request.session.flash('info message')
- >>> request.session.pop_flash()
- ['info message']
- >>> request.session.pop_flash()
- []
-
-The object returned from ``pop_flash`` is a list.
-
-Using the ``session.pop_flash`` Method
---------------------------------------
-
-Once one or more messages has been added to a flash queue by the
-``session.flash`` API, the ``session.peek_flash`` API can be used to "peek"
-at that queue. Unlike ``session.pop_flash``, the queue is not popped from
-flash storage.
-
-.. code-block:: python
- :linenos:
-
- >>> request.session.flash('info message')
- >>> request.session.peek_flash()
- ['info message']
- >>> request.session.peek_flash()
- ['info message']
- >>> request.session.pop_flash()
- ['info message']
- >>> request.session.peek_flash()
- []
diff --git a/docs/narr/forms.rst b/docs/narr/forms.rst
new file mode 100644
index 000000000..080f02159
--- /dev/null
+++ b/docs/narr/forms.rst
@@ -0,0 +1,126 @@
+.. _forms_chapter:
+
+.. index::
+ single: unicode, views, and forms
+ single: forms, views, and unicode
+ single: views, forms, and unicode
+
+Form Handling
+=============
+
+Handling Form Submissions in View Callables (Unicode and Character Set Issues)
+------------------------------------------------------------------------------
+
+Most web applications need to accept form submissions from web browsers and
+various other clients. In :app:`Pyramid`, form submission handling logic is
+always part of a :term:`view`. For a general overview of how to handle form
+submission data using the :term:`WebOb` API, see :ref:`webob_chapter` and
+`"Query and POST variables" within the WebOb documentation
+<http://pythonpaste.org/webob/reference.html#query-post-variables>`_.
+:app:`Pyramid` defers to WebOb for its request and response implementations,
+and handling form submission data is a property of the request
+implementation. Understanding WebOb's request API is the key to
+understanding how to process form submission data.
+
+There are some defaults that you need to be aware of when trying to handle
+form submission data in a :app:`Pyramid` view. Having high-order (i.e.,
+non-ASCII) characters in data contained within form submissions is
+exceedingly common, and the UTF-8 encoding is the most common encoding used
+on the web for character data. Since Unicode values are much saner than
+working with and storing bytestrings, :app:`Pyramid` configures the
+:term:`WebOb` request machinery to attempt to decode form submission values
+into Unicode from UTF-8 implicitly. This implicit decoding happens when view
+code obtains form field values via the ``request.params``, ``request.GET``,
+or ``request.POST`` APIs (see :ref:`request_module` for details about these
+APIs).
+
+.. note::
+
+ Many people find the difference between Unicode and UTF-8 confusing.
+ Unicode is a standard for representing text that supports most of the
+ world's writing systems. However, there are many ways that Unicode data
+ can be encoded into bytes for transit and storage. UTF-8 is a specific
+ encoding for Unicode, that is backwards-compatible with ASCII. This makes
+ UTF-8 very convenient for encoding data where a large subset of that data
+ is ASCII characters, which is largely true on the web. UTF-8 is also the
+ standard character encoding for URLs.
+
+As an example, let's assume that the following form page is served up to a
+browser client, and its ``action`` points at some :app:`Pyramid` view code:
+
+.. code-block:: xml
+ :linenos:
+
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ </head>
+ <form method="POST" action="myview">
+ <div>
+ <input type="text" name="firstname"/>
+ </div>
+ <div>
+ <input type="text" name="lastname"/>
+ </div>
+ <input type="submit" value="Submit"/>
+ </form>
+ </html>
+
+The ``myview`` view code in the :app:`Pyramid` application *must* expect that
+the values returned by ``request.params`` will be of type ``unicode``, as
+opposed to type ``str``. The following will work to accept a form post from
+the above form:
+
+.. code-block:: python
+ :linenos:
+
+ def myview(request):
+ firstname = request.params['firstname']
+ lastname = request.params['lastname']
+
+But the following ``myview`` view code *may not* work, as it tries to decode
+already-decoded (``unicode``) values obtained from ``request.params``:
+
+.. code-block:: python
+ :linenos:
+
+ def myview(request):
+ # the .decode('utf-8') will break below if there are any high-order
+ # characters in the firstname or lastname
+ firstname = request.params['firstname'].decode('utf-8')
+ lastname = request.params['lastname'].decode('utf-8')
+
+For implicit decoding to work reliably, you should ensure that every form you
+render that posts to a :app:`Pyramid` view explicitly defines a charset
+encoding of UTF-8. This can be done via a response that has a
+``;charset=UTF-8`` in its ``Content-Type`` header; or, as in the form above,
+with a ``meta http-equiv`` tag that implies that the charset is UTF-8 within
+the HTML ``head`` of the page containing the form. This must be done
+explicitly because all known browser clients assume that they should encode
+form data in the same character set implied by ``Content-Type`` value of the
+response containing the form when subsequently submitting that form. There is
+no other generally accepted way to tell browser clients which charset to use
+to encode form data. If you do not specify an encoding explicitly, the
+browser client will choose to encode form data in its default character set
+before submitting it, which may not be UTF-8 as the server expects. If a
+request containing form data encoded in a non-UTF8 charset is handled by your
+view code, eventually the request code accessed within your view will throw
+an error when it can't decode some high-order character encoded in another
+character set within form data, e.g., when ``request.params['somename']`` is
+accessed.
+
+If you are using the :class:`pyramid.response.Response` class to generate a
+response, or if you use the ``render_template_*`` templating APIs, the UTF-8
+charset is set automatically as the default via the ``Content-Type`` header.
+If you return a ``Content-Type`` header without an explicit charset, a
+request will add a ``;charset=utf-8`` trailer to the ``Content-Type`` header
+value for you, for response content types that are textual
+(e.g. ``text/html``, ``application/xml``, etc) as it is rendered. If you are
+using your own response object, you will need to ensure you do this yourself.
+
+.. note:: Only the *values* of request params obtained via
+ ``request.params``, ``request.GET`` or ``request.POST`` are decoded
+ to Unicode objects implicitly in the :app:`Pyramid` default
+ configuration. The keys are still (byte) strings.
+
+
diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst
index 2917b5254..cf3f56e87 100644
--- a/docs/narr/hooks.rst
+++ b/docs/narr/hooks.rst
@@ -541,6 +541,94 @@ The default context URL generator is available for perusal as the class
:term:`Pylons` GitHub Pyramid repository.
.. index::
+ single: view mapper
+
+Using a View Mapper
+-------------------
+
+The default calling conventions for view callables are documented in the
+:ref:`views_chapter`. You can change the way users define view callbles by
+employing a :term:`view mapper`.
+
+A view mapper is an object that accepts a set of keyword arguments and which
+returns a callable. The returned callable is called with the :term:`view
+callable` object. The returned callable should itself return another
+callable which can be called with the "internal calling protocol" ``(context,
+request)``.
+
+You can use a view mapper in a number of ways:
+
+- by setting a ``__view_mapper__`` attribute (which is the view mapper
+ object) on the view callable itself
+
+- by passing the mapper object to
+ :meth:`pyramid.config.Configurator.add_view` (or its declarative/decorator
+ equivalents) as the ``mapper`` argument.
+
+- by registering a *default* view mapper.
+
+Here's an example of a view mapper that emulates (somewhat) a Pylons
+"controller". The mapper is initialized with some keyword arguments. Its
+``__call__`` method accepts the view object (which will be a class). It uses
+the ``attr`` keyword argument it is passed to determine which attribute
+should be used as an action method. The wrapper method it returns accepts
+``(context, request)`` and returns the result of calling the action method
+with keyword arguments implied by the :term:`matchdict` after popping the
+``action`` out of it. This somewhat emulates the Pylons style of calling
+action methods with routing parameters pulled out of the route matching dict
+as keyword arguments.
+
+.. code-block:: python
+ :linenos:
+
+ # framework
+
+ class PylonsControllerViewMapper(object):
+ def __init__(self, **kw):
+ self.kw = kw
+
+ def __call__(self, view):
+ attr = self.kw['attr']
+ def wrapper(context, request):
+ matchdict = request.matchdict.copy()
+ matchdict.pop('action', None)
+ inst = view()
+ meth = getattr(inst, attr)
+ return meth(**matchdict)
+ return wrapper
+
+ class BaseController(object):
+ __view_mapper__ = PylonsControllerViewMapper
+
+A user might make use of these framework components like so:
+
+.. code-block:: python
+ :linenos:
+
+ # user application
+
+ from webob import Response
+ from pyramid.config import Configurator
+ from paste.httpserver import serve
+
+ class MyController(BaseController):
+ def index(self, id):
+ return Response(id)
+
+ if __name__ == '__main__':
+ config = Configurator()
+ config.add_handler('one', '/{id}', MyController, action='index')
+ config.add_handler('two', '/{action}/{id}', MyController)
+ serve(config.make_wsgi_app())
+
+The :meth:`pyramid.config.Configurator.set_default_mapper` method can be used
+to set a *default* view mapper (overriding the superdefault view mapper used
+by Pyramid itself).
+
+A *single* view registration can use a view mapper by passing the mapper as
+the ``mapper`` argument to :meth:`pyramid.config.Configuration.add_view`.
+
+.. index::
single: configuration decorator
.. _registering_configuration_decorators:
@@ -581,7 +669,7 @@ follows:
self.path = path
def register(self, scanner, name, wrapped):
- registry = get_current_registry()
+ registry = scanner.config.registry
registry.getUtility(IMyUtility).register(
self.path, wrapped
)
diff --git a/docs/narr/introduction.rst b/docs/narr/introduction.rst
index 3ade3726c..c61ef21d4 100644
--- a/docs/narr/introduction.rst
+++ b/docs/narr/introduction.rst
@@ -63,7 +63,7 @@ A Sense of Fun
Minimalism
:app:`Pyramid` provides only the very basics: *URL to code
- mapping*, *templating*, *security*, and *resources*. There is not
+ mapping*, *templating*, *security*, and *assets*. There is not
much more to the framework than these pieces: you are expected to
provide the rest.
diff --git a/docs/narr/muchadoabouttraversal.rst b/docs/narr/muchadoabouttraversal.rst
new file mode 100644
index 000000000..bc1b48462
--- /dev/null
+++ b/docs/narr/muchadoabouttraversal.rst
@@ -0,0 +1,312 @@
+.. _much_ado_about_traversal_chapter:
+
+========================
+Much Ado About Traversal
+========================
+
+.. note:: This chapter was adapted, with permission, from a blog post by `Rob
+ Miller <http://blog.nonsequitarian.org/>`_, originally published at
+ `http://blog.nonsequitarian.org/2010/much-ado-about-traversal/
+ <http://blog.nonsequitarian.org/2010/much-ado-about-traversal/>`_.
+
+Traversal is an alternative to :term:`URL dispatch` which allows
+:app:`Pyramid` applications to map URLs to code.
+
+.. note::
+
+ Ex-Zope users whom are already familiar with traversal and view lookup
+ conceptually may want to skip directly to the :ref:`traversal_chapter`
+ chapter, which discusses technical details. This chapter is mostly aimed
+ at people who have previous :term:`Pylons` experience or experience in
+ another framework which does not provide traversal, and need an
+ introduction to the "why" of traversal.
+
+Some folks who have been using Pylons and its Routes-based URL matching for a
+long time are being exposed for the first time, via :app:`Pyramid`, to new
+ideas such as ":term:`traversal`" and ":term:`view lookup`" as a way to route
+incoming HTTP requests to callable code. Some of the same folks believe that
+traversal is hard to understand. Others question its usefulness; URL
+matching has worked for them so far, why should they even consider dealing
+with another approach, one which doesn't fit their brain and which doesn't
+provide any immediately obvious value?
+
+You can be assured that if you don't want to understand traversal, you don't
+have to. You can happily build :app:`Pyramid` applications with only
+:term:`URL dispatch`. However, there are some straightforward, real-world
+use cases that are much more easily served by a traversal-based approach than
+by a pattern-matching mechanism. Even if you haven't yet hit one of these
+use cases yourself, understanding these new ideas is worth the effort for any
+web developer so you know when you might want to use them. :term:`Traversal`
+is actually a straightforward metaphor easily comprehended by anyone who's
+ever used a run-of-the-mill file system with folders and files.
+
+URL Dispatch
+------------
+
+Let's step back and consider the problem we're trying to solve, which is
+simple. An HTTP request for a particular path has been routed to our web
+application. The requested path will possibly invoke a specific :term:`view
+callable` function defined somewhere in our app. We're trying to determine
+*which* callable function, if any, should be invoked for a given requested
+URL.
+
+Many systems, including Pyramid, offer a simple solution. They offer the
+concept of "URL matching". URL matching approaches this problem by parsing
+the URL path and comparing the results to a set of registered "patterns",
+defined by a set of regular expressions, or some other URL path templating
+syntax. Each pattern is mapped to a callable function somewhere; if the
+request path matches a specific pattern, the associated function is called.
+If the request path matches more than one pattern, some conflict resolution
+scheme is used, usually a simple order precedence so that the first match
+will take priority over any subsequent matches. If a request path doesn't
+match any of the defined patterns, :app:`Pyramid` a "404 Not Found" response
+is returned.
+
+In Pyramid, we offer an implementation of URL mapping which we call
+:term:`URL dispatch`. Using :app:`Pyramid` syntax, we might have a match
+pattern such as ``/{userid}/photos/{photoid}``, mapped to a ``photo_view()``
+function defined somewhere in our code. Then a request for a path such as
+``/joeschmoe/photos/photo1`` would be a match, and the ``photo_view()``
+function would be invoked to handle the request. Similarly,
+``/{userid}/blog/{year}/{month}/{postid}`` might map to a
+``blog_post_view()`` function, so ``/joeschmoe/blog/2010/12/urlmatching``
+would trigger the function, which presumably would know how to find and
+render the ``urlmatching`` blog post.
+
+Historical Refresher
+--------------------
+
+Now that we've refreshed our understanding of :term:`URL dispatch`, we'll dig
+in to the idea of traversal. Before we do, though, let's take a trip down
+memory lane. If you've been doing web work for a while, you may remember a
+time when we didn't have fancy web frameworks like :term:`Pylons` and
+:app:`Pyramid`. Instead, we had general purpose HTTP servers that primarily
+served files off of a file system. The "root" of a given site mapped to a
+particular folder somewhere on the file system. Each segment of the request
+URL path represented a subdirectory. The final path segment would be either
+a directory or a file, and once the server found the right file it would
+package it up in an HTTP response and send it back to the client. So serving
+up a request for ``/joeschmoe/photos/photo1`` literally meant that there was
+a ``joeschmoe`` folder somewhere, which contained a ``photos`` folder, which
+in turn contained a ``photo1`` file. If at any point along the way we find
+that there is not a folder or file matching the requested path, we return a
+404 response.
+
+As the web grew more dynamic, however, a little bit of extra complexity was
+added. Technologies such as CGI and HTTP server modules were developed.
+Files were still looked up on the file system, but if the file ended with
+(for example) ``.cgi`` or ``.php``, or if it lived in a special folder,
+instead of simply sending the file to the client the server would read the
+file, execute it using an interpreter of some sort, and then send the output
+from this process to the client as the final result. The server
+configuration specified which files would trigger some dynamic code, with the
+default case being to just serve the static file.
+
+Traversal (aka Resource Location)
+---------------------------------
+
+.. index::
+ single: traversal overview
+
+Believe it or not, if you understand how serving files from a file system
+works,you understand traversal. And if you understand that a server might do
+something different based on what type of file a given request specifies,
+then you understand view lookup.
+
+The major difference between file system lookup and traversal is that a file
+system lookup steps through nested directories and files in a file system
+tree, while traversal steps through nested dictionary-type objects in an
+:term:`resource tree`. Let's take a detailed look at one of our example
+paths, so we can see what I mean:
+
+The path ``/joeschmoe/photos/photo1``, has four segments: ``/``,
+``joeschmoe``, ``photos`` and ``photo1``. With file system lookup we might
+have a root folder (``/``) containing a nested folder (``joeschmoe``), which
+contains another nested folder (``photos``), which finally contains a JPG
+file ("photo1"). With traversal, we instead have a dictionary-like root
+object. Asking for the ``joeschmoe`` key gives us another dictionary-like
+object. Asking this in turn for the ``photos`` key gives us yet another
+mapping object, which finally (hopefully) contains the resource that we're
+looking for within its values, referenced by the ``photo1`` key.
+
+In pure Python terms, then, the traversal or "resource location"
+portion of satisfying the ``/joeschmoe/photos/photo1`` request
+will look something like this pseudocode::
+
+ get_root()['joeschmoe']['photos']['photo1']
+
+``get_root()`` is some function that returns a root traversal
+:term:`resource`. If all of the specified keys exist, then the returned
+object will be the resource that is being requested, analogous to the JPG
+file that was retrieved in the file system example. If a :exc:`KeyError` is
+generated anywhere along the way, :app:`Pyramid` will return 404. (This
+isn't precisely true, as you'll see when we learn about view lookup below,
+but the basic idea holds.)
+
+What Is a "Resource"?
+---------------------
+
+"Files on a file system I understand", you might say. "But what are these
+nested dictionary things? Where do these objects, these 'resources', live?
+What *are* they?"
+
+Since :app:`Pyramid` is not a highly opinionated framework, it makes no
+restriction on how a :term:`resource` is implemented; a developer can
+implement them as he wishes. One common pattern used is to persist all of
+the resources, including the root, in a database as a graph. The root object
+is a dictionarylike object. Dictionarylike objects in Python supply a
+``__getitem__`` method which is called when key lookup is done. Under the
+hood, when ``adict`` is a dictionarylike object, Python translates
+``adict['a']`` to ``adict.__getitem__('a')``. Try doing this in a Python
+interpreter prompt if you don't believe us:
+
+.. code-block:: text
+ :linenos:
+
+ Python 2.4.6 (#2, Apr 29 2010, 00:31:48)
+ [GCC 4.4.3] on linux2
+ Type "help", "copyright", "credits" or "license" for more information.
+ >>> adict = {}
+ >>> adict['a'] = 1
+ >>> adict['a']
+ 1
+ >>> adict.__getitem__('a')
+ 1
+
+
+The dictionarylike root object stores the ids of all of its subresources as
+keys, and provides a ``__getitem__`` implementation that fetches them. So
+``get_root()`` fetches the unique root object, while
+``get_root()['joeschmoe']`` returns a different object, also stored in the
+database, which in turn has its own subresources and ``__getitem__``
+implementation, etc. These resources might be persisted in a relational
+database, one of the many "NoSQL" solutions that are becoming popular these
+days, or anywhere else, it doesn't matter. As long as the returned objects
+provide the dictionary-like API (i.e. as long as they have an appropriately
+implemented ``__getitem__`` method) then traversal will work.
+
+In fact, you don't need a "database" at all. You could trivially implement a
+set of objects with ``__getitem__`` methods that search for files in specific
+directories, and thus precisely recreate the older mechanism of having the
+URL path mapped directly to a folder structure on the file system. Or you
+could use plain dictionaries too. Traversal is in fact a superset of file
+system lookup.
+
+.. note:: See the chapter entitled :ref:`resources_chapter` for a more
+ technical overview of resources.
+
+View Lookup
+-----------
+
+At this point we're nearly there. We've covered traversal, which is the
+process by which a specific resource is retrieved according to a specific URL
+path. But what is "view lookup"?
+
+The need for view lookup is simple: there is more than one possible action
+that you might want to take after finding a :term:`resource`. With our photo
+example, for instance, you might want to view the photo in a page, but you
+might also want to provide a way for the user to edit the photo and any
+associated metadata. We'll call the former the ``view`` view, and the latter
+will be the ``edit`` view (Original, I know.) :app:`Pyramid` has a
+centralized view registry where named views can be associated with specific
+resource types. So in our example, we'll assume that we've registered
+``view`` and ``edit`` views for photo objects, and that we've specified the
+``view`` view as the default, so that ``/joeschmoe/photos/photo1/view`` and
+``/joeschmoe/photos/photo1`` are equivalent. The edit view would sensibly be
+provided by a request for ``/joeschmoe/photos/photo1/edit``.
+
+Hopefully it's clear that the first portion of the edit view's URL path is
+going to resolve to the same resource as the non-edit version, specifically
+the resource returned by ``get_root()['joeschmoe']['photos']['photo1']``.
+But traveral ends there; the ``photo1`` resource doesn't have an ``edit``
+key. In fact, it might not even be a dictionary-like object, in which case
+``photo1['edit']`` would be meaningless. When the :app:`Pyramid` resource
+location has been resolved to a *leaf* resource, but the entire request path
+has not yet been expended, the *very next* path segment is treated as a
+:term:`view name`. The registry is then checked to see if a view of the
+given name has been specified for a resource of the given type. If so, the
+view callable is invoked, with the resource passed in as the related
+``context`` object (also available as ``request.context``). If a view
+callable could not be found, :app:`Pyramid` will return a "404 Not Found"
+response.
+
+You might conceptualize a request for ``/joeschmoe/photos/photo1/edit`` as
+ultimately converted into the following piece of Pythonic pseudocode::
+
+ context = get_root()['joeschmoe']['photos']['photo1']
+ view_callable = get_view(context, 'edit')
+ request.context = context
+ view_callable(request)
+
+The ``get_root`` and ``get_view`` functions don't really exist. Internally,
+:app:`Pyramid` does something more complicated. But the example above is a
+reasonable approximation of the view lookup algorithm in pseudocode.
+
+Use Cases
+---------
+
+Why should we care about traversal? URL matching is easier to explain, and
+it's good enough, right?
+
+In some cases, yes, but certainly not in all cases. So far we've had very
+structured URLs, where our paths have had a specific, small number of pieces,
+like this::
+
+ /{userid}/{typename}/{objectid}[/{view_name}]
+
+In all of the examples thus far, we've hard coded the typename value,
+assuming that we'd know at development time what names were going to be used
+("photos", "blog", etc.). But what if we don't know what these names will
+be? Or, worse yet, what if we don't know *anything* about the structure of
+the URLs inside a user's folder? We could be writing a CMS where we want the
+end user to be able to arbitrarily add content and other folders inside his
+folder. He might decide to nest folders dozens of layers deep. How will you
+construct matching patterns that could account for every possible combination
+of paths that might develop?
+
+It's possible, but it will make for some somewhat ugly URLs. And the
+matching patterns are going to become complex quickly as you try to handle
+all of the edge cases.
+
+With traversal, however, it's straightforward. If you want 20 layers of
+nesting, it's no problem. :app:`Pyramid` will happily call ``__getitem__``
+as many times as it needs to, until it runs out of path segments or until a
+resource raises a :exc:`KeyError`. Each resource only needs to know how to
+fetch its immediate children, the traversal algorithm takes care of the rest.
+
+One of the key advantages of traversal is that the structure of the resource
+tree can live in the database, and not in the code. It's simple to let users
+modify the tree at runtime to set up their own personalized directory
+structures.
+
+Another use case in which traversal shines is when there is a need to support
+a context-dependent security policy. One example might be a document
+management infrastructure for a large corporation, where members of different
+departments have varying access levels to the various other departments'
+files. Reasonably, even specific files might need to be made available to
+specific individuals. Traversal does well here if your resources actually
+represent the data objects related to your documents, because the idea of a
+resource authorization is baked right into the code resolution and calling
+process. Resource objects can store ACLs, which can be inherited and/or
+overridden by the subresources.
+
+If each resource can thus generate a context-based ACL, then whenever view
+code is attempting to perform a sensitive action, it can check against that
+ACL to see whether the current user should be allowed to perform the action.
+In this way you achieve so called "instance based" or "row level" security
+which is considerably harder to model using a traditional tabular approach.
+:app:`Pyramid` actively supports such a scheme, and in fact if you register
+your views with guard permissions and use an authorization policy,
+:app:`Pyramid` can check against a resource's ACL when deciding whether or
+not the view itself is available to the current user.
+
+In summary, there are entire classes of problems that are more easily served
+by traversal and view lookup than by :term:`URL dispatch`. If your problems
+don't require it, great: stick with :term:`URL dispatch`. But if you're
+using :app:`Pyramid` and you ever find that you *do* need to support one of
+these use cases, you'll be glad you have traversal in your toolkit.
+
+.. note::
+ It is even possible to mix and match :term:`traversal` with
+ :term:`URL dispatch` in the same :app:`Pyramid` application. See the
+ :ref:`hybrid_chapter` chapter for details.
diff --git a/docs/narr/project.rst b/docs/narr/project.rst
index 55a2711f3..c1017b5c1 100644
--- a/docs/narr/project.rst
+++ b/docs/narr/project.rst
@@ -180,7 +180,7 @@ Installing your Newly Created Project for Development
To install a newly created project for development, you should ``cd`` to the
newly created project directory and use the Python interpreter from the
:term:`virtualenv` you created during :ref:`installing_chapter` to invoke the
-command ``python setup.py develop.py``
+command ``python setup.py develop``
The file named ``setup.py`` will be in the root of the paster-generated
project directory. The ``python`` you're invoking should be the one that
@@ -910,6 +910,8 @@ example.
See :ref:`testing_chapter` for more information about writing :app:`Pyramid`
unit tests.
+.. _modifying_package_structure:
+
Modifying Package Structure
----------------------------
@@ -958,12 +960,14 @@ To this:
.. code-block:: python
:linenos:
- config.add_view('myproject.views.blogs.my_view',
+ config.add_view('myproject.views.blog.my_view',
renderer='myproject:templates/mytemplate.pt')
You can then continue to add files to the ``views`` directory, and refer to
views or handler classes/functions within those files via the dotted name
-passed as the first argument to ``add_view``. For example:
+passed as the first argument to ``add_view``. For example, if you added a
+file named ``anothermodule.py`` to the ``views`` subdirectory, and added a
+view callable named ``my_view`` to it:
.. code-block:: python
:linenos:
diff --git a/docs/narr/renderers.rst b/docs/narr/renderers.rst
index 3804fcf42..d888e3376 100644
--- a/docs/narr/renderers.rst
+++ b/docs/narr/renderers.rst
@@ -3,18 +3,10 @@
Renderers
=========
-In the :ref:`views_chapter` chapter, we said that a view callable must
-return a :term:`Response` object. We lied. A :term:`renderer` is a service
-that attempts to convert a non-Response return value of a function, class, or
-instance that acts as a :term:`view callable` to a :term:`Response` object.
-
-Overview
---------
-
-A view needn't *always* return a Response object. If a view happens to
-return something which does not implement the Pyramid Response interface,
-:app:`Pyramid` will attempt to use a :term:`renderer` to construct a
-response. For example:
+A view needn't *always* return a :term:`Response` object. If a view
+happens to return something which does not implement the Pyramid
+Response interface, :app:`Pyramid` will attempt to use a
+:term:`renderer` to construct a response. For example:
.. code-block:: python
:linenos:
@@ -22,6 +14,7 @@ response. For example:
from pyramid.response import Response
from pyramid.view import view_config
+ @view_config(renderer='json')
def hello_world(request):
return {'content':'Hello!'}
diff --git a/docs/narr/resourcelocation.rst b/docs/narr/resourcelocation.rst
deleted file mode 100644
index 8ddc890ed..000000000
--- a/docs/narr/resourcelocation.rst
+++ /dev/null
@@ -1,103 +0,0 @@
-.. index::
- single: resource location
-
-.. _resourcelocation_chapter:
-
-Resource Location and View Lookup
----------------------------------
-
-:app:`Pyramid` uses two separate but cooperating subsystems to find and
-invoke :term:`view callable` code written by the application developer:
-:term:`resource location` and :term:`view lookup`.
-
-- First, a :app:`Pyramid` :term:`resource location` subsystem is given a
- :term:`request`; it is responsible for finding a :term:`resource` object
- based on information present in the request. When a resource is found via
- resource location, it becomes known as the :term:`context`.
-
-- Next, using the context resource found by :term:`resource location` and the
- :term:`request`, :term:`view lookup` is then responsible for finding and
- invoking a :term:`view callable`. A view callable is a specific bit of
- code written and registered by the application developer which receives the
- :term:`request` and which returns a :term:`response`.
-
-These two subsystems are used by :app:`Pyramid` serially: first, a
-:term:`resource location` subsystem does its job. Then the result of
-resource location is passed to the :term:`view lookup` subsystem. The view
-lookup system finds a :term:`view callable` written by an application
-developer, and invokes it. A view callable returns a :term:`response`. The
-response is returned to the requesting user.
-
-There are two separate :term:`resource location` subsystems in
-:app:`Pyramid`: :term:`traversal` and :term:`URL dispatch`. They can be used
-separately or they can be combined. Three chapters which follow describe
-:term:`resource location`: :ref:`traversal_chapter`,
-:ref:`urldispatch_chapter` and :ref:`hybrid_chapter`.
-
-There is only one :term:`view lookup` subsystem present in :app:`Pyramid`.
-Where appropriate, we will describe how view lookup interacts with context
-finding. One chapter which follows describes :term:`view lookup`:
-:ref:`views_chapter`.
-
-Should I Use Traversal or URL Dispatch for Resource Location?
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-When you use :app:`Pyramid`, you have a choice about how you'd like to
-resolve URLs to code: you can use either :term:`traversal` or :term:`URL
-dispatch`. The choice to use traversal vs. URL dispatch is largely
-"religious". Since :app:`Pyramid` provides support for both approaches, you
-can use either exclusively or combine them as you see fit.
-
-:term:`URL dispatch` is very straightforward. When you limit your
-application to using URL dispatch, you know every URL that your application
-might generate or respond to, all the URL matching elements are listed in a
-single place, and you needn't think about :term:`resource location` or
-:term:`view lookup` at all.
-
-URL dispatch can easily handle URLs such as
-``http://example.com/members/Chris``, where it's assumed that each item
-"below" ``members`` in the URL represents a single member in some system.
-You just match everything "below" ``members`` to a particular :term:`view
-callable`, e.g. ``/members/{memberid}``.
-
-However, URL dispatch is not very convenient if you'd like your URLs to
-represent an arbitrary-depth hierarchy. For example, if you need to infer
-the difference between sets of URLs such as these, where the ``document`` in
-the first URL represents a PDF document, and ``/stuff/page`` in the second
-represents an OpenOffice document in a "stuff" folder.
-
-.. code-block:: text
-
- http://example.com/members/Chris/document
- http://example.com/members/Chris/stuff/page
-
-It takes more pattern matching assertions to be able to make hierarchies work
-in URL-dispatch based systems, and some assertions just aren't possible.
-URL-dispatch based systems just don't deal very well with URLs that represent
-arbitrary-depth hierarchies.
-
-:term:`URL dispatch` tends to collapse the two steps of :term:`resource
-location` and :term:`view lookup` into a single step. Thus, a URL can map
-*directly* to a view callable. This makes URL dispatch easier to understand
-than traversal, because traversal makes you understand how :term:`resource
-location` works. But explicitly locating a resource provides extra
-flexibility. For example, it makes it possible to protect your application
-with declarative context-sensitive instance-level :term:`authorization`.
-
-Unlike URL dispatch, :term:`traversal` works well for URLs that represent
-arbitrary-depth hierarchies. Since the path segments that compose a URL are
-addressed separately, it becomes very easy to form URLs that represent
-arbitrary depth hierarchies in a system that uses traversal. When you're
-willing to treat your application resources as a tree that can be traversed,
-it also becomes easy to provide "instance-level security": you just attach an
-:term:`ACL` security declaration to each resource in the tree. This is not
-nearly as easy to do when using URL dispatch.
-
-Traversal probably just doesn't make any sense when you possess completely
-"square" data stored in a relational database because it requires the
-construction and maintenance of a resource tree and requires that the
-developer think about mapping URLs to code in terms of traversing that tree.
-
-We'll examine both :term:`URL dispatch` and :term:`traversal` in the next two
-chapters.
-
diff --git a/docs/narr/resources.rst b/docs/narr/resources.rst
index da1933190..c97b4c676 100644
--- a/docs/narr/resources.rst
+++ b/docs/narr/resources.rst
@@ -1,21 +1,23 @@
+.. _resources_chapter:
+
Resources
=========
-A :term:`resource` is an object that represents a "place" in a tree related
-to your application. Every :app:`Pyramid` application has at least one
-resource object: the :term:`root` resource (even if you don't define one
-manually, a default root resource is created for you). The root resource is
-the root of a :term:`resource tree`. A resource tree is a set of nested
-dictionary-like objects which you can use to represent your website's
-structure.
+A :term:`resource` is an object that represents a "place" in a tree
+related to your application. Every :app:`Pyramid` application has at
+least one resource object: the :term:`root` resource. Even if you don't
+define a root resource manually, a default one is created for you. The
+root resource is the root of a :term:`resource tree`. A resource tree
+is a set of nested dictionary-like objects which you can use to
+represent your website's structure.
In an application which uses :term:`traversal` to map URLs to code, the
-resource tree structure is used heavily to map a URL to a :term:`view
-callable`. :app:`Pyramid` will walk "up" the resource tree by traversing
-through the nested dictionary structure of the tree when :term:`traversal` is
-used in order to find a :term:`context` resource. Once a context resource is
-found, the context resource and data in the request will be used to find a
-:term:`view callable`.
+resource tree structure is used heavily to map each URL to a :term:`view
+callable`. When :term:`traversal` is used, :app:`Pyramid` will walk
+through the resource tree by traversing through its nested dictionary
+structure in order to find a :term:`context` resource. Once a context
+resource is found, the context resource and data in the request will be
+used to find a :term:`view callable`.
In an application which uses :term:`URL dispatch`, the resource tree is only
used indirectly, and is often "invisible" to the developer. In URL dispatch
@@ -26,7 +28,7 @@ much less important in applications that use URL dispatch than applications
that use traversal.
In "Zope-like" :app:`Pyramid` applications, resource objects also often store
-data persistently and offer methods related to mutating that persistent data.
+data persistently, and offer methods related to mutating that persistent data.
In these kinds of applications, resources not only represent the site
structure of your website, but they become the :term:`domain model` of the
application.
@@ -72,8 +74,8 @@ tree:
the container's ``__getitem__`` should return the sub-resource.
- Leaf resources, which do not contain other resources, must not implement a
- ``__getitem__``, or if they do, their ``__getitem__`` method must raise a
- :exc:`KeyError`.
+ ``__getitem__``, or if they do, their ``__getitem__`` method must always
+ raise a :exc:`KeyError`.
See :ref:`traversal_chapter` for more information about how traversal
works against resource instances.
@@ -239,7 +241,7 @@ A slash is appended to all resource URLs when
:func:`~pyramid.url.resource_url` is used to generate them in this simple
manner, because resources are "places" in the hierarchy, and URLs are meant
to be clicked on to be visited. Relative URLs that you include on HTML pages
-rendered as the result of the default view of a resource are typically more
+rendered as the result of the default view of a resource are more
apt to be relative to these resources than relative to their parent.
You can also pass extra elements to :func:`~pyramid.url.resource_url`:
@@ -404,7 +406,7 @@ Obtaining the Lineage of a Resource
-----------------------------------
:func:`pyramid.location.lineage` returns a generator representing the
-:term:`lineage` of the :term:`location` aware:term:`resource` object.
+:term:`lineage` of the :term:`location` aware :term:`resource` object.
The :func:`~pyramid.location.lineage` function returns the resource it is
passed, then each parent of the resource, in order. For example, if the
@@ -533,7 +535,7 @@ declares that the blog entry implements an :term:`interface`.
implements(IBlogEntry)
def __init__(self, title, body, author):
self.title = title
- self.body = body
+ self.body = body
self.author = author
self.created = datetime.datetime.now()
@@ -568,7 +570,7 @@ To do so, use the :func:`zope.interface.directlyProvides` function:
class BlogEntry(object):
def __init__(self, title, body, author):
self.title = title
- self.body = body
+ self.body = body
self.author = author
self.created = datetime.datetime.now()
@@ -596,7 +598,7 @@ the :func:`zope.interface.alsoProvides` function:
class BlogEntry(object):
def __init__(self, title, body, author):
self.title = title
- self.body = body
+ self.body = body
self.author = author
self.created = datetime.datetime.now()
diff --git a/docs/narr/router.rst b/docs/narr/router.rst
index d3d5bd370..f9e98373c 100644
--- a/docs/narr/router.rst
+++ b/docs/narr/router.rst
@@ -133,6 +133,6 @@ processing?
This is a very high-level overview that leaves out various details.
For more detail about subsystems invoked by the :app:`Pyramid` router
such as traversal, URL dispatch, views, and event processing, see
-:ref:`resourcelocation_chapter`, :ref:`views_chapter`, and
+:ref:`urldispatch_chapter`, :ref:`views_chapter`, and
:ref:`events_chapter`.
diff --git a/docs/narr/security.rst b/docs/narr/security.rst
index c5262faa2..62a4727bc 100644
--- a/docs/narr/security.rst
+++ b/docs/narr/security.rst
@@ -18,8 +18,7 @@ works at a high level:
:term:`resource location`. A context is located differently depending on
whether the application uses :term:`traversal` or :term:`URL dispatch`, but
a context is ultimately found in either case. See
- :ref:`resourcelocation_chapter` for more information about resource
- location.
+ the :ref:`urldispatch_chapter` chapter for more information.
- A :term:`view callable` is located by :term:`view lookup` using the
context as well as other attributes of the request.
diff --git a/docs/narr/sessions.rst b/docs/narr/sessions.rst
index de9add3b7..0ed52b563 100644
--- a/docs/narr/sessions.rst
+++ b/docs/narr/sessions.rst
@@ -3,13 +3,18 @@
.. _sessions_chapter:
-Session Objects
-===============
+Sessions
+========
A :term:`session` is a namespace which is valid for some period of
continual activity that can be used to represent a user's interaction
with a web application.
+This chapter describes how to configure sessions, what session
+implementations :app:`Pyramid` provides out of the box, how to store and
+retrieve data from sessions, and two session-specific features: flash
+messages, and cross-site request forgery attack prevention.
+
.. _using_the_default_session_factory:
Using The Default Session Factory
@@ -32,7 +37,7 @@ limitation:
representation of the session is fewer than 4000. This is
suitable only for very small data sets.
-It is, however, digitally signed, and thus its data cannot easily be
+It is digitally signed, however, and thus its data cannot easily be
tampered with.
You can configure this session factory in your :app:`Pyramid`
@@ -113,7 +118,7 @@ documentation.
Some gotchas:
- Keys and values of session data must be *pickleable*. This means,
- typically, that they must be instances of basic types of objects,
+ typically, that they are instances of basic types of objects,
such as strings, lists, dictionaries, tuples, integers, etc. If you
place an object in a session data key or value that is not
pickleable, an error will be raised when the session is serialized.
@@ -162,3 +167,180 @@ both types are available in
:class:`pyramid.interfaces.ISession`. You might use the cookie
implementation in the :mod:`pyramid.session` module as inspiration.
+.. index::
+ single: flash messages
+
+Flash Messages
+--------------
+
+"Flash messages" are simply a queue of message strings stored in the
+:term:`session`. To use flash messaging, you must enable a :term:`session
+factory` as described in :ref:`using_the_default_session_factory` or
+:ref:`using_alternate_session_factories`.
+
+Flash messaging has two main uses: to display a status message only once to
+the user after performing an internal redirect, and to allow generic code to
+log messages for single-time display without having direct access to an HTML
+template. The user interface consists of a number of methods of the
+:term:`session` object.
+
+Using the ``session.flash`` Method
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To add a message to a flash message queue, use a session object's ``flash()``
+method:
+
+.. code-block:: python
+
+ request.session.flash('mymessage')
+
+The ``flash()`` method appends a message to a flash queue, creating the queue
+if necessary.
+
+``flash()`` accepts three arguments:
+
+.. method:: flash(message, queue='', allow_duplicate=True)
+
+The ``message`` argument is required. It represents a message you wish to
+later display to a user. It is usually a string but the ``message`` you
+provide is not modified in any way.
+
+The ``queue`` argument allows you to choose a queue to which to append
+the message you provide. This can be used to push different kinds of
+messages into flash storage for later display in different places on a
+page. You can pass any name for your queue, but it must be a string.
+Each queue is independent, and can be popped by ``pop_flash()`` or
+examined via ``peek_flash()`` separately. ``queue`` defaults to the
+empty string. The empty string represents the default flash message
+queue.
+
+.. code-block:: python
+
+ request.session.flash(msg, 'myappsqueue')
+
+The ``allow_duplicate`` argument defaults to ``True``. If this is
+``False``, and you attempt to add a message value which is already
+present in the queue, it will not be added.
+
+Using the ``session.pop_flash`` Method
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Once one or more messages have been added to a flash queue by the
+``session.flash()`` API, the ``session.pop_flash()`` API can be used to
+pop an entire queue and return it for use.
+
+To pop a particular queue of messages from the flash object, use the session
+object's ``pop_flash()`` method. This returns a list of the messages
+that were added to the flash queue, and empties the queue.
+
+.. method:: pop_flash(queue='')
+
+.. code-block:: python
+ :linenos:
+
+ >>> request.session.flash('info message')
+ >>> request.session.pop_flash()
+ ['info message']
+
+Calling ``session.pop_flash()`` again like above without a corresponding call
+to ``session.flash()`` will return an empty list, because the queue has already
+been popped.
+
+.. code-block:: python
+ :linenos:
+
+ >>> request.session.flash('info message')
+ >>> request.session.pop_flash()
+ ['info message']
+ >>> request.session.pop_flash()
+ []
+
+Using the ``session.peek_flash`` Method
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Once one or more messages has been added to a flash queue by the
+``session.flash()`` API, the ``session.peek_flash()`` API can be used to
+"peek" at that queue. Unlike ``session.pop_flash()``, the queue is not
+popped from flash storage.
+
+.. method:: peek_flash(queue='')
+
+.. code-block:: python
+ :linenos:
+
+ >>> request.session.flash('info message')
+ >>> request.session.peek_flash()
+ ['info message']
+ >>> request.session.peek_flash()
+ ['info message']
+ >>> request.session.pop_flash()
+ ['info message']
+ >>> request.session.peek_flash()
+ []
+
+.. index::
+ single: preventing cross-site request forgery attacks
+ single: cross-site request forgery attacks, prevention
+
+Preventing Cross-Site Request Forgery Attacks
+---------------------------------------------
+
+`Cross-site request forgery
+<http://en.wikipedia.org/wiki/Cross-site_request_forgery>`_ attacks are a
+phenomenon whereby a user with an identity on your website might click on a
+URL or button on another website which unwittingly redirects the user to your
+application to perform some command that requires elevated privileges.
+
+You can avoid most of these attacks by making sure that the correct *CSRF
+token* has been set in an :app:`Pyramid` session object before performing any
+actions in code which requires elevated privileges that is invoked via a form
+post. To use CSRF token support, you must enable a :term:`session factory`
+as described in :ref:`using_the_default_session_factory` or
+:ref:`using_alternate_session_factories`.
+
+Using the ``session.get_csrf_token`` Method
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To get the current CSRF token from the session, use the
+``session.get_csrf_token()`` method.
+
+.. code-block:: python
+
+ token = request.session.get_csrf_token()
+
+The ``session.get_csrf_token()`` method accepts no arguments. It returns a
+CSRF *token* string. If ``session.get_csrf_token()`` or
+``session.new_csrf_token()`` was invoked previously for this session, the
+existing token will be returned. If no CSRF token previously existed for
+this session, a new token will be will be set into the session and returned.
+The newly created token will be opaque and randomized.
+
+You can use the returned token as the value of a hidden field in a form that
+posts to a method that requires elevated privileges. The handler for the
+form post should use ``session.get_csrf_token()`` *again* to obtain the
+current CSRF token related to the user from the session, and compare it to
+the value of the hidden form field. For example, if your form rendering
+included the CSRF token obtained via ``session.get_csrf_token()`` as a hidden
+input field named ``csrf_token``:
+
+.. code-block:: python
+ :linenos:
+
+ token = request.session.get_csrf_token()
+ if token != request.POST['csrf_token']:
+ raise ValueError('CSRF token did not match')
+
+Using the ``session.new_csrf_token`` Method
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To explicitly add a new CSRF token to the session, use the
+``session.new_csrf_token()`` method. This differs only from
+``session.get_csrf_token()`` inasmuch as it clears any existing CSRF token,
+creates a new CSRF token, sets the token into the session, and returns the
+token.
+
+.. code-block:: python
+
+ token = request.session.new_csrf_token()
+
+
diff --git a/docs/narr/templates.rst b/docs/narr/templates.rst
index 437b823e9..7ef8e1923 100644
--- a/docs/narr/templates.rst
+++ b/docs/narr/templates.rst
@@ -628,7 +628,7 @@ application's configuration section, e.g.:
.. code-block:: ini
:linenos:
- [app:main]
+ [app:MyProject]
use = egg:MyProject#app
debug_templates = true
diff --git a/docs/narr/traversal.rst b/docs/narr/traversal.rst
index 2d7878265..7c6280ba1 100644
--- a/docs/narr/traversal.rst
+++ b/docs/narr/traversal.rst
@@ -3,33 +3,22 @@
Traversal
=========
-:term:`Traversal` provides an alternative to using :term:`URL dispatch` to
-map a URL to a :term:`view callable`. It is the act of locating a
-:term:`context` resource by walking over a :term:`resource tree`, starting
-from a :term:`root` resource, using a :term:`request` object as a source of
-path information. Once a context resource is found, a view callable is
-looked up and invoked.
-
-Using :term:`Traversal` to map a URL to code is optional. It is often less
-easy to understand than URL dispatch, so if you're a rank beginner, it
-probably makes sense to use URL dispatch to map URLs to code instead of
-traversal. In that case, you can skip this chapter.
-
-.. index::
- single: traversal overview
-
-A High-Level Overview of Traversal
-----------------------------------
-
A :term:`traversal` uses the URL (Universal Resource Locator) to find a
-:term:`resource`. This is done by mapping each segment of the path portion
-of the URL into a set of nested dictionary-like objects called the
-:term:`resource tree`. You might think of this as looking up files and
-directories in a file system. Traversal walks down the path until it finds a
-published "directory" or "file". The resource we find as the result of a
-traversal becomes the :term:`context`. A separate :term:`view lookup`
-subsystem is used to then find some view code willing "publish" the context
-resource.
+:term:`resource` located in a :term:`resource tree`, which is a set of
+nested dictionary-like objects. Traversal is done by using each segment
+of the path portion of the URL to navigate through the :term:`resource
+tree`. You might think of this as looking up files and directories in a
+file system. Traversal walks down the path until it finds a published
+resource, analogous to a file system "directory" or "file". The
+resource found as the result of a traversal becomes the
+:term:`context` of the :term:`request`. Then, the :term:`view lookup`
+subsystem is used to find some view code willing "publish" this
+resource by generating a :term:`response`.
+
+Using :term:`Traversal` to map a URL to code is optional. It is often
+less easy to understand than :term:`URL dispatch`, so if you're a rank
+beginner, it probably makes sense to use URL dispatch to map URLs to
+code instead of traversal. In that case, you can skip this chapter.
.. index::
single: traversal details
@@ -37,62 +26,65 @@ resource.
Traversal Details
-----------------
-:term:`Traversal` is dependent on information in a :term:`request` object.
-Every :term:`request` object contains URL path information in the
-``PATH_INFO`` portion of the :term:`WSGI` environment. The ``PATH_INFO``
-portion of the WSGI environment is the portion of a request's URL following
-the hostname and port number, but before any query string elements or
+:term:`Traversal` is dependent on information in a :term:`request`
+object. Every :term:`request` object contains URL path information in
+the ``PATH_INFO`` portion of the :term:`WSGI` environment. The
+``PATH_INFO`` string is the portion of a request's URL following the
+hostname and port number, but before any query string elements or
fragment element. For example the ``PATH_INFO`` portion of the URL
``http://example.com:8080/a/b/c?foo=1`` is ``/a/b/c``.
-Traversal treats the ``PATH_INFO`` segment of a URL as a sequence of path
-segments. For example, the ``PATH_INFO`` string ``/a/b/c`` is converted to
-the sequence ``['a', 'b', 'c']``.
+Traversal treats the ``PATH_INFO`` segment of a URL as a sequence of
+path segments. For example, the ``PATH_INFO`` string ``/a/b/c`` is
+converted to the sequence ``['a', 'b', 'c']``.
-After the path info is converted, a lookup is performed against the resource
-tree for each path segment. Each lookup uses the ``__getitem__`` method of a
-resource in the tree.
+This path sequence is then used to descend through the :term:`resource
+tree`, looking up a resource for each path segment. Each lookup uses the
+``__getitem__`` method of a resource in the tree.
For example, if the path info sequence is ``['a', 'b', 'c']``:
-- :term:`Traversal` pops the first element (``a``) from the path segment
- sequence and attempts to call the root resource's ``__getitem__`` method
- using that value (``a``) as an argument; we'll presume it succeeds.
+- :term:`Traversal` starts by acquiring the :term:`root` resource of the
+ application by calling the :term:`root factory`. The :term:`root factory`
+ can be configured to return whatever object is appropriate as the
+ traversal root of your application.
-- When the root resource's ``__getitem__`` succeeds it will return another
- resource, which we'll call "A". The :term:`context` temporarily becomes
- the "A" resource.
+- Next, the first element (``a``) is popped from the path segment
+ sequence and is used as a key to lookup the corresponding resource
+ in the root. This invokes the root resource's ``__getitem__`` method
+ using that value (``a``) as an argument.
+
+- If the root resource "contains" a resource with key ``a``, its
+ ``__getitem__`` method will return it. The :term:`context` temporarily
+ becomes the "A" resource.
- The next segment (``b``) is popped from the path sequence, and the "A"
resource's ``__getitem__`` is called with that value (``b``) as an
argument; we'll presume it succeeds.
-- When the "A" resource's ``__getitem__`` succeeds it will return another
- resource, which we'll call "B". The :term:`context` temporarily becomes
- the "B" resource.
-
-This process continues until the path segment sequence is exhausted or a path
-element cannot be resolved to a resource. In either case, a :term:`context`
-resource is chosen.
-
-Traversal "stops" when it either reaches a leaf level resource in your
-resource tree or when the path segments implied by the URL "run out". The
-resource that traversal "stops on" becomes the :term:`context`. If at any
-point during traversal any resource in the tree doesn't have a
-``__getitem__`` method, or if the ``__getitem__`` method of a resource raises
-a :exc:`KeyError`, traversal ends immediately, and that resource becomes the
-:term:`context`.
-
-The results of a :term:`traversal` also include a :term:`view name`. The
-:term:`view name` is the *first* URL path segment in the set of ``PATH_INFO``
-segments "left over" in the path segment list popped by the traversal process
-*after* traversal finds a context resource.
-
-The combination of the context resource and the :term:`view name` found via
-traversal is used later in the same request by a separate :app:`Pyramid`
-subsystem -- the :term:`view lookup` subsystem -- to find a :term:`view
-callable` later within the same request. How :app:`Pyramid` performs view
-lookup is explained within the :ref:`views_chapter` chapter.
+- The "A" resource's ``__getitem__`` returns another resource, which
+ we'll call "B". The :term:`context` temporarily becomes the "B"
+ resource.
+
+Traversal continues until the path segment sequence is exhausted or a
+path element cannot be resolved to a resource. In either case, the
+:term:`context` resource is the last object that the traversal
+successfully resolved. If any resource found during traversal lacks a
+``__getitem__`` method, or if its ``__getitem__`` method raises a
+:exc:`KeyError`, traversal ends immediately, and that resource becomes
+the :term:`context`.
+
+The results of a :term:`traversal` also include a :term:`view name`. If
+traversal ends before the path segment sequence is exhausted, the
+:term:`view name` is the *next* remaining path segment element. If the
+:term:`traversal` expends all of the path segments, then the :term:`view
+name` is the empty string (`''`).
+
+The combination of the context resource and the :term:`view name` found
+via traversal is used later in the same request by the :term:`view
+lookup` subsystem to find a :term:`view callable`. How :app:`Pyramid`
+performs view lookup is explained within the :ref:`view_config_chapter`
+chapter.
.. index::
single: object tree
@@ -104,19 +96,20 @@ lookup is explained within the :ref:`views_chapter` chapter.
The Resource Tree
-----------------
-When your application uses :term:`traversal` to resolve URLs to code, the
-application must supply a :term:`resource tree` to :app:`Pyramid`. The
-resource tree is a set of nested dictionary-like objects. The root of the
-tree is represented by a :term:`root` resource. The tree is effectively a
-nested set of dictionary-like objects.
+The resource tree is a set of nested dictionary-like resource objects
+that begins with a :term:`root` resource. In order to use
+:term:`traversal` to resolve URLs to code, your application must supply
+a :term:`resource tree` to :app:`Pyramid`.
-In order to supply a root resource for an application, at system startup
-time, the :app:`Pyramid` :term:`Router` is configured with a callback known
-as a :term:`root factory`. The root factory is supplied by the application
-developer as the ``root_factory`` argument to the application's
-:term:`Configurator`.
+In order to supply a root resource for an application the :app:`Pyramid`
+:term:`Router` is configured with a callback known as a :term:`root
+factory`. The root factory is supplied by the application, at startup
+time, as the ``root_factory`` argument to the :term:`Configurator`.
-Here's an example of a simple root factory:
+The root factory is a Python callable that accepts a :term:`request`
+object, and returns the root object of the :term:`resource tree`. A
+function, or class is typically used as an application's root factory.
+Here's an example of a simple root factory class:
.. code-block:: python
:linenos:
@@ -133,24 +126,23 @@ passing it to an instance of a :term:`Configurator` named ``config``:
config = Configurator(root_factory=Root)
-Using the ``root_factory`` argument to a :class:`pyramid.config.Configurator`
-constructor tells your :app:`Pyramid` application to call this root factory
-to generate a root resource whenever a request enters the application. This
-root factory is also known as the global root factory. A root factory can
-alternately be passed to the ``Configurator`` as a :term:`dotted Python name`
-which refers to a root factory defined in a different module.
-
-A root factory is passed a :term:`request` object and it is expected to
-return an object which represents the root of the resource tree. All
-:term:`traversal` will begin at this root resource. Usually a root factory
-for a traversal-based application will be more complicated than the above
-``Root`` class; in particular it may be associated with a database connection
-or another persistence mechanism.
+The ``root_factory`` argument to the
+:class:`pyramid.config.Configurator` constructor registers this root
+factory to be called to generate a root resource whenever a request
+enters the application. The root factory registered this way is also
+known as the global root factory. A root factory can alternately be
+passed to the ``Configurator`` as a :term:`dotted Python name` which can
+refer to a root factory defined in a different module.
If no :term:`root factory` is passed to the :app:`Pyramid`
-:term:`Configurator` constructor, or the ``root_factory`` is specified as the
-value ``None``, a *default* root factory is used. The default root factory
-always returns a resource that has no child resources.
+:term:`Configurator` constructor, or if the ``root_factory`` value
+specified is ``None``, a *default* root factory is used. The default
+root factory always returns a resource that has no child resources; it
+is effectively empty.
+
+Usually a root factory for a traversal-based application will be more
+complicated than the above ``Root`` class; in particular it may be
+associated with a database connection or another persistence mechanism.
.. sidebar:: Emulating the Default Root Factory
@@ -225,13 +217,14 @@ We'll provide a description of the algorithm, a diagram of how the algorithm
works, and some example traversal scenarios that might help you understand
how the algorithm operates against a specific resource tree.
-We'll also talk a bit about :term:`view lookup`. The :ref:`views_chapter`
-chapter discusses :term:`view lookup` in detail, and it is the canonical
-source for information about views. Technically, :term:`view lookup` is a
-:app:`Pyramid` subsystem that is separated from traversal entirely. However,
-we'll describe the fundamental behavior of view lookup in the examples in the
-next few sections to give you an idea of how traversal and view lookup
-cooperate, because they are almost always used together.
+We'll also talk a bit about :term:`view lookup`. The
+:ref:`view_config_chapter` chapter discusses :term:`view lookup` in
+detail, and it is the canonical source for information about views.
+Technically, :term:`view lookup` is a :app:`Pyramid` subsystem that is
+separated from traversal entirely. However, we'll describe the
+fundamental behavior of view lookup in the examples in the next few
+sections to give you an idea of how traversal and view lookup cooperate,
+because they are almost always used together.
.. index::
single: view name
@@ -469,7 +462,7 @@ References
A tutorial showing how :term:`traversal` can be used within a :app:`Pyramid`
application exists in :ref:`bfg_wiki_tutorial`.
-See the :ref:`views_chapter` chapter for detailed information about
+See the :ref:`view_config_chapter` chapter for detailed information about
:term:`view lookup`.
The :mod:`pyramid.traversal` module contains API functions that deal with
diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst
index 4c601340f..e64513a96 100644
--- a/docs/narr/urldispatch.rst
+++ b/docs/narr/urldispatch.rst
@@ -6,16 +6,28 @@
URL Dispatch
============
-:term:`URL dispatch` provides a simple way to map URLs :term:`view` code
-using a simple pattern matching language. An ordered set of patterns is
-checked one-by-one. If one of the patterns matches the path information
-associated with a request, a particular :term:`view callable` is invoked. If
-no route matches, :app:`Pyramid` falls back to trying to use
-:term:`traversal` to map the current request to a :term:`view callable`.
-
-The presence of calls to the :meth:`pyramid.config.Configurator.add_route`
-method within your application is a sign that you're using :term:`URL
-dispatch`.
+:term:`URL dispatch` provides a simple way to map URLs to :term:`view`
+code using a simple pattern matching language. An ordered set of
+patterns is checked one-by-one. If one of the patterns matches the path
+information associated with a request, a particular :term:`view
+callable` is invoked.
+
+:term:`URL dispatch` is one of two ways to perform :term:`resource
+location` in :app:`Pyramid`; the other way is using :term:`traversal`.
+If no route is matched using :term:`URL dispatch`, :app:`Pyramid` falls
+back to :term:`traversal` to handle the :term:`request`.
+
+It is the responsibility of the :term:`resource location` subsystem
+(i.e., :term:`URL dispatch` or :term:`traversal`) to find the resource
+object that is the :term:`context` of the :term:`request`. Once the
+:term:`context` is determined, :term:`view lookup` is then responsible
+for finding and invoking a :term:`view callable`. A view callable is a
+specific bit of code, defined in your application, that receives the
+:term:`request` and returns a :term:`response` object.
+
+Where appropriate, we will describe how view lookup interacts with
+:term:`resource location`. The :ref:`view_config_chapter` chapter describes
+the details of :term:`view lookup`.
High-Level Operational Overview
-------------------------------
@@ -24,10 +36,9 @@ If route configuration is present in an application, the :app:`Pyramid`
:term:`Router` checks every incoming request against an ordered set of URL
matching patterns present in a *route map*.
-If any route pattern matches the information in the :term:`request` provided
-to :app:`Pyramid`, :app:`Pyramid` will shortcut :term:`traversal`, and will
-invoke :term:`view lookup` using a :term:`context` resource generated by the
-route match.
+If any route pattern matches the information in the :term:`request`,
+:app:`Pyramid` will invoke :term:`view lookup` using a :term:`context`
+resource generated by the route match.
However, if no route pattern matches the information in the :term:`request`
provided to :app:`Pyramid`, it will fail over to using :term:`traversal` to
@@ -72,7 +83,7 @@ example:
.. versionchanged:: 1.0a4
Prior to 1.0a4, routes allow for a marker starting with a ``:``, for
- example ``/prefix/:one/:two``. Starting in 1.0a4, this style is deprecated
+ example ``/prefix/:one/:two``. This style is now deprecated
in favor or ``{}`` usage which allows for additional functionality.
.. index::
@@ -84,7 +95,7 @@ Route Configuration That Names a View Callable
When a route configuration declaration names a ``view`` attribute, the value
of the attribute will reference a :term:`view callable`. This view callable
will be invoked when the route matches. A view callable, as described in
-:ref:`views_chapter`, is developer-supplied code that "does stuff" as the
+:ref:`view_chapter`, is developer-supplied code that "does stuff" as the
result of a request. For more information about how to create view
callables, see :ref:`views_chapter`.
@@ -533,12 +544,12 @@ neither predicates nor view configuration information.
callables. Use custom predicates when no set of predefined predicates does
what you need. Custom predicates can be combined with predefined
predicates as necessary. Each custom predicate callable should accept two
- arguments: ``context`` and ``request`` and should return either ``True`` or
+ arguments: ``info`` and ``request`` and should return either ``True`` or
``False`` after doing arbitrary evaluation of the context resource and/or
the request. If all callables return ``True``, the associated route will
be considered viable for a given request. If any custom predicate returns
- ``False``, route matching continues. Note that the value ``context`` will
- always be ``None`` when passed to a custom route predicate.
+ ``False``, route matching continues. See :ref:`custom_route_predicates`
+ for more information.
**View-Related Arguments**
@@ -854,7 +865,8 @@ The ``mypackage.views`` module referred to above might look like so:
The view has access to the matchdict directly via the request, and can access
variables within it that match keys present as a result of the route pattern.
-See :ref:`views_chapter` for more information about views.
+See :ref:`views_chapter`, and :ref:`view_config_chapter` for more
+information about views.
Example 2
~~~~~~~~~
diff --git a/docs/narr/viewconfig.rst b/docs/narr/viewconfig.rst
new file mode 100644
index 000000000..f8b3fdb24
--- /dev/null
+++ b/docs/narr/viewconfig.rst
@@ -0,0 +1,986 @@
+.. _view_config_chapter:
+
+.. _view_configuration:
+
+View Configuration
+==================
+
+.. index::
+ single: view lookup
+
+:term:`View configuration` controls how :term:`view lookup` operates in
+your application. In earlier chapters, you have been exposed to a few
+simple view configuration declarations without much explanation. In this
+chapter we will explore the subject in detail.
+
+.. _view_lookup:
+
+View Lookup and Invocation
+--------------------------
+
+:term:`View lookup` is the :app:`Pyramid` subsystem responsible for finding
+an invoking a :term:`view callable`. The view lookup subsystem is passed a
+:term:`context` and a :term:`request` object.
+
+:term:`View configuration` information stored within in the
+:term:`application registry` is compared against the context and request by
+the view lookup subsystem in order to find the "best" view callable for the
+set of circumstances implied by the context and request.
+
+:term:`View predicate` attributes are an important part of view
+configuration that enables the :term:`View lookup` subsystem to find and
+invoke the appropriate view. Predicate attributes can be thought of
+like "narrowers". In general, the greater number of predicate
+attributes possessed by a view's configuration, the more specific the
+circumstances need to be before the registered view callable will be
+invoked.
+
+Mapping a Resource or URL Pattern to a View Callable
+----------------------------------------------------
+
+A developer makes a :term:`view callable` available for use within a
+:app:`Pyramid` application via :term:`view configuration`. A view
+configuration associates a view callable with a set of statements that
+determine the set of circumstances which must be true for the view callable
+to be invoked.
+
+A view configuration statement is made about information present in the
+:term:`context` resource and the :term:`request`.
+
+View configuration is performed in one of these ways:
+
+- by running a :term:`scan` against application source code which has a
+ :class:`pyramid.view.view_config` decorator attached to a Python object as
+ per :class:`pyramid.view.view_config` and
+ :ref:`mapping_views_using_a_decorator_section`.
+
+- by using the :meth:`pyramid.config.Configurator.add_view` method as per
+ :meth:`pyramid.config.Configurator.add_view` and
+ :ref:`mapping_views_using_imperative_config_section`.
+
+- By specifying a view within a :term:`route configuration`. View
+ configuration via a route configuration is performed by using the
+ :meth:`pyramid.config.Configurator.add_route` method, passing a ``view``
+ argument specifying a view callable.
+
+- by using the :meth:`pyramid.config.Configurator.add_handler` against a
+ :term:`view handler` class (useful only for :term:`URL dispatch`
+ applications).
+
+.. note:: You can also add view configuration by adding a ``<view>``,
+ ``<route>`` or ``<handler>`` declaration to :term:`ZCML` used by your
+ application as per :ref:`mapping_views_using_zcml_section`,
+ :ref:`view_directive`, :ref:`route_directive` or :ref:`handler_directive`.
+
+.. _view_configuration_parameters:
+
+View Configuration Parameters
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+All forms of view configuration accept the same general types of arguments.
+
+Many arguments supplied during view configuration are :term:`view predicate`
+arguments. View predicate arguments used during view configuration are used
+to narrow the set of circumstances in which :term:`view lookup` will find a
+particular view callable.
+
+In general, the fewer number of predicates which are supplied to a
+particular view configuration, the more likely it is that the associated
+view callable will be invoked. The greater the number supplied, the
+less likely. A view with five predicates will always be found and
+evaluated before a view with two, for example. All predicates must
+match for the associated view to be called.
+
+This does not mean however, that :app:`Pyramid` "stops looking" when it
+finds a view registration with predicates that don't match. If one set
+of view predicates does not match, the "next most specific" view (if
+any) is consulted for predicates, and so on, until a view is found, or
+no view can be matched up with the request. The first view with a set
+of predicates all of which match the request environment will be
+invoked.
+
+If no view can be found with predicates which allow it to be matched up with
+the request, :app:`Pyramid` will return an error to the user's browser,
+representing a "not found" (404) page. See :ref:`changing_the_notfound_view`
+for more information about changing the default notfound view.
+
+Some view configuration arguments are non-predicate arguments. These tend to
+modify the response of the view callable or prevent the view callable from
+being invoked due to an authorization policy. The presence of non-predicate
+arguments in a view configuration does not narrow the circumstances in which
+the view callable will be invoked.
+
+Non-Predicate Arguments
++++++++++++++++++++++++
+
+``permission``
+ The name of a :term:`permission` that the user must possess in order to
+ invoke the :term:`view callable`. See :ref:`view_security_section` for
+ more information about view security and permissions.
+
+ If ``permission`` is not supplied, no permission is registered for this
+ view (it's accessible by any caller).
+
+``attr``
+ The view machinery defaults to using the ``__call__`` method of the
+ :term:`view callable` (or the function itself, if the view callable is a
+ function) to obtain a response. The ``attr`` value allows you to vary the
+ method attribute used to obtain the response. For example, if your view
+ was a class, and the class has a method named ``index`` and you wanted to
+ use this method instead of the class' ``__call__`` method to return the
+ response, you'd say ``attr="index"`` in the view configuration for the
+ view. This is most useful when the view definition is a class.
+
+ If ``attr`` is not supplied, ``None`` is used (implying the function itself
+ if the view is a function, or the ``__call__`` callable attribute if the
+ view is a class).
+
+``renderer``
+ Denotes the :term:`renderer` implementation which will be used to construct
+ a :term:`response` from the associated view callable's return value. (see
+ also :ref:`renderers_chapter`).
+
+ This is either a single string term (e.g. ``json``) or a string implying a
+ path or :term:`asset specification` (e.g. ``templates/views.pt``) naming a
+ :term:`renderer` implementation. If the ``renderer`` value does not
+ contain a dot (``.``), the specified string will be used to look up a
+ renderer implementation, and that renderer implementation will be used to
+ construct a response from the view return value. If the ``renderer`` value
+ contains a dot (``.``), the specified term will be treated as a path, and
+ the filename extension of the last element in the path will be used to look
+ up the renderer implementation, which will be passed the full path.
+
+ When the renderer is a path, although a path is usually just a simple
+ relative pathname (e.g. ``templates/foo.pt``, implying that a template
+ named "foo.pt" is in the "templates" directory relative to the directory of
+ the current :term:`package`), a path can be absolute, starting with a slash
+ on UNIX or a drive letter prefix on Windows. The path can alternately be a
+ :term:`asset specification` in the form
+ ``some.dotted.package_name:relative/path``, making it possible to address
+ template assets which live in a separate package.
+
+ The ``renderer`` attribute is optional. If it is not defined, the "null"
+ renderer is assumed (no rendering is performed and the value is passed back
+ to the upstream :app:`Pyramid` machinery unmolested). Note that if the
+ view callable itself returns a :term:`response` (see :ref:`the_response`),
+ the specified renderer implementation is never called.
+
+``wrapper``
+ The :term:`view name` of a different :term:`view configuration` which will
+ receive the response body of this view as the ``request.wrapped_body``
+ attribute of its own :term:`request`, and the :term:`response` returned by
+ this view as the ``request.wrapped_response`` attribute of its own request.
+ Using a wrapper makes it possible to "chain" views together to form a
+ composite response. The response of the outermost wrapper view will be
+ returned to the user. The wrapper view will be found as any view is found:
+ see :ref:`view_lookup`. The "best" wrapper view will be found based on the
+ lookup ordering: "under the hood" this wrapper view is looked up via
+ ``pyramid.view.render_view_to_response(context, request,
+ 'wrapper_viewname')``. The context and request of a wrapper view is the
+ same context and request of the inner view.
+
+ If ``wrapper`` is not supplied, no wrapper view is used.
+
+``decorator``
+ A :term:`dotted Python name` to function (or the function itself) which
+ will be used to decorate the registered :term:`view callable`. The
+ decorator function will be called with the view callable as a single
+ argument. The view callable it is passed will accept ``(context,
+ request)``. The decorator must return a replacement view callable which
+ also accepts ``(context, request)``.
+
+``mapper``
+ A Python object or :term:`dotted Python name` which refers to a :term:`view
+ mapper`, or ``None``. By default it is ``None``, which indicates that the
+ view should use the default view mapper. This plug-point is useful for
+ Pyramid extension developers, but it's not very useful for 'civilians' who
+ are just developing stock Pyramid applications. Pay no attention to the man
+ behind the curtain.
+
+Predicate Arguments
++++++++++++++++++++
+
+These arguments modify view lookup behavior. In general, the more predicate
+arguments that are supplied, the more specific, and narrower the usage of the
+configured view.
+
+``name``
+ The :term:`view name` required to match this view callable. Read
+ :ref:`traversal_chapter` to understand the concept of a view name.
+
+ If ``name`` is not supplied, the empty string is used (implying the default
+ view).
+
+``context``
+ An object representing a Python class that the :term:`context` resource
+ must be an instance of *or* the :term:`interface` that the :term:`context`
+ resource must provide in order for this view to be found and called. This
+ predicate is true when the :term:`context` resource is an instance of the
+ represented class or if the :term:`context` resource provides the
+ represented interface; it is otherwise false.
+
+ If ``context`` is not supplied, the value ``None``, which matches any
+ resource, is used.
+
+``route_name``
+ If ``route_name`` is supplied, the view callable will be invoked only when
+ the named route has matched.
+
+ This value must match the ``name`` of a :term:`route configuration`
+ declaration (see :ref:`urldispatch_chapter`) that must match before this
+ view will be called. Note that the ``route`` configuration referred to by
+ ``route_name`` will usually have a ``*traverse`` token in the value of its
+ ``pattern``, representing a part of the path that will be used by
+ :term:`traversal` against the result of the route's :term:`root factory`.
+
+ If ``route_name`` is not supplied, the view callable will be have a chance
+ of being invoked if no other route was matched. This is when the
+ request/context pair found via :term:`resource location` does not indicate
+ it matched any configured route.
+
+``request_type``
+ This value should be an :term:`interface` that the :term:`request` must
+ provide in order for this view to be found and called.
+
+ If ``request_type`` is not supplied, the value ``None`` is used, implying
+ any request type.
+
+ *This is an advanced feature, not often used by "civilians"*.
+
+``request_method``
+ This value can either be one of the strings ``GET``, ``POST``, ``PUT``,
+ ``DELETE``, or ``HEAD`` representing an HTTP ``REQUEST_METHOD``. A view
+ declaration with this argument ensures that the view will only be called
+ when the request's ``method`` attribute (aka the ``REQUEST_METHOD`` of the
+ WSGI environment) string matches the supplied value.
+
+ If ``request_method`` is not supplied, the view will be invoked regardless
+ of the ``REQUEST_METHOD`` of the :term:`WSGI` environment.
+
+``request_param``
+ This value can be any string. A view declaration with this argument
+ ensures that the view will only be called when the :term:`request` has a
+ key in the ``request.params`` dictionary (an HTTP ``GET`` or ``POST``
+ variable) that has a name which matches the supplied value.
+
+ If the value supplied has a ``=`` sign in it,
+ e.g. ``request_params="foo=123"``, then the key (``foo``) must both exist
+ in the ``request.params`` dictionary, *and* the value must match the right
+ hand side of the expression (``123``) for the view to "match" the current
+ request.
+
+ If ``request_param`` is not supplied, the view will be invoked without
+ consideration of keys and values in the ``request.params`` dictionary.
+
+``containment``
+ This value should be a reference to a Python class or :term:`interface`
+ that a parent object in the context resource's :term:`lineage` must provide
+ in order for this view to be found and called. The resources in your
+ resource tree must be "location-aware" to use this feature.
+
+ If ``containment`` is not supplied, the interfaces and classes in the
+ lineage are not considered when deciding whether or not to invoke the view
+ callable.
+
+ See :ref:`location_aware` for more information about location-awareness.
+
+``xhr``
+ This value should be either ``True`` or ``False``. If this value is
+ specified and is ``True``, the :term:`WSGI` environment must possess an
+ ``HTTP_X_REQUESTED_WITH`` (aka ``X-Requested-With``) header that has the
+ value ``XMLHttpRequest`` for the associated view callable to be found and
+ called. This is useful for detecting AJAX requests issued from jQuery,
+ Prototype and other Javascript libraries.
+
+ If ``xhr`` is not specified, the ``HTTP_X_REQUESTED_WITH`` HTTP header is
+ not taken into consideration when deciding whether or not to invoke the
+ associated view callable.
+
+``accept``
+ The value of this argument represents a match query for one or more
+ mimetypes in the ``Accept`` HTTP request header. If this value is
+ specified, it must be in one of the following forms: a mimetype match token
+ in the form ``text/plain``, a wildcard mimetype match token in the form
+ ``text/*`` or a match-all wildcard mimetype match token in the form
+ ``*/*``. If any of the forms matches the ``Accept`` header of the request,
+ this predicate will be true.
+
+ If ``accept`` is not specified, the ``HTTP_ACCEPT`` HTTP header is not
+ taken into consideration when deciding whether or not to invoke the
+ associated view callable.
+
+``header``
+ This value represents an HTTP header name or a header name/value pair.
+
+ If ``header`` is specified, it must be a header name or a
+ ``headername:headervalue`` pair.
+
+ If ``header`` is specified without a value (a bare header name only,
+ e.g. ``If-Modified-Since``), the view will only be invoked if the HTTP
+ header exists with any value in the request.
+
+ If ``header`` is specified, and possesses a name/value pair
+ (e.g. ``User-Agent:Mozilla/.*``), the view will only be invoked if the HTTP
+ header exists *and* the HTTP header matches the value requested. When the
+ ``headervalue`` contains a ``:`` (colon), it will be considered a
+ name/value pair (e.g. ``User-Agent:Mozilla/.*`` or ``Host:localhost``).
+ The value portion should be a regular expression.
+
+ Whether or not the value represents a header name or a header name/value
+ pair, the case of the header name is not significant.
+
+ If ``header`` is not specified, the composition, presence or absence of
+ HTTP headers is not taken into consideration when deciding whether or not
+ to invoke the associated view callable.
+
+``path_info``
+ This value represents a regular expression pattern that will be tested
+ against the ``PATH_INFO`` WSGI environment variable to decide whether or
+ not to call the associated view callable. If the regex matches, this
+ predicate will be ``True``.
+
+ If ``path_info`` is not specified, the WSGI ``PATH_INFO`` is not taken into
+ consideration when deciding whether or not to invoke the associated view
+ callable.
+
+``custom_predicates``
+ If ``custom_predicates`` is specified, it must be a sequence of references
+ to custom predicate callables. Use custom predicates when no set of
+ predefined predicates do what you need. Custom predicates can be combined
+ with predefined predicates as necessary. Each custom predicate callable
+ should accept two arguments: ``context`` and ``request`` and should return
+ either ``True`` or ``False`` after doing arbitrary evaluation of the
+ context resource and/or the request. If all callables return ``True``, the
+ associated view callable will be considered viable for a given request.
+
+ If ``custom_predicates`` is not specified, no custom predicates are
+ used.
+
+.. index::
+ single: view_config decorator
+
+.. _mapping_views_using_a_decorator_section:
+
+View Configuration Using the ``@view_config`` Decorator
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For better locality of reference, you may use the
+:class:`pyramid.view.view_config` decorator to associate your view functions
+with URLs instead of using :term:`ZCML` or imperative configuration for the
+same purpose.
+
+.. warning::
+
+ Using this feature tends to slows down application startup slightly, as
+ more work is performed at application startup to scan for view
+ declarations.
+
+Usage of the ``view_config`` decorator is a form of :term:`declarative
+configuration`, like ZCML, but in decorator form.
+:class:`pyramid.view.view_config` can be used to associate :term:`view
+configuration` information -- as done via the equivalent imperative code or
+ZCML -- with a function that acts as a :app:`Pyramid` view callable. All
+arguments to the :meth:`pyramid.config.Configurator.add_view` method (save
+for the ``view`` argument) are available in decorator form and mean precisely
+the same thing.
+
+An example of the :class:`pyramid.view.view_config` decorator might reside in
+a :app:`Pyramid` application module ``views.py``:
+
+.. ignore-next-block
+.. code-block:: python
+ :linenos:
+
+ from resources import MyResource
+ from pyramid.view import view_config
+ from pyramid.response import Response
+
+ @view_config(name='my_view', request_method='POST', context=MyResource,
+ permission='read')
+ def my_view(request):
+ return Response('OK')
+
+Using this decorator as above replaces the need to add this imperative
+configuration stanza:
+
+.. ignore-next-block
+.. code-block:: python
+ :linenos:
+
+ config.add_view('.views.my_view', name='my_view', request_method='POST',
+ context=MyResource, permission='read')
+
+All arguments to ``view_config`` may be omitted. For example:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.response import Response
+ from pyramid.view import view_config
+
+ @view_config()
+ def my_view(request):
+ """ My view """
+ return Response()
+
+Such a registration as the one directly above implies that the view name will
+be ``my_view``, registered with a ``context`` argument that matches any
+resource type, using no permission, registered against requests with any
+request method, request type, request param, route name, or containment.
+
+The mere existence of a ``@view_config`` decorator doesn't suffice to perform
+view configuration. All that the decorator does is "annotate" the function
+with your configuration declarations, it doesn't process them. To make
+:app:`Pyramid` process your :class:`pyramid.view.view_config` declarations,
+you *must* do use the ``scan`` method of a
+:class:`pyramid.config.Configurator`:
+
+.. code-block:: python
+ :linenos:
+
+ # config is assumed to be an instance of the
+ # pyramid.config.Configurator class
+ config.scan()
+
+.. note:: See :ref:`zcml_scanning` for information about how to invoke a scan
+ via ZCML (if you're not using imperative configuration).
+
+Please see :ref:`decorations_and_code_scanning` for detailed information
+about what happens when code is scanned for configuration declarations
+resulting from use of decorators like :class:`pyramid.view.view_config`.
+
+See :ref:`configuration_module` for additional API arguments to the
+:meth:`pyramid.config.Configurator.scan` method. For example, the method
+allows you to supply a ``package`` argument to better control exactly *which*
+code will be scanned.
+
+``@view_config`` Placement
+++++++++++++++++++++++++++
+
+A :class:`pyramid.view.view_config` decorator can be placed in various points
+in your application.
+
+If your view callable is a function, it may be used as a function decorator:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.view import view_config
+ from pyramid.response import Response
+
+ @view_config(name='edit')
+ def edit(request):
+ return Response('edited!')
+
+If your view callable is a class, the decorator can also be used as a class
+decorator in Python 2.6 and better (Python 2.5 and below do not support class
+decorators). All the arguments to the decorator are the same when applied
+against a class as when they are applied against a function. For example:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.response import Response
+ from pyramid.view import view_config
+
+ @view_config()
+ class MyView(object):
+ def __init__(self, request):
+ self.request = request
+
+ def __call__(self):
+ return Response('hello')
+
+You can use the :class:`pyramid.view.view_config` decorator as a simple
+callable to manually decorate classes in Python 2.5 and below without the
+decorator syntactic sugar, if you wish:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.response import Response
+ from pyramid.view import view_config
+
+ class MyView(object):
+ def __init__(self, request):
+ self.request = request
+
+ def __call__(self):
+ return Response('hello')
+
+ my_view = view_config()(MyView)
+
+More than one :class:`pyramid.view.view_config` decorator can be stacked on
+top of any number of others. Each decorator creates a separate view
+registration. For example:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.view import view_config
+ from pyramid.response import Response
+
+ @view_config(name='edit')
+ @view_config(name='change')
+ def edit(request):
+ return Response('edited!')
+
+This registers the same view under two different names.
+
+The decorator can also be used against class methods:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.response import Response
+ from pyramid.view import view_config
+
+ class MyView(object):
+ def __init__(self, request):
+ self.request = request
+
+ @view_config(name='hello')
+ def amethod(self):
+ return Response('hello')
+
+When the decorator is used against a class method, a view is registered for
+the *class*, so the class constructor must accept an argument list in one of
+two forms: either it must accept a single argument ``request`` or it must
+accept two arguments, ``context, request``.
+
+The method which is decorated must return a :term:`response`.
+
+Using the decorator against a particular method of a class is equivalent to
+using the ``attr`` parameter in a decorator attached to the class itself.
+For example, the above registration implied by the decorator being used
+against the ``amethod`` method could be spelled equivalently as the below:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.response import Response
+ from pyramid.view import view_config
+
+ @view_config(attr='amethod', name='hello')
+ class MyView(object):
+ def __init__(self, request):
+ self.request = request
+
+ def amethod(self):
+ return Response('hello')
+
+.. index::
+ single: add_view
+
+.. _mapping_views_using_imperative_config_section:
+
+View Registration Using :meth:`~pyramid.config.Configurator.add_view`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :meth:`pyramid.config.Configurator.add_view` method within
+:ref:`configuration_module` is used to configure a view imperatively. The
+arguments to this method are very similar to the arguments that you provide
+to the ``@view_config`` decorator. For example:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.response import Response
+
+ def hello_world(request):
+ return Response('hello!')
+
+ # config is assumed to be an instance of the
+ # pyramid.config.Configurator class
+ config.add_view(hello_world, name='hello.html')
+
+The first argument, ``view``, is required. It must either be a Python object
+which is the view itself or a :term:`dotted Python name` to such an object.
+All other arguments are optional. See
+:meth:`pyramid.config.Configurator.add_view` for more information.
+
+.. _using_add_handler:
+
+Handler Registration Using :meth:`~pyramid.config.Configurator.add_handler`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:app:`Pyramid` provides the special concept of a :term:`view handler`. View
+handlers are view classes that implement a number of methods, each of which
+is a :term:`view callable` as a convenience for :term:`URL dispatch` users.
+
+.. note::
+
+ View handlers are *not* useful when using :term:`traversal`, only when using
+ :term:`url dispatch`.
+
+Using a view handler instead of a plain function or class :term:`view
+callable` makes it unnecessary to call
+:meth:`pyramid.config.Configurator.add_route` (and/or
+:meth:`pyramid.config.Configurator.add_view`) "by hand" multiple times,
+making it more pleasant to register a collection of views as a single class
+when using :term:`url dispatch`. The view handler machinery also introduces
+the concept of an ``action``, which is used as a :term:`view predicate` to
+control which method of the handler is called. The method name is the
+default *action name* of a handler view callable.
+
+The concept of a view handler is analogous to a "controller" in Pylons 1.0.
+
+The view handler class is initialized by :app:`Pyramid` in the same manner as
+a "plain" view class. Its ``__init__`` is called with a request object (see
+:ref:`class_as_view`). It implements methods, each of which is a :term:`view
+callable`. When a request enters the system which corresponds with an
+*action* related to one of its view callable methods, this method is called,
+and it is expected to return a response.
+
+Here's an example view handler class:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.response import Response
+
+ from pyramid.view import action
+
+ class Hello(object):
+ def __init__(self, request):
+ self.request = request
+
+ def index(self):
+ return Response('Hello world!')
+
+ @action(renderer="mytemplate.mak")
+ def bye(self):
+ return {}
+
+The :class:`pyramid.view.action` decorator is used to fine-tune the view
+parameters for each potential view callable which is a method of the handler.
+
+Handlers are added to application configuration via the
+:meth:`pyramid.config.Configurator.add_handler` API. The
+:meth:`~pyramid.config.Configurator.add_handler` method will scan a
+:term:`view handler` class and automatically set up view configurations for
+its methods that represent "auto-exposed" view callable, or those that were
+decorated explicitly with the :class:`~pyramid.view.action` decorator. This
+decorator is used to setup additional view configuration information for
+individual methods of the class, and can be used repeatedly for a single view
+method to register multiple view configurations for it.
+
+.. code-block:: python
+ :linenos:
+
+ from myapp.handlers import Hello
+ config.add_handler('hello', '/hello/{action}', handler=Hello)
+
+This example will result in a route being added for the pattern
+``/hello/{action}``, and each method of the ``Hello`` class will then be
+examined to see if it should be registered as a potential view callable when
+the ``/hello/{action}`` pattern matches. The value of ``{action}`` in the
+route pattern will be used to determine which view should be called, and each
+view in the class will be setup with a view predicate that requires a
+specific ``action`` name. By default, the action name for a method of a
+handler is the method name.
+
+If the URL was ``/hello/index``, the above example pattern would match, and,
+by default, the ``index`` method of the ``Hello`` class would be called.
+
+Alternatively, the action can be declared specifically for a URL to be
+registered for a *specific* ``action`` name:
+
+.. code-block:: python
+ :linenos:
+
+ from myapp.handlers import Hello
+ config.add_handler('hello_index', '/hello/index',
+ handler=Hello, action='index')
+
+This will result one of the methods that are configured for the ``action`` of
+'index' in the ``Hello`` handler class to be called. In this case the name of
+the method is the same as the action name: ``index``. However, this need not
+be the case, as we will see below.
+
+When calling :meth:`~pyramid.config.Configurator.add_handler`, an ``action``
+is required in either the route pattern or as a keyword argument, but
+**cannot appear in both places**. A ``handler`` argument must also be
+supplied, which can be either a :term:`asset specification` or a Python
+reference to the handler class. Additional keyword arguments are passed
+directly through to :meth:`pyramid.config.Configurator.add_route`.
+
+For example:
+
+.. code-block:: python
+ :linenos:
+
+ config.add_handler('hello', '/hello/{action}',
+ handler='mypackage.handlers.MyHandler')
+
+Multiple :meth:`~pyramid.config.Configurator.add_handler` calls can specify
+the same handler, to register specific route names for different
+handler/action combinations. For example:
+
+.. code-block:: python
+ :linenos:
+
+ config.add_handler('hello_index', '/hello/index',
+ handler=Hello, action='index')
+ config.add_handler('bye_index', '/hello/bye',
+ handler=Hello, action='bye')
+
+.. note::
+
+ Handler configuration may also be added to the system via :term:`ZCML` (see
+ :ref:`zcml_handler_configuration`).
+
+View Setup in the Handler Class
++++++++++++++++++++++++++++++++
+
+A handler class can have a single class level attribute called
+``__autoexpose__`` which should be a regular expression or the value
+``None``. It's used to determine which method names will result in additional
+view configurations being registered.
+
+When :meth:`~pyramid.config.Configurator.add_handler` runs, every method in
+the handler class will be searched and a view registered if the method name
+matches the ``__autoexpose__`` regular expression, or if the method was
+decorated with :class:`~pyramid.view.action`.
+
+Every method in the handler class that has a name meeting the
+``__autoexpose__`` regular expression will have a view registered for an
+``action`` name corresponding to the method name. This functionality can be
+disabled by setting the ``__autoexpose__`` attribute to ``None``:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.view import action
+
+ class Hello(object):
+ __autoexpose__ = None
+
+ def __init__(self, request):
+ self.request = request
+
+ @action()
+ def index(self):
+ return Response('Hello world!')
+
+ @action(renderer="mytemplate.mak")
+ def bye(self):
+ return {}
+
+With auto-expose effectively disabled, no views will be registered for a
+method unless it is specifically decorated with
+:class:`~pyramid.view.action`.
+
+Action Decorators in a Handler
+++++++++++++++++++++++++++++++
+
+The :class:`~pyramid.view.action` decorator registers view configuration
+information on the handler method, which is used by
+:meth:`~pyramid.config.Configurator.add_handler` to setup the view
+configuration.
+
+All keyword arguments are recorded, and passed to
+:meth:`~pyramid.config.Configurator.add_view`. Any valid keyword arguments
+for :meth:`~pyramid.config.Configurator.add_view` can thus be used with the
+:class:`~pyramid.view.action` decorator to further restrict when the view
+will be called.
+
+One important difference is that a handler method can respond to an
+``action`` name that is different from the method name by passing in a
+``name`` argument.
+
+Example:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.view import action
+
+ class Hello(object):
+ def __init__(self, request):
+ self.request = request
+
+ @action(name='index', renderer='created.mak', request_method='POST')
+ def create(self):
+ return {}
+
+ @action(renderer="view_all.mak", request_method='GET')
+ def index(self):
+ return {}
+
+This will register two views that require the ``action`` to be ``index``,
+with the additional view predicate requiring a specific request method.
+
+It can be useful to decorate a single method multiple times with
+:class:`~pyramid.view.action`. Each action decorator will register a new view
+for the method. By specifying different names and renderers for each action,
+the same view logic can be exposed and rendered differently on multiple URLs.
+
+Example:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.view import action
+
+ class Hello(object):
+ def __init__(self, request):
+ self.request = request
+
+ @action(name='home', renderer='home.mak')
+ @action(name='about', renderer='about.mak')
+ def show_template(self):
+ # prep some template vars
+ return {}
+
+ # in the config
+ config.add_handler('hello', '/hello/{action}', handler=Hello)
+
+With this configuration, the url ``/hello/home`` will find a view
+configuration that results in calling the ``show_template`` method, then
+rendering the template with ``home.mak``, and the url ``/hello/about`` will
+call the same method and render the ``about.mak`` template.
+
+.. index::
+ single: resource interfaces
+
+.. _using_resource_interfaces:
+
+Using Resource Interfaces In View Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Instead of registering your views with a ``context`` that names a Python
+resource *class*, you can optionally register a view callable with a
+``context`` which is an :term:`interface`. An interface can be attached
+arbitrarily to any resource object. View lookup treats context interfaces
+specially, and therefore the identity of a resource can be divorced from that
+of the class which implements it. As a result, associating a view with an
+interface can provide more flexibility for sharing a single view between two
+or more different implementations of a resource type. For example, if two
+resource objects of different Python class types share the same interface,
+you can use the same view configuration to specify both of them as a
+``context``.
+
+In order to make use of interfaces in your application during view dispatch,
+you must create an interface and mark up your resource classes or instances
+with interface declarations that refer to this interface.
+
+To attach an interface to a resource *class*, you define the interface and
+use the :func:`zope.interface.implements` function to associate the interface
+with the class.
+
+.. code-block:: python
+ :linenos:
+
+ from zope.interface import Interface
+ from zope.interface import implements
+
+ class IHello(Interface):
+ """ A marker interface """
+
+ class Hello(object):
+ implements(IHello)
+
+To attach an interface to a resource *instance*, you define the interface and
+use the :func:`zope.interface.alsoProvides` function to associate the
+interface with the instance. This function mutates the instance in such a
+way that the interface is attached to it.
+
+.. code-block:: python
+ :linenos:
+
+ from zope.interface import Interface
+ from zope.interface import alsoProvides
+
+ class IHello(Interface):
+ """ A marker interface """
+
+ class Hello(object):
+ pass
+
+ def make_hello():
+ hello = Hello()
+ alsoProvides(hello, IHello)
+ return hello
+
+Regardless of how you associate an interface, with a resource instance, or a
+resource class, the resulting code to associate that interface with a view
+callable is the same. Assuming the above code that defines an ``IHello``
+interface lives in the root of your application, and its module is named
+"resources.py", the interface declaration below will associate the
+``mypackage.views.hello_world`` view with resources that implement, or
+provide, this interface.
+
+.. code-block:: python
+ :linenos:
+
+ # config is an instance of pyramid.config.Configurator
+
+ config.add_view('mypackage.views.hello_world', name='hello.html',
+ context='mypackage.resources.IHello')
+
+Any time a resource that is determined to be the :term:`context` provides
+this interface, and a view named ``hello.html`` is looked up against it as
+per the URL, the ``mypackage.views.hello_world`` view callable will be
+invoked.
+
+Note, in cases where a view is registered against a resource class, and a
+view is also registered against an interface that the resource class
+implements, an ambiguity arises. Views registered for the resource class take
+precedence over any views registered for any interface the resource class
+implements. Thus, if one view configuration names a ``context`` of both the
+class type of a resource, and another view configuration names a ``context``
+of interface implemented by the resource's class, and both view
+configurations are otherwise identical, the view registered for the context's
+class will "win".
+
+For more information about defining resources with interfaces for use within
+view configuration, see :ref:`resources_which_implement_interfaces`.
+
+.. index::
+ single: view security
+ pair: security; view
+
+.. _view_security_section:
+
+Configuring View Security
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If an :term:`authorization policy` is active, any :term:`permission` attached
+to a :term:`view configuration` found during view lookup will be verified.
+This will ensure that the currently authenticated user possesses that
+permission against the :term:`context` resource before the view function is
+actually called. Here's an example of specifying a permission in a view
+configuration using :meth:`pyramid.config.Configurator.add_view`:
+
+.. code-block:: python
+ :linenos:
+
+ # config is an instance of pyramid.config.Configurator
+
+ config.add_view('myproject.views.add_entry', name='add.html',
+ context='myproject.resources.IBlog', permission='add')
+
+When an :term:`authorization policy` is enabled, this view will be protected
+with the ``add`` permission. The view will *not be called* if the user does
+not possess the ``add`` permission relative to the current :term:`context`.
+Instead the :term:`forbidden view` result will be returned to the client as
+per :ref:`protecting_views`.
+
+.. index::
+ single: debugging not found errors
+ single: not found error (debugging)
+
+.. _debug_notfound_section:
+
+:exc:`NotFound` Errors
+~~~~~~~~~~~~~~~~~~~~~~
+
+It's useful to be able to debug :exc:`NotFound` error responses when they
+occur unexpectedly due to an application registry misconfiguration. To debug
+these errors, use the ``PYRAMID_DEBUG_NOTFOUND`` environment variable or the
+``debug_notfound`` configuration file setting. Details of why a view was not
+found will be printed to ``stderr``, and the browser representation of the
+error will include the same information. See :ref:`environment_chapter` for
+more information about how, and where to set these values.
+
diff --git a/docs/narr/views.rst b/docs/narr/views.rst
index ad28e48d4..e8cf2f83c 100644
--- a/docs/narr/views.rst
+++ b/docs/narr/views.rst
@@ -17,11 +17,12 @@ request made to your application.
that implements a view *callable*, and the process of view
*lookup*.
-The chapter :ref:`resourcelocation_chapter` describes how, using information
-from the :term:`request`, a :term:`context` resource is computed. But the
-context resource itself isn't very useful without an associated :term:`view
-callable`. A view callable returns a response to a user, often using the
-context resource to do so.
+The :ref:`urldispatch_chapter`, and :ref:`traversal_chapter` chapters
+describes how, using information from the :term:`request`, a
+:term:`context` resource is computed. But the context resource itself
+isn't very useful without an associated :term:`view callable`. A view
+callable returns a response to a user, often using the context resource
+to do so.
The job of actually locating and invoking the "best" :term:`view callable` is
the job of the :term:`view lookup` subsystem. The view lookup subsystem
@@ -30,31 +31,34 @@ in the :term:`request` against :term:`view configuration` statements made by
the developer to choose the most appropriate view callable for a specific
set of circumstances.
-This chapter provides documentation detailing the process of creating
-view callables, documentation about performing view configuration, and
-a detailed explanation of view lookup.
+This chapter describes how view callables work. In the
+:ref:`view_config_chapter` chapter, there are details about performing
+view configuration, and a detailed explanation of view lookup.
View Callables
--------------
-No matter how a view callable is eventually found, all view callables
-used by :app:`Pyramid` must be constructed in the same way, and
-must return the same kind of return value.
-
-Most view callables accept a single argument named ``request``. This
-argument represents a :app:`Pyramid` :term:`Request` object. A request
-object encapsulates a WSGI environment as represented to :app:`Pyramid` by
-the upstream :term:`WSGI` server.
-
-In general, a view callable must return a :mod:`Pyramid` :term:`Response`
-object.
-
-.. note:: The above statement, though it sounds definitive, isn't always
- true. See :ref:`renderers_chapter` for information related to using a
- :term:`renderer` to convert a non-Response view callable return value into
- a Response object.
-
-View callables can be functions, instances, or classes.
+View callables are, at the risk of sounding obvious, callable Python
+objects. Specifically, view callables can be functions, classes, or
+instances that implement an ``__call__`` method (making the
+instance callable).
+
+View callables must, at a minimum, accept a single argument named
+``request``. This argument represents a :app:`Pyramid` :term:`Request`
+object. A request object encapsulates a WSGI environment provided to
+:app:`Pyramid` by the upstream :term:`WSGI` server. As you might expect,
+the request object contains everything your application needs to know
+about the specific HTTP request being made.
+
+A view callable's ultimate responsibility is to create a :mod:`Pyramid`
+:term:`Response` object. This can be done by creating the response
+object in the view callable code and returning it directly, as we will
+be doing in this chapter. However, if a view callable does not return a
+response itself, it can be configured to use a :term:`renderer` that
+converts its return value into a :term:`Response` object. Using
+renderers is the common way that templates are used with view callables
+to generate markup. See the :ref:`renderers_chapter` chapter for
+details.
.. index::
single: view calling convention
@@ -63,7 +67,7 @@ View callables can be functions, instances, or classes.
.. _function_as_view:
Defining a View Callable as a Function
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+--------------------------------------
One of the easiest way to define a view callable is to create a function that
accepts a single argument named ``request``, and which returns a
@@ -85,7 +89,7 @@ implemented as a function:
.. _class_as_view:
Defining a View Callable as a Class
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-----------------------------------
A view callable may also be represented by a Python class instead of a
function. When a view callable is a class, the calling semantics are
@@ -135,7 +139,7 @@ method expected to return a response, you can either:
.. _request_and_context_view_definitions:
Alternate View Callable Argument/Calling Conventions
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+----------------------------------------------------
Usually, view callables are defined to accept only a single argument:
``request``. However, view callables may alternately be defined as classes,
@@ -210,7 +214,7 @@ access to the context via ``request.context``.
.. _the_response:
View Callable Responses
-~~~~~~~~~~~~~~~~~~~~~~~
+-----------------------
A view callable may always return an object that implements the :app:`Pyramid`
:term:`Response` interface. The easiest way to return something that
@@ -256,7 +260,7 @@ These attributes form the notional "Pyramid Response interface".
.. _http_redirect:
Using a View Callable to Do an HTTP Redirect
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+--------------------------------------------
You can issue an HTTP redirect from within a view by returning a particular
kind of response.
@@ -295,7 +299,7 @@ Unauthorized``.
.. _special_exceptions_in_callables:
Using Special Exceptions In View Callables
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+------------------------------------------
Usually when a Python exception is raised within a view callable,
:app:`Pyramid` allows the exception to propagate all the way out to the
@@ -325,7 +329,7 @@ available to the view which :app:`Pyramid` invokes as
.. _exception_views:
Exception Views
-~~~~~~~~~~~~~~~~
+---------------
The machinery which allows the special :exc:`pyramid.exceptions.NotFound` and
:exc:`pyramid.exceptions.Forbidden` exceptions to be caught by specialized
@@ -404,1089 +408,3 @@ exception views which have a name will be ignored.
Exception views can be configured with any view registration mechanism:
``@view_config`` decorator, ZCML, or imperative ``add_view`` styles.
-.. index::
- single: unicode, views, and forms
- single: forms, views, and unicode
- single: views, forms, and unicode
-
-Handling Form Submissions in View Callables (Unicode and Character Set Issues)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Most web applications need to accept form submissions from web browsers and
-various other clients. In :app:`Pyramid`, form submission handling logic is
-always part of a :term:`view`. For a general overview of how to handle form
-submission data using the :term:`WebOb` API, see :ref:`webob_chapter` and
-`"Query and POST variables" within the WebOb documentation
-<http://pythonpaste.org/webob/reference.html#query-post-variables>`_.
-:app:`Pyramid` defers to WebOb for its request and response implementations,
-and handling form submission data is a property of the request
-implementation. Understanding WebOb's request API is the key to
-understanding how to process form submission data.
-
-There are some defaults that you need to be aware of when trying to handle
-form submission data in a :app:`Pyramid` view. Having high-order (i.e.,
-non-ASCII) characters in data contained within form submissions is
-exceedingly common, and the UTF-8 encoding is the most common encoding used
-on the web for character data. Since Unicode values are much saner than
-working with and storing bytestrings, :app:`Pyramid` configures the
-:term:`WebOb` request machinery to attempt to decode form submission values
-into Unicode from UTF-8 implicitly. This implicit decoding happens when view
-code obtains form field values via the ``request.params``, ``request.GET``,
-or ``request.POST`` APIs (see :ref:`request_module` for details about these
-APIs).
-
-.. note::
-
- Many people find the difference between Unicode and UTF-8 confusing.
- Unicode is a standard for representing text that supports most of the
- world's writing systems. However, there are many ways that Unicode data
- can be encoded into bytes for transit and storage. UTF-8 is a specific
- encoding for Unicode, that is backwards-compatible with ASCII. This makes
- UTF-8 very convenient for encoding data where a large subset of that data
- is ASCII characters, which is largely true on the web. UTF-8 is also the
- standard character encoding for URLs.
-
-As an example, let's assume that the following form page is served up to a
-browser client, and its ``action`` points at some :app:`Pyramid` view code:
-
-.. code-block:: xml
- :linenos:
-
- <html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
- </head>
- <form method="POST" action="myview">
- <div>
- <input type="text" name="firstname"/>
- </div>
- <div>
- <input type="text" name="lastname"/>
- </div>
- <input type="submit" value="Submit"/>
- </form>
- </html>
-
-The ``myview`` view code in the :app:`Pyramid` application *must* expect that
-the values returned by ``request.params`` will be of type ``unicode``, as
-opposed to type ``str``. The following will work to accept a form post from
-the above form:
-
-.. code-block:: python
- :linenos:
-
- def myview(request):
- firstname = request.params['firstname']
- lastname = request.params['lastname']
-
-But the following ``myview`` view code *may not* work, as it tries to decode
-already-decoded (``unicode``) values obtained from ``request.params``:
-
-.. code-block:: python
- :linenos:
-
- def myview(request):
- # the .decode('utf-8') will break below if there are any high-order
- # characters in the firstname or lastname
- firstname = request.params['firstname'].decode('utf-8')
- lastname = request.params['lastname'].decode('utf-8')
-
-For implicit decoding to work reliably, you should ensure that every form you
-render that posts to a :app:`Pyramid` view explicitly defines a charset
-encoding of UTF-8. This can be done via a response that has a
-``;charset=UTF-8`` in its ``Content-Type`` header; or, as in the form above,
-with a ``meta http-equiv`` tag that implies that the charset is UTF-8 within
-the HTML ``head`` of the page containing the form. This must be done
-explicitly because all known browser clients assume that they should encode
-form data in the same character set implied by ``Content-Type`` value of the
-response containing the form when subsequently submitting that form. There is
-no other generally accepted way to tell browser clients which charset to use
-to encode form data. If you do not specify an encoding explicitly, the
-browser client will choose to encode form data in its default character set
-before submitting it, which may not be UTF-8 as the server expects. If a
-request containing form data encoded in a non-UTF8 charset is handled by your
-view code, eventually the request code accessed within your view will throw
-an error when it can't decode some high-order character encoded in another
-character set within form data, e.g., when ``request.params['somename']`` is
-accessed.
-
-If you are using the :class:`pyramid.response.Response` class to generate a
-response, or if you use the ``render_template_*`` templating APIs, the UTF-8
-charset is set automatically as the default via the ``Content-Type`` header.
-If you return a ``Content-Type`` header without an explicit charset, a
-request will add a ``;charset=utf-8`` trailer to the ``Content-Type`` header
-value for you, for response content types that are textual
-(e.g. ``text/html``, ``application/xml``, etc) as it is rendered. If you are
-using your own response object, you will need to ensure you do this yourself.
-
-.. note:: Only the *values* of request params obtained via
- ``request.params``, ``request.GET`` or ``request.POST`` are decoded
- to Unicode objects implicitly in the :app:`Pyramid` default
- configuration. The keys are still (byte) strings.
-
-.. index::
- single: view configuration
-
-.. _view_configuration:
-
-View Configuration: Mapping a Resource or URL Pattern to a View Callable
-------------------------------------------------------------------------
-
-A developer makes a :term:`view callable` available for use within a
-:app:`Pyramid` application via :term:`view configuration`. A view
-configuration associates a view callable with a set of statements that
-determine the set of circumstances which must be true for the view callable
-to be invoked.
-
-A view configuration statement is made about information present in the
-:term:`context` resource and the :term:`request`.
-
-View configuration is performed in one of these ways:
-
-- by running a :term:`scan` against application source code which has a
- :class:`pyramid.view.view_config` decorator attached to a Python object as
- per :class:`pyramid.view.view_config` and
- :ref:`mapping_views_using_a_decorator_section`.
-
-- by using the :meth:`pyramid.config.Configurator.add_view` method as per
- :meth:`pyramid.config.Configurator.add_view` and
- :ref:`mapping_views_using_imperative_config_section`.
-
-- By specifying a view within a :term:`route configuration`. View
- configuration via a route configuration is performed by using the
- :meth:`pyramid.config.Configurator.add_route` method, passing a ``view``
- argument specifying a view callable.
-
-- by using the :meth:`pyramid.config.Configurator.add_handler` against a
- :term:`view handler` class (useful only for :term:`URL dispatch`
- applications).
-
-.. note:: You can also add view configuration by adding a ``<view>``,
- ``<route>`` or ``<handler>`` declaration to :term:`ZCML` used by your
- application as per :ref:`mapping_views_using_zcml_section`,
- :ref:`view_directive`, :ref:`route_directive` or :ref:`handler_directive`.
-
-.. _view_configuration_parameters:
-
-View Configuration Parameters
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-All forms of view configuration accept the same general types of arguments.
-
-Many arguments supplied during view configuration are :term:`view predicate`
-arguments. View predicate arguments used during view configuration are used
-to narrow the set of circumstances in which :mod:`view lookup` will find a
-particular view callable. In general, the fewer number of predicates which
-are supplied to a particular view configuration, the more likely it is that
-the associated view callable will be invoked. The greater the number
-supplied, the less likely.
-
-Some view configuration arguments are non-predicate arguments. These tend to
-modify the response of the view callable or prevent the view callable from
-being invoked due to an authorization policy. The presence of non-predicate
-arguments in a view configuration does not narrow the circumstances in which
-the view callable will be invoked.
-
-Non-Predicate Arguments
-+++++++++++++++++++++++
-
-``permission``
- The name of a :term:`permission` that the user must possess in order to
- invoke the :term:`view callable`. See :ref:`view_security_section` for
- more information about view security and permissions.
-
- If ``permission`` is not supplied, no permission is registered for this
- view (it's accessible by any caller).
-
-``attr``
- The view machinery defaults to using the ``__call__`` method of the
- :term:`view callable` (or the function itself, if the view callable is a
- function) to obtain a response. The ``attr`` value allows you to vary the
- method attribute used to obtain the response. For example, if your view
- was a class, and the class has a method named ``index`` and you wanted to
- use this method instead of the class' ``__call__`` method to return the
- response, you'd say ``attr="index"`` in the view configuration for the
- view. This is most useful when the view definition is a class.
-
- If ``attr`` is not supplied, ``None`` is used (implying the function itself
- if the view is a function, or the ``__call__`` callable attribute if the
- view is a class).
-
-``renderer``
- Denotes the :term:`renderer` implementation which will be used to construct
- a :term:`response` from the associated view callable's return value. (see
- also :ref:`renderers_chapter`).
-
- This is either a single string term (e.g. ``json``) or a string implying a
- path or :term:`asset specification` (e.g. ``templates/views.pt``) naming a
- :term:`renderer` implementation. If the ``renderer`` value does not
- contain a dot (``.``), the specified string will be used to look up a
- renderer implementation, and that renderer implementation will be used to
- construct a response from the view return value. If the ``renderer`` value
- contains a dot (``.``), the specified term will be treated as a path, and
- the filename extension of the last element in the path will be used to look
- up the renderer implementation, which will be passed the full path.
-
- When the renderer is a path, although a path is usually just a simple
- relative pathname (e.g. ``templates/foo.pt``, implying that a template
- named "foo.pt" is in the "templates" directory relative to the directory of
- the current :term:`package`), a path can be absolute, starting with a slash
- on UNIX or a drive letter prefix on Windows. The path can alternately be a
- :term:`asset specification` in the form
- ``some.dotted.package_name:relative/path``, making it possible to address
- template assets which live in a separate package.
-
- The ``renderer`` attribute is optional. If it is not defined, the "null"
- renderer is assumed (no rendering is performed and the value is passed back
- to the upstream :app:`Pyramid` machinery unmolested). Note that if the
- view callable itself returns a :term:`response` (see :ref:`the_response`),
- the specified renderer implementation is never called.
-
-``wrapper``
- The :term:`view name` of a different :term:`view configuration` which will
- receive the response body of this view as the ``request.wrapped_body``
- attribute of its own :term:`request`, and the :term:`response` returned by
- this view as the ``request.wrapped_response`` attribute of its own request.
- Using a wrapper makes it possible to "chain" views together to form a
- composite response. The response of the outermost wrapper view will be
- returned to the user. The wrapper view will be found as any view is found:
- see :ref:`view_lookup`. The "best" wrapper view will be found based on the
- lookup ordering: "under the hood" this wrapper view is looked up via
- ``pyramid.view.render_view_to_response(context, request,
- 'wrapper_viewname')``. The context and request of a wrapper view is the
- same context and request of the inner view.
-
- If ``wrapper`` is not supplied, no wrapper view is used.
-
-Predicate Arguments
-+++++++++++++++++++
-
-These arguments modify view lookup behavior. In general, the more predicate
-arguments that are supplied, the more specific, and narrower the usage of the
-configured view.
-
-``name``
- The :term:`view name` required to match this view callable. Read
- :ref:`traversal_chapter` to understand the concept of a view name.
-
- If ``name`` is not supplied, the empty string is used (implying the default
- view).
-
-``context``
- An object representing a Python class that the :term:`context` resource
- must be an instance of *or* the :term:`interface` that the :term:`context`
- resource must provide in order for this view to be found and called. This
- predicate is true when the :term:`context` resource is an instance of the
- represented class or if the :term:`context` resource provides the
- represented interface; it is otherwise false.
-
- If ``context`` is not supplied, the value ``None``, which matches any
- resource, is used.
-
-``route_name``
- If ``route_name`` is supplied, the view callable will be invoked only when
- the named route has matched.
-
- This value must match the ``name`` of a :term:`route configuration`
- declaration (see :ref:`urldispatch_chapter`) that must match before this
- view will be called. Note that the ``route`` configuration referred to by
- ``route_name`` will usually have a ``*traverse`` token in the value of its
- ``pattern``, representing a part of the path that will be used by
- :term:`traversal` against the result of the route's :term:`root factory`.
-
- If ``route_name`` is not supplied, the view callable will be have a chance
- of being invoked if no other route was matched. This is when the
- request/context pair found via :term:`resource location` does not indicate
- it matched any configured route.
-
-``request_type``
- This value should be an :term:`interface` that the :term:`request` must
- provide in order for this view to be found and called.
-
- If ``request_type`` is not supplied, the value ``None`` is used, implying
- any request type.
-
- *This is an advanced feature, not often used by "civilians"*.
-
-``request_method``
- This value can either be one of the strings ``GET``, ``POST``, ``PUT``,
- ``DELETE``, or ``HEAD`` representing an HTTP ``REQUEST_METHOD``. A view
- declaration with this argument ensures that the view will only be called
- when the request's ``method`` attribute (aka the ``REQUEST_METHOD`` of the
- WSGI environment) string matches the supplied value.
-
- If ``request_method`` is not supplied, the view will be invoked regardless
- of the ``REQUEST_METHOD`` of the :term:`WSGI` environment.
-
-``request_param``
- This value can be any string. A view declaration with this argument
- ensures that the view will only be called when the :term:`request` has a
- key in the ``request.params`` dictionary (an HTTP ``GET`` or ``POST``
- variable) that has a name which matches the supplied value.
-
- If the value supplied has a ``=`` sign in it,
- e.g. ``request_params="foo=123"``, then the key (``foo``) must both exist
- in the ``request.params`` dictionary, *and* the value must match the right
- hand side of the expression (``123``) for the view to "match" the current
- request.
-
- If ``request_param`` is not supplied, the view will be invoked without
- consideration of keys and values in the ``request.params`` dictionary.
-
-``containment``
- This value should be a reference to a Python class or :term:`interface`
- that a parent object in the context resource's :term:`lineage` must provide
- in order for this view to be found and called. The resources in your
- resource tree must be "location-aware" to use this feature.
-
- If ``containment`` is not supplied, the interfaces and classes in the
- lineage are not considered when deciding whether or not to invoke the view
- callable.
-
- See :ref:`location_aware` for more information about location-awareness.
-
-``xhr``
- This value should be either ``True`` or ``False``. If this value is
- specified and is ``True``, the :term:`WSGI` environment must possess an
- ``HTTP_X_REQUESTED_WITH`` (aka ``X-Requested-With``) header that has the
- value ``XMLHttpRequest`` for the associated view callable to be found and
- called. This is useful for detecting AJAX requests issued from jQuery,
- Prototype and other Javascript libraries.
-
- If ``xhr`` is not specified, the ``HTTP_X_REQUESTED_WITH`` HTTP header is
- not taken into consideration when deciding whether or not to invoke the
- associated view callable.
-
-``accept``
- The value of this argument represents a match query for one or more
- mimetypes in the ``Accept`` HTTP request header. If this value is
- specified, it must be in one of the following forms: a mimetype match token
- in the form ``text/plain``, a wildcard mimetype match token in the form
- ``text/*`` or a match-all wildcard mimetype match token in the form
- ``*/*``. If any of the forms matches the ``Accept`` header of the request,
- this predicate will be true.
-
- If ``accept`` is not specified, the ``HTTP_ACCEPT`` HTTP header is not
- taken into consideration when deciding whether or not to invoke the
- associated view callable.
-
-``header``
- This value represents an HTTP header name or a header name/value pair.
-
- If ``header`` is specified, it must be a header name or a
- ``headername:headervalue`` pair.
-
- If ``header`` is specified without a value (a bare header name only,
- e.g. ``If-Modified-Since``), the view will only be invoked if the HTTP
- header exists with any value in the request.
-
- If ``header`` is specified, and possesses a name/value pair
- (e.g. ``User-Agent:Mozilla/.*``), the view will only be invoked if the HTTP
- header exists *and* the HTTP header matches the value requested. When the
- ``headervalue`` contains a ``:`` (colon), it will be considered a
- name/value pair (e.g. ``User-Agent:Mozilla/.*`` or ``Host:localhost``).
- The value portion should be a regular expression.
-
- Whether or not the value represents a header name or a header name/value
- pair, the case of the header name is not significant.
-
- If ``header`` is not specified, the composition, presence or absence of
- HTTP headers is not taken into consideration when deciding whether or not
- to invoke the associated view callable.
-
-``path_info``
- This value represents a regular expression pattern that will be tested
- against the ``PATH_INFO`` WSGI environment variable to decide whether or
- not to call the associated view callable. If the regex matches, this
- predicate will be ``True``.
-
- If ``path_info`` is not specified, the WSGI ``PATH_INFO`` is not taken into
- consideration when deciding whether or not to invoke the associated view
- callable.
-
-``custom_predicates``
- If ``custom_predicates`` is specified, it must be a sequence of references
- to custom predicate callables. Use custom predicates when no set of
- predefined predicates do what you need. Custom predicates can be combined
- with predefined predicates as necessary. Each custom predicate callable
- should accept two arguments: ``context`` and ``request`` and should return
- either ``True`` or ``False`` after doing arbitrary evaluation of the
- context resource and/or the request. If all callables return ``True``, the
- associated view callable will be considered viable for a given request.
-
- If ``custom_predicates`` is not specified, no custom predicates are
- used.
-
-.. index::
- single: view_config decorator
-
-.. _mapping_views_using_a_decorator_section:
-
-View Configuration Using the ``@view_config`` Decorator
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-For better locality of reference, you may use the
-:class:`pyramid.view.view_config` decorator to associate your view functions
-with URLs instead of using :term:`ZCML` or imperative configuration for the
-same purpose.
-
-.. warning::
-
- Using this feature tends to slows down application startup slightly, as
- more work is performed at application startup to scan for view
- declarations.
-
-Usage of the ``view_config`` decorator is a form of :term:`declarative
-configuration`, like ZCML, but in decorator form.
-:class:`pyramid.view.view_config` can be used to associate :term:`view
-configuration` information -- as done via the equivalent imperative code or
-ZCML -- with a function that acts as a :app:`Pyramid` view callable. All
-arguments to the :meth:`pyramid.config.Configurator.add_view` method (save
-for the ``view`` argument) are available in decorator form and mean precisely
-the same thing.
-
-An example of the :class:`pyramid.view.view_config` decorator might reside in
-a :app:`Pyramid` application module ``views.py``:
-
-.. ignore-next-block
-.. code-block:: python
- :linenos:
-
- from resources import MyResource
- from pyramid.view import view_config
- from pyramid.response import Response
-
- @view_config(name='my_view', request_method='POST', context=MyResource,
- permission='read')
- def my_view(request):
- return Response('OK')
-
-Using this decorator as above replaces the need to add this imperative
-configuration stanza:
-
-.. ignore-next-block
-.. code-block:: python
- :linenos:
-
- config.add_view('.views.my_view', name='my_view', request_method='POST',
- context=MyResource, permission='read')
-
-All arguments to ``view_config`` may be omitted. For example:
-
-.. code-block:: python
- :linenos:
-
- from pyramid.response import Response
- from pyramid.view import view_config
-
- @view_config()
- def my_view(request):
- """ My view """
- return Response()
-
-Such a registration as the one directly above implies that the view name will
-be ``my_view``, registered with a ``context`` argument that matches any
-resource type, using no permission, registered against requests with any
-request method, request type, request param, route name, or containment.
-
-The mere existence of a ``@view_config`` decorator doesn't suffice to perform
-view configuration. All that the decorator does is "annotate" the function
-with your configuration declarations, it doesn't process them. To make
-:app:`Pyramid` process your :class:`pyramid.view.view_config` declarations,
-you *must* do use the ``scan`` method of a
-:class:`pyramid.config.Configurator`:
-
-.. code-block:: python
- :linenos:
-
- # config is assumed to be an instance of the
- # pyramid.config.Configurator class
- config.scan()
-
-.. note:: See :ref:`zcml_scanning` for information about how to invoke a scan
- via ZCML (if you're not using imperative configuration).
-
-Please see :ref:`decorations_and_code_scanning` for detailed information
-about what happens when code is scanned for configuration declarations
-resulting from use of decorators like :class:`pyramid.view.view_config`.
-
-See :ref:`configuration_module` for additional API arguments to the
-:meth:`pyramid.config.Configurator.scan` method. For example, the method
-allows you to supply a ``package`` argument to better control exactly *which*
-code will be scanned.
-
-``@view_config`` Placement
-++++++++++++++++++++++++++
-
-A :class:`pyramid.view.view_config` decorator can be placed in various points
-in your application.
-
-If your view callable is a function, it may be used as a function decorator:
-
-.. code-block:: python
- :linenos:
-
- from pyramid.view import view_config
- from pyramid.response import Response
-
- @view_config(name='edit')
- def edit(request):
- return Response('edited!')
-
-If your view callable is a class, the decorator can also be used as a class
-decorator in Python 2.6 and better (Python 2.5 and below do not support class
-decorators). All the arguments to the decorator are the same when applied
-against a class as when they are applied against a function. For example:
-
-.. code-block:: python
- :linenos:
-
- from pyramid.response import Response
- from pyramid.view import view_config
-
- @view_config()
- class MyView(object):
- def __init__(self, request):
- self.request = request
-
- def __call__(self):
- return Response('hello')
-
-You can use the :class:`pyramid.view.view_config` decorator as a simple
-callable to manually decorate classes in Python 2.5 and below without the
-decorator syntactic sugar, if you wish:
-
-.. code-block:: python
- :linenos:
-
- from pyramid.response import Response
- from pyramid.view import view_config
-
- class MyView(object):
- def __init__(self, request):
- self.request = request
-
- def __call__(self):
- return Response('hello')
-
- my_view = view_config()(MyView)
-
-More than one :class:`pyramid.view.view_config` decorator can be stacked on
-top of any number of others. Each decorator creates a separate view
-registration. For example:
-
-.. code-block:: python
- :linenos:
-
- from pyramid.view import view_config
- from pyramid.response import Response
-
- @view_config(name='edit')
- @view_config(name='change')
- def edit(request):
- return Response('edited!')
-
-This registers the same view under two different names.
-
-The decorator can also be used against class methods:
-
-.. code-block:: python
- :linenos:
-
- from pyramid.response import Response
- from pyramid.view import view_config
-
- class MyView(object):
- def __init__(self, request):
- self.request = request
-
- @view_config(name='hello')
- def amethod(self):
- return Response('hello')
-
-When the decorator is used against a class method, a view is registered for
-the *class*, so the class constructor must accept an argument list in one of
-two forms: either it must accept a single argument ``request`` or it must
-accept two arguments, ``context, request``.
-
-The method which is decorated must return a :term:`response`.
-
-Using the decorator against a particular method of a class is equivalent to
-using the ``attr`` parameter in a decorator attached to the class itself.
-For example, the above registration implied by the decorator being used
-against the ``amethod`` method could be spelled equivalently as the below:
-
-.. code-block:: python
- :linenos:
-
- from pyramid.response import Response
- from pyramid.view import view_config
-
- @view_config(attr='amethod', name='hello')
- class MyView(object):
- def __init__(self, request):
- self.request = request
-
- def amethod(self):
- return Response('hello')
-
-.. index::
- single: add_view
-
-.. _mapping_views_using_imperative_config_section:
-
-View Registration Using :meth:`~pyramid.config.Configurator.add_view`
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The :meth:`pyramid.config.Configurator.add_view` method within
-:ref:`configuration_module` is used to configure a view imperatively. The
-arguments to this method are very similar to the arguments that you provide
-to the ``@view_config`` decorator. For example:
-
-.. code-block:: python
- :linenos:
-
- from pyramid.response import Response
-
- def hello_world(request):
- return Response('hello!')
-
- # config is assumed to be an instance of the
- # pyramid.config.Configurator class
- config.add_view(hello_world, name='hello.html')
-
-The first argument, ``view``, is required. It must either be a Python object
-which is the view itself or a :term:`dotted Python name` to such an object.
-All other arguments are optional. See
-:meth:`pyramid.config.Configurator.add_view` for more information.
-
-.. _using_add_handler:
-
-Handler Registration Using :meth:`~pyramid.config.Configurator.add_handler`
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-:app:`Pyramid` provides the special concept of a :term:`view handler`. View
-handlers are view classes that implement a number of methods, each of which
-is a :term:`view callable` as a convenience for :term:`URL dispatch` users.
-
-.. note::
-
- View handlers are *not* useful when using :term:`traversal`, only when using
- :term:`url dispatch`.
-
-Using a view handler instead of a plain function or class :term:`view
-callable` makes it unnecessary to call
-:meth:`pyramid.config.Configurator.add_route` (and/or
-:meth:`pyramid.config.Configurator.add_view`) "by hand" multiple times,
-making it more pleasant to register a collection of views as a single class
-when using :term:`url dispatch`. The view handler machinery also introduces
-the concept of an ``action``, which is used as a :term:`view predicate` to
-control which method of the handler is called. The method name is the
-default *action name* of a handler view callable.
-
-The concept of a view handler is analogous to a "controller" in Pylons 1.0.
-
-The view handler class is initialized by :app:`Pyramid` in the same manner as
-a "plain" view class. Its ``__init__`` is called with a request object (see
-:ref:`class_as_view`). It implements methods, each of which is a :term:`view
-callable`. When a request enters the system which corresponds with an
-*action* related to one of its view callable methods, this method is called,
-and it is expected to return a response.
-
-Here's an example view handler class:
-
-.. code-block:: python
- :linenos:
-
- from pyramid.response import Response
-
- from pyramid.view import action
-
- class Hello(object):
- def __init__(self, request):
- self.request = request
-
- def index(self):
- return Response('Hello world!')
-
- @action(renderer="mytemplate.mak")
- def bye(self):
- return {}
-
-The :class:`pyramid.view.action` decorator is used to fine-tune the view
-parameters for each potential view callable which is a method of the handler.
-
-Handlers are added to application configuration via the
-:meth:`pyramid.config.Configurator.add_handler` API. The
-:meth:`~pyramid.config.Configurator.add_handler` method will scan a
-:term:`view handler` class and automatically set up view configurations for
-its methods that represent "auto-exposed" view callable, or those that were
-decorated explicitly with the :class:`~pyramid.view.action` decorator. This
-decorator is used to setup additional view configuration information for
-individual methods of the class, and can be used repeatedly for a single view
-method to register multiple view configurations for it.
-
-.. code-block:: python
- :linenos:
-
- from myapp.handlers import Hello
- config.add_handler('hello', '/hello/{action}', handler=Hello)
-
-This example will result in a route being added for the pattern
-``/hello/{action}``, and each method of the ``Hello`` class will then be
-examined to see if it should be registered as a potential view callable when
-the ``/hello/{action}`` pattern matches. The value of ``{action}`` in the
-route pattern will be used to determine which view should be called, and each
-view in the class will be setup with a view predicate that requires a
-specific ``action`` name. By default, the action name for a method of a
-handler is the method name.
-
-If the URL was ``/hello/index``, the above example pattern would match, and,
-by default, the ``index`` method of the ``Hello`` class would be called.
-
-Alternatively, the action can be declared specifically for a URL to be
-registered for a *specific* ``action`` name:
-
-.. code-block:: python
- :linenos:
-
- from myapp.handlers import Hello
- config.add_handler('hello_index', '/hello/index',
- handler=Hello, action='index')
-
-This will result one of the methods that are configured for the ``action`` of
-'index' in the ``Hello`` handler class to be called. In this case the name of
-the method is the same as the action name: ``index``. However, this need not
-be the case, as we will see below.
-
-When calling :meth:`~pyramid.config.Configurator.add_handler`, an ``action``
-is required in either the route pattern or as a keyword argument, but
-**cannot appear in both places**. A ``handler`` argument must also be
-supplied, which can be either a :term:`asset specification` or a Python
-reference to the handler class. Additional keyword arguments are passed
-directly through to :meth:`pyramid.config.Configurator.add_route`.
-
-For example:
-
-.. code-block:: python
- :linenos:
-
- config.add_handler('hello', '/hello/{action}',
- handler='mypackage.handlers.MyHandler')
-
-Multiple :meth:`~pyramid.config.Configurator.add_handler` calls can specify
-the same handler, to register specific route names for different
-handler/action combinations. For example:
-
-.. code-block:: python
- :linenos:
-
- config.add_handler('hello_index', '/hello/index',
- handler=Hello, action='index')
- config.add_handler('bye_index', '/hello/bye',
- handler=Hello, action='bye')
-
-.. note::
-
- Handler configuration may also be added to the system via :term:`ZCML` (see
- :ref:`zcml_handler_configuration`).
-
-View Setup in the Handler Class
-+++++++++++++++++++++++++++++++
-
-A handler class can have a single class level attribute called
-``__autoexpose__`` which should be a regular expression or the value
-``None``. It's used to determine which method names will result in additional
-view configurations being registered.
-
-When :meth:`~pyramid.config.Configurator.add_handler` runs, every method in
-the handler class will be searched and a view registered if the method name
-matches the ``__autoexpose__`` regular expression, or if the method was
-decorated with :class:`~pyramid.view.action`.
-
-Every method in the handler class that has a name meeting the
-``__autoexpose__`` regular expression will have a view registered for an
-``action`` name corresponding to the method name. This functionality can be
-disabled by setting the ``__autoexpose__`` attribute to ``None``:
-
-.. code-block:: python
- :linenos:
-
- from pyramid.view import action
-
- class Hello(object):
- __autoexpose__ = None
-
- def __init__(self, request):
- self.request = request
-
- @action()
- def index(self):
- return Response('Hello world!')
-
- @action(renderer="mytemplate.mak")
- def bye(self):
- return {}
-
-With auto-expose effectively disabled, no views will be registered for a
-method unless it is specifically decorated with
-:class:`~pyramid.view.action`.
-
-Action Decorators in a Handler
-++++++++++++++++++++++++++++++
-
-The :class:`~pyramid.view.action` decorator registers view configuration
-information on the handler method, which is used by
-:meth:`~pyramid.config.Configurator.add_handler` to setup the view
-configuration.
-
-All keyword arguments are recorded, and passed to
-:meth:`~pyramid.config.Configurator.add_view`. Any valid keyword arguments
-for :meth:`~pyramid.config.Configurator.add_view` can thus be used with the
-:class:`~pyramid.view.action` decorator to further restrict when the view
-will be called.
-
-One important difference is that a handler method can respond to an
-``action`` name that is different from the method name by passing in a
-``name`` argument.
-
-Example:
-
-.. code-block:: python
- :linenos:
-
- from pyramid.view import action
-
- class Hello(object):
- def __init__(self, request):
- self.request = request
-
- @action(name='index', renderer='created.mak', request_method='POST')
- def create(self):
- return {}
-
- @action(renderer="view_all.mak", request_method='GET')
- def index(self):
- return {}
-
-This will register two views that require the ``action`` to be ``index``,
-with the additional view predicate requiring a specific request method.
-
-It can be useful to decorate a single method multiple times with
-:class:`~pyramid.view.action`. Each action decorator will register a new view
-for the method. By specifying different names and renderers for each action,
-the same view logic can be exposed and rendered differently on multiple URLs.
-
-Example:
-
-.. code-block:: python
- :linenos:
-
- from pyramid.view import action
-
- class Hello(object):
- def __init__(self, request):
- self.request = request
-
- @action(name='home', renderer='home.mak')
- @action(name='about', renderer='about.mak')
- def show_template(self):
- # prep some template vars
- return {}
-
- # in the config
- config.add_handler('hello', '/hello/{action}', handler=Hello)
-
-With this configuration, the url ``/hello/home`` will find a view
-configuration that results in calling the ``show_template`` method, then
-rendering the template with ``home.mak``, and the url ``/hello/about`` will
-call the same method and render the ``about.mak`` template.
-
-.. index::
- single: resource interfaces
-
-.. _using_resource_interfaces:
-
-Using Resource Interfaces In View Configuration
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Instead of registering your views with a ``context`` that names a Python
-resource *class*, you can optionally register a view callable with a
-``context`` which is an :term:`interface`. An interface can be attached
-arbitrarily to any resource object. View lookup treats context interfaces
-specially, and therefore the identity of a resource can be divorced from that
-of the class which implements it. As a result, associating a view with an
-interface can provide more flexibility for sharing a single view between two
-or more different implementations of a resource type. For example, if two
-resource objects of different Python class types share the same interface,
-you can use the same view configuration to specify both of them as a
-``context``.
-
-In order to make use of interfaces in your application during view dispatch,
-you must create an interface and mark up your resource classes or instances
-with interface declarations that refer to this interface.
-
-To attach an interface to a resource *class*, you define the interface and
-use the :func:`zope.interface.implements` function to associate the interface
-with the class.
-
-.. code-block:: python
- :linenos:
-
- from zope.interface import Interface
- from zope.interface import implements
-
- class IHello(Interface):
- """ A marker interface """
-
- class Hello(object):
- implements(IHello)
-
-To attach an interface to a resource *instance*, you define the interface and
-use the :func:`zope.interface.alsoProvides` function to associate the
-interface with the instance. This function mutates the instance in such a
-way that the interface is attached to it.
-
-.. code-block:: python
- :linenos:
-
- from zope.interface import Interface
- from zope.interface import alsoProvides
-
- class IHello(Interface):
- """ A marker interface """
-
- class Hello(object):
- pass
-
- def make_hello():
- hello = Hello()
- alsoProvides(hello, IHello)
- return hello
-
-Regardless of how you associate an interface, with a resource instance, or a
-resource class, the resulting code to associate that interface with a view
-callable is the same. Assuming the above code that defines an ``IHello``
-interface lives in the root of your application, and its module is named
-"resources.py", the interface declaration below will associate the
-``mypackage.views.hello_world`` view with resources that implement, or
-provide, this interface.
-
-.. code-block:: python
- :linenos:
-
- # config is an instance of pyramid.config.Configurator
-
- config.add_view('mypackage.views.hello_world', name='hello.html',
- context='mypackage.resources.IHello')
-
-Any time a resource that is determined to be the :term:`context` provides
-this interface, and a view named ``hello.html`` is looked up against it as
-per the URL, the ``mypackage.views.hello_world`` view callable will be
-invoked.
-
-Note, in cases where a view is registered against a resource class, and a
-view is also registered against an interface that the resource class
-implements, an ambiguity arises. Views registered for the resource class take
-precedence over any views registered for any interface the resource class
-implements. Thus, if one view configuration names a ``context`` of both the
-class type of a resource, and another view configuration names a ``context``
-of interface implemented by the resource's class, and both view
-configurations are otherwise identical, the view registered for the context's
-class will "win".
-
-For more information about defining resources with interfaces for use within
-view configuration, see :ref:`resources_which_implement_interfaces`.
-
-.. index::
- single: view security
- pair: security; view
-
-.. _view_security_section:
-
-Configuring View Security
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If an :term:`authorization policy` is active, any :term:`permission` attached
-to a :term:`view configuration` found during view lookup will be verified.
-This will ensure that the currently authenticated user possesses that
-permission against the :term:`context` resource before the view function is
-actually called. Here's an example of specifying a permission in a view
-configuration using :meth:`pyramid.config.Configurator.add_view`:
-
-.. code-block:: python
- :linenos:
-
- # config is an instance of pyramid.config.Configurator
-
- config.add_view('myproject.views.add_entry', name='add.html',
- context='myproject.resources.IBlog', permission='add')
-
-When an :term:`authorization policy` is enabled, this view will be protected
-with the ``add`` permission. The view will *not be called* if the user does
-not possess the ``add`` permission relative to the current :term:`context`.
-Instead the :term:`forbidden view` result will be returned to the client as
-per :ref:`protecting_views`.
-
-.. index::
- single: view lookup
-
-.. _view_lookup:
-
-View Lookup and Invocation
---------------------------
-
-:term:`View lookup` is the :app:`Pyramid` subsystem responsible for finding
-an invoking a :term:`view callable`. The view lookup subsystem is passed a
-:term:`context` and a :term:`request` object.
-
-:term:`View configuration` information stored within in the
-:term:`application registry` is compared against the context and request by
-the view lookup subsystem in order to find the "best" view callable for the
-set of circumstances implied by the context and request.
-
-Predicate attributes of view configuration can be thought of like
-"narrowers". In general, the greater number of predicate attributes
-possessed by a view's configuration, the more specific the circumstances need
-to be before the registered view callable will be invoked.
-
-For any given request, a view with five predicates will always be found and
-evaluated before a view with two, for example. All predicates must match for
-the associated view to be called.
-
-This does not mean however, that :app:`Pyramid` "stops looking" when it finds
-a view registration with predicates that don't match. If one set of view
-predicates does not match, the "next most specific" view (if any) view is
-consulted for predicates, and so on, until a view is found, or no view can be
-matched up with the request. The first view with a set of predicates all of
-which match the request environment will be invoked.
-
-If no view can be found with predicates which allow it to be matched up with
-the request, :app:`Pyramid` will return an error to the user's browser,
-representing a "not found" (404) page. See :ref:`changing_the_notfound_view`
-for more information about changing the default notfound view.
-
-.. index::
- single: debugging not found errors
- single: not found error (debugging)
-
-.. _debug_notfound_section:
-
-:exc:`NotFound` Errors
-~~~~~~~~~~~~~~~~~~~~~~
-
-It's useful to be able to debug :exc:`NotFound` error responses when they
-occur unexpectedly due to an application registry misconfiguration. To debug
-these errors, use the ``PYRAMID_DEBUG_NOTFOUND`` environment variable or the
-``debug_notfound`` configuration file setting. Details of why a view was not
-found will be printed to ``stderr``, and the browser representation of the
-error will include the same information. See :ref:`environment_chapter` for
-more information about how, and where to set these values.
-
-Further Information
--------------------
-
-The chapter entitled :ref:`renderers_chapter` explains how to create
-functions (or instances/classes) which do not return a :term:`Response`
-object, yet which still can be used as view callables.
-
diff --git a/docs/tutorials/cmf/actions.rst b/docs/tutorials/cmf/actions.rst
deleted file mode 100644
index a6e33fa59..000000000
--- a/docs/tutorials/cmf/actions.rst
+++ /dev/null
@@ -1,28 +0,0 @@
-.. _actions_chapter:
-
-=======
-Actions
-=======
-
-In CMF, the "actions tool" along with "action providers" create an extensible
-mechanism to show links in the CMF management UI that invoke a particular
-behavior or which show a particular template.
-
-:app:`Pyramid` itself has no such concept, and no package provides a direct
-replacement. Actions are such a generic concept that it's simple to
-reimplement action-like navigation in a different way within any given
-application. For example, a module-scope global dictionary which has keys
-that are action names, and values which are tuples of (permission, link).
-Take that concept and expand on it, and you'll have some passable actions
-tool replacement within a single application.
-
-The `pyramid_viewgroup <https://github.com/Pylons/pyramid_viewgroup/>`_
-package provides some functionality for creating "view groups". Each view in
-a viewgroup can provide some snippet of HTML (e.g. a single "tab"), and
-individual views (tabs) within the group which cannot be displayed to the
-user due to the user's lack of permissions will be omitted from the rendered
-output.
-
-The :term:`repoze.lemonade` package provides "list item" support that
-may be used to construct action lists.
-
diff --git a/docs/tutorials/cmf/catalog.rst b/docs/tutorials/cmf/catalog.rst
deleted file mode 100644
index d5e9534ae..000000000
--- a/docs/tutorials/cmf/catalog.rst
+++ /dev/null
@@ -1,73 +0,0 @@
-.. _catalog_chapter:
-
-=======
-Catalog
-=======
-
-The main feature of the CMF catalog is that it filters search results
-from the Zope 2 "catalog" based on the requesting user's ability to
-view a particular cataloged object.
-
-:app:`Pyramid` itself has no cataloging facility, but an addon
-package named :term:`repoze.catalog` offers similar functionality.
-
-Creating an Allowed Index
--------------------------
-
-In CMF, a catalog index named ``getAllowedRolesAndUsers`` along with
-application indexing code allows for filtered search results. It's
-reasonably easy to reproduce this pattern using some custom code.
-
-Creating The ``allowed`` Index
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Here's some code which creates an ``allowed`` index for use in a
-``repoze.catalog`` catalog::
-
- from pyramid.security import principals_allowed_by_permission
- from repoze.catalog.indexes.keyword import CatalogKeywordIndex
- from repoze.catalog.catalog import Catalog
-
- class Allowed:
- def __init__(self, permission):
- self.permission = permission
-
- def __call__(self, context, default):
- principals = principals_allowed_by_permission(context,
- self.permission)
- return principals
-
- def make_allowed_index(permission='View'):
- index = CatalogKeywordIndex(Allowed(permission))
- return index
-
- index = make_allowed_index()
- catalog = Catalog()
- catalog['allowed'] = index
-
-When you index an item, the allowed index will be populated with all
-the principal ids which have the 'View' permission.
-
-Using the ``allowed`` Index
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Here's how you might use the ``allowed`` index within a query::
-
- from pyramid.security import effective_principals
- principals = effective_principals(request)
- catalog.searchResults(allowed={'operator':'or', 'query':principals})
-
-The above query will return all document ids that the current user has
-the 'View' permission against. Add other indexes to the query to get
-a useful result.
-
-See the `repoze.catalog package
-<http://svn.repoze.org/repoze.catalog/trunk>`_ for more information.
-
-
-
-
-
-
-
-
diff --git a/docs/tutorials/cmf/content.rst b/docs/tutorials/cmf/content.rst
deleted file mode 100644
index 85e5b5fbc..000000000
--- a/docs/tutorials/cmf/content.rst
+++ /dev/null
@@ -1,67 +0,0 @@
-.. _content_types_chapter:
-
-=============
-Content Types
-=============
-
-In CMF, a content type is defined as a bag of settings (the type
-information, controlled within the "types tool"), as well as factory
-code which generates an instance of that content. It is possible to
-construct and enumerate content types using APIs defined on the types
-tool.
-
-:app:`Pyramid` itself has no such concept, but an addon package named
-:term:`repoze.lemonade` has a barebones replacement.
-
-Factory Type Information
-------------------------
-
-A factory type information object in CMF allows you to associate a
-title, a description, an internationalization domain, an icon, an
-initial view name, a factory, and a number of security settings with a
-type name. Each type information object knows how to manufacture
-content objects that match its type.
-
-:app:`Pyramid` certainly enforces none of these concepts in any
-particular way, but :term:`repoze.lemonade` does.
-
-``repoze.lemonade`` Content
-+++++++++++++++++++++++++++
-
-:term:`repoze.lemonade` provides a reasonably handy directive and set
-of helper functions which allow you to:
-
-#. Associate a interface with a factory function, making it into a
- "content type".
-
-#. Enumerate all interfaces associated with factory functions.
-
-.. note:: Using this pattern is often plain silly, as it's usually
- just as easy to actually import a class implementation and
- create an instance directly using its constructor. But it
- can be useful in cases where you want to address some set of
- constructors uniformly without doing direct imports in the
- code which performs the construction, or if you need to make
- content construction uniform across a diverse set of model
- types, or if you need to enumerate some set of information
- about "content" types. It's left as an exercise to the
- reader to determine under which circumstances using this
- pattern is an appropriate thing to do. Hint: not very
- often, unless you're doing the indirection solely to aid
- content-agnostic unit tests or if you need to get an
- enumerated subset of content type information to aid in UI
- generation. That said, this *is* a tutorial about how to
- get CMF-like features in :app:`Pyramid`, so we'll assume
- the pattern is useful to readers.
-
-See the `repoze.lemonade package
-<http://svn.repoze.org/repoze.lemonade/trunk>`_ for more information,
-particularly its documentation for "content".
-
-
-
-
-
-
-
-
diff --git a/docs/tutorials/cmf/index.rst b/docs/tutorials/cmf/index.rst
deleted file mode 100644
index 26aa336a9..000000000
--- a/docs/tutorials/cmf/index.rst
+++ /dev/null
@@ -1,38 +0,0 @@
-Converting an Existing Zope/CMF Application to :app:`Pyramid`
-================================================================
-
-The Zope `Content Management Framework
-<http://www.zope.org/Products/CMF/>`_ (aka CMF) is a layer on top of
-:term:`Zope` 2 that provides facilities for creating content-driven
-websites. It's reasonably easy to convert a modern Zope/CMF
-application to :app:`Pyramid`.
-
-The main difference between CMF and :app:`Pyramid` is that :app:`Pyramid`
-does not advertise itself as a system into which you can plug arbitrary
-"packages" that extend a system-supplied management user interface. You
-*could* build a CMF-like layer on top of :app:`Pyramid` but none currently
-exists. For those sorts of high-extensibility, highly-regularized-UI
-systems, CMF is still the better choice.
-
-:app:`Pyramid` (and other more lightweight systems) is often a
-better choice when you're building the a user interface from scratch,
-which often happens when the paradigms of some CMF-provided user
-interface don't match the requirements of an application very closely.
-Even so, a good number of developers tend to use CMF even when they do
-start an application for which they need to build a UI from scratch,
-because CMF happens to provide other helpful services, such as types,
-skins, and workflow; this tutorial is for those sorts of developers
-and projects.
-
-.. toctree::
- :maxdepth: 2
-
- content.rst
- catalog.rst
- skins.rst
- actions.rst
- workflow.rst
- missing.rst
-
-
-
diff --git a/docs/tutorials/cmf/missing.rst b/docs/tutorials/cmf/missing.rst
deleted file mode 100644
index 964e0ab04..000000000
--- a/docs/tutorials/cmf/missing.rst
+++ /dev/null
@@ -1,22 +0,0 @@
-Missing Comparisons
-===================
-
-We currently don't have any comparative Pyramid-vs-CMF information
-about the following concepts within this tutorial:
-
-- Templates
-
-- Forms
-
-- Membership
-
-- Discussions
-
-- Syndication
-
-- Dublincore
-
-Please ask on the `Pylons-devel maillist
-<http://groups.google.com/group/pylons-devel>`_ or on the `#pylons IRC
-channel <http://irc.freenode.net#pylons>`_ about these topics.
-
diff --git a/docs/tutorials/cmf/skins.rst b/docs/tutorials/cmf/skins.rst
deleted file mode 100644
index 676a076b3..000000000
--- a/docs/tutorials/cmf/skins.rst
+++ /dev/null
@@ -1,23 +0,0 @@
-.. _skins_chapter:
-
-=====
-Skins
-=====
-
-In CMF, a "skin layer" is defined as a collection of templates and
-code (Python scripts, DTML methods, etc) that can be activated and
-deactivated within a particular setup. A collection of active "skin
-layers" grouped in a particular order forms a "skin". "Add-on" CMF
-products often provide skin layers that are activated within a
-particular skin to provide the site with additional features.
-
-To override static resources using a "search path" much like a set of
-skin layers, :app:`Pyramid` provides the concept of
-:term:`resource` overrides. See :ref:`overriding_resources_section`
-for more information about resource overrides.
-
-While there is no analogue to a skin layer search path for locating
-Python code (as opposed to resources), :term:`view` code combined with
-differing :term:`predicate` arguments can provide a good deal of
-the same sort of behavior.
-
diff --git a/docs/tutorials/cmf/workflow.rst b/docs/tutorials/cmf/workflow.rst
deleted file mode 100644
index cc70d771a..000000000
--- a/docs/tutorials/cmf/workflow.rst
+++ /dev/null
@@ -1,14 +0,0 @@
-.. _workflow_chapter:
-
-========
-Workflow
-========
-
-In CMF, the "workflow tool" allows developers to design state machines
-which imply transition between content states.
-
-:app:`Pyramid` itself has no such concept, but the
-:term:`repoze.workflow` package provides a simple state machine
-implementation that can act as a barebones workflow tool. See its
-documentation for more information.
-
diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst
index 1b66ace96..ee86eb543 100644
--- a/docs/tutorials/wiki/authorization.rst
+++ b/docs/tutorials/wiki/authorization.rst
@@ -136,7 +136,6 @@ 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``
@@ -146,11 +145,10 @@ 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:
+To do so we'll add this to both templates within the ``<div id="right"
+class="app-welcome align-right">`` div:
.. code-block:: xml
- :linenos:
<span tal:condition="logged_in">
<a href="${request.application_url}/logout">Logout</a>
diff --git a/docs/tutorials/wiki/basiclayout.rst b/docs/tutorials/wiki/basiclayout.rst
index f6e1f800a..4017d7af8 100644
--- a/docs/tutorials/wiki/basiclayout.rst
+++ b/docs/tutorials/wiki/basiclayout.rst
@@ -169,7 +169,6 @@ The ``development.ini`` (in the tutorial :term:`project` directory, as
opposed to the tutorial :term:`package` directory) looks like this:
.. literalinclude:: src/views/development.ini
- :linenos:
:language: ini
diff --git a/docs/tutorials/wiki/definingmodels.rst b/docs/tutorials/wiki/definingmodels.rst
index f201f6dc7..5e4b8fb22 100644
--- a/docs/tutorials/wiki/definingmodels.rst
+++ b/docs/tutorials/wiki/definingmodels.rst
@@ -163,7 +163,7 @@ On Windows:
.. code-block:: text
- c:\bigfntut\tutorial> ..\Scripts\python setup.py test -q
+ c:\pyramidtut\tutorial> ..\Scripts\python setup.py test -q
The expected output is something like this:
diff --git a/docs/tutorials/wiki/definingviews.rst b/docs/tutorials/wiki/definingviews.rst
index 5b0e5dca1..90768f320 100644
--- a/docs/tutorials/wiki/definingviews.rst
+++ b/docs/tutorials/wiki/definingviews.rst
@@ -230,7 +230,6 @@ 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
@@ -257,23 +256,24 @@ 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 Assets
-------------
-Our templates name a single static asset named ``style.css``. We need to
-create this and place it in a file named ``style.css`` within our package's
-``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/static/style.css>`_.
+Our templates name a single static asset named ``pylons.css``. We don't need
+to create this file within our package's ``static`` directory because it was
+provided at the time we created the project. 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/static/pylons.css>`_.
This CSS file will be accessed via
-e.g. ``http://localhost:6543/static/style.css`` by virtue of the call to
+e.g. ``http://localhost:6543/static/pylons.css`` by virtue of the call to
``add_static_view`` directive we've made in the ``__init__`` file. Any
number and type of static assets can be placed in this directory (or
-subdirectories) and are just referred to by URL within templates.
+subdirectories) and are just referred to by URL or by using the convenience
+method ``static_url`` e.g. ``request.static_url('{{package}}:static/foo.css')``
+within templates.
Testing the Views
=================
@@ -308,7 +308,7 @@ On Windows:
.. code-block:: text
- c:\bigfntut\tutorial> ..\Scripts\python setup.py test -q
+ c:\pyramidtut\tutorial> ..\Scripts\python setup.py test -q
The expected result looks something like:
diff --git a/docs/tutorials/wiki/distributing.rst b/docs/tutorials/wiki/distributing.rst
index ad717e72a..855d54eab 100644
--- a/docs/tutorials/wiki/distributing.rst
+++ b/docs/tutorials/wiki/distributing.rst
@@ -18,7 +18,7 @@ On Windows:
.. code-block:: text
- c:\bigfntut> ..\Scripts\python setup.py sdist
+ c:\pyramidtut> ..\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
diff --git a/docs/tutorials/wiki/installation.rst b/docs/tutorials/wiki/installation.rst
index 82265170d..95bc7a03e 100644
--- a/docs/tutorials/wiki/installation.rst
+++ b/docs/tutorials/wiki/installation.rst
@@ -26,28 +26,28 @@ Preparation, UNIX
<http://peak.telecommunity.com/dist/ez_setup.py>`_ and run it using
the ``python`` interpreter of your Python 2.6 installation:
- .. code-block:: bash
+ .. code-block:: text
$ /path/to/my/Python-2.6/bin/python ez_setup.py
#. Use that Python's `bin/easy_install` to install `virtualenv`:
- .. code-block:: bash
+ .. code-block:: text
$ /path/to/my/Python-2.6/bin/easy_install virtualenv
#. Use that Python's virtualenv to make a workspace:
- .. code-block:: bash
+ .. code-block:: text
$ path/to/my/Python-2.6/bin/virtualenv --no-site-packages \
- bigfntut
+ pyramidtut
-#. Switch to the ``bigfntut`` directory:
+#. Switch to the ``pyramidtut`` directory:
- .. code-block:: bash
+ .. code-block:: text
- $ cd bigfntut
+ $ cd pyramidtut
#. (Optional) Consider using ``source bin/activate`` to make your
shell environment wired to use the virtualenv.
@@ -55,14 +55,14 @@ Preparation, UNIX
#. Use ``easy_install`` to get :app:`Pyramid` and its direct
dependencies installed:
- .. code-block:: bash
+ .. code-block:: text
$ bin/easy_install pyramid
#. Use ``easy_install`` to install ``docutils``, ``repoze.tm``,
``repoze.zodbconn``, ``nose`` and ``coverage``:
- .. code-block:: bash
+ .. code-block:: text
$ bin/easy_install docutils repoze.tm repoze.zodbconn \
nose coverage
@@ -79,27 +79,27 @@ Preparation, Windows
the ``python`` interpreter of your Python 2.6 installation using a
command prompt:
- .. code-block:: bat
+ .. code-block:: text
c:\> c:\Python26\python ez_setup.py
#. Use that Python's `bin/easy_install` to install `virtualenv`:
- .. code-block:: bat
+ .. code-block:: text
c:\> c:\Python26\Scripts\easy_install virtualenv
#. Use that Python's virtualenv to make a workspace:
- .. code-block:: bat
+ .. code-block:: text
- c:\> c:\Python26\Scripts\virtualenv --no-site-packages bigfntut
+ c:\> c:\Python26\Scripts\virtualenv --no-site-packages pyramidtut
-#. Switch to the ``bigfntut`` directory:
+#. Switch to the ``pyramidtut`` directory:
- .. code-block:: bat
+ .. code-block:: text
- c:\> cd bigfntut
+ c:\> cd pyramidtut
#. (Optional) Consider using ``bin\activate.bat`` to make your shell
environment wired to use the virtualenv.
@@ -107,16 +107,16 @@ Preparation, Windows
#. Use ``easy_install`` to get :app:`Pyramid` and its direct
dependencies installed:
- .. code-block:: bat
+ .. code-block:: text
- c:\bigfntut> Scripts\easy_install pyramid
+ c:\pyramidtut> Scripts\easy_install pyramid
#. Use ``easy_install`` to install ``docutils``, ``repoze.tm``,
``repoze.zodbconn``, ``nose`` and ``coverage``:
- .. code-block:: bat
+ .. code-block:: text
- c:\bigfntut> Scripts\easy_install docutils repoze.tm \
+ c:\pyramidtut> Scripts\easy_install docutils repoze.tm \
repoze.zodbconn nose coverage
.. _making_a_project:
@@ -129,19 +129,19 @@ 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".
+"virtualenv" named "pyramidtut".
On UNIX:
-.. code-block:: bash
+.. code-block:: text
$ bin/paster create -t pyramid_zodb tutorial
On Windows:
-.. code-block:: bat
+.. code-block:: text
- c:\bigfntut> Scripts\paster create -t pyramid_zodb tutorial
+ c:\pyramidtut> 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
@@ -160,17 +160,17 @@ directory you created in :ref:`making_a_project`, and run the
On UNIX:
-.. code-block:: bash
+.. code-block:: text
$ cd tutorial
$ ../bin/python setup.py develop
On Windows:
-.. code-block:: bat
+.. code-block:: text
- C:\bigfntut> cd tutorial
- C:\bigfntut\tutorial> ..\Scripts\python setup.py develop
+ C:\pyramidtut> cd tutorial
+ C:\pyramidtut\tutorial> ..\Scripts\python setup.py develop
.. _running_tests:
@@ -182,15 +182,15 @@ the tests for the project.
On UNIX:
-.. code-block:: bash
+.. code-block:: text
$ ../bin/python setup.py test -q
On Windows:
-.. code-block:: bat
+.. code-block:: text
- c:\bigfntut\tutorial> ..\Scripts\python setup.py test -q
+ c:\pyramidtut\tutorial> ..\Scripts\python setup.py test -q
Starting the Application
========================
@@ -199,15 +199,15 @@ Start the application.
On UNIX:
-.. code-block:: bash
+.. code-block:: text
$ ../bin/paster serve development.ini --reload
On Windows:
-.. code-block:: bat
+.. code-block:: text
- c:\bifgfntut\tutorial> ..\Scripts\paster serve development.ini --reload
+ c:\pyramidtut\tutorial> ..\Scripts\paster serve development.ini --reload
Exposing Test Coverage Information
==================================
@@ -220,15 +220,15 @@ tests.
On UNIX:
-.. code-block:: bash
+.. code-block:: text
$ ../bin/nosetests --cover-package=tutorial --cover-erase --with-coverage
On Windows:
-.. code-block:: bat
+.. code-block:: text
- c:\bigfntut\tutorial> ..\Scripts\nosetests --cover-package=tutorial \
+ c:\pyramidtut\tutorial> ..\Scripts\nosetests --cover-package=tutorial \
--cover-erase --with-coverage
Looks like the code in the ``pyramid_zodb`` template for ZODB projects is
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/footerbg.png b/docs/tutorials/wiki/src/authorization/tutorial/static/footerbg.png
new file mode 100644
index 000000000..1fbc873da
--- /dev/null
+++ b/docs/tutorials/wiki/src/authorization/tutorial/static/footerbg.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/headerbg.png b/docs/tutorials/wiki/src/authorization/tutorial/static/headerbg.png
new file mode 100644
index 000000000..0596f2020
--- /dev/null
+++ b/docs/tutorials/wiki/src/authorization/tutorial/static/headerbg.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/ie6.css b/docs/tutorials/wiki/src/authorization/tutorial/static/ie6.css
new file mode 100644
index 000000000..b7c8493d8
--- /dev/null
+++ b/docs/tutorials/wiki/src/authorization/tutorial/static/ie6.css
@@ -0,0 +1,8 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
+#wrap{display:table;height:100%}
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/logo.png b/docs/tutorials/wiki/src/authorization/tutorial/static/logo.png
deleted file mode 100644
index 88f5d9865..000000000
--- a/docs/tutorials/wiki/src/authorization/tutorial/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/middlebg.png b/docs/tutorials/wiki/src/authorization/tutorial/static/middlebg.png
new file mode 100644
index 000000000..2369cfb7d
--- /dev/null
+++ b/docs/tutorials/wiki/src/authorization/tutorial/static/middlebg.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css b/docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css
index c153be07f..fd1914d8d 100644
--- a/docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css
@@ -4,34 +4,23 @@ body{line-height:1;}
ol,ul{list-style:none;}
blockquote,q{quotes:none;}
blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-/* remember to define focus styles! */
:focus{outline:0;}
-/* remember to highlight inserts somehow! */
ins{text-decoration:none;}
del{text-decoration:line-through;}
-/* tables still need 'cellspacing="0"' in the markup */
table{border-collapse:collapse;border-spacing:0;}
-/* restyling */
sub{vertical-align:sub;font-size:smaller;line-height:normal;}
sup{vertical-align:super;font-size:smaller;line-height:normal;}
-/* lists */
ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
li{display:list-item;}
-/* nested lists have no top/bottom margins */
ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-/* 2 deep unordered lists use a circle */
ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-/* 3 deep (or more) unordered lists use a square */
ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
.hidden{display:none;}
p{line-height:1.5em;}
-h1{font-size:1.75em;/* 28px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;/* 24px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;/* 20px */
-line-height:1.7em;font-family:helvetica,verdana;}
+h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
+h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
+h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
@@ -42,23 +31,26 @@ body h2,
body h3,
body h4,
body h5,
-body h6{font-family:"Nobile","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#144fb2;font-style:normal;}
-#wrap {min-height: 100%;}
-#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;}
-#header{background-color:#e88f00;top:0;font-size:14px;}
-#footer{background-color:#000000;bottom:0;position: relative;margin-top:-40px;clear:both;}
-.header,.footer{width:700px;margin-right:auto;margin-left:auto;}
+body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
+#wrap{min-height:100%;}
+#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
+#header{background:#000000;top:0;font-size:14px;}
+#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
+.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
.wrapper{width:100%}
-#top,#bottom{width:100%;}
-#top{color:#888;background-color:#eee;height:300px;border-bottom:2px solid #ddd;}
-#bottom{color:#222;background-color:#ffffff;overflow:hidden;padding-bottom:80px;}
-.top,.bottom{width:700px;margin-right:auto;margin-left:auto;}
-.top{padding-top:100px;}
+#top,#top-small,#bottom{width:100%;}
+#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#bottom{color:#222;background-color:#ffffff;}
+.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top{padding-top:40px;}
+.top-small{padding-top:10px;}
+#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
.app-welcome{margin-top:25px;}
.app-name{color:#000000;font-weight:bold;}
.bottom{padding-top:50px;}
-#left{width:325px;float:left;padding-right:25px;}
-#right{width:325px;float:right;padding-left:25px;}
+#left{width:350px;float:left;padding-right:25px;}
+#right{width:350px;float:right;padding-left:25px;}
.align-left{text-align:left;}
.align-right{text-align:right;}
.align-center{text-align:center;}
@@ -67,7 +59,7 @@ ul.links li{list-style-type:none;font-size:14px;}
form{border-style:none;}
fieldset{border-style:none;}
input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text]{}
+input[type=text],input[type=password]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*Opera Fix*/
-body:before {content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/pyramid-small.png b/docs/tutorials/wiki/src/authorization/tutorial/static/pyramid-small.png
new file mode 100644
index 000000000..a5bc0ade7
--- /dev/null
+++ b/docs/tutorials/wiki/src/authorization/tutorial/static/pyramid-small.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/pyramid.png b/docs/tutorials/wiki/src/authorization/tutorial/static/pyramid.png
new file mode 100644
index 000000000..347e05549
--- /dev/null
+++ b/docs/tutorials/wiki/src/authorization/tutorial/static/pyramid.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/style.css b/docs/tutorials/wiki/src/authorization/tutorial/static/style.css
deleted file mode 100644
index cad87e0d4..000000000
--- a/docs/tutorials/wiki/src/authorization/tutorial/static/style.css
+++ /dev/null
@@ -1,109 +0,0 @@
-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;
-}
-
-h2 {
- font-size: 150%;
-}
-
-#footer {
- border: 1px solid #aaa;
- border-top: 0px none;
- color: #999;
- background-color: white;
- padding: 10px;
- font-size: 80%;
- text-align: center;
- width: 757px;
- margin: 0 auto 1em auto;
-}
-
-.code {
- font-family: monospace;
-}
-
-span.code {
- font-weight: bold;
- background: #eee;
-}
-
-#status_block {
- margin: 0 auto 0.5em auto;
- padding: 15px 10px 15px 55px;
- background: #cec URL('../images/ok.png') left center no-repeat;
- border: 1px solid #9c9;
- width: 450px;
- font-size: 120%;
- font-weight: bolder;
-}
-
-.notice {
- margin: 0.5em auto 0.5em auto;
- padding: 15px 10px 15px 55px;
- width: 450px;
- background: #eef URL('../images/info.png') left center no-repeat;
- border: 1px solid #cce;
-}
-
-.fielderror {
- color: red;
- font-weight: bold;
-}
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/transparent.gif b/docs/tutorials/wiki/src/authorization/tutorial/static/transparent.gif
new file mode 100644
index 000000000..0341802e5
--- /dev/null
+++ b/docs/tutorials/wiki/src/authorization/tutorial/static/transparent.gif
Binary files differ
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/templates/edit.pt b/docs/tutorials/wiki/src/authorization/tutorial/templates/edit.pt
index 5f8b22207..0e5858d3b 100644
--- a/docs/tutorials/wiki/src/authorization/tutorial/templates/edit.pt
+++ b/docs/tutorials/wiki/src/authorization/tutorial/templates/edit.pt
@@ -1,34 +1,51 @@
-<!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">
-
+<!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" xml:lang="en" 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" />
+ <title>${page.__name__} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title>
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+ <meta name="keywords" content="python web application" />
+ <meta name="description" content="pyramid web application" />
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
+ <![endif]-->
</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>
+ <div id="wrap">
+ <div id="top-small">
+ <div class="top-small align-center">
+ <div>
+ <img src="${request.static_url('tutorial:static/pyramid-small.png')}" width="220" height="50" alt="pyramid" />
+ </div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-right">
+ <div id="left" class="app-welcome align-left">
+ Editing <b><span tal:replace="page.__name__">Page Name Goes Here</span></b><br/>
+ You can return to the <a href="${request.application_url}">FrontPage</a>.<br/>
+ </div>
+ <div id="right" class="app-welcome align-right">
+ <span tal:condition="logged_in">
+ <a href="${request.application_url}/logout">Logout</a>
+ </span>
+ </div>
+ </div>
+ </div>
+ <div id="bottom">
+ <div class="bottom">
+ <form action="${save_url}" method="post">
+ <textarea name="body" tal:content="page.data" rows="10" cols="60"/><br/>
+ <input type="submit" name="form.submitted" value="Save"/>
+ </form>
+ </div>
+ </div>
+ </div>
+ <div id="footer">
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
+ </div>
</body>
-</html>
+</html> \ No newline at end of file
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/templates/login.pt b/docs/tutorials/wiki/src/authorization/tutorial/templates/login.pt
index c56983d64..974e22f37 100644
--- a/docs/tutorials/wiki/src/authorization/tutorial/templates/login.pt
+++ b/docs/tutorials/wiki/src/authorization/tutorial/templates/login.pt
@@ -1,32 +1,49 @@
-<!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">
-
+<!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" xml:lang="en" 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" />
+ <title>Login - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title>
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+ <meta name="keywords" content="python web application" />
+ <meta name="description" content="pyramid web application" />
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
+ <![endif]-->
</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>
-
+ <div id="wrap">
+ <div id="top-small">
+ <div class="top-small align-center">
+ <div>
+ <img src="${request.static_url('tutorial:static/pyramid-small.png')}" width="220" height="50" alt="pyramid" />
+ </div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-right">
+ <div id="left" class="app-welcome align-left">
+ <b>Login</b><br/>
+ <span tal:replace="message"/>
+ </div>
+ <div id="right" class="app-welcome align-right"></div>
+ </div>
+ </div>
+ <div id="bottom">
+ <div class="bottom">
+ <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>
+ </div>
+ </div>
+ <div id="footer">
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
+ </div>
</body>
-</html>
+</html> \ No newline at end of file
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt
index a5a0dd214..cac9ccaa7 100644
--- a/docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt
@@ -5,65 +5,72 @@
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<meta name="keywords" content="python web application" />
<meta name="description" content="pyramid web application" />
- <link rel="shortcut icon" href="${request.application_url}/static/favicon.ico" />
- <link rel="stylesheet" href="${request.application_url}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
+ <![endif]-->
</head>
<body>
- <div id="header">
- <div class="header">The Pyramid Web Application Development Framework</div>
- </div>
- <div id="top">
- <div class="top align-center">
- <img src="${request.application_url}/static/logo.png" width="300" height="80"/>
- <p class="app-welcome">
- Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
- the Pyramid web application development framework.
- </p>
+ <div id="wrap">
+ <div id="top">
+ <div class="top align-center">
+ <div><img src="${request.static_url('tutorial:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div>
+ </div>
</div>
- </div>
- <div id="bottom">
- <div class="bottom">
- <div id="left" class="align-right">
- <h3>Search Pyramid documentation</h3>
- <form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
- <input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Search" />
- </form>
+ <div id="middle">
+ <div class="middle align-center">
+ <p class="app-welcome">
+ Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
+ the Pyramid web application development framework.
+ </p>
</div>
- <div id="right" class="align-left">
- <h3>Pyramid links</h3>
- <ul class="links">
- <li>
- <a href="http://pylonshq.com">Pylons Website</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#api-documentation">API Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#tutorials">Tutorials</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#change-history">Change History</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#sample-applications">Sample Applications</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#support-and-development">Support and Development</a>
- </li>
- <li>
- <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
- </li>
- </ul>
+ </div>
+ <div id="bottom">
+ <div class="bottom">
+ <div id="left" class="align-right">
+ <h2>Search documentation</h2>
+ <form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
+ <input type="text" id="q" name="q" value="" />
+ <input type="submit" id="x" value="Go" />
+ </form>
+ </div>
+ <div id="right" class="align-left">
+ <h2>Pyramid links</h2>
+ <ul class="links">
+ <li>
+ <a href="http://pylonshq.com">Pylons Website</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#api-documentation">API Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#tutorials">Tutorials</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#change-history">Change History</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#sample-applications">Sample Applications</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#support-and-development">Support and Development</a>
+ </li>
+ <li>
+ <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
+ </li>
+ </ul>
+ </div>
</div>
</div>
</div>
<div id="footer">
- <div class="footer">© Copyright 2008-2010, Agendaless Consulting.</div>
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
</div>
</body>
</html> \ No newline at end of file
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/templates/view.pt b/docs/tutorials/wiki/src/authorization/tutorial/templates/view.pt
index f957176f1..3fd709338 100644
--- a/docs/tutorials/wiki/src/authorization/tutorial/templates/view.pt
+++ b/docs/tutorials/wiki/src/authorization/tutorial/templates/view.pt
@@ -1,31 +1,55 @@
-<!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">
-
+<!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" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal">
<head>
- <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
- <title>${page.__name__} - Pyramid tutorial wiki
- (based on TurboGears 20-Minute Wiki)
- </title>
- <link rel="stylesheet" type="text/css"
- href="${request.application_url}/static/style.css" />
+ <title>${page.__name__} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title>
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+ <meta name="keywords" content="python web application" />
+ <meta name="description" content="pyramid web application" />
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
+ <![endif]-->
</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 tal:replace="structure content">Page text goes here.</div>
-<p><a tal:attributes="href edit_url" href="">Edit this page</a></p>
-</div>
-
-</body></html>
+ <div id="wrap">
+ <div id="top-small">
+ <div class="top-small align-center">
+ <div>
+ <img src="${request.static_url('tutorial:static/pyramid-small.png')}" width="220" height="50" alt="pyramid" />
+ </div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-right">
+ <div id="left" class="app-welcome align-left">
+ Viewing <b><span tal:replace="page.__name__">Page Name Goes Here</span></b><br/>
+ You can return to the <a href="${request.application_url}">FrontPage</a>.<br/>
+ </div>
+ <div id="right" class="app-welcome align-right">
+ <span tal:condition="logged_in">
+ <a href="${request.application_url}/logout">Logout</a>
+ </span>
+ </div>
+ </div>
+ </div>
+ <div id="bottom">
+ <div class="bottom">
+ <div tal:replace="structure content">
+ Page text goes here.
+ </div>
+ <p>
+ <a tal:attributes="href edit_url" href="">
+ Edit this page
+ </a>
+ </p>
+ </div>
+ </div>
+ </div>
+ <div id="footer">
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/footerbg.png b/docs/tutorials/wiki/src/basiclayout/tutorial/static/footerbg.png
new file mode 100644
index 000000000..1fbc873da
--- /dev/null
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/static/footerbg.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/headerbg.png b/docs/tutorials/wiki/src/basiclayout/tutorial/static/headerbg.png
new file mode 100644
index 000000000..0596f2020
--- /dev/null
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/static/headerbg.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/ie6.css b/docs/tutorials/wiki/src/basiclayout/tutorial/static/ie6.css
new file mode 100644
index 000000000..b7c8493d8
--- /dev/null
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/static/ie6.css
@@ -0,0 +1,8 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
+#wrap{display:table;height:100%}
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/logo.png b/docs/tutorials/wiki/src/basiclayout/tutorial/static/logo.png
deleted file mode 100644
index 88f5d9865..000000000
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/middlebg.png b/docs/tutorials/wiki/src/basiclayout/tutorial/static/middlebg.png
new file mode 100644
index 000000000..2369cfb7d
--- /dev/null
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/static/middlebg.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css b/docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css
index c153be07f..fd1914d8d 100644
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css
@@ -4,34 +4,23 @@ body{line-height:1;}
ol,ul{list-style:none;}
blockquote,q{quotes:none;}
blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-/* remember to define focus styles! */
:focus{outline:0;}
-/* remember to highlight inserts somehow! */
ins{text-decoration:none;}
del{text-decoration:line-through;}
-/* tables still need 'cellspacing="0"' in the markup */
table{border-collapse:collapse;border-spacing:0;}
-/* restyling */
sub{vertical-align:sub;font-size:smaller;line-height:normal;}
sup{vertical-align:super;font-size:smaller;line-height:normal;}
-/* lists */
ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
li{display:list-item;}
-/* nested lists have no top/bottom margins */
ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-/* 2 deep unordered lists use a circle */
ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-/* 3 deep (or more) unordered lists use a square */
ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
.hidden{display:none;}
p{line-height:1.5em;}
-h1{font-size:1.75em;/* 28px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;/* 24px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;/* 20px */
-line-height:1.7em;font-family:helvetica,verdana;}
+h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
+h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
+h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
@@ -42,23 +31,26 @@ body h2,
body h3,
body h4,
body h5,
-body h6{font-family:"Nobile","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#144fb2;font-style:normal;}
-#wrap {min-height: 100%;}
-#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;}
-#header{background-color:#e88f00;top:0;font-size:14px;}
-#footer{background-color:#000000;bottom:0;position: relative;margin-top:-40px;clear:both;}
-.header,.footer{width:700px;margin-right:auto;margin-left:auto;}
+body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
+#wrap{min-height:100%;}
+#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
+#header{background:#000000;top:0;font-size:14px;}
+#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
+.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
.wrapper{width:100%}
-#top,#bottom{width:100%;}
-#top{color:#888;background-color:#eee;height:300px;border-bottom:2px solid #ddd;}
-#bottom{color:#222;background-color:#ffffff;overflow:hidden;padding-bottom:80px;}
-.top,.bottom{width:700px;margin-right:auto;margin-left:auto;}
-.top{padding-top:100px;}
+#top,#top-small,#bottom{width:100%;}
+#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#bottom{color:#222;background-color:#ffffff;}
+.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top{padding-top:40px;}
+.top-small{padding-top:10px;}
+#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
.app-welcome{margin-top:25px;}
.app-name{color:#000000;font-weight:bold;}
.bottom{padding-top:50px;}
-#left{width:325px;float:left;padding-right:25px;}
-#right{width:325px;float:right;padding-left:25px;}
+#left{width:350px;float:left;padding-right:25px;}
+#right{width:350px;float:right;padding-left:25px;}
.align-left{text-align:left;}
.align-right{text-align:right;}
.align-center{text-align:center;}
@@ -67,7 +59,7 @@ ul.links li{list-style-type:none;font-size:14px;}
form{border-style:none;}
fieldset{border-style:none;}
input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text]{}
+input[type=text],input[type=password]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*Opera Fix*/
-body:before {content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid-small.png b/docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid-small.png
new file mode 100644
index 000000000..a5bc0ade7
--- /dev/null
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid-small.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid.png b/docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid.png
new file mode 100644
index 000000000..347e05549
--- /dev/null
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/transparent.gif b/docs/tutorials/wiki/src/basiclayout/tutorial/static/transparent.gif
new file mode 100644
index 000000000..0341802e5
--- /dev/null
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/static/transparent.gif
Binary files differ
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt
index 6ad23d44f..cac9ccaa7 100644
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt
@@ -5,23 +5,23 @@
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<meta name="keywords" content="python web application" />
<meta name="description" content="pyramid web application" />
- <link rel="shortcut icon" href="${request.application_url}/static/favicon.ico" />
- <link rel="stylesheet" href="${request.application_url}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
- <!--[if !IE 7]>
- <style type="text/css">
- #wrap {display:table;height:100%}
- </style>
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
<![endif]-->
</head>
<body>
<div id="wrap">
- <div id="header">
- <div class="header">The Pyramid Web Application Development Framework</div>
- </div>
<div id="top">
<div class="top align-center">
- <img src="${request.application_url}/static/logo.png" width="300" height="80"/>
+ <div><img src="${request.static_url('tutorial:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
the Pyramid web application development framework.
@@ -31,22 +31,19 @@
<div id="bottom">
<div class="bottom">
<div id="left" class="align-right">
- <h3>Search Pyramid documentation</h3>
+ <h2>Search documentation</h2>
<form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
<input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Search" />
+ <input type="submit" id="x" value="Go" />
</form>
</div>
<div id="right" class="align-left">
- <h3>Pyramid links</h3>
+ <h2>Pyramid links</h2>
<ul class="links">
<li>
<a href="http://pylonshq.com">Pylons Website</a>
</li>
<li>
- <a href="http://docs.pylonshq.com/">The Pylons Project Documentation</a>
- </li>
- <li>
<a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
</li>
<li>
@@ -73,7 +70,7 @@
</div>
</div>
<div id="footer">
- <div class="footer">© Copyright 2008-2010, Agendaless Consulting.</div>
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
</div>
</body>
</html> \ No newline at end of file
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/footerbg.png b/docs/tutorials/wiki/src/models/tutorial/static/footerbg.png
new file mode 100644
index 000000000..1fbc873da
--- /dev/null
+++ b/docs/tutorials/wiki/src/models/tutorial/static/footerbg.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/headerbg.png b/docs/tutorials/wiki/src/models/tutorial/static/headerbg.png
new file mode 100644
index 000000000..0596f2020
--- /dev/null
+++ b/docs/tutorials/wiki/src/models/tutorial/static/headerbg.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/ie6.css b/docs/tutorials/wiki/src/models/tutorial/static/ie6.css
new file mode 100644
index 000000000..b7c8493d8
--- /dev/null
+++ b/docs/tutorials/wiki/src/models/tutorial/static/ie6.css
@@ -0,0 +1,8 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
+#wrap{display:table;height:100%}
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/logo.png b/docs/tutorials/wiki/src/models/tutorial/static/logo.png
deleted file mode 100644
index 88f5d9865..000000000
--- a/docs/tutorials/wiki/src/models/tutorial/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/middlebg.png b/docs/tutorials/wiki/src/models/tutorial/static/middlebg.png
new file mode 100644
index 000000000..2369cfb7d
--- /dev/null
+++ b/docs/tutorials/wiki/src/models/tutorial/static/middlebg.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/pylons.css b/docs/tutorials/wiki/src/models/tutorial/static/pylons.css
index c153be07f..a9f49cc85 100644
--- a/docs/tutorials/wiki/src/models/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki/src/models/tutorial/static/pylons.css
@@ -4,34 +4,23 @@ body{line-height:1;}
ol,ul{list-style:none;}
blockquote,q{quotes:none;}
blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-/* remember to define focus styles! */
:focus{outline:0;}
-/* remember to highlight inserts somehow! */
ins{text-decoration:none;}
del{text-decoration:line-through;}
-/* tables still need 'cellspacing="0"' in the markup */
table{border-collapse:collapse;border-spacing:0;}
-/* restyling */
sub{vertical-align:sub;font-size:smaller;line-height:normal;}
sup{vertical-align:super;font-size:smaller;line-height:normal;}
-/* lists */
ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
li{display:list-item;}
-/* nested lists have no top/bottom margins */
ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-/* 2 deep unordered lists use a circle */
ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-/* 3 deep (or more) unordered lists use a square */
ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
.hidden{display:none;}
p{line-height:1.5em;}
-h1{font-size:1.75em;/* 28px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;/* 24px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;/* 20px */
-line-height:1.7em;font-family:helvetica,verdana;}
+h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
+h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
+h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
@@ -42,23 +31,26 @@ body h2,
body h3,
body h4,
body h5,
-body h6{font-family:"Nobile","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#144fb2;font-style:normal;}
-#wrap {min-height: 100%;}
-#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;}
-#header{background-color:#e88f00;top:0;font-size:14px;}
-#footer{background-color:#000000;bottom:0;position: relative;margin-top:-40px;clear:both;}
-.header,.footer{width:700px;margin-right:auto;margin-left:auto;}
+body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
+#wrap{min-height:100%;}
+#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
+#header{background:#000000;top:0;font-size:14px;}
+#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
+.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
.wrapper{width:100%}
-#top,#bottom{width:100%;}
-#top{color:#888;background-color:#eee;height:300px;border-bottom:2px solid #ddd;}
-#bottom{color:#222;background-color:#ffffff;overflow:hidden;padding-bottom:80px;}
-.top,.bottom{width:700px;margin-right:auto;margin-left:auto;}
-.top{padding-top:100px;}
+#top,#top-small,#bottom{width:100%;}
+#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#bottom{color:#222;background-color:#ffffff;}
+.top,.top-snall,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top{padding-top:40px;}
+.top-small{padding-top:10px;}
+#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
.app-welcome{margin-top:25px;}
.app-name{color:#000000;font-weight:bold;}
.bottom{padding-top:50px;}
-#left{width:325px;float:left;padding-right:25px;}
-#right{width:325px;float:right;padding-left:25px;}
+#left{width:350px;float:left;padding-right:25px;}
+#right{width:350px;float:right;padding-left:25px;}
.align-left{text-align:left;}
.align-right{text-align:right;}
.align-center{text-align:center;}
@@ -67,7 +59,7 @@ ul.links li{list-style-type:none;font-size:14px;}
form{border-style:none;}
fieldset{border-style:none;}
input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text]{}
+input[type=text],input[type=password]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*Opera Fix*/
-body:before {content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/pyramid-small.png b/docs/tutorials/wiki/src/models/tutorial/static/pyramid-small.png
new file mode 100644
index 000000000..a5bc0ade7
--- /dev/null
+++ b/docs/tutorials/wiki/src/models/tutorial/static/pyramid-small.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/pyramid.png b/docs/tutorials/wiki/src/models/tutorial/static/pyramid.png
new file mode 100644
index 000000000..347e05549
--- /dev/null
+++ b/docs/tutorials/wiki/src/models/tutorial/static/pyramid.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/transparent.gif b/docs/tutorials/wiki/src/models/tutorial/static/transparent.gif
new file mode 100644
index 000000000..0341802e5
--- /dev/null
+++ b/docs/tutorials/wiki/src/models/tutorial/static/transparent.gif
Binary files differ
diff --git a/docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt
index a5a0dd214..cac9ccaa7 100644
--- a/docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt
@@ -5,65 +5,72 @@
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<meta name="keywords" content="python web application" />
<meta name="description" content="pyramid web application" />
- <link rel="shortcut icon" href="${request.application_url}/static/favicon.ico" />
- <link rel="stylesheet" href="${request.application_url}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
+ <![endif]-->
</head>
<body>
- <div id="header">
- <div class="header">The Pyramid Web Application Development Framework</div>
- </div>
- <div id="top">
- <div class="top align-center">
- <img src="${request.application_url}/static/logo.png" width="300" height="80"/>
- <p class="app-welcome">
- Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
- the Pyramid web application development framework.
- </p>
+ <div id="wrap">
+ <div id="top">
+ <div class="top align-center">
+ <div><img src="${request.static_url('tutorial:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div>
+ </div>
</div>
- </div>
- <div id="bottom">
- <div class="bottom">
- <div id="left" class="align-right">
- <h3>Search Pyramid documentation</h3>
- <form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
- <input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Search" />
- </form>
+ <div id="middle">
+ <div class="middle align-center">
+ <p class="app-welcome">
+ Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
+ the Pyramid web application development framework.
+ </p>
</div>
- <div id="right" class="align-left">
- <h3>Pyramid links</h3>
- <ul class="links">
- <li>
- <a href="http://pylonshq.com">Pylons Website</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#api-documentation">API Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#tutorials">Tutorials</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#change-history">Change History</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#sample-applications">Sample Applications</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#support-and-development">Support and Development</a>
- </li>
- <li>
- <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
- </li>
- </ul>
+ </div>
+ <div id="bottom">
+ <div class="bottom">
+ <div id="left" class="align-right">
+ <h2>Search documentation</h2>
+ <form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
+ <input type="text" id="q" name="q" value="" />
+ <input type="submit" id="x" value="Go" />
+ </form>
+ </div>
+ <div id="right" class="align-left">
+ <h2>Pyramid links</h2>
+ <ul class="links">
+ <li>
+ <a href="http://pylonshq.com">Pylons Website</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#api-documentation">API Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#tutorials">Tutorials</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#change-history">Change History</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#sample-applications">Sample Applications</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#support-and-development">Support and Development</a>
+ </li>
+ <li>
+ <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
+ </li>
+ </ul>
+ </div>
</div>
</div>
</div>
<div id="footer">
- <div class="footer">© Copyright 2008-2010, Agendaless Consulting.</div>
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
</div>
</body>
</html> \ No newline at end of file
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/footerbg.png b/docs/tutorials/wiki/src/views/tutorial/static/footerbg.png
new file mode 100644
index 000000000..1fbc873da
--- /dev/null
+++ b/docs/tutorials/wiki/src/views/tutorial/static/footerbg.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/headerbg.png b/docs/tutorials/wiki/src/views/tutorial/static/headerbg.png
new file mode 100644
index 000000000..0596f2020
--- /dev/null
+++ b/docs/tutorials/wiki/src/views/tutorial/static/headerbg.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/ie6.css b/docs/tutorials/wiki/src/views/tutorial/static/ie6.css
new file mode 100644
index 000000000..b7c8493d8
--- /dev/null
+++ b/docs/tutorials/wiki/src/views/tutorial/static/ie6.css
@@ -0,0 +1,8 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
+#wrap{display:table;height:100%}
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/logo.png b/docs/tutorials/wiki/src/views/tutorial/static/logo.png
deleted file mode 100644
index 88f5d9865..000000000
--- a/docs/tutorials/wiki/src/views/tutorial/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/middlebg.png b/docs/tutorials/wiki/src/views/tutorial/static/middlebg.png
new file mode 100644
index 000000000..2369cfb7d
--- /dev/null
+++ b/docs/tutorials/wiki/src/views/tutorial/static/middlebg.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/pylons.css b/docs/tutorials/wiki/src/views/tutorial/static/pylons.css
index c153be07f..fd1914d8d 100644
--- a/docs/tutorials/wiki/src/views/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki/src/views/tutorial/static/pylons.css
@@ -4,34 +4,23 @@ body{line-height:1;}
ol,ul{list-style:none;}
blockquote,q{quotes:none;}
blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-/* remember to define focus styles! */
:focus{outline:0;}
-/* remember to highlight inserts somehow! */
ins{text-decoration:none;}
del{text-decoration:line-through;}
-/* tables still need 'cellspacing="0"' in the markup */
table{border-collapse:collapse;border-spacing:0;}
-/* restyling */
sub{vertical-align:sub;font-size:smaller;line-height:normal;}
sup{vertical-align:super;font-size:smaller;line-height:normal;}
-/* lists */
ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
li{display:list-item;}
-/* nested lists have no top/bottom margins */
ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-/* 2 deep unordered lists use a circle */
ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-/* 3 deep (or more) unordered lists use a square */
ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
.hidden{display:none;}
p{line-height:1.5em;}
-h1{font-size:1.75em;/* 28px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;/* 24px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;/* 20px */
-line-height:1.7em;font-family:helvetica,verdana;}
+h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
+h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
+h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
@@ -42,23 +31,26 @@ body h2,
body h3,
body h4,
body h5,
-body h6{font-family:"Nobile","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#144fb2;font-style:normal;}
-#wrap {min-height: 100%;}
-#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;}
-#header{background-color:#e88f00;top:0;font-size:14px;}
-#footer{background-color:#000000;bottom:0;position: relative;margin-top:-40px;clear:both;}
-.header,.footer{width:700px;margin-right:auto;margin-left:auto;}
+body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
+#wrap{min-height:100%;}
+#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
+#header{background:#000000;top:0;font-size:14px;}
+#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
+.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
.wrapper{width:100%}
-#top,#bottom{width:100%;}
-#top{color:#888;background-color:#eee;height:300px;border-bottom:2px solid #ddd;}
-#bottom{color:#222;background-color:#ffffff;overflow:hidden;padding-bottom:80px;}
-.top,.bottom{width:700px;margin-right:auto;margin-left:auto;}
-.top{padding-top:100px;}
+#top,#top-small,#bottom{width:100%;}
+#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#bottom{color:#222;background-color:#ffffff;}
+.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top{padding-top:40px;}
+.top-small{padding-top:10px;}
+#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
.app-welcome{margin-top:25px;}
.app-name{color:#000000;font-weight:bold;}
.bottom{padding-top:50px;}
-#left{width:325px;float:left;padding-right:25px;}
-#right{width:325px;float:right;padding-left:25px;}
+#left{width:350px;float:left;padding-right:25px;}
+#right{width:350px;float:right;padding-left:25px;}
.align-left{text-align:left;}
.align-right{text-align:right;}
.align-center{text-align:center;}
@@ -67,7 +59,7 @@ ul.links li{list-style-type:none;font-size:14px;}
form{border-style:none;}
fieldset{border-style:none;}
input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text]{}
+input[type=text],input[type=password]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*Opera Fix*/
-body:before {content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/pyramid-small.png b/docs/tutorials/wiki/src/views/tutorial/static/pyramid-small.png
new file mode 100644
index 000000000..a5bc0ade7
--- /dev/null
+++ b/docs/tutorials/wiki/src/views/tutorial/static/pyramid-small.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/pyramid.png b/docs/tutorials/wiki/src/views/tutorial/static/pyramid.png
new file mode 100644
index 000000000..347e05549
--- /dev/null
+++ b/docs/tutorials/wiki/src/views/tutorial/static/pyramid.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/style.css b/docs/tutorials/wiki/src/views/tutorial/static/style.css
deleted file mode 100644
index cad87e0d4..000000000
--- a/docs/tutorials/wiki/src/views/tutorial/static/style.css
+++ /dev/null
@@ -1,109 +0,0 @@
-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;
-}
-
-h2 {
- font-size: 150%;
-}
-
-#footer {
- border: 1px solid #aaa;
- border-top: 0px none;
- color: #999;
- background-color: white;
- padding: 10px;
- font-size: 80%;
- text-align: center;
- width: 757px;
- margin: 0 auto 1em auto;
-}
-
-.code {
- font-family: monospace;
-}
-
-span.code {
- font-weight: bold;
- background: #eee;
-}
-
-#status_block {
- margin: 0 auto 0.5em auto;
- padding: 15px 10px 15px 55px;
- background: #cec URL('../images/ok.png') left center no-repeat;
- border: 1px solid #9c9;
- width: 450px;
- font-size: 120%;
- font-weight: bolder;
-}
-
-.notice {
- margin: 0.5em auto 0.5em auto;
- padding: 15px 10px 15px 55px;
- width: 450px;
- background: #eef URL('../images/info.png') left center no-repeat;
- border: 1px solid #cce;
-}
-
-.fielderror {
- color: red;
- font-weight: bold;
-}
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/transparent.gif b/docs/tutorials/wiki/src/views/tutorial/static/transparent.gif
new file mode 100644
index 000000000..0341802e5
--- /dev/null
+++ b/docs/tutorials/wiki/src/views/tutorial/static/transparent.gif
Binary files differ
diff --git a/docs/tutorials/wiki/src/views/tutorial/templates/edit.pt b/docs/tutorials/wiki/src/views/tutorial/templates/edit.pt
index 1d40f526d..077a0dcb7 100644
--- a/docs/tutorials/wiki/src/views/tutorial/templates/edit.pt
+++ b/docs/tutorials/wiki/src/views/tutorial/templates/edit.pt
@@ -1,32 +1,47 @@
-<!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">
-
+<!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" xml:lang="en" 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" />
+ <title>${page.__name__} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title>
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+ <meta name="keywords" content="python web application" />
+ <meta name="description" content="pyramid web application" />
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
+ <![endif]-->
</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>.
- </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>
+ <div id="wrap">
+ <div id="top-small">
+ <div class="top-small align-center">
+ <div>
+ <img src="${request.static_url('tutorial:static/pyramid-small.png')}" width="220" height="50" alt="pyramid" />
+ </div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-right">
+ <div id="left" class="app-welcome align-left">
+ Editing <b><span tal:replace="page.__name__">Page Name Goes Here</span></b><br/>
+ You can return to the <a href="${request.application_url}">FrontPage</a>.<br/>
+ </div>
+ <div id="right" class="app-welcome align-right"></div>
+ </div>
+ </div>
+ <div id="bottom">
+ <div class="bottom">
+ <form action="${save_url}" method="post">
+ <textarea name="body" tal:content="page.data" rows="10" cols="60"/><br/>
+ <input type="submit" name="form.submitted" value="Save"/>
+ </form>
+ </div>
+ </div>
+ </div>
+ <div id="footer">
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
+ </div>
</body>
-</html>
+</html> \ No newline at end of file
diff --git a/docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt
index a5a0dd214..cac9ccaa7 100644
--- a/docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt
@@ -5,65 +5,72 @@
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<meta name="keywords" content="python web application" />
<meta name="description" content="pyramid web application" />
- <link rel="shortcut icon" href="${request.application_url}/static/favicon.ico" />
- <link rel="stylesheet" href="${request.application_url}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
+ <![endif]-->
</head>
<body>
- <div id="header">
- <div class="header">The Pyramid Web Application Development Framework</div>
- </div>
- <div id="top">
- <div class="top align-center">
- <img src="${request.application_url}/static/logo.png" width="300" height="80"/>
- <p class="app-welcome">
- Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
- the Pyramid web application development framework.
- </p>
+ <div id="wrap">
+ <div id="top">
+ <div class="top align-center">
+ <div><img src="${request.static_url('tutorial:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div>
+ </div>
</div>
- </div>
- <div id="bottom">
- <div class="bottom">
- <div id="left" class="align-right">
- <h3>Search Pyramid documentation</h3>
- <form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
- <input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Search" />
- </form>
+ <div id="middle">
+ <div class="middle align-center">
+ <p class="app-welcome">
+ Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
+ the Pyramid web application development framework.
+ </p>
</div>
- <div id="right" class="align-left">
- <h3>Pyramid links</h3>
- <ul class="links">
- <li>
- <a href="http://pylonshq.com">Pylons Website</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#api-documentation">API Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#tutorials">Tutorials</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#change-history">Change History</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#sample-applications">Sample Applications</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#support-and-development">Support and Development</a>
- </li>
- <li>
- <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
- </li>
- </ul>
+ </div>
+ <div id="bottom">
+ <div class="bottom">
+ <div id="left" class="align-right">
+ <h2>Search documentation</h2>
+ <form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
+ <input type="text" id="q" name="q" value="" />
+ <input type="submit" id="x" value="Go" />
+ </form>
+ </div>
+ <div id="right" class="align-left">
+ <h2>Pyramid links</h2>
+ <ul class="links">
+ <li>
+ <a href="http://pylonshq.com">Pylons Website</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#api-documentation">API Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#tutorials">Tutorials</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#change-history">Change History</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#sample-applications">Sample Applications</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#support-and-development">Support and Development</a>
+ </li>
+ <li>
+ <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
+ </li>
+ </ul>
+ </div>
</div>
</div>
</div>
<div id="footer">
- <div class="footer">© Copyright 2008-2010, Agendaless Consulting.</div>
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
</div>
</body>
</html> \ No newline at end of file
diff --git a/docs/tutorials/wiki/src/views/tutorial/templates/view.pt b/docs/tutorials/wiki/src/views/tutorial/templates/view.pt
index 50719f9e9..f676c1d25 100644
--- a/docs/tutorials/wiki/src/views/tutorial/templates/view.pt
+++ b/docs/tutorials/wiki/src/views/tutorial/templates/view.pt
@@ -1,29 +1,51 @@
-<!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">
-
+<!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" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal">
<head>
- <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
- <title>${page.__name__} - Pyramid tutorial wiki
- (based on TurboGears 20-Minute Wiki)
- </title>
- <link rel="stylesheet" type="text/css"
- href="${request.application_url}/static/style.css" />
+ <title>${page.__name__} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title>
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+ <meta name="keywords" content="python web application" />
+ <meta name="description" content="pyramid web application" />
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
+ <![endif]-->
</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>.
-</div>
-
-<div tal:replace="structure content">Page text goes here.</div>
-<p><a tal:attributes="href edit_url" href="">Edit this page</a></p>
-</div>
-
-</body></html>
+ <div id="wrap">
+ <div id="top-small">
+ <div class="top-small align-center">
+ <div>
+ <img src="${request.static_url('tutorial:static/pyramid-small.png')}" width="220" height="50" alt="pyramid" />
+ </div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-right">
+ <div id="left" class="app-welcome align-left">
+ Viewing <b><span tal:replace="page.__name__">Page Name Goes Here</span></b><br/>
+ You can return to the <a href="${request.application_url}">FrontPage</a>.<br/>
+ </div>
+ <div id="right" class="app-welcome align-right"></div>
+ </div>
+ </div>
+ <div id="bottom">
+ <div class="bottom">
+ <div tal:replace="structure content">
+ Page text goes here.
+ </div>
+ <p>
+ <a tal:attributes="href edit_url" href="">
+ Edit this page
+ </a>
+ </p>
+ </div>
+ </div>
+ </div>
+ <div id="footer">
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst
index 8d30ab807..5b07fe788 100644
--- a/docs/tutorials/wiki2/authorization.rst
+++ b/docs/tutorials/wiki2/authorization.rst
@@ -202,7 +202,6 @@ 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``
@@ -212,11 +211,10 @@ 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:
+To do so we'll add this to both templates within the ``<div id="right"
+class="app-welcome align-right">`` div:
.. code-block:: xml
- :linenos:
<span tal:condition="logged_in">
<a href="${request.application_url}/logout">Logout</a>
@@ -261,13 +259,11 @@ Our ``views.py`` module will look something like this when we're done:
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
diff --git a/docs/tutorials/wiki2/definingviews.rst b/docs/tutorials/wiki2/definingviews.rst
index e3d611136..d4417ed0b 100644
--- a/docs/tutorials/wiki2/definingviews.rst
+++ b/docs/tutorials/wiki2/definingviews.rst
@@ -212,7 +212,6 @@ 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
@@ -240,25 +239,24 @@ 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
-``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/wiki2/src/views/tutorial/static/style.css>`_.
+Static Assets
+-------------
+Our templates name a single static asset named ``pylons.css``. We don't need
+to create this file within our package's ``static`` directory because it was
+provided at the time we created the project. 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/wiki2/src/views/tutorial/static/pylons.css>`_.
This CSS file will be accessed via
-e.g. ``http://localhost:6543/static/style.css`` by virtue of the call we've
-made to :meth:`pyramid.config.Configurator.add_static_view` within our
-``__init__.py`` 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.
+e.g. ``http://localhost:6543/static/pylons.css`` by virtue of the call to
+``add_static_view`` directive we've made in the ``__init__`` file. Any
+number and type of static assets can be placed in this directory (or
+subdirectories) and are just referred to by URL or by using the convenience
+method ``static_url`` e.g. ``request.static_url('{{package}}:static/foo.css')``
+within templates.
Mapping Views to URLs in ``__init__.py``
========================================
diff --git a/docs/tutorials/wiki2/distributing.rst b/docs/tutorials/wiki2/distributing.rst
index f4421e145..9f5db19ae 100644
--- a/docs/tutorials/wiki2/distributing.rst
+++ b/docs/tutorials/wiki2/distributing.rst
@@ -18,7 +18,7 @@ On Windows:
.. code-block:: text
- c:\bigfntut> ..\Scripts\python setup.py sdist
+ c:\pyramidtut> ..\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
diff --git a/docs/tutorials/wiki2/installation.rst b/docs/tutorials/wiki2/installation.rst
index e6fd5e6b9..c957f641e 100644
--- a/docs/tutorials/wiki2/installation.rst
+++ b/docs/tutorials/wiki2/installation.rst
@@ -45,13 +45,13 @@ Preparation, UNIX
.. code-block:: text
- $ path/to/my/Python-2.6/bin/virtualenv --no-site-packages bigfntut
+ $ path/to/my/Python-2.6/bin/virtualenv --no-site-packages pyramidtut
-#. Switch to the ``bigfntut`` directory:
+#. Switch to the ``pyramidtut`` directory:
.. code-block:: text
- $ cd bigfntut
+ $ cd pyramidtut
#. (Optional) Consider using ``source bin/activate`` to make your
shell environment wired to use the virtualenv.
@@ -96,13 +96,13 @@ Preparation, Windows
.. code-block:: text
- c:\> c:\Python26\Scripts\virtualenv --no-site-packages bigfntut
+ c:\> c:\Python26\Scripts\virtualenv --no-site-packages pyramidtut
-#. Switch to the ``bigfntut`` directory:
+#. Switch to the ``pyramidtut`` directory:
.. code-block:: text
- c:\> cd bigfntut
+ c:\> cd pyramidtut
#. (Optional) Consider using ``bin\activate.bat`` to make your shell
environment wired to use the virtualenv.
@@ -112,13 +112,13 @@ Preparation, Windows
.. code-block:: text
- c:\bigfntut> Scripts\easy_install pyramid
+ c:\pyramidtut> Scripts\easy_install pyramid
#. Use ``easy_install`` to install various packages from PyPI.
.. code-block:: text
- c:\bigfntut> Scripts\easy_install -i docutils \
+ c:\pyramidtut> Scripts\easy_install -i docutils \
nose coverage zope.sqlalchemy SQLAlchemy repoze.tm2
@@ -133,7 +133,7 @@ variety of templates to generate sample projects. We will use the
that uses :term:`SQLAlchemy` and :term:`URL dispatch`.
The below instructions assume your current working directory is the
-"virtualenv" named "bigfntut".
+"virtualenv" named "pyramidtut".
On UNIX:
@@ -145,7 +145,7 @@ On Windows:
.. code-block:: text
- c:\bigfntut> Scripts\paster create -t pyramid_routesalchemy tutorial
+ c:\pyramidtut> Scripts\paster create -t pyramid_routesalchemy tutorial
.. note:: If you are using Windows, the ``pyramid_routesalchemy``
Paster template may not deal gracefully with installation into a
@@ -173,8 +173,8 @@ On Windows:
.. code-block:: text
- c:\bigfntut> cd tutorial
- c:\bigfntut\tutorial> ..\Scripts\python setup.py develop
+ c:\pyramidtut> cd tutorial
+ c:\pyramidtut\tutorial> ..\Scripts\python setup.py develop
.. _sql_running_tests:
@@ -194,7 +194,7 @@ On Windows:
.. code-block:: text
- c:\bigfntut\tutorial> ..\Scripts\python setup.py test -q
+ c:\pyramidtut\tutorial> ..\Scripts\python setup.py test -q
Starting the Application
========================
@@ -211,7 +211,7 @@ On Windows:
.. code-block:: text
- c:\bifgfntut\tutorial> ..\Scripts\paster serve development.ini --reload
+ c:\pyramidtut\tutorial> ..\Scripts\paster serve development.ini --reload
Exposing Test Coverage Information
==================================
@@ -235,7 +235,7 @@ On Windows:
.. code-block:: text
- c:\bigfntut\tutorial> ..\Scripts\easy_install nose coverage
+ c:\pyramidtut\tutorial> ..\Scripts\easy_install nose coverage
Once ``nose`` and ``coverage`` are installed, we can actually run the
coverage tests.
@@ -250,7 +250,7 @@ On Windows:
.. code-block:: text
- c:\bigfntut\tutorial> ..\Scripts\nosetests --cover-package=tutorial \
+ c:\pyramidtut\tutorial> ..\Scripts\nosetests --cover-package=tutorial \
--cover-erase --with-coverage
Looks like our package's ``models`` module doesn't quite have 100%
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/footerbg.png b/docs/tutorials/wiki2/src/authorization/tutorial/static/footerbg.png
new file mode 100644
index 000000000..1fbc873da
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/static/footerbg.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/headerbg.png b/docs/tutorials/wiki2/src/authorization/tutorial/static/headerbg.png
new file mode 100644
index 000000000..0596f2020
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/static/headerbg.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/ie6.css b/docs/tutorials/wiki2/src/authorization/tutorial/static/ie6.css
new file mode 100644
index 000000000..b7c8493d8
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/static/ie6.css
@@ -0,0 +1,8 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
+#wrap{display:table;height:100%}
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/logo.png b/docs/tutorials/wiki2/src/authorization/tutorial/static/logo.png
deleted file mode 100644
index 88f5d9865..000000000
--- a/docs/tutorials/wiki2/src/authorization/tutorial/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/middlebg.png b/docs/tutorials/wiki2/src/authorization/tutorial/static/middlebg.png
new file mode 100644
index 000000000..2369cfb7d
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/static/middlebg.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css b/docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css
index c153be07f..fd1914d8d 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css
@@ -4,34 +4,23 @@ body{line-height:1;}
ol,ul{list-style:none;}
blockquote,q{quotes:none;}
blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-/* remember to define focus styles! */
:focus{outline:0;}
-/* remember to highlight inserts somehow! */
ins{text-decoration:none;}
del{text-decoration:line-through;}
-/* tables still need 'cellspacing="0"' in the markup */
table{border-collapse:collapse;border-spacing:0;}
-/* restyling */
sub{vertical-align:sub;font-size:smaller;line-height:normal;}
sup{vertical-align:super;font-size:smaller;line-height:normal;}
-/* lists */
ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
li{display:list-item;}
-/* nested lists have no top/bottom margins */
ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-/* 2 deep unordered lists use a circle */
ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-/* 3 deep (or more) unordered lists use a square */
ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
.hidden{display:none;}
p{line-height:1.5em;}
-h1{font-size:1.75em;/* 28px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;/* 24px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;/* 20px */
-line-height:1.7em;font-family:helvetica,verdana;}
+h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
+h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
+h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
@@ -42,23 +31,26 @@ body h2,
body h3,
body h4,
body h5,
-body h6{font-family:"Nobile","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#144fb2;font-style:normal;}
-#wrap {min-height: 100%;}
-#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;}
-#header{background-color:#e88f00;top:0;font-size:14px;}
-#footer{background-color:#000000;bottom:0;position: relative;margin-top:-40px;clear:both;}
-.header,.footer{width:700px;margin-right:auto;margin-left:auto;}
+body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
+#wrap{min-height:100%;}
+#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
+#header{background:#000000;top:0;font-size:14px;}
+#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
+.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
.wrapper{width:100%}
-#top,#bottom{width:100%;}
-#top{color:#888;background-color:#eee;height:300px;border-bottom:2px solid #ddd;}
-#bottom{color:#222;background-color:#ffffff;overflow:hidden;padding-bottom:80px;}
-.top,.bottom{width:700px;margin-right:auto;margin-left:auto;}
-.top{padding-top:100px;}
+#top,#top-small,#bottom{width:100%;}
+#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#bottom{color:#222;background-color:#ffffff;}
+.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top{padding-top:40px;}
+.top-small{padding-top:10px;}
+#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
.app-welcome{margin-top:25px;}
.app-name{color:#000000;font-weight:bold;}
.bottom{padding-top:50px;}
-#left{width:325px;float:left;padding-right:25px;}
-#right{width:325px;float:right;padding-left:25px;}
+#left{width:350px;float:left;padding-right:25px;}
+#right{width:350px;float:right;padding-left:25px;}
.align-left{text-align:left;}
.align-right{text-align:right;}
.align-center{text-align:center;}
@@ -67,7 +59,7 @@ ul.links li{list-style-type:none;font-size:14px;}
form{border-style:none;}
fieldset{border-style:none;}
input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text]{}
+input[type=text],input[type=password]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*Opera Fix*/
-body:before {content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid-small.png b/docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid-small.png
new file mode 100644
index 000000000..a5bc0ade7
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid-small.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid.png b/docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid.png
new file mode 100644
index 000000000..347e05549
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/style.css b/docs/tutorials/wiki2/src/authorization/tutorial/static/style.css
deleted file mode 100644
index cad87e0d4..000000000
--- a/docs/tutorials/wiki2/src/authorization/tutorial/static/style.css
+++ /dev/null
@@ -1,109 +0,0 @@
-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;
-}
-
-h2 {
- font-size: 150%;
-}
-
-#footer {
- border: 1px solid #aaa;
- border-top: 0px none;
- color: #999;
- background-color: white;
- padding: 10px;
- font-size: 80%;
- text-align: center;
- width: 757px;
- margin: 0 auto 1em auto;
-}
-
-.code {
- font-family: monospace;
-}
-
-span.code {
- font-weight: bold;
- background: #eee;
-}
-
-#status_block {
- margin: 0 auto 0.5em auto;
- padding: 15px 10px 15px 55px;
- background: #cec URL('../images/ok.png') left center no-repeat;
- border: 1px solid #9c9;
- width: 450px;
- font-size: 120%;
- font-weight: bolder;
-}
-
-.notice {
- margin: 0.5em auto 0.5em auto;
- padding: 15px 10px 15px 55px;
- width: 450px;
- background: #eef URL('../images/info.png') left center no-repeat;
- border: 1px solid #cce;
-}
-
-.fielderror {
- color: red;
- font-weight: bold;
-}
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/transparent.gif b/docs/tutorials/wiki2/src/authorization/tutorial/static/transparent.gif
new file mode 100644
index 000000000..0341802e5
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/static/transparent.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt
index 05e2ecd76..e8c59eb10 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt
@@ -1,35 +1,51 @@
-<!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">
-
+<!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" xml:lang="en" 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" />
+ <title>${page.name} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title>
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+ <meta name="keywords" content="python web application" />
+ <meta name="description" content="pyramid web application" />
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
+ <![endif]-->
</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>
+ <div id="wrap">
+ <div id="top-small">
+ <div class="top-small align-center">
+ <div>
+ <img src="${request.static_url('tutorial:static/pyramid-small.png')}" width="220" height="50" alt="pyramid" />
+ </div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-right">
+ <div id="left" class="app-welcome align-left">
+ Editing <b><span tal:replace="page.name">Page Name Goes Here</span></b><br/>
+ You can return to the <a href="${request.application_url}">FrontPage</a>.<br/>
+ </div>
+ <div id="right" class="app-welcome align-right">
+ <span tal:condition="logged_in">
+ <a href="${request.application_url}/logout">Logout</a>
+ </span>
+ </div>
+ </div>
+ </div>
+ <div id="bottom">
+ <div class="bottom">
+ <form action="${save_url}" method="post">
+ <textarea name="body" tal:content="page.data" rows="10" cols="60"/><br/>
+ <input type="submit" name="form.submitted" value="Save"/>
+ </form>
+ </div>
+ </div>
+ </div>
+ <div id="footer">
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
+ </div>
</body>
-</html>
+</html> \ No newline at end of file
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt
index c56983d64..974e22f37 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt
@@ -1,32 +1,49 @@
-<!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">
-
+<!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" xml:lang="en" 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" />
+ <title>Login - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title>
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+ <meta name="keywords" content="python web application" />
+ <meta name="description" content="pyramid web application" />
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
+ <![endif]-->
</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>
-
+ <div id="wrap">
+ <div id="top-small">
+ <div class="top-small align-center">
+ <div>
+ <img src="${request.static_url('tutorial:static/pyramid-small.png')}" width="220" height="50" alt="pyramid" />
+ </div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-right">
+ <div id="left" class="app-welcome align-left">
+ <b>Login</b><br/>
+ <span tal:replace="message"/>
+ </div>
+ <div id="right" class="app-welcome align-right"></div>
+ </div>
+ </div>
+ <div id="bottom">
+ <div class="bottom">
+ <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>
+ </div>
+ </div>
+ <div id="footer">
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
+ </div>
</body>
-</html>
+</html> \ No newline at end of file
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt
index 6ad23d44f..cac9ccaa7 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt
@@ -5,23 +5,23 @@
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<meta name="keywords" content="python web application" />
<meta name="description" content="pyramid web application" />
- <link rel="shortcut icon" href="${request.application_url}/static/favicon.ico" />
- <link rel="stylesheet" href="${request.application_url}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
- <!--[if !IE 7]>
- <style type="text/css">
- #wrap {display:table;height:100%}
- </style>
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
<![endif]-->
</head>
<body>
<div id="wrap">
- <div id="header">
- <div class="header">The Pyramid Web Application Development Framework</div>
- </div>
<div id="top">
<div class="top align-center">
- <img src="${request.application_url}/static/logo.png" width="300" height="80"/>
+ <div><img src="${request.static_url('tutorial:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
the Pyramid web application development framework.
@@ -31,22 +31,19 @@
<div id="bottom">
<div class="bottom">
<div id="left" class="align-right">
- <h3>Search Pyramid documentation</h3>
+ <h2>Search documentation</h2>
<form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
<input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Search" />
+ <input type="submit" id="x" value="Go" />
</form>
</div>
<div id="right" class="align-left">
- <h3>Pyramid links</h3>
+ <h2>Pyramid links</h2>
<ul class="links">
<li>
<a href="http://pylonshq.com">Pylons Website</a>
</li>
<li>
- <a href="http://docs.pylonshq.com/">The Pylons Project Documentation</a>
- </li>
- <li>
<a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
</li>
<li>
@@ -73,7 +70,7 @@
</div>
</div>
<div id="footer">
- <div class="footer">© Copyright 2008-2010, Agendaless Consulting.</div>
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
</div>
</body>
</html> \ No newline at end of file
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt
index 0c654250a..d0360429c 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt
@@ -1,31 +1,55 @@
-<!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">
-
+<!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" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal">
<head>
- <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
- <title>${page.name} - Pyramid tutorial wiki
- (based on TurboGears 20-Minute Wiki)</title>
- <link rel="stylesheet" type="text/css"
- href="${request.application_url}/static/style.css" />
+ <title>${page.name} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title>
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+ <meta name="keywords" content="python web application" />
+ <meta name="description" content="pyramid web application" />
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
+ <![endif]-->
</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 tal:replace="structure content">Page text goes here.</div>
-<p><a tal:attributes="href edit_url" href="">Edit this page</a></p>
-</div>
-
+ <div id="wrap">
+ <div id="top-small">
+ <div class="top-small align-center">
+ <div>
+ <img src="${request.static_url('tutorial:static/pyramid-small.png')}" width="220" height="50" alt="pyramid" />
+ </div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-right">
+ <div id="left" class="app-welcome align-left">
+ Viewing <b><span tal:replace="page.name">Page Name Goes Here</span></b><br/>
+ You can return to the <a href="${request.application_url}">FrontPage</a>.<br/>
+ </div>
+ <div id="right" class="app-welcome align-right">
+ <span tal:condition="logged_in">
+ <a href="${request.application_url}/logout">Logout</a>
+ </span>
+ </div>
+ </div>
+ </div>
+ <div id="bottom">
+ <div class="bottom">
+ <div tal:replace="structure content">
+ Page text goes here.
+ </div>
+ <p>
+ <a tal:attributes="href edit_url" href="">
+ Edit this page
+ </a>
+ </p>
+ </div>
+ </div>
+ </div>
+ <div id="footer">
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
+ </div>
</body>
-</html>
+</html> \ No newline at end of file
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/footerbg.png b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/footerbg.png
new file mode 100644
index 000000000..1fbc873da
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/footerbg.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/headerbg.png b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/headerbg.png
new file mode 100644
index 000000000..0596f2020
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/headerbg.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/ie6.css b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/ie6.css
new file mode 100644
index 000000000..b7c8493d8
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/ie6.css
@@ -0,0 +1,8 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
+#wrap{display:table;height:100%}
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/logo.png b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/logo.png
deleted file mode 100644
index 88f5d9865..000000000
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/middlebg.png b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/middlebg.png
new file mode 100644
index 000000000..2369cfb7d
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/middlebg.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css
index c153be07f..fd1914d8d 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css
@@ -4,34 +4,23 @@ body{line-height:1;}
ol,ul{list-style:none;}
blockquote,q{quotes:none;}
blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-/* remember to define focus styles! */
:focus{outline:0;}
-/* remember to highlight inserts somehow! */
ins{text-decoration:none;}
del{text-decoration:line-through;}
-/* tables still need 'cellspacing="0"' in the markup */
table{border-collapse:collapse;border-spacing:0;}
-/* restyling */
sub{vertical-align:sub;font-size:smaller;line-height:normal;}
sup{vertical-align:super;font-size:smaller;line-height:normal;}
-/* lists */
ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
li{display:list-item;}
-/* nested lists have no top/bottom margins */
ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-/* 2 deep unordered lists use a circle */
ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-/* 3 deep (or more) unordered lists use a square */
ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
.hidden{display:none;}
p{line-height:1.5em;}
-h1{font-size:1.75em;/* 28px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;/* 24px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;/* 20px */
-line-height:1.7em;font-family:helvetica,verdana;}
+h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
+h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
+h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
@@ -42,23 +31,26 @@ body h2,
body h3,
body h4,
body h5,
-body h6{font-family:"Nobile","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#144fb2;font-style:normal;}
-#wrap {min-height: 100%;}
-#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;}
-#header{background-color:#e88f00;top:0;font-size:14px;}
-#footer{background-color:#000000;bottom:0;position: relative;margin-top:-40px;clear:both;}
-.header,.footer{width:700px;margin-right:auto;margin-left:auto;}
+body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
+#wrap{min-height:100%;}
+#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
+#header{background:#000000;top:0;font-size:14px;}
+#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
+.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
.wrapper{width:100%}
-#top,#bottom{width:100%;}
-#top{color:#888;background-color:#eee;height:300px;border-bottom:2px solid #ddd;}
-#bottom{color:#222;background-color:#ffffff;overflow:hidden;padding-bottom:80px;}
-.top,.bottom{width:700px;margin-right:auto;margin-left:auto;}
-.top{padding-top:100px;}
+#top,#top-small,#bottom{width:100%;}
+#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#bottom{color:#222;background-color:#ffffff;}
+.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top{padding-top:40px;}
+.top-small{padding-top:10px;}
+#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
.app-welcome{margin-top:25px;}
.app-name{color:#000000;font-weight:bold;}
.bottom{padding-top:50px;}
-#left{width:325px;float:left;padding-right:25px;}
-#right{width:325px;float:right;padding-left:25px;}
+#left{width:350px;float:left;padding-right:25px;}
+#right{width:350px;float:right;padding-left:25px;}
.align-left{text-align:left;}
.align-right{text-align:right;}
.align-center{text-align:center;}
@@ -67,7 +59,7 @@ ul.links li{list-style-type:none;font-size:14px;}
form{border-style:none;}
fieldset{border-style:none;}
input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text]{}
+input[type=text],input[type=password]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*Opera Fix*/
-body:before {content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid-small.png b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid-small.png
new file mode 100644
index 000000000..a5bc0ade7
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid-small.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid.png b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid.png
new file mode 100644
index 000000000..347e05549
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/transparent.gif b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/transparent.gif
new file mode 100644
index 000000000..0341802e5
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/transparent.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt
index 6ad23d44f..cac9ccaa7 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt
@@ -5,23 +5,23 @@
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<meta name="keywords" content="python web application" />
<meta name="description" content="pyramid web application" />
- <link rel="shortcut icon" href="${request.application_url}/static/favicon.ico" />
- <link rel="stylesheet" href="${request.application_url}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
- <!--[if !IE 7]>
- <style type="text/css">
- #wrap {display:table;height:100%}
- </style>
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
<![endif]-->
</head>
<body>
<div id="wrap">
- <div id="header">
- <div class="header">The Pyramid Web Application Development Framework</div>
- </div>
<div id="top">
<div class="top align-center">
- <img src="${request.application_url}/static/logo.png" width="300" height="80"/>
+ <div><img src="${request.static_url('tutorial:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
the Pyramid web application development framework.
@@ -31,22 +31,19 @@
<div id="bottom">
<div class="bottom">
<div id="left" class="align-right">
- <h3>Search Pyramid documentation</h3>
+ <h2>Search documentation</h2>
<form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
<input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Search" />
+ <input type="submit" id="x" value="Go" />
</form>
</div>
<div id="right" class="align-left">
- <h3>Pyramid links</h3>
+ <h2>Pyramid links</h2>
<ul class="links">
<li>
<a href="http://pylonshq.com">Pylons Website</a>
</li>
<li>
- <a href="http://docs.pylonshq.com/">The Pylons Project Documentation</a>
- </li>
- <li>
<a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
</li>
<li>
@@ -73,7 +70,7 @@
</div>
</div>
<div id="footer">
- <div class="footer">© Copyright 2008-2010, Agendaless Consulting.</div>
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
</div>
</body>
</html> \ No newline at end of file
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/footerbg.png b/docs/tutorials/wiki2/src/models/tutorial/static/footerbg.png
new file mode 100644
index 000000000..1fbc873da
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/static/footerbg.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/headerbg.png b/docs/tutorials/wiki2/src/models/tutorial/static/headerbg.png
new file mode 100644
index 000000000..0596f2020
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/static/headerbg.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/ie6.css b/docs/tutorials/wiki2/src/models/tutorial/static/ie6.css
new file mode 100644
index 000000000..b7c8493d8
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/static/ie6.css
@@ -0,0 +1,8 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
+#wrap{display:table;height:100%}
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/logo.png b/docs/tutorials/wiki2/src/models/tutorial/static/logo.png
deleted file mode 100644
index 88f5d9865..000000000
--- a/docs/tutorials/wiki2/src/models/tutorial/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/middlebg.png b/docs/tutorials/wiki2/src/models/tutorial/static/middlebg.png
new file mode 100644
index 000000000..2369cfb7d
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/static/middlebg.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/pylons.css b/docs/tutorials/wiki2/src/models/tutorial/static/pylons.css
index c153be07f..fd1914d8d 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki2/src/models/tutorial/static/pylons.css
@@ -4,34 +4,23 @@ body{line-height:1;}
ol,ul{list-style:none;}
blockquote,q{quotes:none;}
blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-/* remember to define focus styles! */
:focus{outline:0;}
-/* remember to highlight inserts somehow! */
ins{text-decoration:none;}
del{text-decoration:line-through;}
-/* tables still need 'cellspacing="0"' in the markup */
table{border-collapse:collapse;border-spacing:0;}
-/* restyling */
sub{vertical-align:sub;font-size:smaller;line-height:normal;}
sup{vertical-align:super;font-size:smaller;line-height:normal;}
-/* lists */
ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
li{display:list-item;}
-/* nested lists have no top/bottom margins */
ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-/* 2 deep unordered lists use a circle */
ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-/* 3 deep (or more) unordered lists use a square */
ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
.hidden{display:none;}
p{line-height:1.5em;}
-h1{font-size:1.75em;/* 28px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;/* 24px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;/* 20px */
-line-height:1.7em;font-family:helvetica,verdana;}
+h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
+h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
+h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
@@ -42,23 +31,26 @@ body h2,
body h3,
body h4,
body h5,
-body h6{font-family:"Nobile","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#144fb2;font-style:normal;}
-#wrap {min-height: 100%;}
-#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;}
-#header{background-color:#e88f00;top:0;font-size:14px;}
-#footer{background-color:#000000;bottom:0;position: relative;margin-top:-40px;clear:both;}
-.header,.footer{width:700px;margin-right:auto;margin-left:auto;}
+body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
+#wrap{min-height:100%;}
+#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
+#header{background:#000000;top:0;font-size:14px;}
+#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
+.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
.wrapper{width:100%}
-#top,#bottom{width:100%;}
-#top{color:#888;background-color:#eee;height:300px;border-bottom:2px solid #ddd;}
-#bottom{color:#222;background-color:#ffffff;overflow:hidden;padding-bottom:80px;}
-.top,.bottom{width:700px;margin-right:auto;margin-left:auto;}
-.top{padding-top:100px;}
+#top,#top-small,#bottom{width:100%;}
+#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#bottom{color:#222;background-color:#ffffff;}
+.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top{padding-top:40px;}
+.top-small{padding-top:10px;}
+#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
.app-welcome{margin-top:25px;}
.app-name{color:#000000;font-weight:bold;}
.bottom{padding-top:50px;}
-#left{width:325px;float:left;padding-right:25px;}
-#right{width:325px;float:right;padding-left:25px;}
+#left{width:350px;float:left;padding-right:25px;}
+#right{width:350px;float:right;padding-left:25px;}
.align-left{text-align:left;}
.align-right{text-align:right;}
.align-center{text-align:center;}
@@ -67,7 +59,7 @@ ul.links li{list-style-type:none;font-size:14px;}
form{border-style:none;}
fieldset{border-style:none;}
input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text]{}
+input[type=text],input[type=password]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*Opera Fix*/
-body:before {content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/pyramid-small.png b/docs/tutorials/wiki2/src/models/tutorial/static/pyramid-small.png
new file mode 100644
index 000000000..a5bc0ade7
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/static/pyramid-small.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/pyramid.png b/docs/tutorials/wiki2/src/models/tutorial/static/pyramid.png
new file mode 100644
index 000000000..347e05549
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/static/pyramid.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/transparent.gif b/docs/tutorials/wiki2/src/models/tutorial/static/transparent.gif
new file mode 100644
index 000000000..0341802e5
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/static/transparent.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt
index 6ad23d44f..cac9ccaa7 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt
@@ -5,23 +5,23 @@
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<meta name="keywords" content="python web application" />
<meta name="description" content="pyramid web application" />
- <link rel="shortcut icon" href="${request.application_url}/static/favicon.ico" />
- <link rel="stylesheet" href="${request.application_url}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
- <!--[if !IE 7]>
- <style type="text/css">
- #wrap {display:table;height:100%}
- </style>
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
<![endif]-->
</head>
<body>
<div id="wrap">
- <div id="header">
- <div class="header">The Pyramid Web Application Development Framework</div>
- </div>
<div id="top">
<div class="top align-center">
- <img src="${request.application_url}/static/logo.png" width="300" height="80"/>
+ <div><img src="${request.static_url('tutorial:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
the Pyramid web application development framework.
@@ -31,22 +31,19 @@
<div id="bottom">
<div class="bottom">
<div id="left" class="align-right">
- <h3>Search Pyramid documentation</h3>
+ <h2>Search documentation</h2>
<form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
<input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Search" />
+ <input type="submit" id="x" value="Go" />
</form>
</div>
<div id="right" class="align-left">
- <h3>Pyramid links</h3>
+ <h2>Pyramid links</h2>
<ul class="links">
<li>
<a href="http://pylonshq.com">Pylons Website</a>
</li>
<li>
- <a href="http://docs.pylonshq.com/">The Pylons Project Documentation</a>
- </li>
- <li>
<a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
</li>
<li>
@@ -73,7 +70,7 @@
</div>
</div>
<div id="footer">
- <div class="footer">© Copyright 2008-2010, Agendaless Consulting.</div>
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
</div>
</body>
</html> \ No newline at end of file
diff --git a/docs/tutorials/wiki2/src/views/tutorial/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/__init__.py
index 334fde814..1a8d24499 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/__init__.py
@@ -10,7 +10,7 @@ def main(global_config, **settings):
initialize_sql(engine)
config = Configurator(settings=settings)
config.add_static_view('static', 'tutorial:static')
- config.add_route('home', '/', view='tutorial.views.view_wiki')
+ config.add_route('view_wiki', '/', view='tutorial.views.view_wiki')
config.add_route('view_page', '/{pagename}',
view='tutorial.views.view_page',
view_renderer='tutorial:templates/view.pt')
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/footerbg.png b/docs/tutorials/wiki2/src/views/tutorial/static/footerbg.png
new file mode 100644
index 000000000..1fbc873da
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/static/footerbg.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/headerbg.png b/docs/tutorials/wiki2/src/views/tutorial/static/headerbg.png
new file mode 100644
index 000000000..0596f2020
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/static/headerbg.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/ie6.css b/docs/tutorials/wiki2/src/views/tutorial/static/ie6.css
new file mode 100644
index 000000000..b7c8493d8
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/static/ie6.css
@@ -0,0 +1,8 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
+#wrap{display:table;height:100%}
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/logo.png b/docs/tutorials/wiki2/src/views/tutorial/static/logo.png
deleted file mode 100644
index 88f5d9865..000000000
--- a/docs/tutorials/wiki2/src/views/tutorial/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/middlebg.png b/docs/tutorials/wiki2/src/views/tutorial/static/middlebg.png
new file mode 100644
index 000000000..2369cfb7d
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/static/middlebg.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/pylons.css b/docs/tutorials/wiki2/src/views/tutorial/static/pylons.css
index c153be07f..fd1914d8d 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki2/src/views/tutorial/static/pylons.css
@@ -4,34 +4,23 @@ body{line-height:1;}
ol,ul{list-style:none;}
blockquote,q{quotes:none;}
blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-/* remember to define focus styles! */
:focus{outline:0;}
-/* remember to highlight inserts somehow! */
ins{text-decoration:none;}
del{text-decoration:line-through;}
-/* tables still need 'cellspacing="0"' in the markup */
table{border-collapse:collapse;border-spacing:0;}
-/* restyling */
sub{vertical-align:sub;font-size:smaller;line-height:normal;}
sup{vertical-align:super;font-size:smaller;line-height:normal;}
-/* lists */
ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
li{display:list-item;}
-/* nested lists have no top/bottom margins */
ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-/* 2 deep unordered lists use a circle */
ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-/* 3 deep (or more) unordered lists use a square */
ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
.hidden{display:none;}
p{line-height:1.5em;}
-h1{font-size:1.75em;/* 28px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;/* 24px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;/* 20px */
-line-height:1.7em;font-family:helvetica,verdana;}
+h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
+h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
+h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
@@ -42,23 +31,26 @@ body h2,
body h3,
body h4,
body h5,
-body h6{font-family:"Nobile","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#144fb2;font-style:normal;}
-#wrap {min-height: 100%;}
-#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;}
-#header{background-color:#e88f00;top:0;font-size:14px;}
-#footer{background-color:#000000;bottom:0;position: relative;margin-top:-40px;clear:both;}
-.header,.footer{width:700px;margin-right:auto;margin-left:auto;}
+body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
+#wrap{min-height:100%;}
+#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
+#header{background:#000000;top:0;font-size:14px;}
+#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
+.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
.wrapper{width:100%}
-#top,#bottom{width:100%;}
-#top{color:#888;background-color:#eee;height:300px;border-bottom:2px solid #ddd;}
-#bottom{color:#222;background-color:#ffffff;overflow:hidden;padding-bottom:80px;}
-.top,.bottom{width:700px;margin-right:auto;margin-left:auto;}
-.top{padding-top:100px;}
+#top,#top-small,#bottom{width:100%;}
+#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#bottom{color:#222;background-color:#ffffff;}
+.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top{padding-top:40px;}
+.top-small{padding-top:10px;}
+#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
.app-welcome{margin-top:25px;}
.app-name{color:#000000;font-weight:bold;}
.bottom{padding-top:50px;}
-#left{width:325px;float:left;padding-right:25px;}
-#right{width:325px;float:right;padding-left:25px;}
+#left{width:350px;float:left;padding-right:25px;}
+#right{width:350px;float:right;padding-left:25px;}
.align-left{text-align:left;}
.align-right{text-align:right;}
.align-center{text-align:center;}
@@ -67,7 +59,7 @@ ul.links li{list-style-type:none;font-size:14px;}
form{border-style:none;}
fieldset{border-style:none;}
input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text]{}
+input[type=text],input[type=password]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*Opera Fix*/
-body:before {content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/pyramid-small.png b/docs/tutorials/wiki2/src/views/tutorial/static/pyramid-small.png
new file mode 100644
index 000000000..a5bc0ade7
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/static/pyramid-small.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/pyramid.png b/docs/tutorials/wiki2/src/views/tutorial/static/pyramid.png
new file mode 100644
index 000000000..347e05549
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/static/pyramid.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/style.css b/docs/tutorials/wiki2/src/views/tutorial/static/style.css
deleted file mode 100644
index cad87e0d4..000000000
--- a/docs/tutorials/wiki2/src/views/tutorial/static/style.css
+++ /dev/null
@@ -1,109 +0,0 @@
-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;
-}
-
-h2 {
- font-size: 150%;
-}
-
-#footer {
- border: 1px solid #aaa;
- border-top: 0px none;
- color: #999;
- background-color: white;
- padding: 10px;
- font-size: 80%;
- text-align: center;
- width: 757px;
- margin: 0 auto 1em auto;
-}
-
-.code {
- font-family: monospace;
-}
-
-span.code {
- font-weight: bold;
- background: #eee;
-}
-
-#status_block {
- margin: 0 auto 0.5em auto;
- padding: 15px 10px 15px 55px;
- background: #cec URL('../images/ok.png') left center no-repeat;
- border: 1px solid #9c9;
- width: 450px;
- font-size: 120%;
- font-weight: bolder;
-}
-
-.notice {
- margin: 0.5em auto 0.5em auto;
- padding: 15px 10px 15px 55px;
- width: 450px;
- background: #eef URL('../images/info.png') left center no-repeat;
- border: 1px solid #cce;
-}
-
-.fielderror {
- color: red;
- font-weight: bold;
-}
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/transparent.gif b/docs/tutorials/wiki2/src/views/tutorial/static/transparent.gif
new file mode 100644
index 000000000..0341802e5
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/static/transparent.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.pt b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.pt
index 047a64eb3..b45cea3c6 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.pt
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.pt
@@ -1,32 +1,47 @@
-<!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">
-
+<!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" xml:lang="en" 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" />
+ <title>${page.name} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title>
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+ <meta name="keywords" content="python web application" />
+ <meta name="description" content="pyramid web application" />
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
+ <![endif]-->
</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>.
- </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>
+ <div id="wrap">
+ <div id="top-small">
+ <div class="top-small align-center">
+ <div>
+ <img src="${request.static_url('tutorial:static/pyramid-small.png')}" width="220" height="50" alt="pyramid" />
+ </div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-right">
+ <div id="left" class="app-welcome align-left">
+ Editing <b><span tal:replace="page.name">Page Name Goes Here</span></b><br/>
+ You can return to the <a href="${request.application_url}">FrontPage</a>.<br/>
+ </div>
+ <div id="right" class="app-welcome align-right"></div>
+ </div>
+ </div>
+ <div id="bottom">
+ <div class="bottom">
+ <form action="${save_url}" method="post">
+ <textarea name="body" tal:content="page.data" rows="10" cols="60"/><br/>
+ <input type="submit" name="form.submitted" value="Save"/>
+ </form>
+ </div>
+ </div>
+ </div>
+ <div id="footer">
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
+ </div>
</body>
-</html>
+</html> \ No newline at end of file
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt
index 6ad23d44f..cac9ccaa7 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt
@@ -5,23 +5,23 @@
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<meta name="keywords" content="python web application" />
<meta name="description" content="pyramid web application" />
- <link rel="shortcut icon" href="${request.application_url}/static/favicon.ico" />
- <link rel="stylesheet" href="${request.application_url}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
- <!--[if !IE 7]>
- <style type="text/css">
- #wrap {display:table;height:100%}
- </style>
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
<![endif]-->
</head>
<body>
<div id="wrap">
- <div id="header">
- <div class="header">The Pyramid Web Application Development Framework</div>
- </div>
<div id="top">
<div class="top align-center">
- <img src="${request.application_url}/static/logo.png" width="300" height="80"/>
+ <div><img src="${request.static_url('tutorial:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
the Pyramid web application development framework.
@@ -31,22 +31,19 @@
<div id="bottom">
<div class="bottom">
<div id="left" class="align-right">
- <h3>Search Pyramid documentation</h3>
+ <h2>Search documentation</h2>
<form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
<input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Search" />
+ <input type="submit" id="x" value="Go" />
</form>
</div>
<div id="right" class="align-left">
- <h3>Pyramid links</h3>
+ <h2>Pyramid links</h2>
<ul class="links">
<li>
<a href="http://pylonshq.com">Pylons Website</a>
</li>
<li>
- <a href="http://docs.pylonshq.com/">The Pylons Project Documentation</a>
- </li>
- <li>
<a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
</li>
<li>
@@ -73,7 +70,7 @@
</div>
</div>
<div id="footer">
- <div class="footer">© Copyright 2008-2010, Agendaless Consulting.</div>
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
</div>
</body>
</html> \ No newline at end of file
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/view.pt b/docs/tutorials/wiki2/src/views/tutorial/templates/view.pt
index 86fcc914e..cacb58788 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/templates/view.pt
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/view.pt
@@ -1,28 +1,51 @@
-<!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">
-
+<!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" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal">
<head>
- <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
- <title>${page.name} - Pyramid tutorial wiki
- (based on TurboGears 20-Minute Wiki)</title>
- <link rel="stylesheet" type="text/css"
- href="${request.application_url}/static/style.css" />
+ <title>${page.name} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title>
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+ <meta name="keywords" content="python web application" />
+ <meta name="description" content="pyramid web application" />
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
+ <![endif]-->
</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>.
-</div>
-
-<div tal:replace="structure content">Page text goes here.</div>
-<p><a tal:attributes="href edit_url" href="">Edit this page</a></p>
-</div>
-
+ <div id="wrap">
+ <div id="top-small">
+ <div class="top-small align-center">
+ <div>
+ <img src="${request.static_url('tutorial:static/pyramid-small.png')}" width="220" height="50" alt="pyramid" />
+ </div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-right">
+ <div id="left" class="app-welcome align-left">
+ Viewing <b><span tal:replace="page.name">Page Name Goes Here</span></b><br/>
+ You can return to the <a href="${request.application_url}">FrontPage</a>.<br/>
+ </div>
+ <div id="right" class="app-welcome align-right"></div>
+ </div>
+ </div>
+ <div id="bottom">
+ <div class="bottom">
+ <div tal:replace="structure content">
+ Page text goes here.
+ </div>
+ <p>
+ <a tal:attributes="href edit_url" href="">
+ Edit this page
+ </a>
+ </p>
+ </div>
+ </div>
+ </div>
+ <div id="footer">
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
+ </div>
</body>
-</html>
+</html> \ No newline at end of file
diff --git a/docs/zcml/handler.rst b/docs/zcml/handler.rst
index 01d442ab6..64aac7e78 100644
--- a/docs/zcml/handler.rst
+++ b/docs/zcml/handler.rst
@@ -155,4 +155,4 @@ You can also add a :term:`route configuration` via:
See Also
~~~~~~~~
-See also :ref:`handlers_chapter`.
+See also :ref:`views_chapter`.
diff --git a/docs/zcml/view.rst b/docs/zcml/view.rst
index b4fabdc2c..ba5c7bf23 100644
--- a/docs/zcml/view.rst
+++ b/docs/zcml/view.rst
@@ -213,6 +213,20 @@ Predicate Attributes
associated view callable will be considered viable for a given
request.
+``decorator``
+ A :term:`dotted Python name` to a function that will be used to decorate
+ the registered :term:`view callable`. The decorator function will be
+ called with the view callable as a single argument. The view callable it
+ is passed will accept ``(context, request)``. The decorator must return a
+ replacement view callable which also accepts ``(context, request)``.
+
+``mapper``
+ A :term:`dotted Python name` which refers to a :term:`view mapper`, or
+ ``None``. By default it is ``None``, which indicates that the view should
+ use the default view mapper. This plug-point is useful for Pyramid
+ extension developers, but it's not very useful for 'civilians' who are just
+ developing stock Pyramid applications.
+
Examples
~~~~~~~~
diff --git a/pyramid/authentication.py b/pyramid/authentication.py
index 86d725bcf..9de306b80 100644
--- a/pyramid/authentication.py
+++ b/pyramid/authentication.py
@@ -17,7 +17,7 @@ from pyramid.security import Everyone
class CallbackAuthenticationPolicy(object):
""" Abstract class """
def authenticated_userid(self, request):
- userid = self._get_userid(request)
+ userid = self.unauthenticated_userid(request)
if userid is None:
return None
if self.callback is None:
@@ -27,7 +27,7 @@ class CallbackAuthenticationPolicy(object):
def effective_principals(self, request):
effective_principals = [Everyone]
- userid = self._get_userid(request)
+ userid = self.unauthenticated_userid(request)
if userid is None:
return effective_principals
if self.callback is None:
@@ -89,6 +89,12 @@ class RepozeWho1AuthenticationPolicy(CallbackAuthenticationPolicy):
if self.callback(identity, request) is not None: # is not None!
return identity['repoze.who.userid']
+ def unauthenticated_userid(self, request):
+ identity = self._get_identity(request)
+ if identity is None:
+ return None
+ return identity['repoze.who.userid']
+
def effective_principals(self, request):
effective_principals = [Everyone]
identity = self._get_identity(request)
@@ -147,7 +153,7 @@ class RemoteUserAuthenticationPolicy(CallbackAuthenticationPolicy):
self.environ_key = environ_key
self.callback = callback
- def _get_userid(self, request):
+ def unauthenticated_userid(self, request):
return request.environ.get(self.environ_key)
def remember(self, request, principal, **kw):
@@ -264,7 +270,7 @@ class AuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
)
self.callback = callback
- def _get_userid(self, request):
+ def unauthenticated_userid(self, request):
result = self.cookie.identify(request)
if result:
return result['userid']
@@ -285,6 +291,12 @@ def b64decode(v):
EXPIRE = object()
class AuthTktCookieHelper(object):
+ """
+ A helper class for use in third-party authentication policy
+ implementations. See
+ :class:`pyramid.authentication.AuthTktAuthenticationPolicy` for the
+ meanings of the constructor arguments.
+ """
auth_tkt = auth_tkt # for tests
now = None # for tests
@@ -356,6 +368,8 @@ class AuthTktCookieHelper(object):
return cookies
def identify(self, request):
+ """ Return a dictionary with authentication information, or ``None``
+ if no valid auth_tkt is attached to ``request``"""
environ = request.environ
cookies = get_cookies(environ)
cookie = cookies.get(self.cookie_name)
@@ -411,11 +425,14 @@ class AuthTktCookieHelper(object):
return identity
def forget(self, request):
- # return a set of expires Set-Cookie headers
+ """ Return a set of expires Set-Cookie headers, which will destroy
+ any existing auth_tkt cookie when attached to a response"""
environ = request.environ
return self._get_cookies(environ, '', max_age=EXPIRE)
def remember(self, request, userid, max_age=None):
+ """ Return a set of Set-Cookie headers; when set into a response,
+ these headers will represent a valid authentication ticket."""
max_age = max_age or self.max_age
environ = request.environ
diff --git a/pyramid/chameleon_text.py b/pyramid/chameleon_text.py
index 32896b8e9..b687ecda9 100644
--- a/pyramid/chameleon_text.py
+++ b/pyramid/chameleon_text.py
@@ -4,39 +4,22 @@ from zope.deprecation import deprecated
from zope.interface import implements
try:
- from chameleon.core.template import TemplateFile
- TemplateFile # prevent pyflakes complaining about a redefinition below
+ from chameleon.zpt.template import PageTextTemplateFile
+ # prevent pyflakes complaining about a redefinition below
+ PageTextTemplateFile
except ImportError: # pragma: no cover
exc_class, exc, tb = sys.exc_info()
# Chameleon doesn't work on non-CPython platforms
- class TemplateFile(object):
+ class PageTextTemplateFile(object):
def __init__(self, *arg, **kw):
raise ImportError, exc, tb
-try:
- from chameleon.zpt.language import Parser
- Parser # prevent pyflakes complaining about a redefinition below
-except ImportError: # pragma: no cover
- # Chameleon doesn't work on non-CPython platforms
- class Parser(object):
- pass
-
from pyramid.interfaces import ITemplateRenderer
from pyramid.decorator import reify
from pyramid import renderers
from pyramid.path import caller_package
-class TextTemplateFile(TemplateFile):
- default_parser = Parser()
-
- def __init__(self, filename, parser=None, format='text', doctype=None,
- **kwargs):
- if parser is None:
- parser = self.default_parser
- super(TextTemplateFile, self).__init__(filename, parser, format,
- doctype, **kwargs)
-
def renderer_factory(info):
return renderers.template_renderer_factory(info, TextTemplateRenderer)
@@ -51,10 +34,10 @@ class TextTemplateRenderer(object):
if sys.platform.startswith('java'): # pragma: no cover
raise RuntimeError(
'Chameleon templates are not compatible with Jython')
- return TextTemplateFile(self.path,
- auto_reload=self.lookup.auto_reload,
- debug=self.lookup.debug,
- translate=self.lookup.translate)
+ return PageTextTemplateFile(self.path,
+ auto_reload=self.lookup.auto_reload,
+ debug=self.lookup.debug,
+ translate=self.lookup.translate)
def implementation(self):
return self.template
diff --git a/pyramid/config.py b/pyramid/config.py
index f6b4a2112..ff5d51fc4 100644
--- a/pyramid/config.py
+++ b/pyramid/config.py
@@ -45,6 +45,7 @@ from pyramid.interfaces import ITranslationDirectories
from pyramid.interfaces import ITraverser
from pyramid.interfaces import IView
from pyramid.interfaces import IViewClassifier
+from pyramid.interfaces import IViewMapperFactory
try:
from pyramid import chameleon_text
@@ -124,6 +125,7 @@ def action_method(wrapped):
return result
wrapper.__name__ = wrapped.__name__
wrapper.__doc__ = wrapped.__doc__
+ wrapper.__docobj__ = wrapped # for sphinx
return wrapper
class Configurator(object):
@@ -245,7 +247,13 @@ class Configurator(object):
``autocommit`` is ``True``. If a conflict is detected a
``ConfigurationConflictError`` will be raised. Calling
:meth:`pyramid.config.Configurator.make_wsgi_app` always implies a final
- commit."""
+ commit.
+
+ If ``default_view_mapper`` is passed, it will be used as the default
+ :term:`view mapper` factory for view configurations that don't otherwise
+ specify one (see :class:`pyramid.interfaces.IViewMapperFactory`). If a
+ default_view_mapper is not passed, a superdefault view mapper will be
+ used. """
manager = manager # for testing injection
venusian = venusian # for testing injection
@@ -266,6 +274,7 @@ class Configurator(object):
renderer_globals_factory=None,
default_permission=None,
session_factory=None,
+ default_view_mapper=None,
autocommit=False,
):
if package is None:
@@ -291,6 +300,7 @@ class Configurator(object):
renderer_globals_factory=renderer_globals_factory,
default_permission=default_permission,
session_factory=session_factory,
+ default_view_mapper=default_view_mapper,
)
def _set_settings(self, mapping):
@@ -338,30 +348,39 @@ class Configurator(object):
def _split_spec(self, path_or_spec):
return resolve_asset_spec(path_or_spec, self.package_name)
+ # b/w compat
def _derive_view(self, view, permission=None, predicates=(),
attr=None, renderer=None, wrapper_viewname=None,
viewname=None, accept=None, order=MAX_ORDER,
- phash=DEFAULT_PHASH):
- if renderer is None: # use default renderer if one exists
- default_renderer_factory = self.registry.queryUtility(
- IRendererFactory)
- if default_renderer_factory is not None:
- renderer = {'name':None, 'package':self.package}
+ phash=DEFAULT_PHASH, decorator=None,
+ mapper=None):
view = self.maybe_dotted(view)
- authn_policy = self.registry.queryUtility(IAuthenticationPolicy)
- authz_policy = self.registry.queryUtility(IAuthorizationPolicy)
- settings = self.registry.settings
- logger = self.registry.queryUtility(IDebugLogger)
- mapped_view = _map_view(view, self.registry, attr, renderer)
- owrapped_view = _owrap_view(mapped_view, viewname, wrapper_viewname)
- secured_view = _secure_view(owrapped_view, permission,
- authn_policy, authz_policy)
- debug_view = _authdebug_view(secured_view, permission,
- authn_policy, authz_policy, settings,
- logger)
- predicated_view = _predicate_wrap(debug_view, predicates)
- derived_view = _attr_wrap(predicated_view, accept, order, phash)
- return derived_view
+ mapper = self.maybe_dotted(mapper)
+ if isinstance(renderer, basestring):
+ renderer = RendererHelper(name=renderer, package=self.package,
+ registry = self.registry)
+ if renderer is None:
+ # use default renderer if one exists
+ if self.registry.queryUtility(IRendererFactory) is not None:
+ renderer = RendererHelper(name=None,
+ package=self.package,
+ registry=self.registry)
+
+ deriver = ViewDeriver(registry=self.registry,
+ permission=permission,
+ predicates=predicates,
+ attr=attr,
+ renderer=renderer,
+ wrapper_viewname=wrapper_viewname,
+ viewname=viewname,
+ accept=accept,
+ order=order,
+ phash=phash,
+ package=self.package,
+ mapper=mapper,
+ decorator=decorator)
+
+ return deriver(view)
def _override(self, package, path, override_package, override_prefix,
PackageOverrides=PackageOverrides):
@@ -417,8 +436,8 @@ class Configurator(object):
immediately if ``autocommit`` is ``True``).
.. note:: This method is typically only used by :app:`Pyramid`
- framework extension authors, not by :app:`Pyramid` application
- developers.
+ framework extension authors, not by :app:`Pyramid` application
+ developers.
The ``discriminator`` uniquely identifies the action. It must be
given, but it can be ``None``, to indicate that the action never
@@ -591,9 +610,8 @@ class Configurator(object):
authentication_policy=None, authorization_policy=None,
renderers=DEFAULT_RENDERERS, debug_logger=None,
locale_negotiator=None, request_factory=None,
- renderer_globals_factory=None,
- default_permission=None,
- session_factory=None):
+ renderer_globals_factory=None, default_permission=None,
+ session_factory=None, default_view_mapper=None):
""" When you pass a non-``None`` ``registry`` argument to the
:term:`Configurator` constructor, no initial 'setup' is performed
against the registry. This is because the registry you pass in may
@@ -639,7 +657,13 @@ class Configurator(object):
self.set_default_permission(default_permission)
if session_factory is not None:
self.set_session_factory(session_factory)
+ # commit before adding default_view_mapper, as the
+ # default_exceptionresponse_view above requires the superdefault view
+ # mapper
self.commit()
+ if default_view_mapper is not None:
+ self.set_view_mapper(default_view_mapper)
+ self.commit()
# getSiteManager is a unit testing dep injection
def hook_zca(self, getSiteManager=None):
@@ -757,9 +781,6 @@ class Configurator(object):
a :term:`response` object. If a ``renderer`` argument is not
supplied, the user-supplied view must itself return a
:term:`response` object. """
-
- if renderer is not None and not isinstance(renderer, dict):
- renderer = {'name':renderer, 'package':self.package}
return self._derive_view(view, attr=attr, renderer=renderer)
@action_method
@@ -923,7 +944,7 @@ class Configurator(object):
Any extra keyword arguments are passed along to ``add_route``.
- See :ref:`handlers_chapter` for more explanatory documentation.
+ See :ref:`views_chapter` for more explanatory documentation.
This method returns the result of add_route."""
handler = self.maybe_dotted(handler)
@@ -941,6 +962,20 @@ class Configurator(object):
pattern = route.pattern
+ action_decorator = getattr(handler, '__action_decorator__', None)
+ if action_decorator is not None:
+ if hasattr(action_decorator, 'im_self'):
+ # instance methods have an im_self == None
+ # classmethods have an im_self == cls
+ # staticmethods have no im_self
+ # instances have no im_self
+ if action_decorator.im_self is not handler:
+ raise ConfigurationError(
+ 'The "__action_decorator__" attribute of a handler '
+ 'must not be an instance method (must be a '
+ 'staticmethod, classmethod, function, or an instance '
+ 'which is a callable')
+
path_has_action = ':action' in pattern or '{action}' in pattern
if action and path_has_action:
@@ -970,7 +1005,8 @@ class Configurator(object):
preds.append(ActionPredicate(action))
view_args['custom_predicates'] = preds
self.add_view(view=handler, attr=method_name,
- route_name=route_name, **view_args)
+ route_name=route_name,
+ decorator=action_decorator, **view_args)
else:
method_name = action
if method_name is None:
@@ -993,14 +1029,15 @@ class Configurator(object):
view_args = expose_config.copy()
del view_args['name']
self.add_view(view=handler, attr=meth_name,
- route_name=route_name, **view_args)
+ route_name=route_name,
+ decorator=action_decorator, **view_args)
# Now register the method itself
method = getattr(handler, method_name, None)
configs = getattr(method, '__exposed__', [{}])
for expose_config in configs:
self.add_view(view=handler, attr=action, route_name=route_name,
- **expose_config)
+ decorator=action_decorator, **expose_config)
return route
@@ -1010,7 +1047,7 @@ class Configurator(object):
request_param=None, containment=None, attr=None,
renderer=None, wrapper=None, xhr=False, accept=None,
header=None, path_info=None, custom_predicates=(),
- context=None):
+ context=None, decorator=None, mapper=None):
""" Add a :term:`view configuration` to the current
configuration state. Arguments to ``add_view`` are broken
down below into *predicate* arguments and *non-predicate*
@@ -1119,6 +1156,25 @@ class Configurator(object):
view is the same context and request of the inner view. If
this attribute is unspecified, no view wrapping is done.
+ decorator
+
+ A :term:`dotted Python name` to function (or the function itself)
+ which will be used to decorate the registered :term:`view
+ callable`. The decorator function will be called with the view
+ callable as a single argument. The view callable it is passed will
+ accept ``(context, request)``. The decorator must return a
+ replacement view callable which also accepts ``(context,
+ request)``.
+
+ mapper
+
+ A Python object or :term:`dotted Python name` which refers to a
+ :term:`view mapper`, or ``None``. By default it is ``None``, which
+ indicates that the view should use the default view mapper. This
+ plug-point is useful for Pyramid extension developers, but it's not
+ very useful for 'civilians' who are just developing stock Pyramid
+ applications. Pay no attention to the man behind the curtain.
+
Predicate Arguments
name
@@ -1255,11 +1311,14 @@ class Configurator(object):
the context and/or the request. If all callables return
``True``, the associated view callable will be considered
viable for a given request.
+
"""
view = self.maybe_dotted(view)
context = self.maybe_dotted(context)
for_ = self.maybe_dotted(for_)
containment = self.maybe_dotted(containment)
+ mapper = self.maybe_dotted(mapper)
+ decorator = self.maybe_dotted(decorator)
if not view:
if renderer:
@@ -1293,6 +1352,7 @@ class Configurator(object):
renderer=renderer, wrapper=wrapper, xhr=xhr, accept=accept,
header=header, path_info=path_info,
custom_predicates=custom_predicates, context=context,
+ mapper = mapper,
)
view_info = deferred_views.setdefault(route_name, [])
view_info.append(info)
@@ -1304,9 +1364,6 @@ class Configurator(object):
containment=containment, request_type=request_type,
custom=custom_predicates)
- if renderer is not None and not isinstance(renderer, dict):
- renderer = {'name':renderer, 'package':self.package}
-
if context is None:
context = for_
@@ -1316,16 +1373,37 @@ class Configurator(object):
if not IInterface.providedBy(r_context):
r_context = implementedBy(r_context)
- def register(permission=permission):
+ if isinstance(renderer, basestring):
+ renderer = RendererHelper(name=renderer, package=self.package,
+ registry = self.registry)
+
+ def register(permission=permission, renderer=renderer):
+ if renderer is None:
+ # use default renderer if one exists
+ if self.registry.queryUtility(IRendererFactory) is not None:
+ renderer = RendererHelper(name=None,
+ package=self.package,
+ registry=self.registry)
if permission is None:
# intent: will be None if no default permission is registered
permission = self.registry.queryUtility(IDefaultPermission)
- # NO_PERMISSION_REQUIRED handled by _secure_view
- derived_view = self._derive_view(view, permission, predicates, attr,
- renderer, wrapper, name, accept,
- order, phash)
+ # __no_permission_required__ handled by _secure_view
+ deriver = ViewDeriver(registry=self.registry,
+ permission=permission,
+ predicates=predicates,
+ attr=attr,
+ renderer=renderer,
+ wrapper_viewname=wrapper,
+ viewname=name,
+ accept=accept,
+ order=order,
+ phash=phash,
+ package=self.package,
+ mapper=mapper,
+ decorator=decorator)
+ derived_view = deriver(view)
registered = self.registry.adapters.registered
@@ -1959,8 +2037,9 @@ class Configurator(object):
The ``wrapper`` argument should be the name of another view
which will wrap this view when rendered (see the ``add_view``
method's ``wrapper`` argument for a description)."""
- if renderer is not None and not isinstance(renderer, dict):
- renderer = {'name':renderer, 'package':self.package}
+ if isinstance(renderer, basestring):
+ renderer = RendererHelper(name=renderer, package=self.package,
+ registry = self.registry)
view = self._derive_view(view, attr=attr, renderer=renderer)
def bwcompat_view(context, request):
context = getattr(request, 'context', None)
@@ -1998,8 +2077,9 @@ class Configurator(object):
which will wrap this view when rendered (see the ``add_view``
method's ``wrapper`` argument for a description).
"""
- if renderer is not None and not isinstance(renderer, dict):
- renderer = {'name':renderer, 'package':self.package}
+ if isinstance(renderer, basestring):
+ renderer = RendererHelper(name=renderer, package=self.package,
+ registry=self.registry)
view = self._derive_view(view, attr=attr, renderer=renderer)
def bwcompat_view(context, request):
context = getattr(request, 'context', None)
@@ -2105,6 +2185,29 @@ class Configurator(object):
self.action(IDefaultPermission, None)
@action_method
+ def set_view_mapper(self, mapper):
+ """
+ Setting a :term:`view mapper` makes it possible to make use of
+ :term:`view callable` objects which implement different call
+ signatures than the ones supported by :app:`Pyramid` as described in
+ its narrative documentation.
+
+ The ``mapper`` should argument be an object implementing
+ :class:`pyramid.interfaces.IViewMapperFactory` or a :term:`dotted
+ Python name` to such an object.
+
+ The provided ``mapper`` will become the default view mapper to be
+ used by all subsequent :term:`view configuration` registrations, as
+ if you had passed a ``default_view_mapper`` argument to the
+ :class:`pyramid.config.Configurator` constructor.
+
+ See also :ref:`using_an_alternate_view_mapper`.
+ """
+ mapper = self.maybe_dotted(mapper)
+ self.registry.registerUtility(mapper, IViewMapperFactory)
+ self.action(IViewMapperFactory, None)
+
+ @action_method
def set_session_factory(self, session_factory):
"""
Configure the application with a :term:`session factory`. If
@@ -2158,11 +2261,6 @@ class Configurator(object):
# same function once for each added translation directory,
# which does too much work, but has the same effect.
- def translator(msg):
- request = get_current_request()
- localizer = get_localizer(request)
- return localizer.translate(msg)
-
ctranslate = ChameleonTranslate(translator)
self.registry.registerUtility(ctranslate, IChameleonTranslate)
@@ -2633,52 +2731,327 @@ class MultiView(object):
continue
raise PredicateMismatch(self.name)
-def decorate_view(wrapped_view, original_view):
- if wrapped_view is original_view:
- return False
- wrapped_view.__module__ = original_view.__module__
- wrapped_view.__doc__ = original_view.__doc__
+def wraps_view(wrapped):
+ def inner(self, view):
+ wrapped_view = wrapped(self, view)
+ return preserve_view_attrs(view, wrapped_view)
+ return inner
+
+def preserve_view_attrs(view, wrapped_view):
+ if wrapped_view is view:
+ return view
+ original_view = getattr(view, '__original_view__', None)
+ if original_view is None:
+ original_view = view
+ wrapped_view.__original_view__ = original_view
+ wrapped_view.__module__ = view.__module__
+ wrapped_view.__doc__ = view.__doc__
try:
- wrapped_view.__name__ = original_view.__name__
+ wrapped_view.__name__ = view.__name__
except AttributeError:
- wrapped_view.__name__ = repr(original_view)
+ wrapped_view.__name__ = repr(view)
try:
- wrapped_view.__permitted__ = original_view.__permitted__
+ wrapped_view.__permitted__ = view.__permitted__
except AttributeError:
pass
try:
- wrapped_view.__call_permissive__ = original_view.__call_permissive__
+ wrapped_view.__call_permissive__ = view.__call_permissive__
except AttributeError:
pass
try:
- wrapped_view.__predicated__ = original_view.__predicated__
+ wrapped_view.__predicated__ = view.__predicated__
except AttributeError:
pass
try:
- wrapped_view.__accept__ = original_view.__accept__
+ wrapped_view.__accept__ = view.__accept__
except AttributeError:
pass
try:
- wrapped_view.__order__ = original_view.__order__
+ wrapped_view.__order__ = view.__order__
except AttributeError:
pass
- return True
+ return wrapped_view
+
+class ViewDeriver(object):
+ def __init__(self, **kw):
+ self.kw = kw
+ self.registry = kw['registry']
+ self.authn_policy = self.registry.queryUtility(
+ IAuthenticationPolicy)
+ self.authz_policy = self.registry.queryUtility(
+ IAuthorizationPolicy)
+ self.logger = self.registry.queryUtility(IDebugLogger)
+
+ def __call__(self, view):
+ return self.attr_wrapped_view(
+ self.predicated_view(
+ self.authdebug_view(
+ self.secured_view(
+ self.owrapped_view(
+ self.decorated_view(
+ self.rendered_view(
+ self.mapped_view(view))))))))
+
+ @wraps_view
+ def mapped_view(self, view):
+ mapper = self.kw.get('mapper')
+ if mapper is None:
+ mapper = getattr(view, '__view_mapper__', None)
+ if mapper is None:
+ mapper = self.registry.queryUtility(IViewMapperFactory)
+ if mapper is None:
+ mapper = DefaultViewMapper
+
+ mapped_view = mapper(**self.kw)(view)
+ return mapped_view
+
+ @wraps_view
+ def owrapped_view(self, view):
+ wrapper_viewname = self.kw.get('wrapper_viewname')
+ viewname = self.kw.get('viewname')
+ if not wrapper_viewname:
+ return view
+ def _owrapped_view(context, request):
+ response = view(context, request)
+ request.wrapped_response = response
+ request.wrapped_body = response.body
+ request.wrapped_view = view
+ wrapped_response = render_view_to_response(context, request,
+ wrapper_viewname)
+ if wrapped_response is None:
+ raise ValueError(
+ 'No wrapper view named %r found when executing view '
+ 'named %r' % (wrapper_viewname, viewname))
+ return wrapped_response
+ return _owrapped_view
+
+ @wraps_view
+ def secured_view(self, view):
+ permission = self.kw.get('permission')
+ if permission == '__no_permission_required__':
+ # allow views registered within configurations that have a
+ # default permission to explicitly override the default
+ # permission, replacing it with no permission at all
+ permission = None
+
+ wrapped_view = view
+ if self.authn_policy and self.authz_policy and (permission is not None):
+ def _secured_view(context, request):
+ principals = self.authn_policy.effective_principals(request)
+ if self.authz_policy.permits(context, principals, permission):
+ return view(context, request)
+ msg = getattr(request, 'authdebug_message',
+ 'Unauthorized: %s failed permission check' % view)
+ raise Forbidden(msg)
+ _secured_view.__call_permissive__ = view
+ def _permitted(context, request):
+ principals = self.authn_policy.effective_principals(request)
+ return self.authz_policy.permits(context, principals,
+ permission)
+ _secured_view.__permitted__ = _permitted
+ wrapped_view = _secured_view
+
+ return wrapped_view
+
+ @wraps_view
+ def authdebug_view(self, view):
+ wrapped_view = view
+ settings = self.registry.settings
+ permission = self.kw.get('permission')
+ if settings and settings.get('debug_authorization', False):
+ def _authdebug_view(context, request):
+ view_name = getattr(request, 'view_name', None)
+
+ if self.authn_policy and self.authz_policy:
+ if permission is None:
+ msg = 'Allowed (no permission registered)'
+ else:
+ principals = self.authn_policy.effective_principals(
+ request)
+ msg = str(self.authz_policy.permits(context, principals,
+ permission))
+ else:
+ msg = 'Allowed (no authorization policy in use)'
+
+ view_name = getattr(request, 'view_name', None)
+ url = getattr(request, 'url', None)
+ msg = ('debug_authorization of url %s (view name %r against '
+ 'context %r): %s' % (url, view_name, context, msg))
+ self.logger and self.logger.debug(msg)
+ if request is not None:
+ request.authdebug_message = msg
+ return view(context, request)
+
+ wrapped_view = _authdebug_view
+
+ return wrapped_view
+
+ @wraps_view
+ def predicated_view(self, view):
+ predicates = self.kw.get('predicates', ())
+ if not predicates:
+ return view
+ def predicate_wrapper(context, request):
+ if all((predicate(context, request) for predicate in predicates)):
+ return view(context, request)
+ raise PredicateMismatch('predicate mismatch for view %s' % view)
+ def checker(context, request):
+ return all((predicate(context, request) for predicate in
+ predicates))
+ predicate_wrapper.__predicated__ = checker
+ return predicate_wrapper
+
+ @wraps_view
+ def attr_wrapped_view(self, view):
+ kw = self.kw
+ accept, order, phash = (kw.get('accept', None),
+ kw.get('order', MAX_ORDER),
+ kw.get('phash', DEFAULT_PHASH))
+ # this is a little silly but we don't want to decorate the original
+ # function with attributes that indicate accept, order, and phash,
+ # so we use a wrapper
+ if ( (accept is None) and (order == MAX_ORDER) and
+ (phash == DEFAULT_PHASH) ):
+ return view # defaults
+ def attr_view(context, request):
+ return view(context, request)
+ attr_view.__accept__ = accept
+ attr_view.__order__ = order
+ attr_view.__phash__ = phash
+ return attr_view
+
+ @wraps_view
+ def rendered_view(self, view):
+ wrapped_view = view
+ static_renderer = self.kw.get('renderer')
+ if static_renderer is None:
+ # register a default renderer if you want super-dynamic
+ # rendering. registering a default renderer will also allow
+ # override_renderer to work if a renderer is left unspecified for
+ # a view registration.
+ return view
+
+ def _rendered_view(context, request):
+ renderer = static_renderer
+ response = wrapped_view(context, request)
+ if not is_response(response):
+ attrs = getattr(request, '__dict__', {})
+ if 'override_renderer' in attrs:
+ # renderer overridden by newrequest event or other
+ renderer_name = attrs.pop('override_renderer')
+ renderer = RendererHelper(name=renderer_name,
+ package=self.kw.get('package'),
+ registry = self.kw['registry'])
+ if '__view__' in attrs:
+ view_inst = attrs.pop('__view__')
+ else:
+ view_inst = getattr(wrapped_view, '__original_view__',
+ wrapped_view)
+ return renderer.render_view(request, response, view_inst,
+ context)
+ return response
+
+ return _rendered_view
+
+ @wraps_view
+ def decorated_view(self, view):
+ decorator = self.kw.get('decorator')
+ if decorator is None:
+ return view
+ return decorator(view)
+
+class DefaultViewMapper(object):
+ implements(IViewMapperFactory)
+ def __init__(self, **kw):
+ self.attr = kw.get('attr')
+
+ def __call__(self, view):
+ if inspect.isclass(view):
+ view = self.map_class(view)
+ else:
+ view = self.map_nonclass(view)
+ return view
+
+ def map_class(self, view):
+ ronly = requestonly(view, self.attr)
+ if ronly:
+ mapped_view = self.map_class_requestonly(view)
+ else:
+ mapped_view = self.map_class_native(view)
+ return mapped_view
+
+ def map_nonclass(self, view):
+ # We do more work here than appears necessary to avoid wrapping the
+ # view unless it actually requires wrapping (to avoid function call
+ # overhead).
+ mapped_view = view
+ ronly = requestonly(view, self.attr)
+ if ronly:
+ mapped_view = self.map_nonclass_requestonly(view)
+ elif self.attr:
+ mapped_view = self.map_nonclass_attr(view)
+ return mapped_view
+
+ def map_class_requestonly(self, view):
+ # its a class that has an __init__ which only accepts request
+ attr = self.attr
+ def _class_requestonly_view(context, request):
+ inst = view(request)
+ request.__view__ = inst
+ if attr is None:
+ response = inst()
+ else:
+ response = getattr(inst, attr)()
+ return response
+ return _class_requestonly_view
+
+ def map_class_native(self, view):
+ # its a class that has an __init__ which accepts both context and
+ # request
+ attr = self.attr
+ def _class_view(context, request):
+ inst = view(context, request)
+ request.__view__ = inst
+ if attr is None:
+ response = inst()
+ else:
+ response = getattr(inst, attr)()
+ return response
+ return _class_view
+
+ def map_nonclass_requestonly(self, view):
+ # its a function that has a __call__ which accepts only a single
+ # request argument
+ attr = self.attr
+ def _requestonly_view(context, request):
+ if attr is None:
+ response = view(request)
+ else:
+ response = getattr(view, attr)(request)
+ return response
+ return _requestonly_view
+
+ def map_nonclass_attr(self, view):
+ # its a function that has a __call__ which accepts both context and
+ # request, but still has an attr
+ def _attr_view(context, request):
+ response = getattr(view, self.attr)(context, request)
+ return response
+ return _attr_view
-def requestonly(class_or_callable, attr=None):
- """ Return true of the class or callable accepts only a request argument,
- as opposed to something that accepts context, request """
+def requestonly(view, attr=None):
if attr is None:
attr = '__call__'
- if inspect.isfunction(class_or_callable):
- fn = class_or_callable
- elif inspect.isclass(class_or_callable):
+ if inspect.isfunction(view):
+ fn = view
+ elif inspect.isclass(view):
try:
- fn = class_or_callable.__init__
+ fn = view.__init__
except AttributeError:
return False
else:
try:
- fn = getattr(class_or_callable, attr)
+ fn = getattr(view, attr)
except AttributeError:
return False
@@ -2707,242 +3080,6 @@ def requestonly(class_or_callable, attr=None):
return False
-def is_response(ob):
- if ( hasattr(ob, 'app_iter') and hasattr(ob, 'headerlist') and
- hasattr(ob, 'status') ):
- return True
- return False
-
-def _map_view(view, registry, attr=None, renderer=None):
- wrapped_view = view
-
- helper = None
-
- if renderer is not None:
- helper = RendererHelper(renderer['name'],
- package=renderer['package'],
- registry=registry)
-
- if inspect.isclass(view):
- # If the object we've located is a class, turn it into a
- # function that operates like a Zope view (when it's invoked,
- # construct an instance using 'context' and 'request' as
- # position arguments, then immediately invoke the __call__
- # method of the instance with no arguments; __call__ should
- # return an IResponse).
- if requestonly(view, attr):
- # its __init__ accepts only a single request argument,
- # instead of both context and request
- def _class_requestonly_view(context, request):
- inst = view(request)
- if attr is None:
- response = inst()
- else:
- response = getattr(inst, attr)()
- if helper is not None:
- if not is_response(response):
- system = {
- 'view':inst,
- 'renderer_name':renderer['name'], # b/c
- 'renderer_info':renderer,
- 'context':context,
- 'request':request
- }
- response = helper.render_to_response(response, system,
- request=request)
- return response
- wrapped_view = _class_requestonly_view
- else:
- # its __init__ accepts both context and request
- def _class_view(context, request):
- inst = view(context, request)
- if attr is None:
- response = inst()
- else:
- response = getattr(inst, attr)()
- if helper is not None:
- if not is_response(response):
- system = {'view':inst,
- 'renderer_name':renderer['name'], # b/c
- 'renderer_info':renderer,
- 'context':context,
- 'request':request
- }
- response = helper.render_to_response(response, system,
- request=request)
- return response
- wrapped_view = _class_view
-
- elif requestonly(view, attr):
- # its __call__ accepts only a single request argument,
- # instead of both context and request
- def _requestonly_view(context, request):
- if attr is None:
- response = view(request)
- else:
- response = getattr(view, attr)(request)
-
- if helper is not None:
- if not is_response(response):
- system = {
- 'view':view,
- 'renderer_name':renderer['name'],
- 'renderer_info':renderer,
- 'context':context,
- 'request':request
- }
- response = helper.render_to_response(response, system,
- request=request)
- return response
- wrapped_view = _requestonly_view
-
- elif attr:
- def _attr_view(context, request):
- response = getattr(view, attr)(context, request)
- if helper is not None:
- if not is_response(response):
- system = {
- 'view':view,
- 'renderer_name':renderer['name'],
- 'renderer_info':renderer,
- 'context':context,
- 'request':request
- }
- response = helper.render_to_response(response, system,
- request=request)
- return response
- wrapped_view = _attr_view
-
- elif helper is not None:
- def _rendered_view(context, request):
- response = view(context, request)
- if not is_response(response):
- system = {
- 'view':view,
- 'renderer_name':renderer['name'], # b/c
- 'renderer_info':renderer,
- 'context':context,
- 'request':request
- }
- response = helper.render_to_response(response, system,
- request=request)
- return response
- wrapped_view = _rendered_view
-
- decorate_view(wrapped_view, view)
- return wrapped_view
-
-def _owrap_view(view, viewname, wrapper_viewname):
- if not wrapper_viewname:
- return view
- def _owrapped_view(context, request):
- response = view(context, request)
- request.wrapped_response = response
- request.wrapped_body = response.body
- request.wrapped_view = view
- wrapped_response = render_view_to_response(context, request,
- wrapper_viewname)
- if wrapped_response is None:
- raise ValueError(
- 'No wrapper view named %r found when executing view '
- 'named %r' % (wrapper_viewname, viewname))
- return wrapped_response
- decorate_view(_owrapped_view, view)
- return _owrapped_view
-
-def _predicate_wrap(view, predicates):
- if not predicates:
- return view
- def predicate_wrapper(context, request):
- if all((predicate(context, request) for predicate in predicates)):
- return view(context, request)
- raise PredicateMismatch('predicate mismatch for view %s' % view)
- def checker(context, request):
- return all((predicate(context, request) for predicate in
- predicates))
- predicate_wrapper.__predicated__ = checker
- decorate_view(predicate_wrapper, view)
- return predicate_wrapper
-
-def _secure_view(view, permission, authn_policy, authz_policy):
- if permission == '__no_permission_required__':
- # allow views registered within configurations that have a
- # default permission to explicitly override the default
- # permission, replacing it with no permission at all
- permission = None
-
- wrapped_view = view
- if authn_policy and authz_policy and (permission is not None):
- def _secured_view(context, request):
- principals = authn_policy.effective_principals(request)
- if authz_policy.permits(context, principals, permission):
- return view(context, request)
- msg = getattr(request, 'authdebug_message',
- 'Unauthorized: %s failed permission check' % view)
- raise Forbidden(msg)
- _secured_view.__call_permissive__ = view
- def _permitted(context, request):
- principals = authn_policy.effective_principals(request)
- return authz_policy.permits(context, principals, permission)
- _secured_view.__permitted__ = _permitted
- wrapped_view = _secured_view
- decorate_view(wrapped_view, view)
-
- return wrapped_view
-
-def _authdebug_view(view, permission, authn_policy, authz_policy, settings,
- logger):
- wrapped_view = view
- if settings and settings.get('debug_authorization', False):
- def _authdebug_view(context, request):
- view_name = getattr(request, 'view_name', None)
-
- if authn_policy and authz_policy:
- if permission is None:
- msg = 'Allowed (no permission registered)'
- else:
- principals = authn_policy.effective_principals(request)
- msg = str(authz_policy.permits(context, principals,
- permission))
- else:
- msg = 'Allowed (no authorization policy in use)'
-
- view_name = getattr(request, 'view_name', None)
- url = getattr(request, 'url', None)
- msg = ('debug_authorization of url %s (view name %r against '
- 'context %r): %s' % (url, view_name, context, msg))
- logger and logger.debug(msg)
- if request is not None:
- request.authdebug_message = msg
- return view(context, request)
-
- wrapped_view = _authdebug_view
- decorate_view(wrapped_view, view)
-
- return wrapped_view
-
-def _attr_wrap(view, accept, order, phash):
- # this is a little silly but we don't want to decorate the original
- # function with attributes that indicate accept, order, and phash,
- # so we use a wrapper
- if (accept is None) and (order == MAX_ORDER) and (phash == DEFAULT_PHASH):
- return view # defaults
- def attr_view(context, request):
- return view(context, request)
- attr_view.__accept__ = accept
- attr_view.__order__ = order
- attr_view.__phash__ = phash
- decorate_view(attr_view, view)
- return attr_view
-
-def isexception(o):
- if IInterface.providedBy(o):
- if IException.isEqualOrExtendedBy(o):
- return True
- return (
- isinstance(o, Exception) or
- (inspect.isclass(o) and (issubclass(o, Exception)))
- )
class ActionPredicate(object):
action_name = 'action'
@@ -2984,3 +3121,23 @@ class PyramidConfigurationMachine(ConfigurationMachine):
self._seen_files.add(spec)
return True
+def translator(msg):
+ request = get_current_request()
+ localizer = get_localizer(request)
+ return localizer.translate(msg)
+
+def is_response(ob):
+ if ( hasattr(ob, 'app_iter') and hasattr(ob, 'headerlist') and
+ hasattr(ob, 'status') ):
+ return True
+ return False
+
+def isexception(o):
+ if IInterface.providedBy(o):
+ if IException.isEqualOrExtendedBy(o):
+ return True
+ return (
+ isinstance(o, Exception) or
+ (inspect.isclass(o) and (issubclass(o, Exception)))
+ )
+
diff --git a/pyramid/httpexceptions.py b/pyramid/httpexceptions.py
index 6d05f9475..f56910b53 100644
--- a/pyramid/httpexceptions.py
+++ b/pyramid/httpexceptions.py
@@ -3,6 +3,7 @@ from webob.exc import status_map
# Parent classes
from webob.exc import HTTPException
+from webob.exc import WSGIHTTPException
from webob.exc import HTTPOk
from webob.exc import HTTPRedirection
from webob.exc import HTTPError
diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py
index 32359ca94..14ff100b1 100644
--- a/pyramid/interfaces.py
+++ b/pyramid/interfaces.py
@@ -120,6 +120,27 @@ class ITemplateRenderer(IRenderer):
accepts arbitrary keyword arguments and returns a string or
unicode object """
+class IViewMapper(Interface):
+ def __call__(self, object):
+ """ Provided with an arbitrary object (a function, class, or
+ instance), returns a callable with the call signature ``(context,
+ request)``. The callable returned should itself return a Response
+ object. An IViewMapper is returned by
+ :class:`pyramid.interfaces.IViewMapperFactory`."""
+
+class IViewMapperFactory(Interface):
+ def __call__(self, **kw):
+ """
+ Return an object which implements
+ :class:`pyramid.interfaces.IViewMapper`. ``kw`` will be a dictionary
+ containing view-specific arguments, such as ``permission``,
+ ``predicates``, ``attr``, ``renderer``, and other items. An
+ IViewMapperFactory is used by
+ :meth:`pyramid.config.Configurator.add_view` to provide a plugpoint
+ to extension developers who want to modify potential view callable
+ invocation signatures and response values.
+ """
+
# internal interfaces
class IRequest(Interface):
@@ -134,8 +155,19 @@ class IRouteRequest(Interface):
class IAuthenticationPolicy(Interface):
""" An object representing a Pyramid authentication policy. """
def authenticated_userid(request):
- """ Return the authenticated userid or ``None`` if no
- authenticated userid can be found. """
+ """ Return the authenticated userid or ``None`` if no authenticated
+ userid can be found. This method of the policy should ensure that a
+ record exists in whatever persistent store is used related to the
+ user (the user should not have been deleted); if a record associated
+ with the current id does not exist in a persistent store, it should
+ return ``None``."""
+
+ def unauthenticated_userid(request):
+ """ Return the *unauthenticated* userid. This method performs the
+ same duty as ``authenticated_userid`` but is permitted to return the
+ userid based only on data present in the request; it neednt (and
+ shouldn't) check any persistent store to ensure that the user record
+ related to the request userid exists."""
def effective_principals(request):
""" Return a sequence representing the effective principals
@@ -480,14 +512,17 @@ class ISession(Interface):
:meth:`pyramid.interfaces.ISesssion.flash`
"""
- def new_csrf_token(self):
+ def new_csrf_token():
""" Create and set into the session a new, random cross-site request
forgery protection token. Return the token. It will be a string."""
- def get_csrf_token(self):
- """ Get the CSRF token previously added to the session via
- ``new_csrf_token``, and return the token. If no CSRF token exists,
- the value returned will be ``None``.
+ def get_csrf_token():
+ """ Return a random cross-site request forgery protection token. It
+ will be a string. If a token was previously added to the session via
+ ``new_csrf_token``, that token will be returned. If no CSRF token
+ was previously set into the session, ``new_csrf_token`` will be
+ called, which will create and set a token, and this token will be
+ returned.
"""
# mapping methods
diff --git a/pyramid/paster.py b/pyramid/paster.py
index 57dac6b32..5ac043c19 100644
--- a/pyramid/paster.py
+++ b/pyramid/paster.py
@@ -179,12 +179,10 @@ class PRoutesCommand(PCommand):
print msg
def command(self):
- from pyramid.request import Request
from pyramid.interfaces import IRouteRequest
from pyramid.interfaces import IViewClassifier
from pyramid.interfaces import IView
from zope.interface import Interface
- from zope.interface import providedBy
config_file, section_name = self.args
app = self.get_app(config_file, section_name, loadapp=self.loadapp[0])
registry = app.registry
@@ -201,19 +199,10 @@ class PRoutesCommand(PCommand):
request_iface = registry.queryUtility(IRouteRequest,
name=route.name)
view_callable = None
- if request_iface is not None:
- if route.factory is None:
- context_iface = Interface
- else:
- request = Request.blank('/')
- inst = route.factory(request)
- context_iface = providedBy(inst)
- # try with factory instance as context; views registered for
- # a more general interface will be found if the context
- # iface is very specific
+ if (request_iface is None) or (route.factory is not None):
+ self.out(fmt % (route.name, route.pattern, '<unknown>'))
+ else:
view_callable = registry.adapters.lookup(
- (IViewClassifier, request_iface, context_iface),
+ (IViewClassifier, request_iface, Interface),
IView, name='', default=None)
- self.out(fmt % (route.name, route.pattern, view_callable))
-
-
+ self.out(fmt % (route.name, route.pattern, view_callable))
diff --git a/pyramid/paster_templates/alchemy/+package+/static/footerbg.png b/pyramid/paster_templates/alchemy/+package+/static/footerbg.png
new file mode 100644
index 000000000..1fbc873da
--- /dev/null
+++ b/pyramid/paster_templates/alchemy/+package+/static/footerbg.png
Binary files differ
diff --git a/pyramid/paster_templates/alchemy/+package+/static/headerbg.png b/pyramid/paster_templates/alchemy/+package+/static/headerbg.png
new file mode 100644
index 000000000..0596f2020
--- /dev/null
+++ b/pyramid/paster_templates/alchemy/+package+/static/headerbg.png
Binary files differ
diff --git a/pyramid/paster_templates/alchemy/+package+/static/ie6.css b/pyramid/paster_templates/alchemy/+package+/static/ie6.css
new file mode 100644
index 000000000..b7c8493d8
--- /dev/null
+++ b/pyramid/paster_templates/alchemy/+package+/static/ie6.css
@@ -0,0 +1,8 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
+#wrap{display:table;height:100%}
diff --git a/pyramid/paster_templates/alchemy/+package+/static/logo.png b/pyramid/paster_templates/alchemy/+package+/static/logo.png
deleted file mode 100644
index 88f5d9865..000000000
--- a/pyramid/paster_templates/alchemy/+package+/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/pyramid/paster_templates/alchemy/+package+/static/middlebg.png b/pyramid/paster_templates/alchemy/+package+/static/middlebg.png
new file mode 100644
index 000000000..2369cfb7d
--- /dev/null
+++ b/pyramid/paster_templates/alchemy/+package+/static/middlebg.png
Binary files differ
diff --git a/pyramid/paster_templates/alchemy/+package+/static/pylons.css b/pyramid/paster_templates/alchemy/+package+/static/pylons.css
index c153be07f..fd1914d8d 100644
--- a/pyramid/paster_templates/alchemy/+package+/static/pylons.css
+++ b/pyramid/paster_templates/alchemy/+package+/static/pylons.css
@@ -4,34 +4,23 @@ body{line-height:1;}
ol,ul{list-style:none;}
blockquote,q{quotes:none;}
blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-/* remember to define focus styles! */
:focus{outline:0;}
-/* remember to highlight inserts somehow! */
ins{text-decoration:none;}
del{text-decoration:line-through;}
-/* tables still need 'cellspacing="0"' in the markup */
table{border-collapse:collapse;border-spacing:0;}
-/* restyling */
sub{vertical-align:sub;font-size:smaller;line-height:normal;}
sup{vertical-align:super;font-size:smaller;line-height:normal;}
-/* lists */
ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
li{display:list-item;}
-/* nested lists have no top/bottom margins */
ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-/* 2 deep unordered lists use a circle */
ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-/* 3 deep (or more) unordered lists use a square */
ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
.hidden{display:none;}
p{line-height:1.5em;}
-h1{font-size:1.75em;/* 28px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;/* 24px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;/* 20px */
-line-height:1.7em;font-family:helvetica,verdana;}
+h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
+h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
+h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
@@ -42,23 +31,26 @@ body h2,
body h3,
body h4,
body h5,
-body h6{font-family:"Nobile","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#144fb2;font-style:normal;}
-#wrap {min-height: 100%;}
-#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;}
-#header{background-color:#e88f00;top:0;font-size:14px;}
-#footer{background-color:#000000;bottom:0;position: relative;margin-top:-40px;clear:both;}
-.header,.footer{width:700px;margin-right:auto;margin-left:auto;}
+body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
+#wrap{min-height:100%;}
+#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
+#header{background:#000000;top:0;font-size:14px;}
+#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
+.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
.wrapper{width:100%}
-#top,#bottom{width:100%;}
-#top{color:#888;background-color:#eee;height:300px;border-bottom:2px solid #ddd;}
-#bottom{color:#222;background-color:#ffffff;overflow:hidden;padding-bottom:80px;}
-.top,.bottom{width:700px;margin-right:auto;margin-left:auto;}
-.top{padding-top:100px;}
+#top,#top-small,#bottom{width:100%;}
+#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#bottom{color:#222;background-color:#ffffff;}
+.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top{padding-top:40px;}
+.top-small{padding-top:10px;}
+#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
.app-welcome{margin-top:25px;}
.app-name{color:#000000;font-weight:bold;}
.bottom{padding-top:50px;}
-#left{width:325px;float:left;padding-right:25px;}
-#right{width:325px;float:right;padding-left:25px;}
+#left{width:350px;float:left;padding-right:25px;}
+#right{width:350px;float:right;padding-left:25px;}
.align-left{text-align:left;}
.align-right{text-align:right;}
.align-center{text-align:center;}
@@ -67,7 +59,7 @@ ul.links li{list-style-type:none;font-size:14px;}
form{border-style:none;}
fieldset{border-style:none;}
input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text]{}
+input[type=text],input[type=password]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*Opera Fix*/
-body:before {content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/pyramid/paster_templates/alchemy/+package+/static/pyramid-small.png b/pyramid/paster_templates/alchemy/+package+/static/pyramid-small.png
new file mode 100644
index 000000000..a5bc0ade7
--- /dev/null
+++ b/pyramid/paster_templates/alchemy/+package+/static/pyramid-small.png
Binary files differ
diff --git a/pyramid/paster_templates/alchemy/+package+/static/pyramid.png b/pyramid/paster_templates/alchemy/+package+/static/pyramid.png
new file mode 100644
index 000000000..347e05549
--- /dev/null
+++ b/pyramid/paster_templates/alchemy/+package+/static/pyramid.png
Binary files differ
diff --git a/pyramid/paster_templates/alchemy/+package+/static/transparent.gif b/pyramid/paster_templates/alchemy/+package+/static/transparent.gif
new file mode 100644
index 000000000..0341802e5
--- /dev/null
+++ b/pyramid/paster_templates/alchemy/+package+/static/transparent.gif
Binary files differ
diff --git a/pyramid/paster_templates/alchemy/+package+/templates/model.pt b/pyramid/paster_templates/alchemy/+package+/templates/model.pt_tmpl
index 2d54d969e..1fd3c0b20 100644
--- a/pyramid/paster_templates/alchemy/+package+/templates/model.pt
+++ b/pyramid/paster_templates/alchemy/+package+/templates/model.pt_tmpl
@@ -5,23 +5,23 @@
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<meta name="keywords" content="python web application" />
<meta name="description" content="pyramid web application" />
- <link rel="shortcut icon" href="${request.application_url}/static/favicon.ico" />
- <link rel="stylesheet" href="${request.application_url}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
+ <link rel="shortcut icon" href="${request.static_url('{{package}}:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('{{package}}:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
- <!--[if !IE 7]>
- <style type="text/css">
- #wrap {display:table;height:100%}
- </style>
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('{{package}}:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
<![endif]-->
</head>
<body>
<div id="wrap">
- <div id="header">
- <div class="header">The Pyramid Web Application Development Framework</div>
- </div>
<div id="top">
<div class="top align-center">
- <img src="${request.application_url}/static/logo.png" width="300" height="80"/>
+ <div><img src="${request.static_url('{{package}}:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
the Pyramid web application development framework.
@@ -31,10 +31,10 @@
<div id="bottom">
<div class="bottom">
<div id="left" class="align-right">
- <h3>Search Pyramid documentation</h3>
+ <h2>Search documentation</h2>
<form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
<input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Search" />
+ <input type="submit" id="x" value="Go" />
</form>
<br/>
<p>
@@ -44,15 +44,12 @@
</p>
</div>
<div id="right" class="align-left">
- <h3>Pyramid links</h3>
+ <h2>Pyramid links</h2>
<ul class="links">
<li>
<a href="http://pylonshq.com">Pylons Website</a>
</li>
<li>
- <a href="http://docs.pylonshq.com/">The Pylons Project Documentation</a>
- </li>
- <li>
<a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
</li>
<li>
@@ -79,7 +76,7 @@
</div>
</div>
<div id="footer">
- <div class="footer">© Copyright 2008-2010, Agendaless Consulting.</div>
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
</div>
</body>
</html> \ No newline at end of file
diff --git a/pyramid/paster_templates/alchemy/+package+/templates/root.pt b/pyramid/paster_templates/alchemy/+package+/templates/root.pt_tmpl
index 7dda6a033..ca05896ae 100644
--- a/pyramid/paster_templates/alchemy/+package+/templates/root.pt
+++ b/pyramid/paster_templates/alchemy/+package+/templates/root.pt_tmpl
@@ -5,23 +5,23 @@
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<meta name="keywords" content="python web application" />
<meta name="description" content="pyramid web application" />
- <link rel="shortcut icon" href="${request.application_url}/static/favicon.ico" />
- <link rel="stylesheet" href="${request.application_url}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
+ <link rel="shortcut icon" href="${request.static_url('{{package}}:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('{{package}}:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
- <!--[if !IE 7]>
- <style type="text/css">
- #wrap {display:table;height:100%}
- </style>
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('{{package}}:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
<![endif]-->
</head>
<body>
<div id="wrap">
- <div id="header">
- <div class="header">The Pyramid Web Application Development Framework</div>
- </div>
<div id="top">
<div class="top align-center">
- <img src="${request.application_url}/static/logo.png" width="300" height="80"/>
+ <div><img src="${request.static_url('{{package}}:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
the Pyramid web application development framework.
@@ -31,10 +31,10 @@
<div id="bottom">
<div class="bottom">
<div id="left" class="align-right">
- <h3>Search Pyramid documentation</h3>
+ <h2>Search documentation</h2>
<form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
<input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Search" />
+ <input type="submit" id="x" value="Go" />
</form>
<br/>
<p tal:repeat="item items">
@@ -42,15 +42,12 @@
</p>
</div>
<div id="right" class="align-left">
- <h3>Pyramid links</h3>
+ <h2>Pyramid links</h2>
<ul class="links">
<li>
<a href="http://pylonshq.com">Pylons Website</a>
</li>
<li>
- <a href="http://docs.pylonshq.com/">The Pylons Project Documentation</a>
- </li>
- <li>
<a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
</li>
<li>
@@ -77,7 +74,7 @@
</div>
</div>
<div id="footer">
- <div class="footer">© Copyright 2008-2010, Agendaless Consulting.</div>
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
</div>
</body>
</html> \ No newline at end of file
diff --git a/pyramid/paster_templates/pylons_basic/+package+/static/footerbg.png b/pyramid/paster_templates/pylons_basic/+package+/static/footerbg.png
new file mode 100644
index 000000000..1fbc873da
--- /dev/null
+++ b/pyramid/paster_templates/pylons_basic/+package+/static/footerbg.png
Binary files differ
diff --git a/pyramid/paster_templates/pylons_basic/+package+/static/headerbg.png b/pyramid/paster_templates/pylons_basic/+package+/static/headerbg.png
new file mode 100644
index 000000000..0596f2020
--- /dev/null
+++ b/pyramid/paster_templates/pylons_basic/+package+/static/headerbg.png
Binary files differ
diff --git a/pyramid/paster_templates/pylons_basic/+package+/static/ie6.css b/pyramid/paster_templates/pylons_basic/+package+/static/ie6.css
new file mode 100644
index 000000000..b7c8493d8
--- /dev/null
+++ b/pyramid/paster_templates/pylons_basic/+package+/static/ie6.css
@@ -0,0 +1,8 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
+#wrap{display:table;height:100%}
diff --git a/pyramid/paster_templates/pylons_basic/+package+/static/logo.png b/pyramid/paster_templates/pylons_basic/+package+/static/logo.png
deleted file mode 100644
index 88f5d9865..000000000
--- a/pyramid/paster_templates/pylons_basic/+package+/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/pyramid/paster_templates/pylons_basic/+package+/static/middlebg.png b/pyramid/paster_templates/pylons_basic/+package+/static/middlebg.png
new file mode 100644
index 000000000..2369cfb7d
--- /dev/null
+++ b/pyramid/paster_templates/pylons_basic/+package+/static/middlebg.png
Binary files differ
diff --git a/pyramid/paster_templates/pylons_basic/+package+/static/pylons.css b/pyramid/paster_templates/pylons_basic/+package+/static/pylons.css
index c153be07f..fd1914d8d 100644
--- a/pyramid/paster_templates/pylons_basic/+package+/static/pylons.css
+++ b/pyramid/paster_templates/pylons_basic/+package+/static/pylons.css
@@ -4,34 +4,23 @@ body{line-height:1;}
ol,ul{list-style:none;}
blockquote,q{quotes:none;}
blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-/* remember to define focus styles! */
:focus{outline:0;}
-/* remember to highlight inserts somehow! */
ins{text-decoration:none;}
del{text-decoration:line-through;}
-/* tables still need 'cellspacing="0"' in the markup */
table{border-collapse:collapse;border-spacing:0;}
-/* restyling */
sub{vertical-align:sub;font-size:smaller;line-height:normal;}
sup{vertical-align:super;font-size:smaller;line-height:normal;}
-/* lists */
ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
li{display:list-item;}
-/* nested lists have no top/bottom margins */
ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-/* 2 deep unordered lists use a circle */
ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-/* 3 deep (or more) unordered lists use a square */
ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
.hidden{display:none;}
p{line-height:1.5em;}
-h1{font-size:1.75em;/* 28px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;/* 24px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;/* 20px */
-line-height:1.7em;font-family:helvetica,verdana;}
+h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
+h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
+h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
@@ -42,23 +31,26 @@ body h2,
body h3,
body h4,
body h5,
-body h6{font-family:"Nobile","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#144fb2;font-style:normal;}
-#wrap {min-height: 100%;}
-#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;}
-#header{background-color:#e88f00;top:0;font-size:14px;}
-#footer{background-color:#000000;bottom:0;position: relative;margin-top:-40px;clear:both;}
-.header,.footer{width:700px;margin-right:auto;margin-left:auto;}
+body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
+#wrap{min-height:100%;}
+#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
+#header{background:#000000;top:0;font-size:14px;}
+#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
+.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
.wrapper{width:100%}
-#top,#bottom{width:100%;}
-#top{color:#888;background-color:#eee;height:300px;border-bottom:2px solid #ddd;}
-#bottom{color:#222;background-color:#ffffff;overflow:hidden;padding-bottom:80px;}
-.top,.bottom{width:700px;margin-right:auto;margin-left:auto;}
-.top{padding-top:100px;}
+#top,#top-small,#bottom{width:100%;}
+#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#bottom{color:#222;background-color:#ffffff;}
+.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top{padding-top:40px;}
+.top-small{padding-top:10px;}
+#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
.app-welcome{margin-top:25px;}
.app-name{color:#000000;font-weight:bold;}
.bottom{padding-top:50px;}
-#left{width:325px;float:left;padding-right:25px;}
-#right{width:325px;float:right;padding-left:25px;}
+#left{width:350px;float:left;padding-right:25px;}
+#right{width:350px;float:right;padding-left:25px;}
.align-left{text-align:left;}
.align-right{text-align:right;}
.align-center{text-align:center;}
@@ -67,7 +59,7 @@ ul.links li{list-style-type:none;font-size:14px;}
form{border-style:none;}
fieldset{border-style:none;}
input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text]{}
+input[type=text],input[type=password]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*Opera Fix*/
-body:before {content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/pyramid/paster_templates/pylons_basic/+package+/static/pyramid-small.png b/pyramid/paster_templates/pylons_basic/+package+/static/pyramid-small.png
new file mode 100644
index 000000000..a5bc0ade7
--- /dev/null
+++ b/pyramid/paster_templates/pylons_basic/+package+/static/pyramid-small.png
Binary files differ
diff --git a/pyramid/paster_templates/pylons_basic/+package+/static/pyramid.png b/pyramid/paster_templates/pylons_basic/+package+/static/pyramid.png
new file mode 100644
index 000000000..347e05549
--- /dev/null
+++ b/pyramid/paster_templates/pylons_basic/+package+/static/pyramid.png
Binary files differ
diff --git a/pyramid/paster_templates/pylons_basic/+package+/static/transparent.gif b/pyramid/paster_templates/pylons_basic/+package+/static/transparent.gif
new file mode 100644
index 000000000..0341802e5
--- /dev/null
+++ b/pyramid/paster_templates/pylons_basic/+package+/static/transparent.gif
Binary files differ
diff --git a/pyramid/paster_templates/starter/+package+/templates/mytemplate.pt b/pyramid/paster_templates/pylons_basic/+package+/templates/mytemplate.mako_tmpl
index 02fc00eeb..a658ee2c9 100644
--- a/pyramid/paster_templates/starter/+package+/templates/mytemplate.pt
+++ b/pyramid/paster_templates/pylons_basic/+package+/templates/mytemplate.mako_tmpl
@@ -5,19 +5,19 @@
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<meta name="keywords" content="python web application" />
<meta name="description" content="pyramid web application" />
- <link rel="shortcut icon" href="${request.application_url}/static/favicon.ico" />
- <link rel="stylesheet" href="${request.application_url}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
- <link href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" rel="stylesheet" type="text/css" media="screen" charset="utf-8" />
+ <link rel="shortcut icon" href="${request.static_url('{{package}}:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('{{package}}:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
<!--[if lte IE 6]>
- <link rel="stylesheet" href="${request.application_url}/static/ie6.css" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="${request.static_url('{{package}}:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
<![endif]-->
</head>
<body>
<div id="wrap">
<div id="top">
<div class="top align-center">
- <div><img src="${request.application_url}/static/pyramid.png" width="750" height="169" alt="pyramid"/></div>
+ <div><img src="${request.static_url('{{package}}:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div>
</div>
</div>
<div id="middle">
@@ -70,7 +70,7 @@
</div>
</div>
<div id="footer">
- <div class="footer">© Copyright 2008-2010, Agendaless Consulting.</div>
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
</div>
</body>
</html> \ No newline at end of file
diff --git a/pyramid/paster_templates/pylons_minimal/+package+/static/footerbg.png b/pyramid/paster_templates/pylons_minimal/+package+/static/footerbg.png
new file mode 100644
index 000000000..1fbc873da
--- /dev/null
+++ b/pyramid/paster_templates/pylons_minimal/+package+/static/footerbg.png
Binary files differ
diff --git a/pyramid/paster_templates/pylons_minimal/+package+/static/headerbg.png b/pyramid/paster_templates/pylons_minimal/+package+/static/headerbg.png
new file mode 100644
index 000000000..0596f2020
--- /dev/null
+++ b/pyramid/paster_templates/pylons_minimal/+package+/static/headerbg.png
Binary files differ
diff --git a/pyramid/paster_templates/pylons_minimal/+package+/static/ie6.css b/pyramid/paster_templates/pylons_minimal/+package+/static/ie6.css
new file mode 100644
index 000000000..b7c8493d8
--- /dev/null
+++ b/pyramid/paster_templates/pylons_minimal/+package+/static/ie6.css
@@ -0,0 +1,8 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
+#wrap{display:table;height:100%}
diff --git a/pyramid/paster_templates/pylons_minimal/+package+/static/logo.png b/pyramid/paster_templates/pylons_minimal/+package+/static/logo.png
deleted file mode 100644
index 88f5d9865..000000000
--- a/pyramid/paster_templates/pylons_minimal/+package+/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/pyramid/paster_templates/pylons_minimal/+package+/static/middlebg.png b/pyramid/paster_templates/pylons_minimal/+package+/static/middlebg.png
new file mode 100644
index 000000000..2369cfb7d
--- /dev/null
+++ b/pyramid/paster_templates/pylons_minimal/+package+/static/middlebg.png
Binary files differ
diff --git a/pyramid/paster_templates/pylons_minimal/+package+/static/pylons.css b/pyramid/paster_templates/pylons_minimal/+package+/static/pylons.css
index c153be07f..fd1914d8d 100644
--- a/pyramid/paster_templates/pylons_minimal/+package+/static/pylons.css
+++ b/pyramid/paster_templates/pylons_minimal/+package+/static/pylons.css
@@ -4,34 +4,23 @@ body{line-height:1;}
ol,ul{list-style:none;}
blockquote,q{quotes:none;}
blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-/* remember to define focus styles! */
:focus{outline:0;}
-/* remember to highlight inserts somehow! */
ins{text-decoration:none;}
del{text-decoration:line-through;}
-/* tables still need 'cellspacing="0"' in the markup */
table{border-collapse:collapse;border-spacing:0;}
-/* restyling */
sub{vertical-align:sub;font-size:smaller;line-height:normal;}
sup{vertical-align:super;font-size:smaller;line-height:normal;}
-/* lists */
ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
li{display:list-item;}
-/* nested lists have no top/bottom margins */
ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-/* 2 deep unordered lists use a circle */
ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-/* 3 deep (or more) unordered lists use a square */
ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
.hidden{display:none;}
p{line-height:1.5em;}
-h1{font-size:1.75em;/* 28px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;/* 24px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;/* 20px */
-line-height:1.7em;font-family:helvetica,verdana;}
+h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
+h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
+h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
@@ -42,23 +31,26 @@ body h2,
body h3,
body h4,
body h5,
-body h6{font-family:"Nobile","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#144fb2;font-style:normal;}
-#wrap {min-height: 100%;}
-#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;}
-#header{background-color:#e88f00;top:0;font-size:14px;}
-#footer{background-color:#000000;bottom:0;position: relative;margin-top:-40px;clear:both;}
-.header,.footer{width:700px;margin-right:auto;margin-left:auto;}
+body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
+#wrap{min-height:100%;}
+#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
+#header{background:#000000;top:0;font-size:14px;}
+#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
+.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
.wrapper{width:100%}
-#top,#bottom{width:100%;}
-#top{color:#888;background-color:#eee;height:300px;border-bottom:2px solid #ddd;}
-#bottom{color:#222;background-color:#ffffff;overflow:hidden;padding-bottom:80px;}
-.top,.bottom{width:700px;margin-right:auto;margin-left:auto;}
-.top{padding-top:100px;}
+#top,#top-small,#bottom{width:100%;}
+#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#bottom{color:#222;background-color:#ffffff;}
+.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top{padding-top:40px;}
+.top-small{padding-top:10px;}
+#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
.app-welcome{margin-top:25px;}
.app-name{color:#000000;font-weight:bold;}
.bottom{padding-top:50px;}
-#left{width:325px;float:left;padding-right:25px;}
-#right{width:325px;float:right;padding-left:25px;}
+#left{width:350px;float:left;padding-right:25px;}
+#right{width:350px;float:right;padding-left:25px;}
.align-left{text-align:left;}
.align-right{text-align:right;}
.align-center{text-align:center;}
@@ -67,7 +59,7 @@ ul.links li{list-style-type:none;font-size:14px;}
form{border-style:none;}
fieldset{border-style:none;}
input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text]{}
+input[type=text],input[type=password]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*Opera Fix*/
-body:before {content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/pyramid/paster_templates/pylons_minimal/+package+/static/pyramid-small.png b/pyramid/paster_templates/pylons_minimal/+package+/static/pyramid-small.png
new file mode 100644
index 000000000..a5bc0ade7
--- /dev/null
+++ b/pyramid/paster_templates/pylons_minimal/+package+/static/pyramid-small.png
Binary files differ
diff --git a/pyramid/paster_templates/pylons_minimal/+package+/static/pyramid.png b/pyramid/paster_templates/pylons_minimal/+package+/static/pyramid.png
new file mode 100644
index 000000000..347e05549
--- /dev/null
+++ b/pyramid/paster_templates/pylons_minimal/+package+/static/pyramid.png
Binary files differ
diff --git a/pyramid/paster_templates/pylons_minimal/+package+/static/transparent.gif b/pyramid/paster_templates/pylons_minimal/+package+/static/transparent.gif
new file mode 100644
index 000000000..0341802e5
--- /dev/null
+++ b/pyramid/paster_templates/pylons_minimal/+package+/static/transparent.gif
Binary files differ
diff --git a/pyramid/paster_templates/pylons_minimal/+package+/templates/mytemplate.mako b/pyramid/paster_templates/pylons_minimal/+package+/templates/mytemplate.mako_tmpl
index 6ad23d44f..a658ee2c9 100644
--- a/pyramid/paster_templates/pylons_minimal/+package+/templates/mytemplate.mako
+++ b/pyramid/paster_templates/pylons_minimal/+package+/templates/mytemplate.mako_tmpl
@@ -5,23 +5,23 @@
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<meta name="keywords" content="python web application" />
<meta name="description" content="pyramid web application" />
- <link rel="shortcut icon" href="${request.application_url}/static/favicon.ico" />
- <link rel="stylesheet" href="${request.application_url}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
+ <link rel="shortcut icon" href="${request.static_url('{{package}}:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('{{package}}:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
- <!--[if !IE 7]>
- <style type="text/css">
- #wrap {display:table;height:100%}
- </style>
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('{{package}}:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
<![endif]-->
</head>
<body>
<div id="wrap">
- <div id="header">
- <div class="header">The Pyramid Web Application Development Framework</div>
- </div>
<div id="top">
<div class="top align-center">
- <img src="${request.application_url}/static/logo.png" width="300" height="80"/>
+ <div><img src="${request.static_url('{{package}}:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
the Pyramid web application development framework.
@@ -31,22 +31,19 @@
<div id="bottom">
<div class="bottom">
<div id="left" class="align-right">
- <h3>Search Pyramid documentation</h3>
+ <h2>Search documentation</h2>
<form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
<input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Search" />
+ <input type="submit" id="x" value="Go" />
</form>
</div>
<div id="right" class="align-left">
- <h3>Pyramid links</h3>
+ <h2>Pyramid links</h2>
<ul class="links">
<li>
<a href="http://pylonshq.com">Pylons Website</a>
</li>
<li>
- <a href="http://docs.pylonshq.com/">The Pylons Project Documentation</a>
- </li>
- <li>
<a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
</li>
<li>
@@ -73,7 +70,7 @@
</div>
</div>
<div id="footer">
- <div class="footer">© Copyright 2008-2010, Agendaless Consulting.</div>
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
</div>
</body>
</html> \ No newline at end of file
diff --git a/pyramid/paster_templates/pylons_sqla/+package+/static/footerbg.png b/pyramid/paster_templates/pylons_sqla/+package+/static/footerbg.png
new file mode 100644
index 000000000..1fbc873da
--- /dev/null
+++ b/pyramid/paster_templates/pylons_sqla/+package+/static/footerbg.png
Binary files differ
diff --git a/pyramid/paster_templates/pylons_sqla/+package+/static/headerbg.png b/pyramid/paster_templates/pylons_sqla/+package+/static/headerbg.png
new file mode 100644
index 000000000..0596f2020
--- /dev/null
+++ b/pyramid/paster_templates/pylons_sqla/+package+/static/headerbg.png
Binary files differ
diff --git a/pyramid/paster_templates/pylons_sqla/+package+/static/ie6.css b/pyramid/paster_templates/pylons_sqla/+package+/static/ie6.css
new file mode 100644
index 000000000..b7c8493d8
--- /dev/null
+++ b/pyramid/paster_templates/pylons_sqla/+package+/static/ie6.css
@@ -0,0 +1,8 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
+#wrap{display:table;height:100%}
diff --git a/pyramid/paster_templates/pylons_sqla/+package+/static/logo.png b/pyramid/paster_templates/pylons_sqla/+package+/static/logo.png
deleted file mode 100644
index 88f5d9865..000000000
--- a/pyramid/paster_templates/pylons_sqla/+package+/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/pyramid/paster_templates/pylons_sqla/+package+/static/middlebg.png b/pyramid/paster_templates/pylons_sqla/+package+/static/middlebg.png
new file mode 100644
index 000000000..2369cfb7d
--- /dev/null
+++ b/pyramid/paster_templates/pylons_sqla/+package+/static/middlebg.png
Binary files differ
diff --git a/pyramid/paster_templates/pylons_sqla/+package+/static/pylons.css b/pyramid/paster_templates/pylons_sqla/+package+/static/pylons.css
index c153be07f..fd1914d8d 100644
--- a/pyramid/paster_templates/pylons_sqla/+package+/static/pylons.css
+++ b/pyramid/paster_templates/pylons_sqla/+package+/static/pylons.css
@@ -4,34 +4,23 @@ body{line-height:1;}
ol,ul{list-style:none;}
blockquote,q{quotes:none;}
blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-/* remember to define focus styles! */
:focus{outline:0;}
-/* remember to highlight inserts somehow! */
ins{text-decoration:none;}
del{text-decoration:line-through;}
-/* tables still need 'cellspacing="0"' in the markup */
table{border-collapse:collapse;border-spacing:0;}
-/* restyling */
sub{vertical-align:sub;font-size:smaller;line-height:normal;}
sup{vertical-align:super;font-size:smaller;line-height:normal;}
-/* lists */
ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
li{display:list-item;}
-/* nested lists have no top/bottom margins */
ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-/* 2 deep unordered lists use a circle */
ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-/* 3 deep (or more) unordered lists use a square */
ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
.hidden{display:none;}
p{line-height:1.5em;}
-h1{font-size:1.75em;/* 28px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;/* 24px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;/* 20px */
-line-height:1.7em;font-family:helvetica,verdana;}
+h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
+h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
+h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
@@ -42,23 +31,26 @@ body h2,
body h3,
body h4,
body h5,
-body h6{font-family:"Nobile","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#144fb2;font-style:normal;}
-#wrap {min-height: 100%;}
-#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;}
-#header{background-color:#e88f00;top:0;font-size:14px;}
-#footer{background-color:#000000;bottom:0;position: relative;margin-top:-40px;clear:both;}
-.header,.footer{width:700px;margin-right:auto;margin-left:auto;}
+body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
+#wrap{min-height:100%;}
+#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
+#header{background:#000000;top:0;font-size:14px;}
+#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
+.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
.wrapper{width:100%}
-#top,#bottom{width:100%;}
-#top{color:#888;background-color:#eee;height:300px;border-bottom:2px solid #ddd;}
-#bottom{color:#222;background-color:#ffffff;overflow:hidden;padding-bottom:80px;}
-.top,.bottom{width:700px;margin-right:auto;margin-left:auto;}
-.top{padding-top:100px;}
+#top,#top-small,#bottom{width:100%;}
+#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#bottom{color:#222;background-color:#ffffff;}
+.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top{padding-top:40px;}
+.top-small{padding-top:10px;}
+#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
.app-welcome{margin-top:25px;}
.app-name{color:#000000;font-weight:bold;}
.bottom{padding-top:50px;}
-#left{width:325px;float:left;padding-right:25px;}
-#right{width:325px;float:right;padding-left:25px;}
+#left{width:350px;float:left;padding-right:25px;}
+#right{width:350px;float:right;padding-left:25px;}
.align-left{text-align:left;}
.align-right{text-align:right;}
.align-center{text-align:center;}
@@ -67,7 +59,7 @@ ul.links li{list-style-type:none;font-size:14px;}
form{border-style:none;}
fieldset{border-style:none;}
input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text]{}
+input[type=text],input[type=password]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*Opera Fix*/
-body:before {content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/pyramid/paster_templates/pylons_sqla/+package+/static/pyramid-small.png b/pyramid/paster_templates/pylons_sqla/+package+/static/pyramid-small.png
new file mode 100644
index 000000000..a5bc0ade7
--- /dev/null
+++ b/pyramid/paster_templates/pylons_sqla/+package+/static/pyramid-small.png
Binary files differ
diff --git a/pyramid/paster_templates/pylons_sqla/+package+/static/pyramid.png b/pyramid/paster_templates/pylons_sqla/+package+/static/pyramid.png
new file mode 100644
index 000000000..347e05549
--- /dev/null
+++ b/pyramid/paster_templates/pylons_sqla/+package+/static/pyramid.png
Binary files differ
diff --git a/pyramid/paster_templates/pylons_sqla/+package+/static/transparent.gif b/pyramid/paster_templates/pylons_sqla/+package+/static/transparent.gif
new file mode 100644
index 000000000..0341802e5
--- /dev/null
+++ b/pyramid/paster_templates/pylons_sqla/+package+/static/transparent.gif
Binary files differ
diff --git a/pyramid/paster_templates/pylons_sqla/+package+/templates/mytemplate.mako b/pyramid/paster_templates/pylons_sqla/+package+/templates/mytemplate.mako_tmpl
index 8b65d6a2b..9130f6da8 100644
--- a/pyramid/paster_templates/pylons_sqla/+package+/templates/mytemplate.mako
+++ b/pyramid/paster_templates/pylons_sqla/+package+/templates/mytemplate.mako_tmpl
@@ -5,23 +5,23 @@
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<meta name="keywords" content="python web application" />
<meta name="description" content="pyramid web application" />
- <link rel="shortcut icon" href="${request.application_url}/static/favicon.ico" />
- <link rel="stylesheet" href="${request.application_url}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
+ <link rel="shortcut icon" href="${request.static_url('{{package}}:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('{{package}}:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
- <!--[if !IE 7]>
- <style type="text/css">
- #wrap {display:table;height:100%}
- </style>
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('{{package}}:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
<![endif]-->
</head>
<body>
<div id="wrap">
- <div id="header">
- <div class="header">The Pyramid Web Application Development Framework</div>
- </div>
<div id="top">
<div class="top align-center">
- <img src="${request.application_url}/static/logo.png" width="300" height="80"/>
+ <div><img src="${request.static_url('{{package}}:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
the Pyramid web application development framework.
@@ -31,24 +31,21 @@
<div id="bottom">
<div class="bottom">
<div id="left" class="align-right">
- <h3>Search Pyramid documentation</h3>
+ <h2>Search documentation</h2>
<form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
<input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Search" />
+ <input type="submit" id="x" value="Go" />
</form>
<br/>
<p>The root object's name is "${root.name}"</p>
</div>
<div id="right" class="align-left">
- <h3>Pyramid links</h3>
+ <h2>Pyramid links</h2>
<ul class="links">
<li>
<a href="http://pylonshq.com">Pylons Website</a>
</li>
<li>
- <a href="http://docs.pylonshq.com/">The Pylons Project Documentation</a>
- </li>
- <li>
<a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
</li>
<li>
@@ -75,7 +72,7 @@
</div>
</div>
<div id="footer">
- <div class="footer">© Copyright 2008-2010, Agendaless Consulting.</div>
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
</div>
</body>
</html> \ No newline at end of file
diff --git a/pyramid/paster_templates/routesalchemy/+package+/static/footerbg.png b/pyramid/paster_templates/routesalchemy/+package+/static/footerbg.png
new file mode 100644
index 000000000..1fbc873da
--- /dev/null
+++ b/pyramid/paster_templates/routesalchemy/+package+/static/footerbg.png
Binary files differ
diff --git a/pyramid/paster_templates/routesalchemy/+package+/static/headerbg.png b/pyramid/paster_templates/routesalchemy/+package+/static/headerbg.png
new file mode 100644
index 000000000..0596f2020
--- /dev/null
+++ b/pyramid/paster_templates/routesalchemy/+package+/static/headerbg.png
Binary files differ
diff --git a/pyramid/paster_templates/routesalchemy/+package+/static/ie6.css b/pyramid/paster_templates/routesalchemy/+package+/static/ie6.css
new file mode 100644
index 000000000..b7c8493d8
--- /dev/null
+++ b/pyramid/paster_templates/routesalchemy/+package+/static/ie6.css
@@ -0,0 +1,8 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
+#wrap{display:table;height:100%}
diff --git a/pyramid/paster_templates/routesalchemy/+package+/static/logo.png b/pyramid/paster_templates/routesalchemy/+package+/static/logo.png
deleted file mode 100644
index 88f5d9865..000000000
--- a/pyramid/paster_templates/routesalchemy/+package+/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/pyramid/paster_templates/routesalchemy/+package+/static/middlebg.png b/pyramid/paster_templates/routesalchemy/+package+/static/middlebg.png
new file mode 100644
index 000000000..2369cfb7d
--- /dev/null
+++ b/pyramid/paster_templates/routesalchemy/+package+/static/middlebg.png
Binary files differ
diff --git a/pyramid/paster_templates/routesalchemy/+package+/static/pylons.css b/pyramid/paster_templates/routesalchemy/+package+/static/pylons.css
index c153be07f..fd1914d8d 100644
--- a/pyramid/paster_templates/routesalchemy/+package+/static/pylons.css
+++ b/pyramid/paster_templates/routesalchemy/+package+/static/pylons.css
@@ -4,34 +4,23 @@ body{line-height:1;}
ol,ul{list-style:none;}
blockquote,q{quotes:none;}
blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-/* remember to define focus styles! */
:focus{outline:0;}
-/* remember to highlight inserts somehow! */
ins{text-decoration:none;}
del{text-decoration:line-through;}
-/* tables still need 'cellspacing="0"' in the markup */
table{border-collapse:collapse;border-spacing:0;}
-/* restyling */
sub{vertical-align:sub;font-size:smaller;line-height:normal;}
sup{vertical-align:super;font-size:smaller;line-height:normal;}
-/* lists */
ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
li{display:list-item;}
-/* nested lists have no top/bottom margins */
ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-/* 2 deep unordered lists use a circle */
ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-/* 3 deep (or more) unordered lists use a square */
ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
.hidden{display:none;}
p{line-height:1.5em;}
-h1{font-size:1.75em;/* 28px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;/* 24px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;/* 20px */
-line-height:1.7em;font-family:helvetica,verdana;}
+h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
+h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
+h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
@@ -42,23 +31,26 @@ body h2,
body h3,
body h4,
body h5,
-body h6{font-family:"Nobile","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#144fb2;font-style:normal;}
-#wrap {min-height: 100%;}
-#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;}
-#header{background-color:#e88f00;top:0;font-size:14px;}
-#footer{background-color:#000000;bottom:0;position: relative;margin-top:-40px;clear:both;}
-.header,.footer{width:700px;margin-right:auto;margin-left:auto;}
+body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
+#wrap{min-height:100%;}
+#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
+#header{background:#000000;top:0;font-size:14px;}
+#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
+.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
.wrapper{width:100%}
-#top,#bottom{width:100%;}
-#top{color:#888;background-color:#eee;height:300px;border-bottom:2px solid #ddd;}
-#bottom{color:#222;background-color:#ffffff;overflow:hidden;padding-bottom:80px;}
-.top,.bottom{width:700px;margin-right:auto;margin-left:auto;}
-.top{padding-top:100px;}
+#top,#top-small,#bottom{width:100%;}
+#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#bottom{color:#222;background-color:#ffffff;}
+.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top{padding-top:40px;}
+.top-small{padding-top:10px;}
+#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
.app-welcome{margin-top:25px;}
.app-name{color:#000000;font-weight:bold;}
.bottom{padding-top:50px;}
-#left{width:325px;float:left;padding-right:25px;}
-#right{width:325px;float:right;padding-left:25px;}
+#left{width:350px;float:left;padding-right:25px;}
+#right{width:350px;float:right;padding-left:25px;}
.align-left{text-align:left;}
.align-right{text-align:right;}
.align-center{text-align:center;}
@@ -67,7 +59,7 @@ ul.links li{list-style-type:none;font-size:14px;}
form{border-style:none;}
fieldset{border-style:none;}
input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text]{}
+input[type=text],input[type=password]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*Opera Fix*/
-body:before {content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/pyramid/paster_templates/routesalchemy/+package+/static/pyramid-small.png b/pyramid/paster_templates/routesalchemy/+package+/static/pyramid-small.png
new file mode 100644
index 000000000..a5bc0ade7
--- /dev/null
+++ b/pyramid/paster_templates/routesalchemy/+package+/static/pyramid-small.png
Binary files differ
diff --git a/pyramid/paster_templates/routesalchemy/+package+/static/pyramid.png b/pyramid/paster_templates/routesalchemy/+package+/static/pyramid.png
new file mode 100644
index 000000000..347e05549
--- /dev/null
+++ b/pyramid/paster_templates/routesalchemy/+package+/static/pyramid.png
Binary files differ
diff --git a/pyramid/paster_templates/routesalchemy/+package+/static/transparent.gif b/pyramid/paster_templates/routesalchemy/+package+/static/transparent.gif
new file mode 100644
index 000000000..0341802e5
--- /dev/null
+++ b/pyramid/paster_templates/routesalchemy/+package+/static/transparent.gif
Binary files differ
diff --git a/pyramid/paster_templates/routesalchemy/+package+/templates/mytemplate.pt b/pyramid/paster_templates/routesalchemy/+package+/templates/mytemplate.pt
deleted file mode 100644
index 6ad23d44f..000000000
--- a/pyramid/paster_templates/routesalchemy/+package+/templates/mytemplate.pt
+++ /dev/null
@@ -1,79 +0,0 @@
-<!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" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal">
-<head>
- <title>The Pyramid Web Application Development Framework</title>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
- <meta name="keywords" content="python web application" />
- <meta name="description" content="pyramid web application" />
- <link rel="shortcut icon" href="${request.application_url}/static/favicon.ico" />
- <link rel="stylesheet" href="${request.application_url}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
- <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
- <!--[if !IE 7]>
- <style type="text/css">
- #wrap {display:table;height:100%}
- </style>
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="header">
- <div class="header">The Pyramid Web Application Development Framework</div>
- </div>
- <div id="top">
- <div class="top align-center">
- <img src="${request.application_url}/static/logo.png" width="300" height="80"/>
- <p class="app-welcome">
- Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
- the Pyramid web application development framework.
- </p>
- </div>
- </div>
- <div id="bottom">
- <div class="bottom">
- <div id="left" class="align-right">
- <h3>Search Pyramid documentation</h3>
- <form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
- <input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Search" />
- </form>
- </div>
- <div id="right" class="align-left">
- <h3>Pyramid links</h3>
- <ul class="links">
- <li>
- <a href="http://pylonshq.com">Pylons Website</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/">The Pylons Project Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#api-documentation">API Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#tutorials">Tutorials</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#change-history">Change History</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#sample-applications">Sample Applications</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#support-and-development">Support and Development</a>
- </li>
- <li>
- <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
- </li>
- </ul>
- </div>
- </div>
- </div>
- </div>
- <div id="footer">
- <div class="footer">© Copyright 2008-2010, Agendaless Consulting.</div>
- </div>
-</body>
-</html> \ No newline at end of file
diff --git a/pyramid/paster_templates/pylons_basic/+package+/templates/mytemplate.mako b/pyramid/paster_templates/routesalchemy/+package+/templates/mytemplate.pt_tmpl
index 63ec0172a..a658ee2c9 100644
--- a/pyramid/paster_templates/pylons_basic/+package+/templates/mytemplate.mako
+++ b/pyramid/paster_templates/routesalchemy/+package+/templates/mytemplate.pt_tmpl
@@ -5,23 +5,23 @@
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<meta name="keywords" content="python web application" />
<meta name="description" content="pyramid web application" />
- <link rel="shortcut icon" href="${request.application_url}/static/favicon.ico" />
- <link rel="stylesheet" href="${request.application_url}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
+ <link rel="shortcut icon" href="${request.static_url('{{package}}:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('{{package}}:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
- <!--[if !IE 7]>
- <style type="text/css">
- #wrap {display:table;height:100%}
- </style>
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('{{package}}:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
<![endif]-->
</head>
<body>
<div id="wrap">
- <div id="header">
- <div class="header">The Pyramid Web Application Development Framework</div>
- </div>
<div id="top">
<div class="top align-center">
- <img src="${request.application_url}/static/logo.png" width="300" height="80"/>
+ <div><img src="${request.static_url('{{package}}:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
the Pyramid web application development framework.
@@ -31,14 +31,14 @@
<div id="bottom">
<div class="bottom">
<div id="left" class="align-right">
- <h3>Search Pyramid documentation</h3>
+ <h2>Search documentation</h2>
<form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
<input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Search" />
+ <input type="submit" id="x" value="Go" />
</form>
</div>
<div id="right" class="align-left">
- <h3>Pyramid links</h3>
+ <h2>Pyramid links</h2>
<ul class="links">
<li>
<a href="http://pylonshq.com">Pylons Website</a>
@@ -47,9 +47,6 @@
<a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
</li>
<li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
- </li>
- <li>
<a href="http://docs.pylonshq.com/pyramid/dev/#api-documentation">API Documentation</a>
</li>
<li>
@@ -73,7 +70,7 @@
</div>
</div>
<div id="footer">
- <div class="footer">© Copyright 2008-2010, Agendaless Consulting.</div>
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
</div>
</body>
</html> \ No newline at end of file
diff --git a/pyramid/paster_templates/starter/+package+/static/pylons.css b/pyramid/paster_templates/starter/+package+/static/pylons.css
index 33b21ac1a..fd1914d8d 100644
--- a/pyramid/paster_templates/starter/+package+/static/pylons.css
+++ b/pyramid/paster_templates/starter/+package+/static/pylons.css
@@ -38,12 +38,13 @@ body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weig
#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
.wrapper{width:100%}
-#top,#bottom{width:100%;}
-#top{color:#000000;height:230px;
-background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top,#top-small,#bottom{width:100%;}
+#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
#bottom{color:#222;background-color:#ffffff;}
-.top,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
.top{padding-top:40px;}
+.top-small{padding-top:10px;}
#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
.app-welcome{margin-top:25px;}
.app-name{color:#000000;font-weight:bold;}
@@ -58,7 +59,7 @@ ul.links li{list-style-type:none;font-size:14px;}
form{border-style:none;}
fieldset{border-style:none;}
input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text]{width:205px;}
+input[type=text],input[type=password]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*Opera Fix*/
body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/pyramid/paster_templates/starter/+package+/static/pyramid-small.png b/pyramid/paster_templates/starter/+package+/static/pyramid-small.png
new file mode 100644
index 000000000..a5bc0ade7
--- /dev/null
+++ b/pyramid/paster_templates/starter/+package+/static/pyramid-small.png
Binary files differ
diff --git a/pyramid/paster_templates/starter/+package+/templates/mytemplate.pt_tmpl b/pyramid/paster_templates/starter/+package+/templates/mytemplate.pt_tmpl
new file mode 100644
index 000000000..a658ee2c9
--- /dev/null
+++ b/pyramid/paster_templates/starter/+package+/templates/mytemplate.pt_tmpl
@@ -0,0 +1,76 @@
+<!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" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal">
+<head>
+ <title>The Pyramid Web Application Development Framework</title>
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+ <meta name="keywords" content="python web application" />
+ <meta name="description" content="pyramid web application" />
+ <link rel="shortcut icon" href="${request.static_url('{{package}}:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('{{package}}:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('{{package}}:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
+ <![endif]-->
+</head>
+<body>
+ <div id="wrap">
+ <div id="top">
+ <div class="top align-center">
+ <div><img src="${request.static_url('{{package}}:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-center">
+ <p class="app-welcome">
+ Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
+ the Pyramid web application development framework.
+ </p>
+ </div>
+ </div>
+ <div id="bottom">
+ <div class="bottom">
+ <div id="left" class="align-right">
+ <h2>Search documentation</h2>
+ <form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
+ <input type="text" id="q" name="q" value="" />
+ <input type="submit" id="x" value="Go" />
+ </form>
+ </div>
+ <div id="right" class="align-left">
+ <h2>Pyramid links</h2>
+ <ul class="links">
+ <li>
+ <a href="http://pylonshq.com">Pylons Website</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#api-documentation">API Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#tutorials">Tutorials</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#change-history">Change History</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#sample-applications">Sample Applications</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#support-and-development">Support and Development</a>
+ </li>
+ <li>
+ <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="footer">
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/pyramid/paster_templates/starter_zcml/+package+/static/footerbg.png b/pyramid/paster_templates/starter_zcml/+package+/static/footerbg.png
new file mode 100644
index 000000000..1fbc873da
--- /dev/null
+++ b/pyramid/paster_templates/starter_zcml/+package+/static/footerbg.png
Binary files differ
diff --git a/pyramid/paster_templates/starter_zcml/+package+/static/headerbg.png b/pyramid/paster_templates/starter_zcml/+package+/static/headerbg.png
new file mode 100644
index 000000000..0596f2020
--- /dev/null
+++ b/pyramid/paster_templates/starter_zcml/+package+/static/headerbg.png
Binary files differ
diff --git a/pyramid/paster_templates/starter_zcml/+package+/static/ie6.css b/pyramid/paster_templates/starter_zcml/+package+/static/ie6.css
new file mode 100644
index 000000000..b7c8493d8
--- /dev/null
+++ b/pyramid/paster_templates/starter_zcml/+package+/static/ie6.css
@@ -0,0 +1,8 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
+#wrap{display:table;height:100%}
diff --git a/pyramid/paster_templates/starter_zcml/+package+/static/logo.png b/pyramid/paster_templates/starter_zcml/+package+/static/logo.png
deleted file mode 100644
index 88f5d9865..000000000
--- a/pyramid/paster_templates/starter_zcml/+package+/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/pyramid/paster_templates/starter_zcml/+package+/static/middlebg.png b/pyramid/paster_templates/starter_zcml/+package+/static/middlebg.png
new file mode 100644
index 000000000..2369cfb7d
--- /dev/null
+++ b/pyramid/paster_templates/starter_zcml/+package+/static/middlebg.png
Binary files differ
diff --git a/pyramid/paster_templates/starter_zcml/+package+/static/pylons.css b/pyramid/paster_templates/starter_zcml/+package+/static/pylons.css
index c153be07f..fd1914d8d 100644
--- a/pyramid/paster_templates/starter_zcml/+package+/static/pylons.css
+++ b/pyramid/paster_templates/starter_zcml/+package+/static/pylons.css
@@ -4,34 +4,23 @@ body{line-height:1;}
ol,ul{list-style:none;}
blockquote,q{quotes:none;}
blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-/* remember to define focus styles! */
:focus{outline:0;}
-/* remember to highlight inserts somehow! */
ins{text-decoration:none;}
del{text-decoration:line-through;}
-/* tables still need 'cellspacing="0"' in the markup */
table{border-collapse:collapse;border-spacing:0;}
-/* restyling */
sub{vertical-align:sub;font-size:smaller;line-height:normal;}
sup{vertical-align:super;font-size:smaller;line-height:normal;}
-/* lists */
ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
li{display:list-item;}
-/* nested lists have no top/bottom margins */
ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-/* 2 deep unordered lists use a circle */
ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-/* 3 deep (or more) unordered lists use a square */
ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
.hidden{display:none;}
p{line-height:1.5em;}
-h1{font-size:1.75em;/* 28px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;/* 24px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;/* 20px */
-line-height:1.7em;font-family:helvetica,verdana;}
+h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
+h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
+h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
@@ -42,23 +31,26 @@ body h2,
body h3,
body h4,
body h5,
-body h6{font-family:"Nobile","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#144fb2;font-style:normal;}
-#wrap {min-height: 100%;}
-#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;}
-#header{background-color:#e88f00;top:0;font-size:14px;}
-#footer{background-color:#000000;bottom:0;position: relative;margin-top:-40px;clear:both;}
-.header,.footer{width:700px;margin-right:auto;margin-left:auto;}
+body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
+#wrap{min-height:100%;}
+#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
+#header{background:#000000;top:0;font-size:14px;}
+#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
+.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
.wrapper{width:100%}
-#top,#bottom{width:100%;}
-#top{color:#888;background-color:#eee;height:300px;border-bottom:2px solid #ddd;}
-#bottom{color:#222;background-color:#ffffff;overflow:hidden;padding-bottom:80px;}
-.top,.bottom{width:700px;margin-right:auto;margin-left:auto;}
-.top{padding-top:100px;}
+#top,#top-small,#bottom{width:100%;}
+#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#bottom{color:#222;background-color:#ffffff;}
+.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top{padding-top:40px;}
+.top-small{padding-top:10px;}
+#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
.app-welcome{margin-top:25px;}
.app-name{color:#000000;font-weight:bold;}
.bottom{padding-top:50px;}
-#left{width:325px;float:left;padding-right:25px;}
-#right{width:325px;float:right;padding-left:25px;}
+#left{width:350px;float:left;padding-right:25px;}
+#right{width:350px;float:right;padding-left:25px;}
.align-left{text-align:left;}
.align-right{text-align:right;}
.align-center{text-align:center;}
@@ -67,7 +59,7 @@ ul.links li{list-style-type:none;font-size:14px;}
form{border-style:none;}
fieldset{border-style:none;}
input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text]{}
+input[type=text],input[type=password]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*Opera Fix*/
-body:before {content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/pyramid/paster_templates/starter_zcml/+package+/static/pyramid-small.png b/pyramid/paster_templates/starter_zcml/+package+/static/pyramid-small.png
new file mode 100644
index 000000000..a5bc0ade7
--- /dev/null
+++ b/pyramid/paster_templates/starter_zcml/+package+/static/pyramid-small.png
Binary files differ
diff --git a/pyramid/paster_templates/starter_zcml/+package+/static/pyramid.png b/pyramid/paster_templates/starter_zcml/+package+/static/pyramid.png
new file mode 100644
index 000000000..347e05549
--- /dev/null
+++ b/pyramid/paster_templates/starter_zcml/+package+/static/pyramid.png
Binary files differ
diff --git a/pyramid/paster_templates/starter_zcml/+package+/static/transparent.gif b/pyramid/paster_templates/starter_zcml/+package+/static/transparent.gif
new file mode 100644
index 000000000..0341802e5
--- /dev/null
+++ b/pyramid/paster_templates/starter_zcml/+package+/static/transparent.gif
Binary files differ
diff --git a/pyramid/paster_templates/starter_zcml/+package+/templates/mytemplate.pt b/pyramid/paster_templates/starter_zcml/+package+/templates/mytemplate.pt
deleted file mode 100644
index 6ad23d44f..000000000
--- a/pyramid/paster_templates/starter_zcml/+package+/templates/mytemplate.pt
+++ /dev/null
@@ -1,79 +0,0 @@
-<!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" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal">
-<head>
- <title>The Pyramid Web Application Development Framework</title>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
- <meta name="keywords" content="python web application" />
- <meta name="description" content="pyramid web application" />
- <link rel="shortcut icon" href="${request.application_url}/static/favicon.ico" />
- <link rel="stylesheet" href="${request.application_url}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
- <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
- <!--[if !IE 7]>
- <style type="text/css">
- #wrap {display:table;height:100%}
- </style>
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="header">
- <div class="header">The Pyramid Web Application Development Framework</div>
- </div>
- <div id="top">
- <div class="top align-center">
- <img src="${request.application_url}/static/logo.png" width="300" height="80"/>
- <p class="app-welcome">
- Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
- the Pyramid web application development framework.
- </p>
- </div>
- </div>
- <div id="bottom">
- <div class="bottom">
- <div id="left" class="align-right">
- <h3>Search Pyramid documentation</h3>
- <form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
- <input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Search" />
- </form>
- </div>
- <div id="right" class="align-left">
- <h3>Pyramid links</h3>
- <ul class="links">
- <li>
- <a href="http://pylonshq.com">Pylons Website</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/">The Pylons Project Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#api-documentation">API Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#tutorials">Tutorials</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#change-history">Change History</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#sample-applications">Sample Applications</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#support-and-development">Support and Development</a>
- </li>
- <li>
- <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
- </li>
- </ul>
- </div>
- </div>
- </div>
- </div>
- <div id="footer">
- <div class="footer">© Copyright 2008-2010, Agendaless Consulting.</div>
- </div>
-</body>
-</html> \ No newline at end of file
diff --git a/pyramid/paster_templates/starter_zcml/+package+/templates/mytemplate.pt_tmpl b/pyramid/paster_templates/starter_zcml/+package+/templates/mytemplate.pt_tmpl
new file mode 100644
index 000000000..a658ee2c9
--- /dev/null
+++ b/pyramid/paster_templates/starter_zcml/+package+/templates/mytemplate.pt_tmpl
@@ -0,0 +1,76 @@
+<!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" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal">
+<head>
+ <title>The Pyramid Web Application Development Framework</title>
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+ <meta name="keywords" content="python web application" />
+ <meta name="description" content="pyramid web application" />
+ <link rel="shortcut icon" href="${request.static_url('{{package}}:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('{{package}}:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('{{package}}:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
+ <![endif]-->
+</head>
+<body>
+ <div id="wrap">
+ <div id="top">
+ <div class="top align-center">
+ <div><img src="${request.static_url('{{package}}:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-center">
+ <p class="app-welcome">
+ Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
+ the Pyramid web application development framework.
+ </p>
+ </div>
+ </div>
+ <div id="bottom">
+ <div class="bottom">
+ <div id="left" class="align-right">
+ <h2>Search documentation</h2>
+ <form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
+ <input type="text" id="q" name="q" value="" />
+ <input type="submit" id="x" value="Go" />
+ </form>
+ </div>
+ <div id="right" class="align-left">
+ <h2>Pyramid links</h2>
+ <ul class="links">
+ <li>
+ <a href="http://pylonshq.com">Pylons Website</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#api-documentation">API Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#tutorials">Tutorials</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#change-history">Change History</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#sample-applications">Sample Applications</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#support-and-development">Support and Development</a>
+ </li>
+ <li>
+ <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="footer">
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/pyramid/paster_templates/zodb/+package+/static/footerbg.png b/pyramid/paster_templates/zodb/+package+/static/footerbg.png
new file mode 100644
index 000000000..1fbc873da
--- /dev/null
+++ b/pyramid/paster_templates/zodb/+package+/static/footerbg.png
Binary files differ
diff --git a/pyramid/paster_templates/zodb/+package+/static/headerbg.png b/pyramid/paster_templates/zodb/+package+/static/headerbg.png
new file mode 100644
index 000000000..0596f2020
--- /dev/null
+++ b/pyramid/paster_templates/zodb/+package+/static/headerbg.png
Binary files differ
diff --git a/pyramid/paster_templates/zodb/+package+/static/ie6.css b/pyramid/paster_templates/zodb/+package+/static/ie6.css
new file mode 100644
index 000000000..b7c8493d8
--- /dev/null
+++ b/pyramid/paster_templates/zodb/+package+/static/ie6.css
@@ -0,0 +1,8 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
+#wrap{display:table;height:100%}
diff --git a/pyramid/paster_templates/zodb/+package+/static/logo.png b/pyramid/paster_templates/zodb/+package+/static/logo.png
deleted file mode 100644
index 88f5d9865..000000000
--- a/pyramid/paster_templates/zodb/+package+/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/pyramid/paster_templates/zodb/+package+/static/middlebg.png b/pyramid/paster_templates/zodb/+package+/static/middlebg.png
new file mode 100644
index 000000000..2369cfb7d
--- /dev/null
+++ b/pyramid/paster_templates/zodb/+package+/static/middlebg.png
Binary files differ
diff --git a/pyramid/paster_templates/zodb/+package+/static/pylons.css b/pyramid/paster_templates/zodb/+package+/static/pylons.css
index c153be07f..fd1914d8d 100644
--- a/pyramid/paster_templates/zodb/+package+/static/pylons.css
+++ b/pyramid/paster_templates/zodb/+package+/static/pylons.css
@@ -4,34 +4,23 @@ body{line-height:1;}
ol,ul{list-style:none;}
blockquote,q{quotes:none;}
blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-/* remember to define focus styles! */
:focus{outline:0;}
-/* remember to highlight inserts somehow! */
ins{text-decoration:none;}
del{text-decoration:line-through;}
-/* tables still need 'cellspacing="0"' in the markup */
table{border-collapse:collapse;border-spacing:0;}
-/* restyling */
sub{vertical-align:sub;font-size:smaller;line-height:normal;}
sup{vertical-align:super;font-size:smaller;line-height:normal;}
-/* lists */
ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
li{display:list-item;}
-/* nested lists have no top/bottom margins */
ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-/* 2 deep unordered lists use a circle */
ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-/* 3 deep (or more) unordered lists use a square */
ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
.hidden{display:none;}
p{line-height:1.5em;}
-h1{font-size:1.75em;/* 28px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;/* 24px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;/* 20px */
-line-height:1.7em;font-family:helvetica,verdana;}
+h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
+h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
+h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
@@ -42,23 +31,26 @@ body h2,
body h3,
body h4,
body h5,
-body h6{font-family:"Nobile","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#144fb2;font-style:normal;}
-#wrap {min-height: 100%;}
-#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;}
-#header{background-color:#e88f00;top:0;font-size:14px;}
-#footer{background-color:#000000;bottom:0;position: relative;margin-top:-40px;clear:both;}
-.header,.footer{width:700px;margin-right:auto;margin-left:auto;}
+body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
+#wrap{min-height:100%;}
+#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
+#header{background:#000000;top:0;font-size:14px;}
+#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
+.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
.wrapper{width:100%}
-#top,#bottom{width:100%;}
-#top{color:#888;background-color:#eee;height:300px;border-bottom:2px solid #ddd;}
-#bottom{color:#222;background-color:#ffffff;overflow:hidden;padding-bottom:80px;}
-.top,.bottom{width:700px;margin-right:auto;margin-left:auto;}
-.top{padding-top:100px;}
+#top,#top-small,#bottom{width:100%;}
+#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#bottom{color:#222;background-color:#ffffff;}
+.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top{padding-top:40px;}
+.top-small{padding-top:10px;}
+#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
.app-welcome{margin-top:25px;}
.app-name{color:#000000;font-weight:bold;}
.bottom{padding-top:50px;}
-#left{width:325px;float:left;padding-right:25px;}
-#right{width:325px;float:right;padding-left:25px;}
+#left{width:350px;float:left;padding-right:25px;}
+#right{width:350px;float:right;padding-left:25px;}
.align-left{text-align:left;}
.align-right{text-align:right;}
.align-center{text-align:center;}
@@ -67,7 +59,7 @@ ul.links li{list-style-type:none;font-size:14px;}
form{border-style:none;}
fieldset{border-style:none;}
input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text]{}
+input[type=text],input[type=password]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*Opera Fix*/
-body:before {content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/pyramid/paster_templates/zodb/+package+/static/pyramid-small.png b/pyramid/paster_templates/zodb/+package+/static/pyramid-small.png
new file mode 100644
index 000000000..a5bc0ade7
--- /dev/null
+++ b/pyramid/paster_templates/zodb/+package+/static/pyramid-small.png
Binary files differ
diff --git a/pyramid/paster_templates/zodb/+package+/static/pyramid.png b/pyramid/paster_templates/zodb/+package+/static/pyramid.png
new file mode 100644
index 000000000..347e05549
--- /dev/null
+++ b/pyramid/paster_templates/zodb/+package+/static/pyramid.png
Binary files differ
diff --git a/pyramid/paster_templates/zodb/+package+/static/transparent.gif b/pyramid/paster_templates/zodb/+package+/static/transparent.gif
new file mode 100644
index 000000000..0341802e5
--- /dev/null
+++ b/pyramid/paster_templates/zodb/+package+/static/transparent.gif
Binary files differ
diff --git a/pyramid/paster_templates/zodb/+package+/templates/mytemplate.pt b/pyramid/paster_templates/zodb/+package+/templates/mytemplate.pt
deleted file mode 100644
index 6ad23d44f..000000000
--- a/pyramid/paster_templates/zodb/+package+/templates/mytemplate.pt
+++ /dev/null
@@ -1,79 +0,0 @@
-<!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" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal">
-<head>
- <title>The Pyramid Web Application Development Framework</title>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
- <meta name="keywords" content="python web application" />
- <meta name="description" content="pyramid web application" />
- <link rel="shortcut icon" href="${request.application_url}/static/favicon.ico" />
- <link rel="stylesheet" href="${request.application_url}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
- <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
- <!--[if !IE 7]>
- <style type="text/css">
- #wrap {display:table;height:100%}
- </style>
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="header">
- <div class="header">The Pyramid Web Application Development Framework</div>
- </div>
- <div id="top">
- <div class="top align-center">
- <img src="${request.application_url}/static/logo.png" width="300" height="80"/>
- <p class="app-welcome">
- Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
- the Pyramid web application development framework.
- </p>
- </div>
- </div>
- <div id="bottom">
- <div class="bottom">
- <div id="left" class="align-right">
- <h3>Search Pyramid documentation</h3>
- <form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
- <input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Search" />
- </form>
- </div>
- <div id="right" class="align-left">
- <h3>Pyramid links</h3>
- <ul class="links">
- <li>
- <a href="http://pylonshq.com">Pylons Website</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/">The Pylons Project Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#api-documentation">API Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#tutorials">Tutorials</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#change-history">Change History</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#sample-applications">Sample Applications</a>
- </li>
- <li>
- <a href="http://docs.pylonshq.com/pyramid/dev/#support-and-development">Support and Development</a>
- </li>
- <li>
- <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
- </li>
- </ul>
- </div>
- </div>
- </div>
- </div>
- <div id="footer">
- <div class="footer">© Copyright 2008-2010, Agendaless Consulting.</div>
- </div>
-</body>
-</html> \ No newline at end of file
diff --git a/pyramid/paster_templates/zodb/+package+/templates/mytemplate.pt_tmpl b/pyramid/paster_templates/zodb/+package+/templates/mytemplate.pt_tmpl
new file mode 100644
index 000000000..a658ee2c9
--- /dev/null
+++ b/pyramid/paster_templates/zodb/+package+/templates/mytemplate.pt_tmpl
@@ -0,0 +1,76 @@
+<!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" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal">
+<head>
+ <title>The Pyramid Web Application Development Framework</title>
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+ <meta name="keywords" content="python web application" />
+ <meta name="description" content="pyramid web application" />
+ <link rel="shortcut icon" href="${request.static_url('{{package}}:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('{{package}}:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('{{package}}:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
+ <![endif]-->
+</head>
+<body>
+ <div id="wrap">
+ <div id="top">
+ <div class="top align-center">
+ <div><img src="${request.static_url('{{package}}:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-center">
+ <p class="app-welcome">
+ Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
+ the Pyramid web application development framework.
+ </p>
+ </div>
+ </div>
+ <div id="bottom">
+ <div class="bottom">
+ <div id="left" class="align-right">
+ <h2>Search documentation</h2>
+ <form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
+ <input type="text" id="q" name="q" value="" />
+ <input type="submit" id="x" value="Go" />
+ </form>
+ </div>
+ <div id="right" class="align-left">
+ <h2>Pyramid links</h2>
+ <ul class="links">
+ <li>
+ <a href="http://pylonshq.com">Pylons Website</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#api-documentation">API Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#tutorials">Tutorials</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#change-history">Change History</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#sample-applications">Sample Applications</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#support-and-development">Support and Development</a>
+ </li>
+ <li>
+ <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="footer">
+ <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/pyramid/renderers.py b/pyramid/renderers.py
index c7fe86452..2e0514b01 100644
--- a/pyramid/renderers.py
+++ b/pyramid/renderers.py
@@ -282,6 +282,18 @@ class RendererHelper(object):
def get_renderer(self):
return self.renderer
+ def render_view(self, request, response, view, context):
+ system = {
+ 'view':view,
+ 'renderer_name':self.name, # b/c
+ 'renderer_info':{'name':self.name, 'package':self.package},
+ 'context':context,
+ 'request':request
+ }
+ return self.render_to_response(response, system,
+ request=request)
+
+
def render(self, value, system_values, request=None):
renderer = self.renderer
if system_values is None:
diff --git a/pyramid/request.py b/pyramid/request.py
index 74418f1bb..475df744a 100644
--- a/pyramid/request.py
+++ b/pyramid/request.py
@@ -24,7 +24,7 @@ class Request(WebobRequest):
argument.
The documentation below (save for the ``add_response_callback`` and
- ''add_finished_callback`` methods, which are defined in this subclass
+ ``add_finished_callback`` methods, which are defined in this subclass
itself, and the attributes ``context``, ``registry``, ``root``,
``subpath``, ``traversed``, ``view_name``, ``virtual_root`` , and
``virtual_root_path``, each of which is added to the request by the
diff --git a/pyramid/security.py b/pyramid/security.py
index 723e87a87..51c0802d5 100644
--- a/pyramid/security.py
+++ b/pyramid/security.py
@@ -64,6 +64,24 @@ def authenticated_userid(request):
return None
return policy.authenticated_userid(request)
+def unauthenticated_userid(request):
+ """ Return an object which represents the *claimed* (not verified) user
+ id of the credentials present in the request. ``None`` if there is no
+ :term:`authentication policy` in effect or there is no user data
+ associated with the current request. This differs from
+ :func:`~pyramid.security.authenticated_userid`, because the effective
+ authentication policy will not ensure that a record associated with the
+ userid exists in persistent storage."""
+ try:
+ reg = request.registry
+ except AttributeError:
+ reg = get_current_registry() # b/c
+
+ policy = reg.queryUtility(IAuthenticationPolicy)
+ if policy is None:
+ return None
+ return policy.unauthenticated_userid(request)
+
def effective_principals(request):
""" Return the list of 'effective' :term:`principal` identifiers
for the ``request``. This will include the userid of the
diff --git a/pyramid/session.py b/pyramid/session.py
index 516815d99..9fbb79bf7 100644
--- a/pyramid/session.py
+++ b/pyramid/session.py
@@ -194,7 +194,10 @@ def UnencryptedCookieSessionFactoryConfig(
@manage_accessed
def get_csrf_token(self):
- return self.get('_csrft_', None)
+ token = self.get('_csrft_', None)
+ if token is None:
+ token = self.new_csrf_token()
+ return token
# non-API methods
def _set_cookie(self, response):
diff --git a/pyramid/testing.py b/pyramid/testing.py
index 61bb1843a..96388b709 100644
--- a/pyramid/testing.py
+++ b/pyramid/testing.py
@@ -11,6 +11,7 @@ from pyramid.interfaces import IRequest
from pyramid.interfaces import ISecuredView
from pyramid.interfaces import IView
from pyramid.interfaces import IViewClassifier
+from pyramid.interfaces import ISession
from pyramid.config import Configurator
from pyramid.exceptions import Forbidden
@@ -44,9 +45,10 @@ def registerDummySecurityPolicy(userid=None, groupids=(), permissive=True):
:func:`pyramid.security.authenticated_userid` or
:func:`pyramid.security.effective_principals` APIs are used.
- This function is most useful when testing code that uses the APIs
- named :func:`pyramid.security.has_permission`,
+ This function is most useful when testing code that uses the APIs named
+ :func:`pyramid.security.has_permission`,
:func:`pyramid.security.authenticated_userid`,
+ :func:`pyramid.security.unauthenticated_userid`,
:func:`pyramid.security.effective_principals`, and
:func:`pyramid.security.principals_allowed_by_permission`.
@@ -332,6 +334,9 @@ class DummySecurityPolicy(object):
def authenticated_userid(self, request):
return self.userid
+ def unauthenticated_userid(self, request):
+ return self.userid
+
def effective_principals(self, request):
effective_principals = [Everyone]
if self.userid:
@@ -429,7 +434,12 @@ class DummyResource:
that will be attached to the resulting resource via
:func:`zope.interface.alsoProvides`. Any extra keywords passed
in the ``kw`` argumnent will be set as direct attributes of
- the resource object."""
+ the resource object.
+
+ .. note:: For backwards compatibility purposes, this class can also
+ be imported as :class:`pyramid.testing.DummyModel`.
+
+ """
self.__name__ = __name__
self.__parent__ = __parent__
if __provides__ is not None:
@@ -502,6 +512,37 @@ class DummyResource:
DummyModel = DummyResource # b/w compat (forever)
+class DummySession(dict):
+ implements(ISession)
+ created = None
+ new = True
+ def changed(self):
+ pass
+
+ def invalidate(self):
+ self.clear()
+
+ def flash(self, msg, queue='', allow_duplicate=True):
+ storage = self.setdefault('_f_' + queue, [])
+ if allow_duplicate or (msg not in storage):
+ storage.append(msg)
+
+ def pop_flash(self, queue=''):
+ storage = self.pop('_f_' + queue, [])
+ return storage
+
+ def peek_flash(self, queue=''):
+ storage = self.get('_f_' + queue, [])
+ return storage
+
+ def new_csrf_token(self):
+ token = 'csrft'
+ self['_csrft_'] = token
+ return token
+
+ def get_csrf_token(self):
+ return self.get('_csrft_', None)
+
class DummyRequest(object):
""" A dummy request object (imitates a :term:`request` object).
@@ -517,8 +558,6 @@ class DummyRequest(object):
Extra keyword arguments are assigned as attributes of the request
itself.
- .. note:: For backwards compatibility purposes, this class can also be
- imported as :class:`pyramid.testing.DummyModel`.
"""
implements(IRequest)
method = 'GET'
@@ -565,6 +604,7 @@ class DummyRequest(object):
self.virtual_root = None
self.marshalled = params # repoze.monty
self.registry = get_current_registry()
+ self.session = DummySession()
self.__dict__.update(kw)
def add_response_callback(self, callback):
diff --git a/pyramid/tests/test_authentication.py b/pyramid/tests/test_authentication.py
index d9d0c2c97..49d655466 100644
--- a/pyramid/tests/test_authentication.py
+++ b/pyramid/tests/test_authentication.py
@@ -18,11 +18,22 @@ class TestRepozeWho1AuthenticationPolicy(unittest.TestCase):
from pyramid.interfaces import IAuthenticationPolicy
verifyObject(IAuthenticationPolicy, self._makeOne())
+ def test_unauthenticated_userid_returns_None(self):
+ request = DummyRequest({})
+ policy = self._makeOne()
+ self.assertEqual(policy.unauthenticated_userid(request), None)
+
+ def test_unauthenticated_userid(self):
+ request = DummyRequest(
+ {'repoze.who.identity':{'repoze.who.userid':'fred'}})
+ policy = self._makeOne()
+ self.assertEqual(policy.unauthenticated_userid(request), 'fred')
+
def test_authenticated_userid_None(self):
request = DummyRequest({})
policy = self._makeOne()
self.assertEqual(policy.authenticated_userid(request), None)
-
+
def test_authenticated_userid(self):
request = DummyRequest(
{'repoze.who.identity':{'repoze.who.userid':'fred'}})
@@ -132,6 +143,16 @@ class TestRemoteUserAuthenticationPolicy(unittest.TestCase):
from pyramid.interfaces import IAuthenticationPolicy
verifyObject(IAuthenticationPolicy, self._makeOne())
+ def test_unauthenticated_userid_returns_None(self):
+ request = DummyRequest({})
+ policy = self._makeOne()
+ self.assertEqual(policy.unauthenticated_userid(request), None)
+
+ def test_unauthenticated_userid(self):
+ request = DummyRequest({'REMOTE_USER':'fred'})
+ policy = self._makeOne()
+ self.assertEqual(policy.unauthenticated_userid(request), 'fred')
+
def test_authenticated_userid_None(self):
request = DummyRequest({})
policy = self._makeOne()
@@ -196,6 +217,16 @@ class TestAutkTktAuthenticationPolicy(unittest.TestCase):
from pyramid.interfaces import IAuthenticationPolicy
verifyObject(IAuthenticationPolicy, self._makeOne(None, None))
+ def test_unauthenticated_userid_returns_None(self):
+ request = DummyRequest({})
+ policy = self._makeOne(None, None)
+ self.assertEqual(policy.unauthenticated_userid(request), None)
+
+ def test_unauthenticated_userid(self):
+ request = DummyRequest({'REMOTE_USER':'fred'})
+ policy = self._makeOne(None, {'userid':'fred'})
+ self.assertEqual(policy.unauthenticated_userid(request), 'fred')
+
def test_authenticated_userid_no_cookie_identity(self):
request = DummyRequest({})
policy = self._makeOne(None, None)
diff --git a/pyramid/tests/test_chameleon_text.py b/pyramid/tests/test_chameleon_text.py
index 79bc7984c..789c78bbe 100644
--- a/pyramid/tests/test_chameleon_text.py
+++ b/pyramid/tests/test_chameleon_text.py
@@ -1,23 +1,16 @@
import unittest
-from pyramid.testing import cleanUp
from pyramid.testing import skip_on
from pyramid import testing
class Base:
def setUp(self):
- cleanUp()
- import os
- try:
- # avoid spew from chameleon logger?
- os.unlink(self._getTemplatePath('minimal.txt.py'))
- except:
- pass
+ self.config = testing.setUp()
from zope.deprecation import __show__
__show__.off()
def tearDown(self):
- cleanUp()
+ testing.tearDown()
from zope.deprecation import __show__
__show__.on()
@@ -27,22 +20,10 @@ class Base:
return os.path.join(here, 'fixtures', name)
def _registerUtility(self, utility, iface, name=''):
- from pyramid.threadlocal import get_current_registry
- reg = get_current_registry()
+ reg = self.config.registry
reg.registerUtility(utility, iface, name=name)
- return reg
-
class TextTemplateRendererTests(Base, unittest.TestCase):
- def setUp(self):
- from pyramid.registry import Registry
- registry = Registry()
- self.config = testing.setUp(registry=registry)
- self.config.begin()
-
- def tearDown(self):
- self.config.end()
-
def _getTargetClass(self):
from pyramid.chameleon_text import TextTemplateRenderer
return TextTemplateRenderer
diff --git a/pyramid/tests/test_chameleon_zpt.py b/pyramid/tests/test_chameleon_zpt.py
index 4601c2f12..4fceb809c 100644
--- a/pyramid/tests/test_chameleon_zpt.py
+++ b/pyramid/tests/test_chameleon_zpt.py
@@ -1,17 +1,16 @@
import unittest
-from pyramid.testing import cleanUp
from pyramid.testing import skip_on
from pyramid import testing
class Base(object):
def setUp(self):
- cleanUp()
+ self.config = testing.setUp()
from zope.deprecation import __show__
__show__.off()
def tearDown(self):
- cleanUp()
+ testing.tearDown()
from zope.deprecation import __show__
__show__.on()
@@ -21,21 +20,11 @@ class Base(object):
return os.path.join(here, 'fixtures', name)
def _registerUtility(self, utility, iface, name=''):
- from pyramid.threadlocal import get_current_registry
- reg = get_current_registry()
+ reg = self.config.registry
reg.registerUtility(utility, iface, name=name)
return reg
class ZPTTemplateRendererTests(Base, unittest.TestCase):
- def setUp(self):
- from pyramid.registry import Registry
- registry = Registry()
- self.config = testing.setUp(registry=registry)
- self.config.begin()
-
- def tearDown(self):
- self.config.end()
-
def _getTargetClass(self):
from pyramid.chameleon_zpt import ZPTTemplateRenderer
return ZPTTemplateRenderer
diff --git a/pyramid/tests/test_config.py b/pyramid/tests/test_config.py
index c129b21ae..ac459d7e3 100644
--- a/pyramid/tests/test_config.py
+++ b/pyramid/tests/test_config.py
@@ -63,27 +63,11 @@ class ConfiguratorTests(unittest.TestCase):
config.registry.registerHandler(subscriber, (event_iface,))
return L
- def _registerLogger(self, config):
- from pyramid.interfaces import IDebugLogger
- logger = DummyLogger()
- config.registry.registerUtility(logger, IDebugLogger)
- return logger
-
def _makeRequest(self, config):
request = DummyRequest()
request.registry = config.registry
return request
- def _registerSecurityPolicy(self, config, permissive):
- from pyramid.interfaces import IAuthenticationPolicy
- from pyramid.interfaces import IAuthorizationPolicy
- policy = DummySecurityPolicy(permissive)
- config.registry.registerUtility(policy, IAuthenticationPolicy)
- config.registry.registerUtility(policy, IAuthorizationPolicy)
-
- def _registerSettings(self, config, **settings):
- config.registry.settings = settings
-
def test_ctor_no_registry(self):
import sys
from pyramid.interfaces import ISettings
@@ -196,6 +180,13 @@ class ConfiguratorTests(unittest.TestCase):
config = self._makeOne(session_factory='factory')
self.assertEqual(config.registry.getUtility(ISessionFactory), 'factory')
+ def test_ctor_default_view_mapper(self):
+ from pyramid.interfaces import IViewMapperFactory
+ mapper = object()
+ config = self._makeOne(default_view_mapper=mapper)
+ self.assertEqual(config.registry.getUtility(IViewMapperFactory),
+ mapper)
+
def test_with_package_module(self):
from pyramid.tests import test_configuration
import pyramid.tests
@@ -739,6 +730,22 @@ class ConfiguratorTests(unittest.TestCase):
result = wrapper(None, None)
self.assertEqual(result, 'OK')
+ def test_add_view_with_decorator(self):
+ def view(request):
+ """ ABC """
+ return 'OK'
+ def view_wrapper(fn):
+ def inner(context, request):
+ return fn(context, request)
+ return inner
+ config = self._makeOne(autocommit=True)
+ config.add_view(view=view, decorator=view_wrapper)
+ wrapper = self._getViewCallable(config)
+ self.failIf(wrapper is view)
+ self.assertEqual(wrapper.__doc__, view.__doc__)
+ result = wrapper(None, None)
+ self.assertEqual(result, 'OK')
+
def test_add_view_as_instance(self):
class AView:
def __call__(self, context, request):
@@ -774,8 +781,10 @@ class ConfiguratorTests(unittest.TestCase):
config = self._makeOne(autocommit=True)
config.add_view(view=view)
wrapper = self._getViewCallable(config)
- result = wrapper(None, None)
+ request = self._makeRequest(config)
+ result = wrapper(None, request)
self.assertEqual(result, 'OK')
+ self.assertEqual(request.__view__.__class__, view)
def test_add_view_as_oldstyle_class_requestonly(self):
class view:
@@ -787,8 +796,11 @@ class ConfiguratorTests(unittest.TestCase):
config = self._makeOne(autocommit=True)
config.add_view(view=view)
wrapper = self._getViewCallable(config)
- result = wrapper(None, None)
+
+ request = self._makeRequest(config)
+ result = wrapper(None, request)
self.assertEqual(result, 'OK')
+ self.assertEqual(request.__view__.__class__, view)
def test_add_view_context_as_class(self):
from zope.interface import implementedBy
@@ -1410,6 +1422,27 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(result.name, fixture)
self.assertEqual(result.settings, settings)
+ def test_add_view_with_default_renderer(self):
+ class view(object):
+ def __init__(self, context, request):
+ self.request = request
+ self.context = context
+
+ def __call__(self):
+ return {'a':'1'}
+ config = self._makeOne(autocommit=True)
+ class moo(object):
+ def __init__(self, *arg, **kw):
+ pass
+ def __call__(self, *arg, **kw):
+ return 'moo'
+ config.add_renderer(None, moo)
+ config.add_view(view=view)
+ wrapper = self._getViewCallable(config)
+ request = self._makeRequest(config)
+ result = wrapper(None, request)
+ self.assertEqual(result.body, 'moo')
+
def test_add_view_with_template_renderer_no_callable(self):
import pyramid.tests
from pyramid.interfaces import ISettings
@@ -1964,6 +1997,33 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(view['attr'], 'action')
self.assertEqual(view['view'], MyView)
+ def test_add_handler_with_action_decorator(self):
+ config = self._makeOne(autocommit=True)
+ views = []
+ def dummy_add_view(**kw):
+ views.append(kw)
+ config.add_view = dummy_add_view
+ class MyHandler(object):
+ @classmethod
+ def __action_decorator__(cls, fn): # pragma: no cover
+ return fn
+ def action(self): # pragma: no cover
+ return 'response'
+ config.add_handler('name', '/{action}', MyHandler)
+ self.assertEqual(len(views), 1)
+ self.assertEqual(views[0]['decorator'], MyHandler.__action_decorator__)
+
+ def test_add_handler_with_action_decorator_fail_on_instancemethod(self):
+ config = self._makeOne(autocommit=True)
+ class MyHandler(object):
+ def __action_decorator__(self, fn): # pragma: no cover
+ return fn
+ def action(self): # pragma: no cover
+ return 'response'
+ from pyramid.exceptions import ConfigurationError
+ self.assertRaises(ConfigurationError, config.add_handler,
+ 'name', '/{action}', MyHandler)
+
def test_add_handler_doesnt_mutate_expose_dict(self):
config = self._makeOne(autocommit=True)
views = []
@@ -2274,7 +2334,8 @@ class ConfiguratorTests(unittest.TestCase):
request_type = self._getRouteRequestIface(config, 'name')
wrapper = self._getViewCallable(config, None, request_type)
self._assertRoute(config, 'name', 'path')
- self.assertEqual(wrapper(None, None), 'OK')
+ request = self._makeRequest(config)
+ self.assertEqual(wrapper(None, request), 'OK')
def test_add_route_with_view_renderer_alias(self):
config = self._makeOne(autocommit=True)
@@ -2371,6 +2432,52 @@ class ConfiguratorTests(unittest.TestCase):
else: # pragma: no cover
raise AssertionError
+ def test_derive_view_function(self):
+ def view(request):
+ return 'OK'
+ config = self._makeOne()
+ result = config.derive_view(view)
+ self.failIf(result is view)
+ self.assertEqual(result(None, None), 'OK')
+
+ def test_derive_view_dottedname(self):
+ config = self._makeOne()
+ result = config.derive_view(
+ 'pyramid.tests.test_config.dummy_view')
+ self.failIf(result is dummy_view)
+ self.assertEqual(result(None, None), 'OK')
+
+ def test_derive_view_with_default_renderer_no_explicit_renderer(self):
+ config = self._makeOne()
+ class moo(object):
+ def __init__(self, view):
+ pass
+ def __call__(self, *arg, **kw):
+ return 'moo'
+ config.add_renderer(None, moo)
+ def view(request):
+ return 'OK'
+ result = config.derive_view(view)
+ self.failIf(result is view)
+ self.assertEqual(result(None, None).body, 'moo')
+
+ def test_derive_view_with_default_renderer_with_explicit_renderer(self):
+ class moo(object): pass
+ class foo(object):
+ def __init__(self, view):
+ pass
+ def __call__(self, *arg, **kw):
+ return 'foo'
+ def view(request):
+ return 'OK'
+ config = self._makeOne()
+ config.add_renderer(None, moo)
+ config.add_renderer('foo', foo)
+ result = config.derive_view(view, renderer='foo')
+ self.failIf(result is view)
+ request = self._makeRequest(config)
+ self.assertEqual(result(None, request).body, 'foo')
+
def test__override_not_yet_registered(self):
from pyramid.interfaces import IPackageOverrides
package = DummyPackage('package')
@@ -2567,6 +2674,22 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(config.registry.getUtility(IDefaultPermission),
'view')
+ def test_add_view_mapper(self):
+ from pyramid.interfaces import IViewMapperFactory
+ config = self._makeOne(autocommit=True)
+ mapper = object()
+ config.set_view_mapper(mapper)
+ result = config.registry.getUtility(IViewMapperFactory)
+ self.assertEqual(result, mapper)
+
+ def test_add_view_mapper_dottedname(self):
+ from pyramid.interfaces import IViewMapperFactory
+ config = self._makeOne(autocommit=True)
+ config.set_view_mapper('pyramid.tests.test_config')
+ result = config.registry.getUtility(IViewMapperFactory)
+ from pyramid.tests import test_config
+ self.assertEqual(result, test_config)
+
def test_set_session_factory(self):
from pyramid.interfaces import ISessionFactory
config = self._makeOne(autocommit=True)
@@ -2614,404 +2737,6 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(config.registry.getUtility(ITranslationDirectories),
[locale])
- def test_derive_view_function(self):
- def view(request):
- return 'OK'
- config = self._makeOne()
- result = config.derive_view(view)
- self.failIf(result is view)
- self.assertEqual(result(None, None), 'OK')
-
- def test_derive_view_dottedname(self):
- config = self._makeOne()
- result = config.derive_view(
- 'pyramid.tests.test_config.dummy_view')
- self.failIf(result is dummy_view)
- self.assertEqual(result(None, None), 'OK')
-
- def test_derive_view_with_renderer(self):
- def view(request):
- return 'OK'
- config = self._makeOne(autocommit=True)
- class moo(object):
- def __init__(self, *arg, **kw):
- pass
- def __call__(self, *arg, **kw):
- return 'moo'
- config.add_renderer('moo', moo)
- result = config.derive_view(view, renderer='moo')
- self.failIf(result is view)
- self.assertEqual(result(None, None).body, 'moo')
-
- def test_derive_view_with_default_renderer_no_explicit_renderer(self):
- def view(request):
- return 'OK'
- config = self._makeOne(autocommit=True)
- class moo(object):
- def __init__(self, *arg, **kw):
- pass
- def __call__(self, *arg, **kw):
- return 'moo'
- config.add_renderer(None, moo)
- result = config.derive_view(view)
- self.failIf(result is view)
- self.assertEqual(result(None, None).body, 'moo')
-
- def test_derive_view_with_default_renderer_with_explicit_renderer(self):
- def view(request):
- return 'OK'
- config = self._makeOne(autocommit=True)
- class moo(object): pass
- class foo(object):
- def __init__(self, *arg, **kw):
- pass
- def __call__(self, *arg, **kw):
- return 'foo'
- config.add_renderer(None, moo)
- config.add_renderer('foo', foo)
- result = config.derive_view(view, renderer='foo')
- self.failIf(result is view)
- self.assertEqual(result(None, None).body, 'foo')
-
- def test_derive_view_class_without_attr(self):
- class View(object):
- def __init__(self, request):
- pass
- def __call__(self):
- return 'OK'
- config = self._makeOne()
- result = config.derive_view(View)
- self.assertEqual(result(None, None), 'OK')
-
- def test_derive_view_class_with_attr(self):
- class View(object):
- def __init__(self, request):
- pass
- def another(self):
- return 'OK'
- config = self._makeOne()
- result = config.derive_view(View, attr='another')
- self.assertEqual(result(None, None), 'OK')
-
- def test__derive_view_as_function_context_and_request(self):
- def view(context, request):
- return 'OK'
- config = self._makeOne()
- result = config._derive_view(view)
- self.failUnless(result is view)
- self.failIf(hasattr(result, '__call_permissive__'))
- self.assertEqual(view(None, None), 'OK')
-
- def test__derive_view_as_function_requestonly(self):
- def view(request):
- return 'OK'
- config = self._makeOne()
- result = config._derive_view(view)
- self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.failIf(hasattr(result, '__call_permissive__'))
- self.assertEqual(result(None, None), 'OK')
-
- def test__derive_view_as_newstyle_class_context_and_request(self):
- class view(object):
- def __init__(self, context, request):
- pass
- def __call__(self):
- return 'OK'
- config = self._makeOne()
- result = config._derive_view(view)
- self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.failIf(hasattr(result, '__call_permissive__'))
- self.assertEqual(result(None, None), 'OK')
-
- def test__derive_view_as_newstyle_class_requestonly(self):
- class view(object):
- def __init__(self, context, request):
- pass
- def __call__(self):
- return 'OK'
- config = self._makeOne()
- result = config._derive_view(view)
- self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.failIf(hasattr(result, '__call_permissive__'))
- self.assertEqual(result(None, None), 'OK')
-
- def test__derive_view_as_oldstyle_class_context_and_request(self):
- class view:
- def __init__(self, context, request):
- pass
- def __call__(self):
- return 'OK'
- config = self._makeOne()
- result = config._derive_view(view)
- self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.failIf(hasattr(result, '__call_permissive__'))
- self.assertEqual(result(None, None), 'OK')
-
- def test__derive_view_as_oldstyle_class_requestonly(self):
- class view:
- def __init__(self, context, request):
- pass
- def __call__(self):
- return 'OK'
- config = self._makeOne()
- result = config._derive_view(view)
- self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.failIf(hasattr(result, '__call_permissive__'))
- self.assertEqual(result(None, None), 'OK')
-
- def test__derive_view_as_instance_context_and_request(self):
- class View:
- def __call__(self, context, request):
- return 'OK'
- view = View()
- config = self._makeOne()
- result = config._derive_view(view)
- self.failUnless(result is view)
- self.failIf(hasattr(result, '__call_permissive__'))
- self.assertEqual(result(None, None), 'OK')
-
- def test__derive_view_as_instance_requestonly(self):
- class View:
- def __call__(self, request):
- return 'OK'
- view = View()
- config = self._makeOne()
- result = config._derive_view(view)
- self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.failUnless('instance' in result.__name__)
- self.failIf(hasattr(result, '__call_permissive__'))
- self.assertEqual(result(None, None), 'OK')
-
- def test__derive_view_with_debug_authorization_no_authpol(self):
- view = lambda *arg: 'OK'
- config = self._makeOne()
- self._registerSettings(config,
- debug_authorization=True, reload_templates=True)
- logger = self._registerLogger(config)
- result = config._derive_view(view, permission='view')
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.failIf(hasattr(result, '__call_permissive__'))
- request = self._makeRequest(config)
- request.view_name = 'view_name'
- request.url = 'url'
- self.assertEqual(result(None, request), 'OK')
- self.assertEqual(len(logger.messages), 1)
- self.assertEqual(logger.messages[0],
- "debug_authorization of url url (view name "
- "'view_name' against context None): Allowed "
- "(no authorization policy in use)")
-
- def test__derive_view_with_debug_authorization_no_permission(self):
- view = lambda *arg: 'OK'
- config = self._makeOne()
- self._registerSettings(config,
- debug_authorization=True, reload_templates=True)
- self._registerSecurityPolicy(config, True)
- logger = self._registerLogger(config)
- result = config._derive_view(view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.failIf(hasattr(result, '__call_permissive__'))
- request = self._makeRequest(config)
- request.view_name = 'view_name'
- request.url = 'url'
- self.assertEqual(result(None, request), 'OK')
- self.assertEqual(len(logger.messages), 1)
- self.assertEqual(logger.messages[0],
- "debug_authorization of url url (view name "
- "'view_name' against context None): Allowed ("
- "no permission registered)")
-
- def test__derive_view_debug_auth_permission_authpol_permitted(self):
- view = lambda *arg: 'OK'
- config = self._makeOne()
- self._registerSettings(config, debug_authorization=True,
- reload_templates=True)
- logger = self._registerLogger(config)
- self._registerSecurityPolicy(config, True)
- result = config._derive_view(view, permission='view')
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result.__call_permissive__, view)
- request = self._makeRequest(config)
- request.view_name = 'view_name'
- request.url = 'url'
- self.assertEqual(result(None, request), 'OK')
- self.assertEqual(len(logger.messages), 1)
- self.assertEqual(logger.messages[0],
- "debug_authorization of url url (view name "
- "'view_name' against context None): True")
-
- def test__derive_view_debug_auth_permission_authpol_denied(self):
- from pyramid.exceptions import Forbidden
- view = lambda *arg: 'OK'
- config = self._makeOne()
- self._registerSettings(config,
- debug_authorization=True, reload_templates=True)
- logger = self._registerLogger(config)
- self._registerSecurityPolicy(config, False)
- result = config._derive_view(view, permission='view')
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result.__call_permissive__, view)
- request = self._makeRequest(config)
- request.view_name = 'view_name'
- request.url = 'url'
- self.assertRaises(Forbidden, result, None, request)
- self.assertEqual(len(logger.messages), 1)
- self.assertEqual(logger.messages[0],
- "debug_authorization of url url (view name "
- "'view_name' against context None): False")
-
- def test__derive_view_debug_auth_permission_authpol_denied2(self):
- view = lambda *arg: 'OK'
- config = self._makeOne()
- self._registerSettings(config,
- debug_authorization=True, reload_templates=True)
- self._registerLogger(config)
- self._registerSecurityPolicy(config, False)
- result = config._derive_view(view, permission='view')
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- request = self._makeRequest(config)
- request.view_name = 'view_name'
- request.url = 'url'
- permitted = result.__permitted__(None, None)
- self.assertEqual(permitted, False)
-
- def test__derive_view_debug_auth_permission_authpol_overridden(self):
- view = lambda *arg: 'OK'
- config = self._makeOne()
- self._registerSettings(config,
- debug_authorization=True, reload_templates=True)
- logger = self._registerLogger(config)
- self._registerSecurityPolicy(config, False)
- result = config._derive_view(view,
- permission='__no_permission_required__')
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.failIf(hasattr(result, '__call_permissive__'))
- request = self._makeRequest(config)
- request.view_name = 'view_name'
- request.url = 'url'
- self.assertEqual(result(None, request), 'OK')
- self.assertEqual(len(logger.messages), 1)
- self.assertEqual(logger.messages[0],
- "debug_authorization of url url (view name "
- "'view_name' against context None): False")
-
- def test__derive_view_with_predicates_all(self):
- view = lambda *arg: 'OK'
- predicates = []
- def predicate1(context, request):
- predicates.append(True)
- return True
- def predicate2(context, request):
- predicates.append(True)
- return True
- config = self._makeOne()
- result = config._derive_view(view, predicates=[predicate1, predicate2])
- request = self._makeRequest(config)
- request.method = 'POST'
- next = result(None, None)
- self.assertEqual(next, 'OK')
- self.assertEqual(predicates, [True, True])
-
- def test__derive_view_with_predicates_checker(self):
- view = lambda *arg: 'OK'
- predicates = []
- def predicate1(context, request):
- predicates.append(True)
- return True
- def predicate2(context, request):
- predicates.append(True)
- return True
- config = self._makeOne()
- result = config._derive_view(view, predicates=[predicate1, predicate2])
- request = self._makeRequest(config)
- request.method = 'POST'
- next = result.__predicated__(None, None)
- self.assertEqual(next, True)
- self.assertEqual(predicates, [True, True])
-
- def test__derive_view_with_predicates_notall(self):
- from pyramid.exceptions import NotFound
- view = lambda *arg: 'OK'
- predicates = []
- def predicate1(context, request):
- predicates.append(True)
- return True
- def predicate2(context, request):
- predicates.append(True)
- return False
- config = self._makeOne()
- result = config._derive_view(view, predicates=[predicate1, predicate2])
- request = self._makeRequest(config)
- request.method = 'POST'
- self.assertRaises(NotFound, result, None, None)
- self.assertEqual(predicates, [True, True])
-
- def test__derive_view_with_wrapper_viewname(self):
- from webob import Response
- from pyramid.interfaces import IView
- from pyramid.interfaces import IViewClassifier
- inner_response = Response('OK')
- def inner_view(context, request):
- return inner_response
- def outer_view(context, request):
- self.assertEqual(request.wrapped_response, inner_response)
- self.assertEqual(request.wrapped_body, inner_response.body)
- self.assertEqual(request.wrapped_view, inner_view)
- return Response('outer ' + request.wrapped_body)
- config = self._makeOne()
- config.registry.registerAdapter(
- outer_view, (IViewClassifier, None, None), IView, 'owrap')
- result = config._derive_view(inner_view, viewname='inner',
- wrapper_viewname='owrap')
- self.failIf(result is inner_view)
- self.assertEqual(inner_view.__module__, result.__module__)
- self.assertEqual(inner_view.__doc__, result.__doc__)
- request = self._makeRequest(config)
- request.registry = config.registry
- response = result(None, request)
- self.assertEqual(response.body, 'outer OK')
-
- def test__derive_view_with_wrapper_viewname_notfound(self):
- from webob import Response
- inner_response = Response('OK')
- def inner_view(context, request):
- return inner_response
- config = self._makeOne()
- request = self._makeRequest(config)
- request.registry = config.registry
- wrapped = config._derive_view(
- inner_view, viewname='inner', wrapper_viewname='owrap')
- self.assertRaises(ValueError, wrapped, None, request)
-
def test_override_asset_samename(self):
from pyramid.exceptions import ConfigurationError
config = self._makeOne()
@@ -3491,353 +3216,837 @@ class ConfiguratorTests(unittest.TestCase):
for confinst in conflict:
yield confinst[2]
-class Test__map_view(unittest.TestCase):
+class TestViewDeriver(unittest.TestCase):
def setUp(self):
- from pyramid.registry import Registry
- self.registry = Registry()
- testing.setUp(registry=self.registry)
+ self.config = testing.setUp()
def tearDown(self):
- del self.registry
- testing.tearDown()
-
- def _registerRenderer(self, typ='.txt'):
- from pyramid.interfaces import IRendererFactory
- from pyramid.interfaces import ITemplateRenderer
- from zope.interface import implements
- class Renderer:
- implements(ITemplateRenderer)
- spec = 'abc' + typ
- def __init__(self, path):
- self.__class__.path = path
- def __call__(self, *arg):
- return 'Hello!'
- self.registry.registerUtility(Renderer, IRendererFactory, name=typ)
- return Renderer
-
+ self.config = None
+
+ def _makeOne(self, **kw):
+ kw['registry'] = self.config.registry
+ from pyramid.config import ViewDeriver
+ return ViewDeriver(**kw)
+
def _makeRequest(self):
request = DummyRequest()
- request.registry = self.registry
+ request.registry = self.config.registry
return request
- def _callFUT(self, view, **kw):
- from pyramid.config import _map_view
- return _map_view(view, self.registry, **kw)
+ def _registerLogger(self):
+ from pyramid.interfaces import IDebugLogger
+ logger = DummyLogger()
+ self.config.registry.registerUtility(logger, IDebugLogger)
+ return logger
- def test__map_view_as_function_context_and_request(self):
- def view(context, request):
+ def _registerSecurityPolicy(self, permissive):
+ from pyramid.interfaces import IAuthenticationPolicy
+ from pyramid.interfaces import IAuthorizationPolicy
+ policy = DummySecurityPolicy(permissive)
+ self.config.registry.registerUtility(policy, IAuthenticationPolicy)
+ self.config.registry.registerUtility(policy, IAuthorizationPolicy)
+
+ def test_requestonly_function(self):
+ def view(request):
return 'OK'
- result = self._callFUT(view)
- self.failUnless(result is view)
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.failIf(result is view)
self.assertEqual(result(None, None), 'OK')
- def test__map_view_as_function_with_attr(self):
- def view(context, request):
- """ """
- result = self._callFUT(view, attr='__name__')
+ def test_requestonly_function_with_renderer(self):
+ class moo(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, 'OK')
+ self.assertEqual(view_inst, view)
+ self.assertEqual(ctx, context)
+ return 'moo'
+ def view(request):
+ return 'OK'
+ deriver = self._makeOne(renderer=moo())
+ result = deriver(view)
self.failIf(result is view)
- self.assertRaises(TypeError, result, None, None)
-
- def test__map_view_as_function_with_attr_and_renderer(self):
- renderer = self._registerRenderer()
- view = lambda *arg: 'OK'
- info = {'name':renderer.spec, 'package':None}
- result = self._callFUT(view, attr='__name__', renderer=info)
+ request = self._makeRequest()
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), 'moo')
+
+ def test_requestonly_function_with_renderer_request_override(self):
+ def moo(info):
+ def inner(value, system):
+ self.assertEqual(value, 'OK')
+ self.assertEqual(system['request'], request)
+ self.assertEqual(system['context'], context)
+ return 'moo'
+ return inner
+ def view(request):
+ return 'OK'
+ self.config.add_renderer('moo', moo)
+ deriver = self._makeOne(renderer='string')
+ result = deriver(view)
self.failIf(result is view)
- self.assertRaises(TypeError, result, None, None)
+ request = self._makeRequest()
+ request.override_renderer = 'moo'
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request).body, 'moo')
- def test__map_view_as_function_requestonly(self):
+ def test_requestonly_function_with_renderer_request_has_view(self):
+ class moo(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, 'OK')
+ self.assertEqual(view_inst, 'view')
+ self.assertEqual(ctx, context)
+ return 'moo'
def view(request):
return 'OK'
- result = self._callFUT(view)
+ deriver = self._makeOne(renderer=moo())
+ result = deriver(view)
self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result(None, None), 'OK')
+ request = self._makeRequest()
+ request.__view__ = 'view'
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), 'moo')
+ self.failIf(hasattr(request, '__view__'))
+
+ def test_class_without_attr(self):
+ class View(object):
+ def __init__(self, request):
+ pass
+ def __call__(self):
+ return 'OK'
+ deriver = self._makeOne()
+ result = deriver(View)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+ self.assertEqual(request.__view__.__class__, View)
+
+ def test_class_with_attr(self):
+ class View(object):
+ def __init__(self, request):
+ pass
+ def another(self):
+ return 'OK'
+ deriver = self._makeOne(attr='another')
+ result = deriver(View)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+ self.assertEqual(request.__view__.__class__, View)
- def test__map_view_as_function_requestonly_with_attr(self):
+ def test_as_function_context_and_request(self):
+ def view(context, request):
+ return 'OK'
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.failUnless(result is view)
+ self.failIf(hasattr(result, '__call_permissive__'))
+ self.assertEqual(view(None, None), 'OK')
+
+ def test_as_function_requestonly(self):
def view(request):
- """ """
- result = self._callFUT(view, attr='__name__')
+ return 'OK'
+ deriver = self._makeOne()
+ result = deriver(view)
self.failIf(result is view)
self.assertEqual(view.__module__, result.__module__)
self.assertEqual(view.__doc__, result.__doc__)
self.assertEqual(view.__name__, result.__name__)
- self.assertRaises(TypeError, result, None, None)
+ self.failIf(hasattr(result, '__call_permissive__'))
+ self.assertEqual(result(None, None), 'OK')
- def test__map_view_as_newstyle_class_context_and_request(self):
+ def test_as_newstyle_class_context_and_request(self):
class view(object):
def __init__(self, context, request):
pass
def __call__(self):
return 'OK'
- result = self._callFUT(view)
+ deriver = self._makeOne()
+ result = deriver(view)
self.failIf(result is view)
self.assertEqual(view.__module__, result.__module__)
self.assertEqual(view.__doc__, result.__doc__)
self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result(None, None), 'OK')
+ self.failIf(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+ self.assertEqual(request.__view__.__class__, view)
- def test__map_view_as_newstyle_class_context_and_request_with_attr(self):
+ def test_as_newstyle_class_requestonly(self):
class view(object):
def __init__(self, context, request):
pass
- def index(self):
+ def __call__(self):
return 'OK'
- result = self._callFUT(view, attr='index')
+ deriver = self._makeOne()
+ result = deriver(view)
self.failIf(result is view)
self.assertEqual(view.__module__, result.__module__)
self.assertEqual(view.__doc__, result.__doc__)
self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result(None, None), 'OK')
+ self.failIf(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+ self.assertEqual(request.__view__.__class__, view)
- def test__map_view_as_newstyle_class_context_and_request_attr_and_renderer(
- self):
- renderer = self._registerRenderer()
- class view(object):
+ def test_as_oldstyle_class_context_and_request(self):
+ class view:
def __init__(self, context, request):
pass
- def index(self):
- return {'a':'1'}
- info = {'name':renderer.spec, 'package':None}
- result = self._callFUT(view, attr='index', renderer=info)
+ def __call__(self):
+ return 'OK'
+ deriver = self._makeOne()
+ result = deriver(view)
self.failIf(result is view)
self.assertEqual(view.__module__, result.__module__)
self.assertEqual(view.__doc__, result.__doc__)
self.assertEqual(view.__name__, result.__name__)
+ self.failIf(hasattr(result, '__call_permissive__'))
request = self._makeRequest()
- self.assertEqual(result(None, request).body, 'Hello!')
+ self.assertEqual(result(None, request), 'OK')
+ self.assertEqual(request.__view__.__class__, view)
- def test__map_view_as_newstyle_class_requestonly(self):
- class view(object):
- def __init__(self, request):
+ def test_as_oldstyle_class_requestonly(self):
+ class view:
+ def __init__(self, context, request):
pass
def __call__(self):
return 'OK'
- result = self._callFUT(view)
+ deriver = self._makeOne()
+ result = deriver(view)
self.failIf(result is view)
self.assertEqual(view.__module__, result.__module__)
self.assertEqual(view.__doc__, result.__doc__)
self.assertEqual(view.__name__, result.__name__)
+ self.failIf(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+ self.assertEqual(request.__view__.__class__, view)
+
+ def test_as_instance_context_and_request(self):
+ class View:
+ def __call__(self, context, request):
+ return 'OK'
+ view = View()
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.failUnless(result is view)
+ self.failIf(hasattr(result, '__call_permissive__'))
self.assertEqual(result(None, None), 'OK')
- def test__map_view_as_newstyle_class_requestonly_with_attr(self):
- class view(object):
- def __init__(self, request):
- pass
- def index(self):
+ def test_as_instance_requestonly(self):
+ class View:
+ def __call__(self, request):
return 'OK'
- result = self._callFUT(view, attr='index')
+ view = View()
+ deriver = self._makeOne()
+ result = deriver(view)
self.failIf(result is view)
self.assertEqual(view.__module__, result.__module__)
self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
+ self.failUnless('instance' in result.__name__)
+ self.failIf(hasattr(result, '__call_permissive__'))
self.assertEqual(result(None, None), 'OK')
- def test__map_view_as_newstyle_class_requestonly_attr_and_renderer(self):
- renderer = self._registerRenderer()
- class view(object):
+ def test_with_debug_authorization_no_authpol(self):
+ view = lambda *arg: 'OK'
+ self.config.registry.settings = dict(
+ debug_authorization=True, reload_templates=True)
+ logger = self._registerLogger()
+ deriver = self._makeOne(permission='view')
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.failIf(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ self.assertEqual(result(None, request), 'OK')
+ self.assertEqual(len(logger.messages), 1)
+ self.assertEqual(logger.messages[0],
+ "debug_authorization of url url (view name "
+ "'view_name' against context None): Allowed "
+ "(no authorization policy in use)")
+
+ def test_with_debug_authorization_no_permission(self):
+ view = lambda *arg: 'OK'
+ self.config.registry.settings = dict(
+ debug_authorization=True, reload_templates=True)
+ self._registerSecurityPolicy(True)
+ logger = self._registerLogger()
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.failIf(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ self.assertEqual(result(None, request), 'OK')
+ self.assertEqual(len(logger.messages), 1)
+ self.assertEqual(logger.messages[0],
+ "debug_authorization of url url (view name "
+ "'view_name' against context None): Allowed ("
+ "no permission registered)")
+
+ def test_debug_auth_permission_authpol_permitted(self):
+ view = lambda *arg: 'OK'
+ self.config.registry.settings = dict(
+ debug_authorization=True, reload_templates=True)
+ logger = self._registerLogger()
+ self._registerSecurityPolicy(True)
+ deriver = self._makeOne(permission='view')
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.assertEqual(result.__call_permissive__, view)
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ self.assertEqual(result(None, request), 'OK')
+ self.assertEqual(len(logger.messages), 1)
+ self.assertEqual(logger.messages[0],
+ "debug_authorization of url url (view name "
+ "'view_name' against context None): True")
+
+ def test_debug_auth_permission_authpol_denied(self):
+ from pyramid.exceptions import Forbidden
+ view = lambda *arg: 'OK'
+ self.config.registry.settings = dict(
+ debug_authorization=True, reload_templates=True)
+ logger = self._registerLogger()
+ self._registerSecurityPolicy(False)
+ deriver = self._makeOne(permission='view')
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.assertEqual(result.__call_permissive__, view)
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ self.assertRaises(Forbidden, result, None, request)
+ self.assertEqual(len(logger.messages), 1)
+ self.assertEqual(logger.messages[0],
+ "debug_authorization of url url (view name "
+ "'view_name' against context None): False")
+
+ def test_debug_auth_permission_authpol_denied2(self):
+ view = lambda *arg: 'OK'
+ self.config.registry.settings = dict(
+ debug_authorization=True, reload_templates=True)
+ self._registerLogger()
+ self._registerSecurityPolicy(False)
+ deriver = self._makeOne(permission='view')
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ permitted = result.__permitted__(None, None)
+ self.assertEqual(permitted, False)
+
+ def test_debug_auth_permission_authpol_overridden(self):
+ view = lambda *arg: 'OK'
+ self.config.registry.settings = dict(
+ debug_authorization=True, reload_templates=True)
+ logger = self._registerLogger()
+ self._registerSecurityPolicy(False)
+ deriver = self._makeOne(permission='__no_permission_required__')
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.failIf(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ self.assertEqual(result(None, request), 'OK')
+ self.assertEqual(len(logger.messages), 1)
+ self.assertEqual(logger.messages[0],
+ "debug_authorization of url url (view name "
+ "'view_name' against context None): False")
+
+ def test_with_predicates_all(self):
+ view = lambda *arg: 'OK'
+ predicates = []
+ def predicate1(context, request):
+ predicates.append(True)
+ return True
+ def predicate2(context, request):
+ predicates.append(True)
+ return True
+ deriver = self._makeOne(predicates=[predicate1, predicate2])
+ result = deriver(view)
+ request = self._makeRequest()
+ request.method = 'POST'
+ next = result(None, None)
+ self.assertEqual(next, 'OK')
+ self.assertEqual(predicates, [True, True])
+
+ def test_with_predicates_checker(self):
+ view = lambda *arg: 'OK'
+ predicates = []
+ def predicate1(context, request):
+ predicates.append(True)
+ return True
+ def predicate2(context, request):
+ predicates.append(True)
+ return True
+ deriver = self._makeOne(predicates=[predicate1, predicate2])
+ result = deriver(view)
+ request = self._makeRequest()
+ request.method = 'POST'
+ next = result.__predicated__(None, None)
+ self.assertEqual(next, True)
+ self.assertEqual(predicates, [True, True])
+
+ def test_with_predicates_notall(self):
+ from pyramid.exceptions import NotFound
+ view = lambda *arg: 'OK'
+ predicates = []
+ def predicate1(context, request):
+ predicates.append(True)
+ return True
+ def predicate2(context, request):
+ predicates.append(True)
+ return False
+ deriver = self._makeOne(predicates=[predicate1, predicate2])
+ result = deriver(view)
+ request = self._makeRequest()
+ request.method = 'POST'
+ self.assertRaises(NotFound, result, None, None)
+ self.assertEqual(predicates, [True, True])
+
+ def test_with_wrapper_viewname(self):
+ from webob import Response
+ from pyramid.interfaces import IView
+ from pyramid.interfaces import IViewClassifier
+ inner_response = Response('OK')
+ def inner_view(context, request):
+ return inner_response
+ def outer_view(context, request):
+ self.assertEqual(request.wrapped_response, inner_response)
+ self.assertEqual(request.wrapped_body, inner_response.body)
+ self.assertEqual(request.wrapped_view, inner_view)
+ return Response('outer ' + request.wrapped_body)
+ self.config.registry.registerAdapter(
+ outer_view, (IViewClassifier, None, None), IView, 'owrap')
+ deriver = self._makeOne(viewname='inner',
+ wrapper_viewname='owrap')
+ result = deriver(inner_view)
+ self.failIf(result is inner_view)
+ self.assertEqual(inner_view.__module__, result.__module__)
+ self.assertEqual(inner_view.__doc__, result.__doc__)
+ request = self._makeRequest()
+ response = result(None, request)
+ self.assertEqual(response.body, 'outer OK')
+
+ def test_with_wrapper_viewname_notfound(self):
+ from webob import Response
+ inner_response = Response('OK')
+ def inner_view(context, request):
+ return inner_response
+ deriver = self._makeOne(viewname='inner', wrapper_viewname='owrap')
+ wrapped = deriver(inner_view)
+ request = self._makeRequest()
+ self.assertRaises(ValueError, wrapped, None, request)
+
+ def test_as_newstyle_class_context_and_request_attr_and_renderer(self):
+ class renderer(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, {'a':'1'})
+ self.assertEqual(view_inst.__class__, View)
+ self.assertEqual(ctx, context)
+ return resp
+ class View(object):
+ def __init__(self, context, request):
+ pass
+ def index(self):
+ return {'a':'1'}
+ deriver = self._makeOne(renderer=renderer(), attr='index')
+ result = deriver(View)
+ self.failIf(result is View)
+ self.assertEqual(result.__module__, View.__module__)
+ self.assertEqual(result.__doc__, View.__doc__)
+ self.assertEqual(result.__name__, View.__name__)
+ request = self._makeRequest()
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), {'a':'1'})
+
+ def test_as_newstyle_class_requestonly_attr_and_renderer(self):
+ class renderer(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, {'a':'1'})
+ self.assertEqual(view_inst.__class__, View)
+ self.assertEqual(ctx, context)
+ return resp
+ class View(object):
+ def __init__(self, request):
+ pass
+ def index(self):
+ return {'a':'1'}
+ deriver = self._makeOne(renderer=renderer(), attr='index')
+ result = deriver(View)
+ self.failIf(result is View)
+ self.assertEqual(result.__module__, View.__module__)
+ self.assertEqual(result.__doc__, View.__doc__)
+ self.assertEqual(result.__name__, View.__name__)
+ request = self._makeRequest()
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), {'a':'1'})
+
+ def test_as_oldstyle_cls_context_request_attr_and_renderer(self):
+ class renderer(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, {'a':'1'})
+ self.assertEqual(view_inst.__class__, View)
+ self.assertEqual(ctx, context)
+ return resp
+ class View:
+ def __init__(self, context, request):
+ pass
+ def index(self):
+ return {'a':'1'}
+ deriver = self._makeOne(renderer=renderer(), attr='index')
+ result = deriver(View)
+ self.failIf(result is View)
+ self.assertEqual(result.__module__, View.__module__)
+ self.assertEqual(result.__doc__, View.__doc__)
+ self.assertEqual(result.__name__, View.__name__)
+ request = self._makeRequest()
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), {'a':'1'})
+
+ def test_as_oldstyle_cls_requestonly_attr_and_renderer(self):
+ class renderer(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, {'a':'1'})
+ self.assertEqual(view_inst.__class__, View)
+ self.assertEqual(ctx, context)
+ return resp
+ class View:
def __init__(self, request):
pass
def index(self):
return {'a':'1'}
- info = {'name':renderer.spec, 'package':None}
- result = self._callFUT(view, attr='index', renderer=info)
+ deriver = self._makeOne(renderer=renderer(), attr='index')
+ result = deriver(View)
+ self.failIf(result is View)
+ self.assertEqual(result.__module__, View.__module__)
+ self.assertEqual(result.__doc__, View.__doc__)
+ self.assertEqual(result.__name__, View.__name__)
+ request = self._makeRequest()
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), {'a':'1'})
+
+ def test_as_instance_context_and_request_attr_and_renderer(self):
+ class renderer(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, {'a':'1'})
+ self.assertEqual(view_inst, view)
+ self.assertEqual(ctx, context)
+ return resp
+ class View:
+ def index(self, context, request):
+ return {'a':'1'}
+ deriver = self._makeOne(renderer=renderer(), attr='index')
+ view = View()
+ result = deriver(view)
self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
+ self.assertEqual(result.__module__, view.__module__)
+ self.assertEqual(result.__doc__, view.__doc__)
+ request = self._makeRequest()
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), {'a':'1'})
+
+ def test_as_instance_requestonly_attr_and_renderer(self):
+ class renderer(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, {'a':'1'})
+ self.assertEqual(view_inst, view)
+ self.assertEqual(ctx, context)
+ return resp
+ class View:
+ def index(self, request):
+ return {'a':'1'}
+ deriver = self._makeOne(renderer=renderer(), attr='index')
+ view = View()
+ result = deriver(view)
+ self.failIf(result is view)
+ self.assertEqual(result.__module__, view.__module__)
+ self.assertEqual(result.__doc__, view.__doc__)
request = self._makeRequest()
- self.assertEqual(result(None, request).body, 'Hello!')
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), {'a':'1'})
+
+ def test_with_view_mapper_config_specified(self):
+ class mapper(object):
+ def __init__(self, **kw):
+ self.kw = kw
+ def __call__(self, view):
+ def wrapped(context, request):
+ return 'OK'
+ return wrapped
+ def view(context, request): return 'NOTOK'
+ deriver = self._makeOne(mapper=mapper)
+ result = deriver(view)
+ self.failIf(result is view)
+ self.assertEqual(result(None, None), 'OK')
- def test__map_view_as_oldstyle_class_context_and_request(self):
- class view:
+ def test_with_view_mapper_view_specified(self):
+ def mapper(**kw):
+ def inner(view):
+ def superinner(context, request):
+ self.assertEqual(request, None)
+ return 'OK'
+ return superinner
+ return inner
+ def view(context, request): return 'NOTOK'
+ view.__view_mapper__ = mapper
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.failIf(result is view)
+ self.assertEqual(result(None, None), 'OK')
+
+ def test_with_view_mapper_default_mapper_specified(self):
+ def mapper(**kw):
+ def inner(view):
+ def superinner(context, request):
+ self.assertEqual(request, None)
+ return 'OK'
+ return superinner
+ return inner
+ self.config.set_view_mapper(mapper)
+ def view(context, request): return 'NOTOK'
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.failIf(result is view)
+ self.assertEqual(result(None, None), 'OK')
+
+class TestDefaultViewMapper(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+ self.registry = self.config.registry
+
+ def tearDown(self):
+ del self.registry
+ testing.tearDown()
+
+ def _makeOne(self, **kw):
+ from pyramid.config import DefaultViewMapper
+ kw['registry'] = self.registry
+ return DefaultViewMapper(**kw)
+
+ def _makeRequest(self):
+ request = DummyRequest()
+ request.registry = self.registry
+ return request
+
+ def test_view_as_function_context_and_request(self):
+ def view(context, request):
+ return 'OK'
+ mapper = self._makeOne()
+ result = mapper(view)
+ self.failUnless(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test__view_as_function_with_attr(self):
+ def view(context, request):
+ """ """
+ mapper = self._makeOne(attr='__name__')
+ result = mapper(view)
+ self.failIf(result is view)
+ request = self._makeRequest()
+ self.assertRaises(TypeError, result, None, request)
+
+ def test_view_as_function_requestonly(self):
+ def view(request):
+ return 'OK'
+ mapper = self._makeOne()
+ result = mapper(view)
+ self.failIf(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test_view_as_function_requestonly_with_attr(self):
+ def view(request):
+ """ """
+ mapper = self._makeOne(attr='__name__')
+ result = mapper(view)
+ self.failIf(result is view)
+ request = self._makeRequest()
+ self.assertRaises(TypeError, result, None, request)
+
+ def test_view_as_newstyle_class_context_and_request(self):
+ class view(object):
def __init__(self, context, request):
pass
def __call__(self):
return 'OK'
- result = self._callFUT(view)
+ mapper = self._makeOne()
+ result = mapper(view)
self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result(None, None), 'OK')
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
- def test__map_view_as_oldstyle_class_context_and_request_with_attr(self):
- class view:
+ def test_view_as_newstyle_class_context_and_request_with_attr(self):
+ class view(object):
def __init__(self, context, request):
pass
def index(self):
return 'OK'
- result = self._callFUT(view, attr='index')
+ mapper = self._makeOne(attr='index')
+ result = mapper(view)
self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result(None, None), 'OK')
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
- def test__map_view_as_oldstyle_cls_context_request_attr_and_renderer(self):
- renderer = self._registerRenderer()
- class view:
- def __init__(self, context, request):
+ def test_view_as_newstyle_class_requestonly(self):
+ class view(object):
+ def __init__(self, request):
+ pass
+ def __call__(self):
+ return 'OK'
+ mapper = self._makeOne()
+ result = mapper(view)
+ self.failIf(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test_view_as_newstyle_class_requestonly_with_attr(self):
+ class view(object):
+ def __init__(self, request):
pass
def index(self):
- return {'a':'1'}
- info = {'name':renderer.spec, 'package':None}
- result = self._callFUT(view, attr='index', renderer=info)
+ return 'OK'
+ mapper = self._makeOne(attr='index')
+ result = mapper(view)
self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
request = self._makeRequest()
- self.assertEqual(result(None, request).body, 'Hello!')
+ self.assertEqual(result(None, request), 'OK')
- def test__map_view_as_oldstyle_class_requestonly(self):
+ def test_view_as_oldstyle_class_context_and_request(self):
class view:
- def __init__(self, request):
+ def __init__(self, context, request):
pass
def __call__(self):
return 'OK'
- result = self._callFUT(view)
+ mapper = self._makeOne()
+ result = mapper(view)
self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result(None, None), 'OK')
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
- def test__map_view_as_oldstyle_class_requestonly_with_attr(self):
+ def test_view_as_oldstyle_class_context_and_request_with_attr(self):
class view:
- def __init__(self, request):
+ def __init__(self, context, request):
pass
def index(self):
return 'OK'
- result = self._callFUT(view, attr='index')
+ mapper = self._makeOne(attr='index')
+ result = mapper(view)
self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result(None, None), 'OK')
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test_view_as_oldstyle_class_requestonly(self):
+ class view:
+ def __init__(self, request):
+ pass
+ def __call__(self):
+ return 'OK'
+ mapper = self._makeOne()
+ result = mapper(view)
+ self.failIf(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
- def test__map_view_as_oldstyle_class_requestonly_attr_and_renderer(self):
- renderer = self._registerRenderer()
+ def test_view_as_oldstyle_class_requestonly_with_attr(self):
class view:
def __init__(self, request):
pass
def index(self):
- return {'a':'1'}
- info = {'name':renderer.spec, 'package':None}
- result = self._callFUT(view, attr='index', renderer=info)
+ return 'OK'
+ mapper = self._makeOne(attr='index')
+ result = mapper(view)
self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
request = self._makeRequest()
- self.assertEqual(result(None, request).body, 'Hello!')
+ self.assertEqual(result(None, request), 'OK')
- def test__map_view_as_instance_context_and_request(self):
+ def test_view_as_instance_context_and_request(self):
class View:
def __call__(self, context, request):
return 'OK'
view = View()
- result = self._callFUT(view)
+ mapper = self._makeOne()
+ result = mapper(view)
self.failUnless(result is view)
- self.assertEqual(result(None, None), 'OK')
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
- def test__map_view_as_instance_context_and_request_and_attr(self):
+ def test_view_as_instance_context_and_request_and_attr(self):
class View:
def index(self, context, request):
return 'OK'
view = View()
- result = self._callFUT(view, attr='index')
- self.failIf(result is view)
- self.assertEqual(result(None, None), 'OK')
-
- def test__map_view_as_instance_context_and_request_attr_and_renderer(self):
- renderer = self._registerRenderer()
- class View:
- def index(self, context, request):
- return {'a':'1'}
- view = View()
- info = {'name':renderer.spec, 'package':None}
- result = self._callFUT(view, attr='index', renderer=info)
+ mapper = self._makeOne(attr='index')
+ result = mapper(view)
self.failIf(result is view)
request = self._makeRequest()
- self.assertEqual(result(None, request).body, 'Hello!')
+ self.assertEqual(result(None, request), 'OK')
- def test__map_view_as_instance_requestonly(self):
+ def test_view_as_instance_requestonly(self):
class View:
def __call__(self, request):
return 'OK'
view = View()
- result = self._callFUT(view)
+ mapper = self._makeOne()
+ result = mapper(view)
self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.failUnless('instance' in result.__name__)
- self.assertEqual(result(None, None), 'OK')
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
- def test__map_view_as_instance_requestonly_with_attr(self):
+ def test_view_as_instance_requestonly_with_attr(self):
class View:
def index(self, request):
return 'OK'
view = View()
- result = self._callFUT(view, attr='index')
- self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.failUnless('instance' in result.__name__)
- self.assertEqual(result(None, None), 'OK')
-
- def test__map_view_as_instance_requestonly_with_attr_and_renderer(self):
- renderer = self._registerRenderer()
- class View:
- def index(self, request):
- return {'a':'1'}
- view = View()
- info = {'name':renderer.spec, 'package':None}
- result = self._callFUT(view, attr='index', renderer=info)
- self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.failUnless('instance' in result.__name__)
- request = self._makeRequest()
- self.assertEqual(result(None, request).body, 'Hello!')
-
- def test__map_view_rendereronly(self):
- renderer = self._registerRenderer()
- def view(context, request):
- return {'a':'1'}
- info = {'name':renderer.spec, 'package':None}
- result = self._callFUT(view, renderer=info)
- self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- request = self._makeRequest()
- self.assertEqual(result(None, request).body, 'Hello!')
-
- def test__map_view_with_registry(self):
- renderer = self._registerRenderer()
- def view(context, request):
- return {'a':'1'}
- info = {'name':renderer.spec, 'package':None}
- result = self._callFUT(view, renderer=info)
+ mapper = self._makeOne(attr='index')
+ result = mapper(view)
self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
request = self._makeRequest()
- self.assertEqual(result(None, request).body, 'Hello!')
+ self.assertEqual(result(None, request), 'OK')
-class Test_decorate_view(unittest.TestCase):
- def _callFUT(self, wrapped, original):
- from pyramid.config import decorate_view
- return decorate_view(wrapped, original)
+class Test_preserve_view_attrs(unittest.TestCase):
+ def _callFUT(self, view, wrapped_view):
+ from pyramid.config import preserve_view_attrs
+ return preserve_view_attrs(view, wrapped_view)
def test_it_same(self):
def view(context, request):
""" """
result = self._callFUT(view, view)
- self.assertEqual(result, False)
+ self.failUnless(result is view)
+
+ def test_it_different_with_existing_original_view(self):
+ def view1(context, request): pass
+ view1.__original_view__ = 'abc'
+ def view2(context, request): pass
+ result = self._callFUT(view1, view2)
+ self.assertEqual(result.__original_view__, 'abc')
+ self.failIf(result is view1)
def test_it_different(self):
class DummyView1:
@@ -3846,9 +4055,9 @@ class Test_decorate_view(unittest.TestCase):
__module__ = '1'
def __call__(self, context, request):
""" """
- def __call_permissive__(self, context, reuqest):
+ def __call_permissive__(self, context, request):
""" """
- def __predicated__(self, context, reuqest):
+ def __predicated__(self, context, request):
""" """
def __permitted__(self, context, request):
""" """
@@ -3858,16 +4067,17 @@ class Test_decorate_view(unittest.TestCase):
__module__ = '2'
def __call__(self, context, request):
""" """
- def __call_permissive__(self, context, reuqest):
+ def __call_permissive__(self, context, request):
""" """
- def __predicated__(self, context, reuqest):
+ def __predicated__(self, context, request):
""" """
def __permitted__(self, context, request):
""" """
view1 = DummyView1()
view2 = DummyView2()
- result = self._callFUT(view1, view2)
- self.assertEqual(result, True)
+ result = self._callFUT(view2, view1)
+ self.assertEqual(result, view1)
+ self.failUnless(view1.__original_view__ is view2)
self.failUnless(view1.__doc__ is view2.__doc__)
self.failUnless(view1.__module__ is view2.__module__)
self.failUnless(view1.__name__ is view2.__name__)
@@ -4333,24 +4543,23 @@ class TestMultiView(unittest.TestCase):
response = mv(context, request)
self.assertEqual(response, expected_response)
-
-class TestRequestOnly(unittest.TestCase):
- def _callFUT(self, arg):
+class Test_requestonly(unittest.TestCase):
+ def _callFUT(self, view, attr=None):
from pyramid.config import requestonly
- return requestonly(arg)
+ return requestonly(view, attr)
- def test_newstyle_class_no_init(self):
+ def test_requestonly_newstyle_class_no_init(self):
class foo(object):
""" """
self.assertFalse(self._callFUT(foo))
- def test_newstyle_class_init_toomanyargs(self):
+ def test_requestonly_newstyle_class_init_toomanyargs(self):
class foo(object):
def __init__(self, context, request):
""" """
self.assertFalse(self._callFUT(foo))
- def test_newstyle_class_init_onearg_named_request(self):
+ def test_requestonly_newstyle_class_init_onearg_named_request(self):
class foo(object):
def __init__(self, request):
""" """
@@ -4426,6 +4635,22 @@ class TestRequestOnly(unittest.TestCase):
""" """
self.assertFalse(self._callFUT(foo))
+ def test_function_with_attr_false(self):
+ def bar(context, request):
+ """ """
+ def foo(context, request):
+ """ """
+ foo.bar = bar
+ self.assertFalse(self._callFUT(foo, 'bar'))
+
+ def test_function_with_attr_true(self):
+ def bar(context, request):
+ """ """
+ def foo(request):
+ """ """
+ foo.bar = bar
+ self.assertTrue(self._callFUT(foo, 'bar'))
+
def test_function_onearg_named_request(self):
def foo(request):
""" """
diff --git a/pyramid/tests/test_paster.py b/pyramid/tests/test_paster.py
index a5f613f8e..35349b7c7 100644
--- a/pyramid/tests/test_paster.py
+++ b/pyramid/tests/test_paster.py
@@ -133,7 +133,31 @@ class TestPRoutesCommand(unittest.TestCase):
self.assertEqual(result, None)
self.assertEqual(L, [])
+ def test_single_route_no_route_registered(self):
+ command = self._makeOne()
+ route = DummyRoute('a', '/a')
+ mapper = DummyMapper(route)
+ command._get_mapper = lambda *arg: mapper
+ L = []
+ command.out = L.append
+ app = DummyApp()
+ loadapp = DummyLoadApp(app)
+ command.loadapp = (loadapp,)
+ command.args = ('/foo/bar/myapp.ini', 'myapp')
+ result = command.command()
+ self.assertEqual(result, None)
+ self.assertEqual(len(L), 3)
+ self.assertEqual(L[-1].split(), ['a', '/a', '<unknown>'])
+
def test_single_route_no_views_registered(self):
+ from zope.interface import Interface
+ from pyramid.registry import Registry
+ from pyramid.interfaces import IRouteRequest
+ registry = Registry()
+ def view():pass
+ class IMyRoute(Interface):
+ pass
+ registry.registerUtility(IMyRoute, IRouteRequest, name='a')
command = self._makeOne()
route = DummyRoute('a', '/a')
mapper = DummyMapper(route)
@@ -141,13 +165,14 @@ class TestPRoutesCommand(unittest.TestCase):
L = []
command.out = L.append
app = DummyApp()
+ app.registry = registry
loadapp = DummyLoadApp(app)
command.loadapp = (loadapp,)
command.args = ('/foo/bar/myapp.ini', 'myapp')
result = command.command()
self.assertEqual(result, None)
self.assertEqual(len(L), 3)
- self.assertEqual(L[-1].split(), ['a', '/a', 'None'])
+ self.assertEqual(L[-1].split()[:3], ['a', '/a', 'None'])
def test_single_route_one_view_registered(self):
from zope.interface import Interface
@@ -181,7 +206,6 @@ class TestPRoutesCommand(unittest.TestCase):
def test_single_route_one_view_registered_with_factory(self):
from zope.interface import Interface
- from zope.interface import implements
from pyramid.registry import Registry
from pyramid.interfaces import IRouteRequest
from pyramid.interfaces import IViewClassifier
@@ -190,8 +214,6 @@ class TestPRoutesCommand(unittest.TestCase):
def view():pass
class IMyRoot(Interface):
pass
- class Root(object):
- implements(IMyRoot)
class IMyRoute(Interface):
pass
registry.registerAdapter(view,
@@ -199,8 +221,7 @@ class TestPRoutesCommand(unittest.TestCase):
IView, '')
registry.registerUtility(IMyRoute, IRouteRequest, name='a')
command = self._makeOne()
- def factory(request):
- return Root()
+ def factory(request): pass
route = DummyRoute('a', '/a', factory=factory)
mapper = DummyMapper(route)
command._get_mapper = lambda *arg: mapper
@@ -214,7 +235,7 @@ class TestPRoutesCommand(unittest.TestCase):
result = command.command()
self.assertEqual(result, None)
self.assertEqual(len(L), 3)
- self.assertEqual(L[-1].split()[:4], ['a', '/a', '<function', 'view'])
+ self.assertEqual(L[-1].split()[:3], ['a', '/a', '<unknown>'])
def test__get_mapper(self):
from pyramid.registry import Registry
diff --git a/pyramid/tests/test_security.py b/pyramid/tests/test_security.py
index dd9d48f45..94cefa642 100644
--- a/pyramid/tests/test_security.py
+++ b/pyramid/tests/test_security.py
@@ -224,6 +224,36 @@ class TestAuthenticatedUserId(unittest.TestCase):
result = self._callFUT(request)
self.assertEqual(result, 'yo')
+class TestUnauthenticatedUserId(unittest.TestCase):
+ def setUp(self):
+ cleanUp()
+
+ def tearDown(self):
+ cleanUp()
+
+ def _callFUT(self, request):
+ from pyramid.security import unauthenticated_userid
+ return unauthenticated_userid(request)
+
+ def test_no_authentication_policy(self):
+ request = _makeRequest()
+ result = self._callFUT(request)
+ self.assertEqual(result, None)
+
+ def test_with_authentication_policy(self):
+ request = _makeRequest()
+ _registerAuthenticationPolicy(request.registry, 'yo')
+ result = self._callFUT(request)
+ self.assertEqual(result, 'yo')
+
+ def test_with_authentication_policy_no_reg_on_request(self):
+ from pyramid.threadlocal import get_current_registry
+ request = DummyRequest({})
+ registry = get_current_registry()
+ _registerAuthenticationPolicy(registry, 'yo')
+ result = self._callFUT(request)
+ self.assertEqual(result, 'yo')
+
class TestEffectivePrincipals(unittest.TestCase):
def setUp(self):
cleanUp()
@@ -355,6 +385,9 @@ class DummyAuthenticationPolicy:
def effective_principals(self, request):
return self.result
+ def unauthenticated_userid(self, request):
+ return self.result
+
def authenticated_userid(self, request):
return self.result
diff --git a/pyramid/tests/test_session.py b/pyramid/tests/test_session.py
index 0e88b28cd..251d92c23 100644
--- a/pyramid/tests/test_session.py
+++ b/pyramid/tests/test_session.py
@@ -178,6 +178,13 @@ class TestUnencryptedCookieSession(unittest.TestCase):
self.assertEqual(token, 'token')
self.failUnless('_csrft_' in session)
+ def test_get_csrf_token_new(self):
+ request = testing.DummyRequest()
+ session = self._makeOne(request)
+ token = session.get_csrf_token()
+ self.failUnless(token)
+ self.failUnless('_csrft_' in session)
+
class Test_manage_accessed(unittest.TestCase):
def _makeOne(self, wrapped):
from pyramid.session import manage_accessed
diff --git a/pyramid/tests/test_testing.py b/pyramid/tests/test_testing.py
index ec6fdac5f..eaaad6aef 100644
--- a/pyramid/tests/test_testing.py
+++ b/pyramid/tests/test_testing.py
@@ -297,7 +297,11 @@ class TestDummySecurityPolicy(unittest.TestCase):
def test_authenticated_userid(self):
policy = self._makeOne('user')
self.assertEqual(policy.authenticated_userid(None), 'user')
-
+
+ def test_unauthenticated_userid(self):
+ policy = self._makeOne('user')
+ self.assertEqual(policy.unauthenticated_userid(None), 'user')
+
def test_effective_principals_userid(self):
policy = self._makeOne('user', ('group1',))
from pyramid.security import Everyone
@@ -409,6 +413,7 @@ class TestDummyRequest(unittest.TestCase):
def test_defaults(self):
from pyramid.threadlocal import get_current_registry
+ from pyramid.testing import DummySession
request = self._makeOne()
self.assertEqual(request.method, 'GET')
self.assertEqual(request.application_url, 'http://example.com')
@@ -434,6 +439,7 @@ class TestDummyRequest(unittest.TestCase):
self.assertEqual(request.virtual_root, None)
self.assertEqual(request.virtual_root_path, ())
self.assertEqual(request.registry, get_current_registry())
+ self.assertEqual(request.session.__class__, DummySession)
def test_params_explicit(self):
request = self._makeOne(params = {'foo':'bar'})
@@ -719,6 +725,86 @@ class Test_skip_on(unittest.TestCase):
decorated = self._callFUT('ok')(foo)
self.assertEqual(decorated(), True)
+class TestDummySession(unittest.TestCase):
+ def _makeOne(self):
+ from pyramid.testing import DummySession
+ return DummySession()
+
+ def test_instance_conforms(self):
+ from zope.interface.verify import verifyObject
+ from pyramid.interfaces import ISession
+ session = self._makeOne()
+ verifyObject(ISession, session)
+
+ def test_changed(self):
+ session = self._makeOne()
+ self.assertEqual(session.changed(), None)
+
+ def test_invalidate(self):
+ session = self._makeOne()
+ session['a'] = 1
+ self.assertEqual(session.invalidate(), None)
+ self.failIf('a' in session)
+
+ def test_flash_default(self):
+ session = self._makeOne()
+ session.flash('msg1')
+ session.flash('msg2')
+ self.assertEqual(session['_f_'], ['msg1', 'msg2'])
+
+ def test_flash_mixed(self):
+ session = self._makeOne()
+ session.flash('warn1', 'warn')
+ session.flash('warn2', 'warn')
+ session.flash('err1', 'error')
+ session.flash('err2', 'error')
+ self.assertEqual(session['_f_warn'], ['warn1', 'warn2'])
+
+ def test_pop_flash_default_queue(self):
+ session = self._makeOne()
+ queue = ['one', 'two']
+ session['_f_'] = queue
+ result = session.pop_flash()
+ self.assertEqual(result, queue)
+ self.assertEqual(session.get('_f_'), None)
+
+ def test_pop_flash_nodefault_queue(self):
+ session = self._makeOne()
+ queue = ['one', 'two']
+ session['_f_error'] = queue
+ result = session.pop_flash('error')
+ self.assertEqual(result, queue)
+ self.assertEqual(session.get('_f_error'), None)
+
+ def test_peek_flash_default_queue(self):
+ session = self._makeOne()
+ queue = ['one', 'two']
+ session['_f_'] = queue
+ result = session.peek_flash()
+ self.assertEqual(result, queue)
+ self.assertEqual(session.get('_f_'), queue)
+
+ def test_peek_flash_nodefault_queue(self):
+ session = self._makeOne()
+ queue = ['one', 'two']
+ session['_f_error'] = queue
+ result = session.peek_flash('error')
+ self.assertEqual(result, queue)
+ self.assertEqual(session.get('_f_error'), queue)
+
+ def test_new_csrf_token(self):
+ session = self._makeOne()
+ token = session.new_csrf_token()
+ self.assertEqual(token, session['_csrft_'])
+
+ def test_get_csrf_token(self):
+ session = self._makeOne()
+ session['_csrft_'] = 'token'
+ token = session.get_csrf_token()
+ self.assertEqual(token, 'token')
+ self.failUnless('_csrft_' in session)
+
+
from zope.interface import Interface
from zope.interface import implements
diff --git a/pyramid/tests/test_url.py b/pyramid/tests/test_url.py
index f11d36aca..a40727e9b 100644
--- a/pyramid/tests/test_url.py
+++ b/pyramid/tests/test_url.py
@@ -209,6 +209,47 @@ class TestRouteUrl(unittest.TestCase):
self.assertEqual(result, 'http://example2.com/1/2/3/a')
self.assertEqual(route.kw, {}) # shouldnt have anchor/query
+class TestCurrentRouteUrl(unittest.TestCase):
+ def setUp(self):
+ cleanUp()
+
+ def tearDown(self):
+ cleanUp()
+
+ def _callFUT(self, *arg, **kw):
+ from pyramid.url import current_route_url
+ return current_route_url(*arg, **kw)
+
+ def test_current_request_has_no_route(self):
+ request = _makeRequest()
+ self.assertRaises(ValueError, self._callFUT, request)
+
+ def test_with_elements_query_and_anchor(self):
+ from pyramid.interfaces import IRoutesMapper
+ request = _makeRequest()
+ route = DummyRoute('/1/2/3')
+ mapper = DummyRoutesMapper(route=route)
+ request.matched_route = route
+ request.matchdict = {}
+ request.registry.registerUtility(mapper, IRoutesMapper)
+ result = self._callFUT(request, 'extra1', 'extra2', _query={'a':1},
+ _anchor=u"foo")
+ self.assertEqual(result,
+ 'http://example.com:5432/1/2/3/extra1/extra2?a=1#foo')
+
+ def test_with__route_name(self):
+ from pyramid.interfaces import IRoutesMapper
+ request = _makeRequest()
+ route = DummyRoute('/1/2/3')
+ mapper = DummyRoutesMapper(route=route)
+ request.matched_route = route
+ request.matchdict = {}
+ request.registry.registerUtility(mapper, IRoutesMapper)
+ result = self._callFUT(request, 'extra1', 'extra2', _query={'a':1},
+ _anchor=u"foo", _route_name='bar')
+ self.assertEqual(result,
+ 'http://example.com:5432/1/2/3/extra1/extra2?a=1#foo')
+
class TestRoutePath(unittest.TestCase):
def setUp(self):
cleanUp()
@@ -302,6 +343,7 @@ class DummyRoutesMapper:
class DummyRoute:
pregenerator = None
+ name = 'route'
def __init__(self, result='/1/2/3'):
self.result = result
diff --git a/pyramid/tests/test_view.py b/pyramid/tests/test_view.py
index 79e363756..204547cf2 100644
--- a/pyramid/tests/test_view.py
+++ b/pyramid/tests/test_view.py
@@ -229,11 +229,14 @@ class TestViewConfigDecorator(unittest.TestCase):
def test_create_nondefaults(self):
decorator = self._makeOne(name=None, request_type=None, for_=None,
- permission='foo')
+ permission='foo', mapper='mapper',
+ decorator='decorator')
self.assertEqual(decorator.name, None)
self.assertEqual(decorator.request_type, None)
self.assertEqual(decorator.context, None)
self.assertEqual(decorator.permission, 'foo')
+ self.assertEqual(decorator.mapper, 'mapper')
+ self.assertEqual(decorator.decorator, 'decorator')
def test_call_function(self):
decorator = self._makeOne()
@@ -317,10 +320,9 @@ class TestViewConfigDecorator(unittest.TestCase):
settings = call_venusian(venusian)
self.assertEqual(len(settings), 1)
renderer = settings[0]['renderer']
- self.assertEqual(renderer,
- {'name':'fixtures/minimal.pt',
- 'package':pyramid.tests,
- })
+ self.assertEqual(renderer.name, 'fixtures/minimal.pt')
+ self.assertEqual(renderer.package, pyramid.tests)
+ self.assertEqual(renderer.registry.__class__, DummyRegistry)
def test_call_with_renderer_dict(self):
decorator = self._makeOne(renderer={'a':1})
@@ -494,9 +496,13 @@ class DummyVenusian(object):
self.attachments.append((wrapped, callback, category))
return self.info
+class DummyRegistry(object):
+ pass
+
class DummyConfig(object):
def __init__(self):
self.settings = []
+ self.registry = DummyRegistry()
def add_view(self, **kw):
self.settings.append(kw)
diff --git a/pyramid/traversal.py b/pyramid/traversal.py
index f3377b0d0..f32b43493 100644
--- a/pyramid/traversal.py
+++ b/pyramid/traversal.py
@@ -352,6 +352,9 @@ def resource_path_tuple(resource, *elements):
"""
return tuple(_resource_path_list(resource, *elements))
+model_path_tuple = resource_path_tuple # b/w compat
+
+
def _resource_path_list(resource, *elements):
""" Implementation detail shared by resource_path and resource_path_tuple"""
path = [loc.__name__ or '' for loc in lineage(resource)]
diff --git a/pyramid/url.py b/pyramid/url.py
index e1eaaaa1e..3126ad26c 100644
--- a/pyramid/url.py
+++ b/pyramid/url.py
@@ -32,7 +32,7 @@ def route_url(route_name, request, *elements, **kw):
enough arguments, for example).
For example, if you've defined a route named "foobar" with the path
- ``:foo/{bar}/*traverse``::
+ ``{foo}/{bar}/*traverse``::
route_url('foobar', request, foo='1') => <KeyError exception>
route_url('foobar', request, foo='1', bar='2') => <KeyError exception>
@@ -53,7 +53,7 @@ def route_url(route_name, request, *elements, **kw):
``*remainder`` replacement value, it is tacked on to the URL
untouched.
- If a keyword argument ``_query`` is present, it will used to
+ If a keyword argument ``_query`` is present, it will be used to
compose a query string that will be tacked on to the end of the
URL. The value of ``_query`` must be a sequence of two-tuples
*or* a data structure with an ``.items()`` method that returns a
@@ -221,7 +221,7 @@ def resource_url(resource, request, *elements, **kw):
``elements`` are used, the generated URL will *not*
end in trailing a slash.
- If a keyword argument ``query`` is present, it will used to
+ If a keyword argument ``query`` is present, it will be used to
compose a query string that will be tacked on to the end of the
URL. The value of ``query`` must be a sequence of two-tuples *or*
a data structure with an ``.items()`` method that returns a
@@ -366,6 +366,60 @@ def static_url(path, request, **kw):
return info.generate(path, request, **kw)
+def current_route_url(request, *elements, **kw):
+ """Generates a fully qualified URL for a named :app:`Pyramid`
+ :term:`route configuration` based on the 'current route'.
+
+ This function supplements :func:`pyramid.url.route_url`. It presents an
+ easy way to generate a URL for the 'current route' (defined as the route
+ which matched when the request was generated).
+
+ The arguments to this function have the same meaning as those with the
+ same names passed to :func:`pyramid.url.route_url`. It also understands
+ an extra argument which ``route_url`` does not named ``_route_name``.
+
+ The route name used to generate a URL is taken from either the
+ ``_route_name`` keyword argument or the name of the route which is
+ currently associated with the request if ``_route_name`` was not passed.
+ Keys and values from the current request :term:`matchdict` are combined
+ with the ``kw`` arguments to form a set of defaults named ``newkw``.
+ Then ``route_url(route_name, request, *elements, **newkw)`` is called,
+ returning a URL.
+
+ Examples follow.
+
+ If the 'current route' has the route pattern ``/foo/{page}`` and the
+ current url path is ``/foo/1`` , the matchdict will be ``{'page':'1'}``.
+ The result of ``current_route_url(request)`` in this situation will be
+ ``/foo/1``.
+
+ If the 'current route' has the route pattern ``/foo/{page}`` and the
+ current current url path is ``/foo/1``, the matchdict will be
+ ``{'page':'1'}``. The result of ``current_route_url(request, page='2')``
+ in this situation will be ``/foo/2``.
+
+ Usage of the ``_route_name`` keyword argument: if our routing table
+ defines routes ``/foo/{action}`` named 'foo' and ``/foo/{action}/{page}``
+ named ``fooaction``, and the current url pattern is ``/foo/view`` (which
+ has matched the ``/foo/{action}`` route), we may want to use the
+ matchdict args to generate a URL to the ``fooaction`` route. In this
+ scenario, ``current_url(request, _route_name='fooaction', page='5')``
+ Will return string like: ``/foo/view/5``.
+ """
+
+ if '_route_name' in kw:
+ route_name = kw.pop('_route_name')
+ else:
+ route = getattr(request, 'matched_route', None)
+ route_name = getattr(route, 'name', None)
+ if route_name is None:
+ raise ValueError('Current request matches no route')
+
+ newkw = {}
+ newkw.update(request.matchdict)
+ newkw.update(kw)
+ return route_url(route_name, request, *elements, **newkw)
+
@lru_cache(1000)
def _join_elements(elements):
return '/'.join([quote_path_segment(s) for s in elements])
diff --git a/pyramid/view.py b/pyramid/view.py
index 3dc110863..26f64d5fc 100644
--- a/pyramid/view.py
+++ b/pyramid/view.py
@@ -19,6 +19,7 @@ from pyramid.interfaces import IView
from pyramid.interfaces import IViewClassifier
from pyramid.httpexceptions import HTTPFound
+from pyramid.renderers import RendererHelper
from pyramid.static import static_view
from pyramid.threadlocal import get_current_registry
@@ -275,6 +276,9 @@ class view_config(object):
``False``). The view will only be invoked if all custom
predicates return ``True``.
+ See the :meth:`pyramid.config.Configurator.add_view`` method for
+ descriptions of the ``decorator`` and ``mapper`` arguments.
+
Any individual or all parameters can be omitted. The simplest
:class:`pyramid.view.view_config` declaration is::
@@ -382,7 +386,8 @@ class view_config(object):
route_name=None, request_method=None, request_param=None,
containment=None, attr=None, renderer=None, wrapper=None,
xhr=False, accept=None, header=None, path_info=None,
- custom_predicates=(), context=None):
+ custom_predicates=(), context=None, decorator=None,
+ mapper=None):
self.name = name
self.request_type = request_type
self.context = context or for_
@@ -399,11 +404,19 @@ class view_config(object):
self.header = header
self.path_info = path_info
self.custom_predicates = custom_predicates
+ self.decorator = decorator
+ self.mapper = mapper
def __call__(self, wrapped):
settings = self.__dict__.copy()
def callback(context, name, ob):
+ renderer = settings.get('renderer')
+ if isinstance(renderer, basestring):
+ renderer = RendererHelper(name=renderer,
+ package=info.module,
+ registry=context.config.registry)
+ settings['renderer'] = renderer
context.config.add_view(view=ob, **settings)
info = self.venusian.attach(wrapped, callback, category='pyramid')
@@ -415,10 +428,6 @@ class view_config(object):
if settings['attr'] is None:
settings['attr'] = wrapped.__name__
- renderer_name = settings.get('renderer')
- if renderer_name is not None and not isinstance(renderer_name, dict):
- settings['renderer'] = {'name':renderer_name,
- 'package':info.module}
settings['_info'] = info.codeinfo
return wrapped
diff --git a/pyramid/zcml.py b/pyramid/zcml.py
index f668e3b4b..298086912 100644
--- a/pyramid/zcml.py
+++ b/pyramid/zcml.py
@@ -128,6 +128,14 @@ class IViewDirective(Interface):
description=(u'Accepts a regular expression.'),
required = False)
+ decorator = GlobalObject(
+ title = u'View decorator',
+ required = False)
+
+ mapper = GlobalObject(
+ title = u'View mapper',
+ required = False)
+
custom_predicates = Tokens(
title=u"One or more custom dotted names to custom predicate callables",
description=(u"A list of dotted name references to callables that "
@@ -156,17 +164,14 @@ def view(
header=None,
path_info=None,
traverse=None,
+ decorator=None,
+ mapper=None,
custom_predicates=(),
context=None,
cacheable=True, # not used, here for b/w compat < 0.8
):
- if renderer is not None:
- package = getattr(_context, 'package', None)
- renderer = {'name':renderer, 'package':package}
-
context = context or for_
-
config = Configurator.with_context(_context)
config.add_view(
permission=permission, context=context, view=view, name=name,
@@ -174,7 +179,8 @@ def view(
request_method=request_method, request_param=request_param,
containment=containment, attr=attr, renderer=renderer,
wrapper=wrapper, xhr=xhr, accept=accept, header=header,
- path_info=path_info, custom_predicates=custom_predicates)
+ path_info=path_info, custom_predicates=custom_predicates,
+ decorator=decorator, mapper=mapper)
_view = view # for directives that take a view arg
diff --git a/setup.py b/setup.py
index b158547af..d58641487 100644
--- a/setup.py
+++ b/setup.py
@@ -12,7 +12,7 @@
#
##############################################################################
-__version__ = '1.0a8'
+__version__ = '0.0'
import os
import platform