summaryrefslogtreecommitdiff
path: root/docs/tutorials/wiki2/src
diff options
context:
space:
mode:
Diffstat (limited to 'docs/tutorials/wiki2/src')
-rw-r--r--docs/tutorials/wiki2/src/authorization/CHANGES.txt4
-rw-r--r--docs/tutorials/wiki2/src/authorization/README.txt4
-rw-r--r--docs/tutorials/wiki2/src/authorization/setup.cfg27
-rw-r--r--docs/tutorials/wiki2/src/authorization/setup.py46
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial.ini23
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/__init__.py2
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/configure.zcml65
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/login.py39
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/models.py52
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/run.py25
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/security.py8
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt35
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt32
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt99
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/static/default.css380
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/static/images/img01.gifbin0 -> 3840 bytes
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/static/images/img02.gifbin0 -> 4689 bytes
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/static/images/img03.gifbin0 -> 229 bytes
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/static/images/img04.gifbin0 -> 92 bytes
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/static/images/spacer.gifbin0 -> 43 bytes
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/static/style.css109
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/static/templatelicense.txt243
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt31
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/tests.py139
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/views.py71
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/CHANGES.txt4
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/README.txt4
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/setup.cfg27
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/setup.py45
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial.ini22
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py2
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/configure.zcml18
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/models.py45
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/run.py24
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt99
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/default.css380
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/images/img01.gifbin0 -> 3840 bytes
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/images/img02.gifbin0 -> 4689 bytes
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/images/img03.gifbin0 -> 229 bytes
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/images/img04.gifbin0 -> 92 bytes
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/images/spacer.gifbin0 -> 43 bytes
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/templatelicense.txt243
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py24
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/views.py7
-rw-r--r--docs/tutorials/wiki2/src/models/CHANGES.txt4
-rw-r--r--docs/tutorials/wiki2/src/models/README.txt4
-rw-r--r--docs/tutorials/wiki2/src/models/setup.cfg27
-rw-r--r--docs/tutorials/wiki2/src/models/setup.py45
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial.ini22
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/__init__.py2
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/configure.zcml18
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/models.py43
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/run.py24
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt99
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/templates/static/default.css380
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/templates/static/images/img01.gifbin0 -> 3840 bytes
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/templates/static/images/img02.gifbin0 -> 4689 bytes
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/templates/static/images/img03.gifbin0 -> 229 bytes
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/templates/static/images/img04.gifbin0 -> 92 bytes
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/templates/static/images/spacer.gifbin0 -> 43 bytes
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/templates/static/templatelicense.txt243
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/tests.py24
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/views.py7
-rw-r--r--docs/tutorials/wiki2/src/views/CHANGES.txt4
-rw-r--r--docs/tutorials/wiki2/src/views/README.txt4
-rw-r--r--docs/tutorials/wiki2/src/views/setup.cfg27
-rw-r--r--docs/tutorials/wiki2/src/views/setup.py46
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial.ini23
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/__init__.py2
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/configure.zcml38
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/models.py43
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/run.py24
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/edit.pt32
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt99
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/static/default.css380
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/static/images/img01.gifbin0 -> 3840 bytes
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/static/images/img02.gifbin0 -> 4689 bytes
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/static/images/img03.gifbin0 -> 229 bytes
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/static/images/img04.gifbin0 -> 92 bytes
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/static/images/spacer.gifbin0 -> 43 bytes
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/static/style.css109
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/static/templatelicense.txt243
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/view.pt28
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/tests.py140
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/views.py65
85 files changed, 4627 insertions, 0 deletions
diff --git a/docs/tutorials/wiki2/src/authorization/CHANGES.txt b/docs/tutorials/wiki2/src/authorization/CHANGES.txt
new file mode 100644
index 000000000..35a34f332
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/CHANGES.txt
@@ -0,0 +1,4 @@
+0.0
+---
+
+- Initial version
diff --git a/docs/tutorials/wiki2/src/authorization/README.txt b/docs/tutorials/wiki2/src/authorization/README.txt
new file mode 100644
index 000000000..d41f7f90f
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/README.txt
@@ -0,0 +1,4 @@
+tutorial README
+
+
+
diff --git a/docs/tutorials/wiki2/src/authorization/setup.cfg b/docs/tutorials/wiki2/src/authorization/setup.cfg
new file mode 100644
index 000000000..23b2ad983
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/setup.cfg
@@ -0,0 +1,27 @@
+[nosetests]
+match=^test
+nocapture=1
+cover-package=tutorial
+with-coverage=1
+cover-erase=1
+
+[compile_catalog]
+directory = tutorial/locale
+domain = tutorial
+statistics = true
+
+[extract_messages]
+add_comments = TRANSLATORS:
+output_file = tutorial/locale/tutorial.pot
+width = 80
+
+[init_catalog]
+domain = tutorial
+input_file = tutorial/locale/tutorial.pot
+output_dir = tutorial/locale
+
+[update_catalog]
+domain = tutorial
+input_file = tutorial/locale/tutorial.pot
+output_dir = tutorial/locale
+previous = true
diff --git a/docs/tutorials/wiki2/src/authorization/setup.py b/docs/tutorials/wiki2/src/authorization/setup.py
new file mode 100644
index 000000000..eeac5c397
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/setup.py
@@ -0,0 +1,46 @@
+import os
+import sys
+
+from setuptools import setup, find_packages
+
+here = os.path.abspath(os.path.dirname(__file__))
+README = open(os.path.join(here, 'README.txt')).read()
+CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
+
+requires = [
+ 'pyramid',
+ 'SQLAlchemy',
+ 'transaction',
+ 'repoze.tm2',
+ 'zope.sqlalchemy',
+ 'docutils'
+ ]
+
+if sys.version_info[:3] < (2,5,0):
+ requires.append('pysqlite')
+
+setup(name='tutorial',
+ version='0.0',
+ description='tutorial',
+ long_description=README + '\n\n' + CHANGES,
+ classifiers=[
+ "Programming Language :: Python",
+ "Framework :: Pylons",
+ "Topic :: Internet :: WWW/HTTP",
+ "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
+ ],
+ author='',
+ author_email='',
+ url='',
+ keywords='web wsgi pylons pyramid bfg',
+ packages=find_packages(),
+ include_package_data=True,
+ zip_safe=False,
+ test_suite='tutorial',
+ install_requires = requires,
+ entry_points = """\
+ [paste.app_factory]
+ app = tutorial.run:app
+ """
+ )
+
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial.ini b/docs/tutorials/wiki2/src/authorization/tutorial.ini
new file mode 100644
index 000000000..85f131c2e
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial.ini
@@ -0,0 +1,23 @@
+[DEFAULT]
+debug = true
+
+[app:sqlalchemy]
+use = egg:tutorial#app
+reload_templates = true
+debug_authorization = false
+debug_notfound = false
+debug_templates = true
+default_locale_name = en
+db_string = sqlite:///%(here)s/tutorial.db
+db_echo = false
+
+[pipeline:main]
+pipeline =
+ egg:Paste#evalerror
+ egg:repoze.tm2#tm
+ sqlalchemy
+
+[server:main]
+use = egg:Paste#http
+host = 0.0.0.0
+port = 6543
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
new file mode 100644
index 000000000..cbdfd3ac6
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
@@ -0,0 +1,2 @@
+# A package
+
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/configure.zcml b/docs/tutorials/wiki2/src/authorization/tutorial/configure.zcml
new file mode 100644
index 000000000..03b0e5b5a
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/configure.zcml
@@ -0,0 +1,65 @@
+<configure xmlns="http://pylonshq.com/pyramid">
+
+ <!-- this must be included for the view declarations to work -->
+ <include package="pyramid.includes" />
+
+ <static
+ path="templates/static"
+ name="static"
+ />
+
+ <route
+ pattern="login"
+ name="login"
+ view=".login.login"
+ view_renderer="templates/login.pt"
+ />
+
+ <route
+ pattern="logout"
+ name="logout"
+ view=".login.logout"
+ />
+
+ <route
+ pattern=""
+ name="view_wiki"
+ view=".views.view_wiki"
+ />
+
+ <route
+ pattern=":pagename"
+ name="view_page"
+ view=".views.view_page"
+ view_renderer="templates/view.pt"
+ />
+
+ <route
+ pattern="add_page/:pagename"
+ name="add_page"
+ view=".views.add_page"
+ view_renderer="templates/edit.pt"
+ view_permission="edit"
+ />
+
+ <route
+ pattern=":pagename/edit_page"
+ name="edit_page"
+ view=".views.edit_page"
+ view_renderer="templates/edit.pt"
+ view_permission="edit"
+ />
+
+ <view
+ view=".login.login"
+ renderer="templates/login.pt"
+ for="pyramid.exceptions.Forbidden"/>
+
+ <authtktauthenticationpolicy
+ secret="sosecret"
+ callback=".security.groupfinder"
+ />
+
+ <aclauthorizationpolicy/>
+
+</configure>
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/login.py b/docs/tutorials/wiki2/src/authorization/tutorial/login.py
new file mode 100644
index 000000000..1a54d575c
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/login.py
@@ -0,0 +1,39 @@
+from webob.exc import HTTPFound
+
+from pyramid.security import remember
+from pyramid.security import forget
+from pyramid.url import route_url
+
+from tutorial.security import USERS
+
+def login(request):
+ login_url = route_url('login', request)
+ referrer = request.url
+ if referrer == login_url:
+ referrer = '/' # never use the login form itself as came_from
+ came_from = request.params.get('came_from', referrer)
+ message = ''
+ login = ''
+ password = ''
+ if 'form.submitted' in request.params:
+ login = request.params['login']
+ password = request.params['password']
+ if USERS.get(login) == password:
+ headers = remember(request, login)
+ return HTTPFound(location = came_from,
+ headers = headers)
+ message = 'Failed login'
+
+ return dict(
+ message = message,
+ url = request.application_url + '/login',
+ came_from = came_from,
+ login = login,
+ password = password,
+ )
+
+def logout(request):
+ headers = forget(request)
+ return HTTPFound(location = route_url('view_wiki', request),
+ headers = headers)
+
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models.py b/docs/tutorials/wiki2/src/authorization/tutorial/models.py
new file mode 100644
index 000000000..a77b4964c
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/models.py
@@ -0,0 +1,52 @@
+import transaction
+
+from pyramid.security import Allow
+from pyramid.security import Everyone
+
+from sqlalchemy import create_engine
+from sqlalchemy import Column
+from sqlalchemy import Integer
+from sqlalchemy import Text
+
+from sqlalchemy.exc import IntegrityError
+from sqlalchemy.ext.declarative import declarative_base
+
+from sqlalchemy.orm import scoped_session
+from sqlalchemy.orm import sessionmaker
+
+from zope.sqlalchemy import ZopeTransactionExtension
+
+DBSession = scoped_session(sessionmaker(
+ extension=ZopeTransactionExtension()))
+Base = declarative_base()
+
+class Page(Base):
+ """ The SQLAlchemy declarative model class for a Page object. """
+ __tablename__ = 'pages'
+ id = Column(Integer, primary_key=True)
+ name = Column(Text, unique=True)
+ data = Column(Text)
+
+ def __init__(self, name, data):
+ self.name = name
+ self.data = data
+
+def initialize_sql(db_string, echo=False):
+ engine = create_engine(db_string, echo=echo)
+ DBSession.configure(bind=engine)
+ Base.metadata.bind = engine
+ Base.metadata.create_all(engine)
+ try:
+ session = DBSession()
+ page = Page('FrontPage', 'initial data')
+ session.add(page)
+ transaction.commit()
+ except IntegrityError:
+ # already created
+ pass
+
+class RootFactory(object):
+ __acl__ = [ (Allow, Everyone, 'view'),
+ (Allow, 'group:editors', 'edit') ]
+ def __init__(self, request):
+ self.__dict__.update(request.matchdict)
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/run.py b/docs/tutorials/wiki2/src/authorization/tutorial/run.py
new file mode 100644
index 000000000..82ce37490
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/run.py
@@ -0,0 +1,25 @@
+from pyramid.configuration import Configurator
+from paste.deploy.converters import asbool
+
+from tutorial.models import initialize_sql
+from tutorial.models import RootFactory
+
+def app(global_config, **settings):
+ """ This function returns a WSGI application.
+
+ It is usually called by the PasteDeploy framework during
+ ``paster serve``.
+ """
+ zcml_file = settings.get('configure_zcml', 'configure.zcml')
+ db_string = settings.get('db_string')
+ if db_string is None:
+ raise ValueError(
+ "No 'db_string' value in application configuration.")
+ db_echo = settings.get('db_echo', 'false')
+ initialize_sql(db_string, asbool(db_echo))
+ config = Configurator(settings=settings, root_factory=RootFactory)
+ config.begin()
+ config.load_zcml(zcml_file)
+ config.end()
+ return config.make_wsgi_app()
+
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/security.py b/docs/tutorials/wiki2/src/authorization/tutorial/security.py
new file mode 100644
index 000000000..cfd13071e
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/security.py
@@ -0,0 +1,8 @@
+USERS = {'editor':'editor',
+ 'viewer':'viewer'}
+GROUPS = {'editor':['group:editors']}
+
+def groupfinder(userid, request):
+ if userid in USERS:
+ return GROUPS.get(userid, [])
+
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt
new file mode 100644
index 000000000..05e2ecd76
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt
@@ -0,0 +1,35 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal">
+
+<head>
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
+ <title>Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)
+ Editing: ${page.name}</title>
+ <link rel="stylesheet" type="text/css"
+ href="${request.application_url}/static/style.css" />
+</head>
+
+<body>
+
+<div class="main_content">
+ <div style="float:right; width: 10em;"> Viewing
+ <span tal:replace="page.name">Page Name Goes Here</span> <br/>
+ You can return to the <a href="${request.application_url}"
+ >FrontPage</a>.
+ <span tal:condition="logged_in">
+ <a href="${request.application_url}/logout">Logout</a>
+ </span>
+ </div>
+
+ <div>
+ <form action="${save_url}" method="post">
+ <textarea name="body" tal:content="page.data" rows="10" cols="60"/>
+ <input type="submit" name="form.submitted" value="Save"/>
+ </form>
+ </div>
+</div>
+</body>
+</html>
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt
new file mode 100644
index 000000000..c56983d64
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt
@@ -0,0 +1,32 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal">
+
+<head>
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
+ <title>Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title>
+ <link rel="stylesheet" type="text/css"
+ href="${request.application_url}/static/style.css" />
+</head>
+
+<body>
+
+<h1>Log In</h1>
+
+<div tal:replace="message"/>
+
+<div class="main_content">
+ <form action="${url}" method="post">
+ <input type="hidden" name="came_from" value="${came_from}"/>
+ <input type="text" name="login" value="${login}"/>
+ <br/>
+ <input type="password" name="password" value="${password}"/>
+ <br/>
+ <input type="submit" name="form.submitted" value="Log In"/>
+ </form>
+</div>
+
+</body>
+</html>
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt
new file mode 100644
index 000000000..9178b5866
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt
@@ -0,0 +1,99 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<title>${project} Application</title>
+<meta name="keywords" content="python web application" />
+<meta name="description" content="pyramid web application" />
+<link href="${request.application_url}/static/default.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+<!-- start header -->
+<div id="logo">
+ <h2><code>${project}</code>, a <code>Pyramid</code> application</h2>
+</div>
+<div id="header">
+ <div id="menu">
+ </div>
+</div>
+<!-- end header -->
+<div id="wrapper">
+ <!-- start page -->
+ <div id="page">
+ <!-- start content -->
+ <div id="content">
+ <div class="post">
+ <h1 class="title">Welcome to <code>${project}</code>, an
+ application generated by the <a
+ href="http://pylonshq.com/pyramid">Pyramid</a> web
+ application framework.</h1>
+ </div>
+ </div>
+ <!-- end content -->
+ <!-- start sidebar -->
+ <div id="sidebar">
+ <ul>
+ <li id="search">
+ <h2>Search<br/> <code>Pyramid</code> Documentation</h2>
+ <form method="get"
+ action="http://pylonshq.com/docs/pyramid/current/searchresults">
+ <fieldset>
+ <input type="text" id="q" name="text" value="" />
+ <input type="submit" id="x" value="Search" />
+ </fieldset>
+ </form>
+ </li>
+ <li>
+ <h2><code>Pyramid</code> links</h2>
+ <ul>
+ <li><a
+ href="http://pylonshq.com/docs/pyramid/current/#narrative-documentation">Narrative
+ Documentation</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#api-documentation">API
+ Documentation</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#tutorials">Tutorials</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#change-history">Change
+ History</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#sample-applications">Sample
+ Applications</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#support-and-development">Support
+ and Development</a>
+ </li>
+ <li>
+ <a
+ href="irc://irc.freenode.net#pylons">IRC Channel</a>
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+ <!-- end sidebar -->
+ <div style="clear: both;">&nbsp;</div>
+ </div>
+</div>
+<!-- end page -->
+<!-- start footer -->
+<div id="footer">
+ <p id="legal">( c ) 2008. All Rights Reserved. Template design
+ by <a href="http://www.freecsstemplates.org/">Free CSS
+ Templates</a>.</p>
+</div>
+<!-- end footer -->
+</body>
+</html>
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/default.css b/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/default.css
new file mode 100644
index 000000000..41b3debde
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/default.css
@@ -0,0 +1,380 @@
+/*
+Design by Free CSS Templates
+http://www.freecsstemplates.org
+Released for free under a Creative Commons Attribution 2.5 License
+*/
+
+body {
+ margin: 0;
+ padding: 0;
+ background: url(images/img01.gif) repeat-x left top;
+ font-size: 13px;
+ font-family: "Trebuchet MS", Georgia, "Times New Roman", Times, serif;
+ text-align: justify;
+ color: #FFFFFF;
+}
+
+h1, h2, h3 {
+ margin: 0;
+ text-transform: lowercase;
+ font-weight: normal;
+ color: #FFFFFF;
+}
+
+h1 {
+ letter-spacing: -1px;
+ font-size: 32px;
+}
+
+h2 {
+ font-size: 23px;
+}
+
+p, ul, ol {
+ margin: 0 0 2em 0;
+ text-align: justify;
+ line-height: 26px;
+}
+
+a:link {
+ color: #8BD80E;
+}
+
+a:hover, a:active {
+ text-decoration: none;
+ color: #8BD80E;
+}
+
+a:visited {
+ color: #8BD80E;
+}
+
+img {
+ border: none;
+}
+
+img.left {
+ float: left;
+ margin-right: 15px;
+}
+
+img.right {
+ float: right;
+ margin-left: 15px;
+}
+
+/* Form */
+
+form {
+ margin: 0;
+ padding: 0;
+}
+
+fieldset {
+ margin: 0;
+ padding: 0;
+ border: none;
+}
+
+legend {
+ display: none;
+}
+
+input, textarea, select {
+ font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
+ font-size: 13px;
+ color: #333333;
+}
+
+#wrapper {
+ margin: 0;
+ padding: 0;
+ background: #000000;
+}
+
+/* Header */
+
+#header {
+ width: 713px;
+ margin: 0 auto;
+ height: 42px;
+}
+
+/* Menu */
+
+#menu {
+ float: left;
+ width: 713px;
+ height: 50px;
+ background: url(images/img02.gif) no-repeat left top;
+}
+
+#menu ul {
+ margin: 0;
+ padding: 0px 0 0 10px;
+ list-style: none;
+ line-height: normal;
+}
+
+#menu li {
+ display: block;
+ float: left;
+}
+
+#menu a {
+ display: block;
+ float: left;
+ background: url(images/img04.gif) no-repeat right 55%;
+ margin-top: 5px;
+ margin-right: 3px;
+ padding: 8px 17px;
+ text-decoration: none;
+ font-size: 13px;
+ color: #000000;
+}
+
+#menu a:hover {
+ color: #000000;
+}
+
+#menu .current_page_item a {
+ color: #000000;
+}
+
+/** LOGO */
+
+#logo {
+ width: 713px;
+ height: 80px;
+ margin: 0 auto;
+}
+
+#logo h1, #logo h2 {
+ float: left;
+ margin: 0;
+ padding: 30px 0 0 0px;
+ line-height: normal;
+}
+
+#logo h1 {
+ font-family: Georgia, "Times New Roman", Times, serif;
+ font-size:40px;
+}
+
+#logo h1 a {
+ text-decoration: none;
+ color: #4C4C4C;
+}
+
+#logo h1 a:hover { text-decoration: underline; }
+
+#logo h2 {
+ float: left;
+ padding: 45px 0 0 18px;
+ font: 18px Georgia, "Times New Roman", Times, serif;
+ color: #8BD80E;
+}
+
+#logo p a {
+ text-decoration: none;
+ color: #8BD80E;
+}
+
+#logo p a:hover { text-decoration: underline; }
+
+
+
+/* Page */
+
+#page {
+ width: 663px;
+ margin: 0 auto;
+ background: #4C4C4C url(images/img03.gif) no-repeat left bottom;
+ padding: 0 25px;
+}
+
+/* Content */
+
+#content {
+ float: left;
+ width: 410px;
+
+}
+
+/* Post */
+
+.post {
+ padding: 15px 0px;
+ margin-bottom: 20px;
+}
+
+.post .title {
+ margin-bottom: 20px;
+ padding-bottom: 5px;
+}
+
+.post h1 {
+ padding: 0px 0 0 0px;
+ background: url(images/img08.jpg) no-repeat left top;
+ font-size: 24px;
+ color: #FFFFFF;
+}
+
+.post h2 {
+ padding: 0px 0 0 0px;
+ font-size: 22px;
+ color: #FFFFFF;
+}
+
+.post .entry {
+}
+
+.post .meta {
+ padding: 15px 15px 30px 0px;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 11px;
+}
+
+.post .meta p {
+ margin: 0;
+ padding-top: 15px;
+ line-height: normal;
+ color: #FFFFFF;
+}
+
+.post .meta .byline {
+ float: left;
+}
+
+.post .meta .links {
+ float: right;
+}
+
+.post .meta .more {
+ padding: 0 10px 0 18px;
+}
+
+.post .meta .comments {
+}
+
+.post .meta b {
+ display: none;
+}
+
+
+/* Sidebar */
+
+#sidebar {
+ width: 210px;
+ float: right;
+ margin: 0;
+ padding: 0;
+}
+
+#sidebar ul {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+#sidebar li {
+ margin-bottom: 40px;
+}
+
+#sidebar li ul {
+}
+
+#sidebar li li {
+ margin: 0;
+}
+
+#sidebar h2 {
+ width: 250px;
+ padding: 8px 0 0 0px;
+ margin-bottom: 10px;
+ background: url(images/img07.jpg) no-repeat left top;
+ font-size: 20px;
+ color: #FFFFFF;
+}
+
+/* Search */
+
+#search {
+
+}
+
+#search h2 {
+ margin-bottom: 20px;
+}
+
+#s {
+ width: 140px;
+ margin-right: 5px;
+ padding: 3px;
+ border: 1px solid #BED99C;
+}
+
+#x {
+ padding: 3px;
+ border: none;
+ background: #8BD80E;
+ text-transform: lowercase;
+ font-size: 11px;
+ color: #FFFFFF;
+}
+
+/* Boxes */
+
+.box1 {
+ padding: 20px;
+}
+
+.box2 {
+ color: #BABABA;
+}
+
+.box2 h2 {
+ margin-bottom: 15px;
+ font-size: 16px;
+ color: #FFFFFF;
+}
+
+.box2 ul {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+.box2 a:link, .box2 a:hover, .box2 a:active, .box2 a:visited {
+ color: #EDEDED;
+}
+
+/* Footer */
+#footer-wrap {
+}
+
+#footer {
+ margin: 0 auto;
+ padding: 20px 0 10px 0;
+ background: #000000;
+}
+
+html>body #footer {
+ height: auto;
+}
+
+#footer p {
+ font-size: 11px;
+}
+
+#legal {
+ clear: both;
+ padding-top: 17px;
+ text-align: center;
+ color: #FFFFFF;
+}
+
+#legal a {
+ font-weight: normal;
+ color: #FFFFFF;
+}
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/images/img01.gif b/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/images/img01.gif
new file mode 100644
index 000000000..5f082bd99
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/images/img01.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/images/img02.gif b/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/images/img02.gif
new file mode 100644
index 000000000..45a3ae976
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/images/img02.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/images/img03.gif b/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/images/img03.gif
new file mode 100644
index 000000000..d92ea38f9
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/images/img03.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/images/img04.gif b/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/images/img04.gif
new file mode 100644
index 000000000..950c4af9d
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/images/img04.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/images/spacer.gif b/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/images/spacer.gif
new file mode 100644
index 000000000..5bfd67a2d
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/images/spacer.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/style.css b/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/style.css
new file mode 100644
index 000000000..cad87e0d4
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/style.css
@@ -0,0 +1,109 @@
+html, body {
+ color: black;
+ background-color: #ddd;
+ font: x-small "Lucida Grande", "Lucida Sans Unicode", geneva, sans-serif;
+ margin: 0;
+ padding: 0;
+}
+
+td, th {padding:3px;border:none;}
+tr th {text-align:left;background-color:#f0f0f0;color:#333;}
+tr.odd td {background-color:#edf3fe;}
+tr.even td {background-color:#fff;}
+
+#header {
+ height: 80px;
+ width: 777px;
+ background: blue URL('../images/header_inner.png') no-repeat;
+ border-left: 1px solid #aaa;
+ border-right: 1px solid #aaa;
+ margin: 0 auto 0 auto;
+}
+
+a.link, a, a.active {
+ color: #369;
+}
+
+
+#main_content {
+ color: black;
+ font-size: 127%;
+ background-color: white;
+ width: 757px;
+ margin: 0 auto 0 auto;
+ border-left: 1px solid #aaa;
+ border-right: 1px solid #aaa;
+ padding: 10px;
+}
+
+#sidebar {
+ border: 1px solid #aaa;
+ background-color: #eee;
+ margin: 0.5em;
+ padding: 1em;
+ float: right;
+ width: 200px;
+ font-size: 88%;
+}
+
+#sidebar h2 {
+ margin-top: 0;
+}
+
+#sidebar ul {
+ margin-left: 1.5em;
+ padding-left: 0;
+}
+
+h1,h2,h3,h4,h5,h6,#getting_started_steps {
+ font-family: "Century Schoolbook L", Georgia, serif;
+ font-weight: bold;
+}
+
+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/templates/static/templatelicense.txt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/templatelicense.txt
new file mode 100644
index 000000000..ccb6b06ab
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/static/templatelicense.txt
@@ -0,0 +1,243 @@
+Creative Commons </>
+
+Creative Commons Legal Code
+
+*Attribution 2.5*
+
+CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
+ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION
+ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE
+INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ITS USE.
+
+/License/
+
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
+COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
+COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
+AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
+TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE
+RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS
+AND CONDITIONS.
+
+*1. Definitions*
+
+ 1. *"Collective Work"* means a work, such as a periodical issue,
+ anthology or encyclopedia, in which the Work in its entirety in
+ unmodified form, along with a number of other contributions,
+ constituting separate and independent works in themselves, are
+ assembled into a collective whole. A work that constitutes a
+ Collective Work will not be considered a Derivative Work (as
+ defined below) for the purposes of this License.
+ 2. *"Derivative Work"* means a work based upon the Work or upon the
+ Work and other pre-existing works, such as a translation, musical
+ arrangement, dramatization, fictionalization, motion picture
+ version, sound recording, art reproduction, abridgment,
+ condensation, or any other form in which the Work may be recast,
+ transformed, or adapted, except that a work that constitutes a
+ Collective Work will not be considered a Derivative Work for the
+ purpose of this License. For the avoidance of doubt, where the
+ Work is a musical composition or sound recording, the
+ synchronization of the Work in timed-relation with a moving image
+ ("synching") will be considered a Derivative Work for the purpose
+ of this License.
+ 3. *"Licensor"* means the individual or entity that offers the Work
+ under the terms of this License.
+ 4. *"Original Author"* means the individual or entity who created the
+ Work.
+ 5. *"Work"* means the copyrightable work of authorship offered under
+ the terms of this License.
+ 6. *"You"* means an individual or entity exercising rights under this
+ License who has not previously violated the terms of this License
+ with respect to the Work, or who has received express permission
+ from the Licensor to exercise rights under this License despite a
+ previous violation.
+
+*2. Fair Use Rights.* Nothing in this license is intended to reduce,
+limit, or restrict any rights arising from fair use, first sale or other
+limitations on the exclusive rights of the copyright owner under
+copyright law or other applicable laws.
+
+*3. License Grant.* Subject to the terms and conditions of this License,
+Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
+perpetual (for the duration of the applicable copyright) license to
+exercise the rights in the Work as stated below:
+
+ 1. to reproduce the Work, to incorporate the Work into one or more
+ Collective Works, and to reproduce the Work as incorporated in the
+ Collective Works;
+ 2. to create and reproduce Derivative Works;
+ 3. to distribute copies or phonorecords of, display publicly, perform
+ publicly, and perform publicly by means of a digital audio
+ transmission the Work including as incorporated in Collective Works;
+ 4. to distribute copies or phonorecords of, display publicly, perform
+ publicly, and perform publicly by means of a digital audio
+ transmission Derivative Works.
+ 5.
+
+ For the avoidance of doubt, where the work is a musical composition:
+
+ 1. *Performance Royalties Under Blanket Licenses*. Licensor
+ waives the exclusive right to collect, whether individually
+ or via a performance rights society (e.g. ASCAP, BMI,
+ SESAC), royalties for the public performance or public
+ digital performance (e.g. webcast) of the Work.
+ 2. *Mechanical Rights and Statutory Royalties*. Licensor waives
+ the exclusive right to collect, whether individually or via
+ a music rights agency or designated agent (e.g. Harry Fox
+ Agency), royalties for any phonorecord You create from the
+ Work ("cover version") and distribute, subject to the
+ compulsory license created by 17 USC Section 115 of the US
+ Copyright Act (or the equivalent in other jurisdictions).
+ 6. *Webcasting Rights and Statutory Royalties*. For the avoidance of
+ doubt, where the Work is a sound recording, Licensor waives the
+ exclusive right to collect, whether individually or via a
+ performance-rights society (e.g. SoundExchange), royalties for the
+ public digital performance (e.g. webcast) of the Work, subject to
+ the compulsory license created by 17 USC Section 114 of the US
+ Copyright Act (or the equivalent in other jurisdictions).
+
+The above rights may be exercised in all media and formats whether now
+known or hereafter devised. The above rights include the right to make
+such modifications as are technically necessary to exercise the rights
+in other media and formats. All rights not expressly granted by Licensor
+are hereby reserved.
+
+*4. Restrictions.*The license granted in Section 3 above is expressly
+made subject to and limited by the following restrictions:
+
+ 1. You may distribute, publicly display, publicly perform, or
+ publicly digitally perform the Work only under the terms of this
+ License, and You must include a copy of, or the Uniform Resource
+ Identifier for, this License with every copy or phonorecord of the
+ Work You distribute, publicly display, publicly perform, or
+ publicly digitally perform. You may not offer or impose any terms
+ on the Work that alter or restrict the terms of this License or
+ the recipients' exercise of the rights granted hereunder. You may
+ not sublicense the Work. You must keep intact all notices that
+ refer to this License and to the disclaimer of warranties. You may
+ not distribute, publicly display, publicly perform, or publicly
+ digitally perform the Work with any technological measures that
+ control access or use of the Work in a manner inconsistent with
+ the terms of this License Agreement. The above applies to the Work
+ as incorporated in a Collective Work, but this does not require
+ the Collective Work apart from the Work itself to be made subject
+ to the terms of this License. If You create a Collective Work,
+ upon notice from any Licensor You must, to the extent practicable,
+ remove from the Collective Work any credit as required by clause
+ 4(b), as requested. If You create a Derivative Work, upon notice
+ from any Licensor You must, to the extent practicable, remove from
+ the Derivative Work any credit as required by clause 4(b), as
+ requested.
+ 2. If you distribute, publicly display, publicly perform, or publicly
+ digitally perform the Work or any Derivative Works or Collective
+ Works, You must keep intact all copyright notices for the Work and
+ provide, reasonable to the medium or means You are utilizing: (i)
+ the name of the Original Author (or pseudonym, if applicable) if
+ supplied, and/or (ii) if the Original Author and/or Licensor
+ designate another party or parties (e.g. a sponsor institute,
+ publishing entity, journal) for attribution in Licensor's
+ copyright notice, terms of service or by other reasonable means,
+ the name of such party or parties; the title of the Work if
+ supplied; to the extent reasonably practicable, the Uniform
+ Resource Identifier, if any, that Licensor specifies to be
+ associated with the Work, unless such URI does not refer to the
+ copyright notice or licensing information for the Work; and in the
+ case of a Derivative Work, a credit identifying the use of the
+ Work in the Derivative Work (e.g., "French translation of the Work
+ by Original Author," or "Screenplay based on original Work by
+ Original Author"). Such credit may be implemented in any
+ reasonable manner; provided, however, that in the case of a
+ Derivative Work or Collective Work, at a minimum such credit will
+ appear where any other comparable authorship credit appears and in
+ a manner at least as prominent as such other comparable authorship
+ credit.
+
+*5. Representations, Warranties and Disclaimer*
+
+UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
+OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
+KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
+INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
+FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
+LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
+WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE
+EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
+
+*6. Limitation on Liability.* EXCEPT TO THE EXTENT REQUIRED BY
+APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL
+THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY
+DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF
+LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+*7. Termination*
+
+ 1. This License and the rights granted hereunder will terminate
+ automatically upon any breach by You of the terms of this License.
+ Individuals or entities who have received Derivative Works or
+ Collective Works from You under this License, however, will not
+ have their licenses terminated provided such individuals or
+ entities remain in full compliance with those licenses. Sections
+ 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
+ 2. Subject to the above terms and conditions, the license granted
+ here is perpetual (for the duration of the applicable copyright in
+ the Work). Notwithstanding the above, Licensor reserves the right
+ to release the Work under different license terms or to stop
+ distributing the Work at any time; provided, however that any such
+ election will not serve to withdraw this License (or any other
+ license that has been, or is required to be, granted under the
+ terms of this License), and this License will continue in full
+ force and effect unless terminated as stated above.
+
+*8. Miscellaneous*
+
+ 1. Each time You distribute or publicly digitally perform the Work or
+ a Collective Work, the Licensor offers to the recipient a license
+ to the Work on the same terms and conditions as the license
+ granted to You under this License.
+ 2. Each time You distribute or publicly digitally perform a
+ Derivative Work, Licensor offers to the recipient a license to the
+ original Work on the same terms and conditions as the license
+ granted to You under this License.
+ 3. If any provision of this License is invalid or unenforceable under
+ applicable law, it shall not affect the validity or enforceability
+ of the remainder of the terms of this License, and without further
+ action by the parties to this agreement, such provision shall be
+ reformed to the minimum extent necessary to make such provision
+ valid and enforceable.
+ 4. No term or provision of this License shall be deemed waived and no
+ breach consented to unless such waiver or consent shall be in
+ writing and signed by the party to be charged with such waiver or
+ consent.
+ 5. This License constitutes the entire agreement between the parties
+ with respect to the Work licensed here. There are no
+ understandings, agreements or representations with respect to the
+ Work not specified here. Licensor shall not be bound by any
+ additional provisions that may appear in any communication from
+ You. This License may not be modified without the mutual written
+ agreement of the Licensor and You.
+
+Creative Commons is not a party to this License, and makes no warranty
+whatsoever in connection with the Work. Creative Commons will not be
+liable to You or any party on any legal theory for any damages
+whatsoever, including without limitation any general, special,
+incidental or consequential damages arising in connection to this
+license. Notwithstanding the foregoing two (2) sentences, if Creative
+Commons has expressly identified itself as the Licensor hereunder, it
+shall have all rights and obligations of Licensor.
+
+Except for the limited purpose of indicating to the public that the Work
+is licensed under the CCPL, neither party will use the trademark
+"Creative Commons" or any related trademark or logo of Creative Commons
+without the prior written consent of Creative Commons. Any permitted use
+will be in compliance with Creative Commons' then-current trademark
+usage guidelines, as may be published on its website or otherwise made
+available upon request from time to time.
+
+Creative Commons may be contacted at http://creativecommons.org/
+<http://creativecommons.org>.
+
+« Back to Commons Deed <./>
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt
new file mode 100644
index 000000000..0c654250a
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt
@@ -0,0 +1,31 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal">
+
+<head>
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
+ <title>${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" />
+</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>
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/tests.py b/docs/tutorials/wiki2/src/authorization/tutorial/tests.py
new file mode 100644
index 000000000..65330ce17
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/tests.py
@@ -0,0 +1,139 @@
+import unittest
+
+from pyramid.configuration import Configurator
+from pyramid import testing
+
+def _initTestingDB():
+ from tutorial.models import DBSession
+ from tutorial.models import Base
+ from sqlalchemy import create_engine
+ engine = create_engine('sqlite://')
+ DBSession.configure(bind=engine)
+ Base.metadata.bind = engine
+ Base.metadata.create_all(engine)
+ return DBSession
+
+def _registerRoutes(config):
+ config.add_route('view_page', ':pagename')
+ config.add_route('edit_page', ':pagename/edit_page')
+ config.add_route('add_page', 'add_page/:pagename')
+
+class ViewWikiTests(unittest.TestCase):
+ def setUp(self):
+ self.config = Configurator()
+ self.config.begin()
+
+ def tearDown(self):
+ self.config.end()
+
+ def test_it(self):
+ from tutorial.views import view_wiki
+ self.config.add_route('view_page', ':pagename')
+ request = testing.DummyRequest()
+ response = view_wiki(request)
+ self.assertEqual(response.location, 'http://example.com/FrontPage')
+
+class ViewPageTests(unittest.TestCase):
+ def setUp(self):
+ self.session = _initTestingDB()
+ self.config = Configurator()
+ self.config.begin()
+
+ def tearDown(self):
+ self.session.remove()
+ self.config.end()
+
+ def _callFUT(self, request):
+ from tutorial.views import view_page
+ return view_page(request)
+
+ def test_it(self):
+ from tutorial.models import Page
+ request = testing.DummyRequest()
+ request.matchdict['pagename'] = 'IDoExist'
+ page = Page('IDoExist', 'Hello CruelWorld IDoExist')
+ self.session.add(page)
+ _registerRoutes(self.config)
+ info = self._callFUT(request)
+ self.assertEqual(info['page'], page)
+ self.assertEqual(
+ info['content'],
+ '<div class="document">\n'
+ '<p>Hello <a href="http://example.com/add_page/CruelWorld">'
+ 'CruelWorld</a> '
+ '<a href="http://example.com/IDoExist">'
+ 'IDoExist</a>'
+ '</p>\n</div>\n')
+ self.assertEqual(info['edit_url'],
+ 'http://example.com/IDoExist/edit_page')
+
+
+class AddPageTests(unittest.TestCase):
+ def setUp(self):
+ self.session = _initTestingDB()
+ self.config = Configurator()
+ self.config.begin()
+
+ def tearDown(self):
+ self.session.remove()
+ self.config.end()
+
+ def _callFUT(self, request):
+ from tutorial.views import add_page
+ return add_page(request)
+
+ def test_it_notsubmitted(self):
+ _registerRoutes(self.config)
+ request = testing.DummyRequest()
+ request.matchdict = {'pagename':'AnotherPage'}
+ info = self._callFUT(request)
+ self.assertEqual(info['page'].data,'')
+ self.assertEqual(info['save_url'],
+ 'http://example.com/add_page/AnotherPage')
+
+ def test_it_submitted(self):
+ from tutorial.models import Page
+ _registerRoutes(self.config)
+ request = testing.DummyRequest({'form.submitted':True,
+ 'body':'Hello yo!'})
+ request.matchdict = {'pagename':'AnotherPage'}
+ self._callFUT(request)
+ page = self.session.query(Page).filter_by(name='AnotherPage').one()
+ self.assertEqual(page.data, 'Hello yo!')
+
+class EditPageTests(unittest.TestCase):
+ def setUp(self):
+ self.session = _initTestingDB()
+ self.config = Configurator()
+ self.config.begin()
+
+ def tearDown(self):
+ self.session.remove()
+ self.config.end()
+
+ def _callFUT(self, request):
+ from tutorial.views import edit_page
+ return edit_page(request)
+
+ def test_it_notsubmitted(self):
+ from tutorial.models import Page
+ _registerRoutes(self.config)
+ request = testing.DummyRequest()
+ request.matchdict = {'pagename':'abc'}
+ page = Page('abc', 'hello')
+ self.session.add(page)
+ info = self._callFUT(request)
+ self.assertEqual(info['page'], page)
+ self.assertEqual(info['save_url'], 'http://example.com/abc/edit_page')
+
+ def test_it_submitted(self):
+ from tutorial.models import Page
+ _registerRoutes(self.config)
+ request = testing.DummyRequest({'form.submitted':True,
+ 'body':'Hello yo!'})
+ request.matchdict = {'pagename':'abc'}
+ page = Page('abc', 'hello')
+ self.session.add(page)
+ response = self._callFUT(request)
+ self.assertEqual(response.location, 'http://example.com/abc')
+ self.assertEqual(page.data, 'Hello yo!')
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views.py b/docs/tutorials/wiki2/src/authorization/tutorial/views.py
new file mode 100644
index 000000000..a7e7a57c7
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/views.py
@@ -0,0 +1,71 @@
+import re
+
+from docutils.core import publish_parts
+
+from webob.exc import HTTPFound
+
+from pyramid.security import authenticated_userid
+from pyramid.url import route_url
+
+from tutorial.models import DBSession
+from tutorial.models import Page
+
+# regular expression used to find WikiWords
+wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)")
+
+def view_wiki(request):
+ return HTTPFound(location = route_url('view_page', request,
+ pagename='FrontPage'))
+
+def view_page(request):
+ pagename = request.matchdict['pagename']
+ session = DBSession()
+ page = session.query(Page).filter_by(name=pagename).one()
+
+ def check(match):
+ word = match.group(1)
+ exists = session.query(Page).filter_by(name=word).all()
+ if exists:
+ view_url = route_url('view_page', request, pagename=word)
+ return '<a href="%s">%s</a>' % (view_url, word)
+ else:
+ add_url = route_url('add_page', request, pagename=word)
+ return '<a href="%s">%s</a>' % (add_url, word)
+
+ content = publish_parts(page.data, writer_name='html')['html_body']
+ content = wikiwords.sub(check, content)
+ edit_url = route_url('edit_page', request, pagename=pagename)
+ logged_in = authenticated_userid(request)
+ return dict(page=page, content=content, edit_url=edit_url,
+ logged_in=logged_in)
+
+def add_page(request):
+ name = request.matchdict['pagename']
+ if 'form.submitted' in request.params:
+ session = DBSession()
+ body = request.params['body']
+ page = Page(name, body)
+ session.add(page)
+ return HTTPFound(location = route_url('view_page', request,
+ pagename=name))
+ save_url = route_url('add_page', request, pagename=name)
+ page = Page('', '')
+ logged_in = authenticated_userid(request)
+ return dict(page=page, save_url=save_url, logged_in=logged_in)
+
+def edit_page(request):
+ name = request.matchdict['pagename']
+ session = DBSession()
+ page = session.query(Page).filter_by(name=name).one()
+ if 'form.submitted' in request.params:
+ page.data = request.params['body']
+ session.add(page)
+ return HTTPFound(location = route_url('view_page', request,
+ pagename=name))
+
+ logged_in = authenticated_userid(request)
+ return dict(
+ page=page,
+ save_url = route_url('edit_page', request, pagename=name),
+ logged_in = logged_in,
+ )
diff --git a/docs/tutorials/wiki2/src/basiclayout/CHANGES.txt b/docs/tutorials/wiki2/src/basiclayout/CHANGES.txt
new file mode 100644
index 000000000..35a34f332
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/CHANGES.txt
@@ -0,0 +1,4 @@
+0.0
+---
+
+- Initial version
diff --git a/docs/tutorials/wiki2/src/basiclayout/README.txt b/docs/tutorials/wiki2/src/basiclayout/README.txt
new file mode 100644
index 000000000..d41f7f90f
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/README.txt
@@ -0,0 +1,4 @@
+tutorial README
+
+
+
diff --git a/docs/tutorials/wiki2/src/basiclayout/setup.cfg b/docs/tutorials/wiki2/src/basiclayout/setup.cfg
new file mode 100644
index 000000000..23b2ad983
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/setup.cfg
@@ -0,0 +1,27 @@
+[nosetests]
+match=^test
+nocapture=1
+cover-package=tutorial
+with-coverage=1
+cover-erase=1
+
+[compile_catalog]
+directory = tutorial/locale
+domain = tutorial
+statistics = true
+
+[extract_messages]
+add_comments = TRANSLATORS:
+output_file = tutorial/locale/tutorial.pot
+width = 80
+
+[init_catalog]
+domain = tutorial
+input_file = tutorial/locale/tutorial.pot
+output_dir = tutorial/locale
+
+[update_catalog]
+domain = tutorial
+input_file = tutorial/locale/tutorial.pot
+output_dir = tutorial/locale
+previous = true
diff --git a/docs/tutorials/wiki2/src/basiclayout/setup.py b/docs/tutorials/wiki2/src/basiclayout/setup.py
new file mode 100644
index 000000000..aca548b32
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/setup.py
@@ -0,0 +1,45 @@
+import os
+import sys
+
+from setuptools import setup, find_packages
+
+here = os.path.abspath(os.path.dirname(__file__))
+README = open(os.path.join(here, 'README.txt')).read()
+CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
+
+requires = [
+ 'pyramid',
+ 'SQLAlchemy',
+ 'transaction',
+ 'repoze.tm2',
+ 'zope.sqlalchemy',
+ ]
+
+if sys.version_info[:3] < (2,5,0):
+ requires.append('pysqlite')
+
+setup(name='tutorial',
+ version='0.0',
+ description='tutorial',
+ long_description=README + '\n\n' + CHANGES,
+ classifiers=[
+ "Programming Language :: Python",
+ "Framework :: Pylons",
+ "Topic :: Internet :: WWW/HTTP",
+ "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
+ ],
+ author='',
+ author_email='',
+ url='',
+ keywords='web wsgi pylons pyramid bfg',
+ packages=find_packages(),
+ include_package_data=True,
+ zip_safe=False,
+ test_suite='tutorial',
+ install_requires = requires,
+ entry_points = """\
+ [paste.app_factory]
+ app = tutorial.run:app
+ """
+ )
+
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial.ini b/docs/tutorials/wiki2/src/basiclayout/tutorial.ini
new file mode 100644
index 000000000..73b5ed9a4
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial.ini
@@ -0,0 +1,22 @@
+[DEFAULT]
+debug = true
+
+[app:sqlalchemy]
+use = egg:tutorial#app
+reload_templates = true
+debug_authorization = false
+debug_notfound = false
+debug_templates = true
+default_locale_name = en
+db_string = sqlite:///%(here)s/tutorial.db
+db_echo = false
+
+[pipeline:main]
+pipeline =
+ egg:repoze.tm2#tm
+ sqlalchemy
+
+[server:main]
+use = egg:Paste#http
+host = 0.0.0.0
+port = 6543
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py
new file mode 100644
index 000000000..cbdfd3ac6
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py
@@ -0,0 +1,2 @@
+# A package
+
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/configure.zcml b/docs/tutorials/wiki2/src/basiclayout/tutorial/configure.zcml
new file mode 100644
index 000000000..47adfbefb
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/configure.zcml
@@ -0,0 +1,18 @@
+<configure xmlns="http://pylonshq.com/pyramid">
+
+ <!-- this must be included for the view declarations to work -->
+ <include package="pyramid.includes" />
+
+ <route
+ pattern=""
+ name="home"
+ view=".views.my_view"
+ view_renderer="templates/mytemplate.pt"
+ />
+
+ <static
+ name="static"
+ path="templates/static"
+ />
+
+</configure>
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py
new file mode 100644
index 000000000..ae71e7943
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py
@@ -0,0 +1,45 @@
+import transaction
+
+from sqlalchemy import create_engine
+from sqlalchemy import Column
+from sqlalchemy import Integer
+from sqlalchemy import Unicode
+
+from sqlalchemy.exc import IntegrityError
+from sqlalchemy.ext.declarative import declarative_base
+
+from sqlalchemy.orm import scoped_session
+from sqlalchemy.orm import sessionmaker
+
+from zope.sqlalchemy import ZopeTransactionExtension
+
+DBSession = scoped_session(sessionmaker(
+ extension=ZopeTransactionExtension()))
+Base = declarative_base()
+
+class MyModel(Base):
+ __tablename__ = 'models'
+ id = Column(Integer, primary_key=True)
+ name = Column(Unicode(255), unique=True)
+ value = Column(Integer)
+
+ def __init__(self, name, value):
+ self.name = name
+ self.value = value
+
+def populate():
+ session = DBSession()
+ model = MyModel(name=u'root',value=55)
+ session.add(model)
+ session.flush()
+ transaction.commit()
+
+def initialize_sql(db_string, db_echo=False):
+ engine = create_engine(db_string, echo=db_echo)
+ DBSession.configure(bind=engine)
+ Base.metadata.bind = engine
+ Base.metadata.create_all(engine)
+ try:
+ populate()
+ except IntegrityError:
+ pass
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/run.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/run.py
new file mode 100644
index 000000000..7225987ee
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/run.py
@@ -0,0 +1,24 @@
+from pyramid.configuration import Configurator
+from paste.deploy.converters import asbool
+
+from tutorial.models import initialize_sql
+
+def app(global_config, **settings):
+ """ This function returns a WSGI application.
+
+ It is usually called by the PasteDeploy framework during
+ ``paster serve``.
+ """
+ zcml_file = settings.get('configure_zcml', 'configure.zcml')
+ db_string = settings.get('db_string')
+ if db_string is None:
+ raise ValueError(
+ "No 'db_string' value in application configuration.")
+ db_echo = settings.get('db_echo', 'false')
+ initialize_sql(db_string, asbool(db_echo))
+ config = Configurator(settings=settings)
+ config.begin()
+ config.load_zcml(zcml_file)
+ config.end()
+ return config.make_wsgi_app()
+
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt
new file mode 100644
index 000000000..9178b5866
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt
@@ -0,0 +1,99 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<title>${project} Application</title>
+<meta name="keywords" content="python web application" />
+<meta name="description" content="pyramid web application" />
+<link href="${request.application_url}/static/default.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+<!-- start header -->
+<div id="logo">
+ <h2><code>${project}</code>, a <code>Pyramid</code> application</h2>
+</div>
+<div id="header">
+ <div id="menu">
+ </div>
+</div>
+<!-- end header -->
+<div id="wrapper">
+ <!-- start page -->
+ <div id="page">
+ <!-- start content -->
+ <div id="content">
+ <div class="post">
+ <h1 class="title">Welcome to <code>${project}</code>, an
+ application generated by the <a
+ href="http://pylonshq.com/pyramid">Pyramid</a> web
+ application framework.</h1>
+ </div>
+ </div>
+ <!-- end content -->
+ <!-- start sidebar -->
+ <div id="sidebar">
+ <ul>
+ <li id="search">
+ <h2>Search<br/> <code>Pyramid</code> Documentation</h2>
+ <form method="get"
+ action="http://pylonshq.com/docs/pyramid/current/searchresults">
+ <fieldset>
+ <input type="text" id="q" name="text" value="" />
+ <input type="submit" id="x" value="Search" />
+ </fieldset>
+ </form>
+ </li>
+ <li>
+ <h2><code>Pyramid</code> links</h2>
+ <ul>
+ <li><a
+ href="http://pylonshq.com/docs/pyramid/current/#narrative-documentation">Narrative
+ Documentation</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#api-documentation">API
+ Documentation</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#tutorials">Tutorials</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#change-history">Change
+ History</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#sample-applications">Sample
+ Applications</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#support-and-development">Support
+ and Development</a>
+ </li>
+ <li>
+ <a
+ href="irc://irc.freenode.net#pylons">IRC Channel</a>
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+ <!-- end sidebar -->
+ <div style="clear: both;">&nbsp;</div>
+ </div>
+</div>
+<!-- end page -->
+<!-- start footer -->
+<div id="footer">
+ <p id="legal">( c ) 2008. All Rights Reserved. Template design
+ by <a href="http://www.freecsstemplates.org/">Free CSS
+ Templates</a>.</p>
+</div>
+<!-- end footer -->
+</body>
+</html>
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/default.css b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/default.css
new file mode 100644
index 000000000..41b3debde
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/default.css
@@ -0,0 +1,380 @@
+/*
+Design by Free CSS Templates
+http://www.freecsstemplates.org
+Released for free under a Creative Commons Attribution 2.5 License
+*/
+
+body {
+ margin: 0;
+ padding: 0;
+ background: url(images/img01.gif) repeat-x left top;
+ font-size: 13px;
+ font-family: "Trebuchet MS", Georgia, "Times New Roman", Times, serif;
+ text-align: justify;
+ color: #FFFFFF;
+}
+
+h1, h2, h3 {
+ margin: 0;
+ text-transform: lowercase;
+ font-weight: normal;
+ color: #FFFFFF;
+}
+
+h1 {
+ letter-spacing: -1px;
+ font-size: 32px;
+}
+
+h2 {
+ font-size: 23px;
+}
+
+p, ul, ol {
+ margin: 0 0 2em 0;
+ text-align: justify;
+ line-height: 26px;
+}
+
+a:link {
+ color: #8BD80E;
+}
+
+a:hover, a:active {
+ text-decoration: none;
+ color: #8BD80E;
+}
+
+a:visited {
+ color: #8BD80E;
+}
+
+img {
+ border: none;
+}
+
+img.left {
+ float: left;
+ margin-right: 15px;
+}
+
+img.right {
+ float: right;
+ margin-left: 15px;
+}
+
+/* Form */
+
+form {
+ margin: 0;
+ padding: 0;
+}
+
+fieldset {
+ margin: 0;
+ padding: 0;
+ border: none;
+}
+
+legend {
+ display: none;
+}
+
+input, textarea, select {
+ font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
+ font-size: 13px;
+ color: #333333;
+}
+
+#wrapper {
+ margin: 0;
+ padding: 0;
+ background: #000000;
+}
+
+/* Header */
+
+#header {
+ width: 713px;
+ margin: 0 auto;
+ height: 42px;
+}
+
+/* Menu */
+
+#menu {
+ float: left;
+ width: 713px;
+ height: 50px;
+ background: url(images/img02.gif) no-repeat left top;
+}
+
+#menu ul {
+ margin: 0;
+ padding: 0px 0 0 10px;
+ list-style: none;
+ line-height: normal;
+}
+
+#menu li {
+ display: block;
+ float: left;
+}
+
+#menu a {
+ display: block;
+ float: left;
+ background: url(images/img04.gif) no-repeat right 55%;
+ margin-top: 5px;
+ margin-right: 3px;
+ padding: 8px 17px;
+ text-decoration: none;
+ font-size: 13px;
+ color: #000000;
+}
+
+#menu a:hover {
+ color: #000000;
+}
+
+#menu .current_page_item a {
+ color: #000000;
+}
+
+/** LOGO */
+
+#logo {
+ width: 713px;
+ height: 80px;
+ margin: 0 auto;
+}
+
+#logo h1, #logo h2 {
+ float: left;
+ margin: 0;
+ padding: 30px 0 0 0px;
+ line-height: normal;
+}
+
+#logo h1 {
+ font-family: Georgia, "Times New Roman", Times, serif;
+ font-size:40px;
+}
+
+#logo h1 a {
+ text-decoration: none;
+ color: #4C4C4C;
+}
+
+#logo h1 a:hover { text-decoration: underline; }
+
+#logo h2 {
+ float: left;
+ padding: 45px 0 0 18px;
+ font: 18px Georgia, "Times New Roman", Times, serif;
+ color: #8BD80E;
+}
+
+#logo p a {
+ text-decoration: none;
+ color: #8BD80E;
+}
+
+#logo p a:hover { text-decoration: underline; }
+
+
+
+/* Page */
+
+#page {
+ width: 663px;
+ margin: 0 auto;
+ background: #4C4C4C url(images/img03.gif) no-repeat left bottom;
+ padding: 0 25px;
+}
+
+/* Content */
+
+#content {
+ float: left;
+ width: 410px;
+
+}
+
+/* Post */
+
+.post {
+ padding: 15px 0px;
+ margin-bottom: 20px;
+}
+
+.post .title {
+ margin-bottom: 20px;
+ padding-bottom: 5px;
+}
+
+.post h1 {
+ padding: 0px 0 0 0px;
+ background: url(images/img08.jpg) no-repeat left top;
+ font-size: 24px;
+ color: #FFFFFF;
+}
+
+.post h2 {
+ padding: 0px 0 0 0px;
+ font-size: 22px;
+ color: #FFFFFF;
+}
+
+.post .entry {
+}
+
+.post .meta {
+ padding: 15px 15px 30px 0px;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 11px;
+}
+
+.post .meta p {
+ margin: 0;
+ padding-top: 15px;
+ line-height: normal;
+ color: #FFFFFF;
+}
+
+.post .meta .byline {
+ float: left;
+}
+
+.post .meta .links {
+ float: right;
+}
+
+.post .meta .more {
+ padding: 0 10px 0 18px;
+}
+
+.post .meta .comments {
+}
+
+.post .meta b {
+ display: none;
+}
+
+
+/* Sidebar */
+
+#sidebar {
+ width: 210px;
+ float: right;
+ margin: 0;
+ padding: 0;
+}
+
+#sidebar ul {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+#sidebar li {
+ margin-bottom: 40px;
+}
+
+#sidebar li ul {
+}
+
+#sidebar li li {
+ margin: 0;
+}
+
+#sidebar h2 {
+ width: 250px;
+ padding: 8px 0 0 0px;
+ margin-bottom: 10px;
+ background: url(images/img07.jpg) no-repeat left top;
+ font-size: 20px;
+ color: #FFFFFF;
+}
+
+/* Search */
+
+#search {
+
+}
+
+#search h2 {
+ margin-bottom: 20px;
+}
+
+#s {
+ width: 140px;
+ margin-right: 5px;
+ padding: 3px;
+ border: 1px solid #BED99C;
+}
+
+#x {
+ padding: 3px;
+ border: none;
+ background: #8BD80E;
+ text-transform: lowercase;
+ font-size: 11px;
+ color: #FFFFFF;
+}
+
+/* Boxes */
+
+.box1 {
+ padding: 20px;
+}
+
+.box2 {
+ color: #BABABA;
+}
+
+.box2 h2 {
+ margin-bottom: 15px;
+ font-size: 16px;
+ color: #FFFFFF;
+}
+
+.box2 ul {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+.box2 a:link, .box2 a:hover, .box2 a:active, .box2 a:visited {
+ color: #EDEDED;
+}
+
+/* Footer */
+#footer-wrap {
+}
+
+#footer {
+ margin: 0 auto;
+ padding: 20px 0 10px 0;
+ background: #000000;
+}
+
+html>body #footer {
+ height: auto;
+}
+
+#footer p {
+ font-size: 11px;
+}
+
+#legal {
+ clear: both;
+ padding-top: 17px;
+ text-align: center;
+ color: #FFFFFF;
+}
+
+#legal a {
+ font-weight: normal;
+ color: #FFFFFF;
+}
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/images/img01.gif b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/images/img01.gif
new file mode 100644
index 000000000..5f082bd99
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/images/img01.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/images/img02.gif b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/images/img02.gif
new file mode 100644
index 000000000..45a3ae976
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/images/img02.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/images/img03.gif b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/images/img03.gif
new file mode 100644
index 000000000..d92ea38f9
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/images/img03.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/images/img04.gif b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/images/img04.gif
new file mode 100644
index 000000000..950c4af9d
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/images/img04.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/images/spacer.gif b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/images/spacer.gif
new file mode 100644
index 000000000..5bfd67a2d
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/images/spacer.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/templatelicense.txt b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/templatelicense.txt
new file mode 100644
index 000000000..ccb6b06ab
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/static/templatelicense.txt
@@ -0,0 +1,243 @@
+Creative Commons </>
+
+Creative Commons Legal Code
+
+*Attribution 2.5*
+
+CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
+ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION
+ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE
+INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ITS USE.
+
+/License/
+
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
+COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
+COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
+AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
+TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE
+RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS
+AND CONDITIONS.
+
+*1. Definitions*
+
+ 1. *"Collective Work"* means a work, such as a periodical issue,
+ anthology or encyclopedia, in which the Work in its entirety in
+ unmodified form, along with a number of other contributions,
+ constituting separate and independent works in themselves, are
+ assembled into a collective whole. A work that constitutes a
+ Collective Work will not be considered a Derivative Work (as
+ defined below) for the purposes of this License.
+ 2. *"Derivative Work"* means a work based upon the Work or upon the
+ Work and other pre-existing works, such as a translation, musical
+ arrangement, dramatization, fictionalization, motion picture
+ version, sound recording, art reproduction, abridgment,
+ condensation, or any other form in which the Work may be recast,
+ transformed, or adapted, except that a work that constitutes a
+ Collective Work will not be considered a Derivative Work for the
+ purpose of this License. For the avoidance of doubt, where the
+ Work is a musical composition or sound recording, the
+ synchronization of the Work in timed-relation with a moving image
+ ("synching") will be considered a Derivative Work for the purpose
+ of this License.
+ 3. *"Licensor"* means the individual or entity that offers the Work
+ under the terms of this License.
+ 4. *"Original Author"* means the individual or entity who created the
+ Work.
+ 5. *"Work"* means the copyrightable work of authorship offered under
+ the terms of this License.
+ 6. *"You"* means an individual or entity exercising rights under this
+ License who has not previously violated the terms of this License
+ with respect to the Work, or who has received express permission
+ from the Licensor to exercise rights under this License despite a
+ previous violation.
+
+*2. Fair Use Rights.* Nothing in this license is intended to reduce,
+limit, or restrict any rights arising from fair use, first sale or other
+limitations on the exclusive rights of the copyright owner under
+copyright law or other applicable laws.
+
+*3. License Grant.* Subject to the terms and conditions of this License,
+Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
+perpetual (for the duration of the applicable copyright) license to
+exercise the rights in the Work as stated below:
+
+ 1. to reproduce the Work, to incorporate the Work into one or more
+ Collective Works, and to reproduce the Work as incorporated in the
+ Collective Works;
+ 2. to create and reproduce Derivative Works;
+ 3. to distribute copies or phonorecords of, display publicly, perform
+ publicly, and perform publicly by means of a digital audio
+ transmission the Work including as incorporated in Collective Works;
+ 4. to distribute copies or phonorecords of, display publicly, perform
+ publicly, and perform publicly by means of a digital audio
+ transmission Derivative Works.
+ 5.
+
+ For the avoidance of doubt, where the work is a musical composition:
+
+ 1. *Performance Royalties Under Blanket Licenses*. Licensor
+ waives the exclusive right to collect, whether individually
+ or via a performance rights society (e.g. ASCAP, BMI,
+ SESAC), royalties for the public performance or public
+ digital performance (e.g. webcast) of the Work.
+ 2. *Mechanical Rights and Statutory Royalties*. Licensor waives
+ the exclusive right to collect, whether individually or via
+ a music rights agency or designated agent (e.g. Harry Fox
+ Agency), royalties for any phonorecord You create from the
+ Work ("cover version") and distribute, subject to the
+ compulsory license created by 17 USC Section 115 of the US
+ Copyright Act (or the equivalent in other jurisdictions).
+ 6. *Webcasting Rights and Statutory Royalties*. For the avoidance of
+ doubt, where the Work is a sound recording, Licensor waives the
+ exclusive right to collect, whether individually or via a
+ performance-rights society (e.g. SoundExchange), royalties for the
+ public digital performance (e.g. webcast) of the Work, subject to
+ the compulsory license created by 17 USC Section 114 of the US
+ Copyright Act (or the equivalent in other jurisdictions).
+
+The above rights may be exercised in all media and formats whether now
+known or hereafter devised. The above rights include the right to make
+such modifications as are technically necessary to exercise the rights
+in other media and formats. All rights not expressly granted by Licensor
+are hereby reserved.
+
+*4. Restrictions.*The license granted in Section 3 above is expressly
+made subject to and limited by the following restrictions:
+
+ 1. You may distribute, publicly display, publicly perform, or
+ publicly digitally perform the Work only under the terms of this
+ License, and You must include a copy of, or the Uniform Resource
+ Identifier for, this License with every copy or phonorecord of the
+ Work You distribute, publicly display, publicly perform, or
+ publicly digitally perform. You may not offer or impose any terms
+ on the Work that alter or restrict the terms of this License or
+ the recipients' exercise of the rights granted hereunder. You may
+ not sublicense the Work. You must keep intact all notices that
+ refer to this License and to the disclaimer of warranties. You may
+ not distribute, publicly display, publicly perform, or publicly
+ digitally perform the Work with any technological measures that
+ control access or use of the Work in a manner inconsistent with
+ the terms of this License Agreement. The above applies to the Work
+ as incorporated in a Collective Work, but this does not require
+ the Collective Work apart from the Work itself to be made subject
+ to the terms of this License. If You create a Collective Work,
+ upon notice from any Licensor You must, to the extent practicable,
+ remove from the Collective Work any credit as required by clause
+ 4(b), as requested. If You create a Derivative Work, upon notice
+ from any Licensor You must, to the extent practicable, remove from
+ the Derivative Work any credit as required by clause 4(b), as
+ requested.
+ 2. If you distribute, publicly display, publicly perform, or publicly
+ digitally perform the Work or any Derivative Works or Collective
+ Works, You must keep intact all copyright notices for the Work and
+ provide, reasonable to the medium or means You are utilizing: (i)
+ the name of the Original Author (or pseudonym, if applicable) if
+ supplied, and/or (ii) if the Original Author and/or Licensor
+ designate another party or parties (e.g. a sponsor institute,
+ publishing entity, journal) for attribution in Licensor's
+ copyright notice, terms of service or by other reasonable means,
+ the name of such party or parties; the title of the Work if
+ supplied; to the extent reasonably practicable, the Uniform
+ Resource Identifier, if any, that Licensor specifies to be
+ associated with the Work, unless such URI does not refer to the
+ copyright notice or licensing information for the Work; and in the
+ case of a Derivative Work, a credit identifying the use of the
+ Work in the Derivative Work (e.g., "French translation of the Work
+ by Original Author," or "Screenplay based on original Work by
+ Original Author"). Such credit may be implemented in any
+ reasonable manner; provided, however, that in the case of a
+ Derivative Work or Collective Work, at a minimum such credit will
+ appear where any other comparable authorship credit appears and in
+ a manner at least as prominent as such other comparable authorship
+ credit.
+
+*5. Representations, Warranties and Disclaimer*
+
+UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
+OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
+KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
+INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
+FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
+LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
+WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE
+EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
+
+*6. Limitation on Liability.* EXCEPT TO THE EXTENT REQUIRED BY
+APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL
+THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY
+DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF
+LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+*7. Termination*
+
+ 1. This License and the rights granted hereunder will terminate
+ automatically upon any breach by You of the terms of this License.
+ Individuals or entities who have received Derivative Works or
+ Collective Works from You under this License, however, will not
+ have their licenses terminated provided such individuals or
+ entities remain in full compliance with those licenses. Sections
+ 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
+ 2. Subject to the above terms and conditions, the license granted
+ here is perpetual (for the duration of the applicable copyright in
+ the Work). Notwithstanding the above, Licensor reserves the right
+ to release the Work under different license terms or to stop
+ distributing the Work at any time; provided, however that any such
+ election will not serve to withdraw this License (or any other
+ license that has been, or is required to be, granted under the
+ terms of this License), and this License will continue in full
+ force and effect unless terminated as stated above.
+
+*8. Miscellaneous*
+
+ 1. Each time You distribute or publicly digitally perform the Work or
+ a Collective Work, the Licensor offers to the recipient a license
+ to the Work on the same terms and conditions as the license
+ granted to You under this License.
+ 2. Each time You distribute or publicly digitally perform a
+ Derivative Work, Licensor offers to the recipient a license to the
+ original Work on the same terms and conditions as the license
+ granted to You under this License.
+ 3. If any provision of this License is invalid or unenforceable under
+ applicable law, it shall not affect the validity or enforceability
+ of the remainder of the terms of this License, and without further
+ action by the parties to this agreement, such provision shall be
+ reformed to the minimum extent necessary to make such provision
+ valid and enforceable.
+ 4. No term or provision of this License shall be deemed waived and no
+ breach consented to unless such waiver or consent shall be in
+ writing and signed by the party to be charged with such waiver or
+ consent.
+ 5. This License constitutes the entire agreement between the parties
+ with respect to the Work licensed here. There are no
+ understandings, agreements or representations with respect to the
+ Work not specified here. Licensor shall not be bound by any
+ additional provisions that may appear in any communication from
+ You. This License may not be modified without the mutual written
+ agreement of the Licensor and You.
+
+Creative Commons is not a party to this License, and makes no warranty
+whatsoever in connection with the Work. Creative Commons will not be
+liable to You or any party on any legal theory for any damages
+whatsoever, including without limitation any general, special,
+incidental or consequential damages arising in connection to this
+license. Notwithstanding the foregoing two (2) sentences, if Creative
+Commons has expressly identified itself as the Licensor hereunder, it
+shall have all rights and obligations of Licensor.
+
+Except for the limited purpose of indicating to the public that the Work
+is licensed under the CCPL, neither party will use the trademark
+"Creative Commons" or any related trademark or logo of Creative Commons
+without the prior written consent of Creative Commons. Any permitted use
+will be in compliance with Creative Commons' then-current trademark
+usage guidelines, as may be published on its website or otherwise made
+available upon request from time to time.
+
+Creative Commons may be contacted at http://creativecommons.org/
+<http://creativecommons.org>.
+
+« Back to Commons Deed <./>
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py
new file mode 100644
index 000000000..72f0c89d8
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py
@@ -0,0 +1,24 @@
+import unittest
+from pyramid.configuration import Configurator
+from pyramid import testing
+
+def _initTestingDB():
+ from tutorial.models import initialize_sql
+ session = initialize_sql('sqlite://')
+ return session
+
+class TestMyView(unittest.TestCase):
+ def setUp(self):
+ self.config = Configurator()
+ self.config.begin()
+ _initTestingDB()
+
+ def tearDown(self):
+ self.config.end()
+
+ def test_it(self):
+ from tutorial.views import my_view
+ request = testing.DummyRequest()
+ info = my_view(request)
+ self.assertEqual(info['root'].name, 'root')
+ self.assertEqual(info['project'], 'tutorial')
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/views.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/views.py
new file mode 100644
index 000000000..e550e3257
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/views.py
@@ -0,0 +1,7 @@
+from tutorial.models import DBSession
+from tutorial.models import MyModel
+
+def my_view(request):
+ dbsession = DBSession()
+ root = dbsession.query(MyModel).filter(MyModel.name==u'root').first()
+ return {'root':root, 'project':'tutorial'}
diff --git a/docs/tutorials/wiki2/src/models/CHANGES.txt b/docs/tutorials/wiki2/src/models/CHANGES.txt
new file mode 100644
index 000000000..35a34f332
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/CHANGES.txt
@@ -0,0 +1,4 @@
+0.0
+---
+
+- Initial version
diff --git a/docs/tutorials/wiki2/src/models/README.txt b/docs/tutorials/wiki2/src/models/README.txt
new file mode 100644
index 000000000..d41f7f90f
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/README.txt
@@ -0,0 +1,4 @@
+tutorial README
+
+
+
diff --git a/docs/tutorials/wiki2/src/models/setup.cfg b/docs/tutorials/wiki2/src/models/setup.cfg
new file mode 100644
index 000000000..23b2ad983
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/setup.cfg
@@ -0,0 +1,27 @@
+[nosetests]
+match=^test
+nocapture=1
+cover-package=tutorial
+with-coverage=1
+cover-erase=1
+
+[compile_catalog]
+directory = tutorial/locale
+domain = tutorial
+statistics = true
+
+[extract_messages]
+add_comments = TRANSLATORS:
+output_file = tutorial/locale/tutorial.pot
+width = 80
+
+[init_catalog]
+domain = tutorial
+input_file = tutorial/locale/tutorial.pot
+output_dir = tutorial/locale
+
+[update_catalog]
+domain = tutorial
+input_file = tutorial/locale/tutorial.pot
+output_dir = tutorial/locale
+previous = true
diff --git a/docs/tutorials/wiki2/src/models/setup.py b/docs/tutorials/wiki2/src/models/setup.py
new file mode 100644
index 000000000..aca548b32
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/setup.py
@@ -0,0 +1,45 @@
+import os
+import sys
+
+from setuptools import setup, find_packages
+
+here = os.path.abspath(os.path.dirname(__file__))
+README = open(os.path.join(here, 'README.txt')).read()
+CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
+
+requires = [
+ 'pyramid',
+ 'SQLAlchemy',
+ 'transaction',
+ 'repoze.tm2',
+ 'zope.sqlalchemy',
+ ]
+
+if sys.version_info[:3] < (2,5,0):
+ requires.append('pysqlite')
+
+setup(name='tutorial',
+ version='0.0',
+ description='tutorial',
+ long_description=README + '\n\n' + CHANGES,
+ classifiers=[
+ "Programming Language :: Python",
+ "Framework :: Pylons",
+ "Topic :: Internet :: WWW/HTTP",
+ "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
+ ],
+ author='',
+ author_email='',
+ url='',
+ keywords='web wsgi pylons pyramid bfg',
+ packages=find_packages(),
+ include_package_data=True,
+ zip_safe=False,
+ test_suite='tutorial',
+ install_requires = requires,
+ entry_points = """\
+ [paste.app_factory]
+ app = tutorial.run:app
+ """
+ )
+
diff --git a/docs/tutorials/wiki2/src/models/tutorial.ini b/docs/tutorials/wiki2/src/models/tutorial.ini
new file mode 100644
index 000000000..73b5ed9a4
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial.ini
@@ -0,0 +1,22 @@
+[DEFAULT]
+debug = true
+
+[app:sqlalchemy]
+use = egg:tutorial#app
+reload_templates = true
+debug_authorization = false
+debug_notfound = false
+debug_templates = true
+default_locale_name = en
+db_string = sqlite:///%(here)s/tutorial.db
+db_echo = false
+
+[pipeline:main]
+pipeline =
+ egg:repoze.tm2#tm
+ sqlalchemy
+
+[server:main]
+use = egg:Paste#http
+host = 0.0.0.0
+port = 6543
diff --git a/docs/tutorials/wiki2/src/models/tutorial/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/__init__.py
new file mode 100644
index 000000000..cbdfd3ac6
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/__init__.py
@@ -0,0 +1,2 @@
+# A package
+
diff --git a/docs/tutorials/wiki2/src/models/tutorial/configure.zcml b/docs/tutorials/wiki2/src/models/tutorial/configure.zcml
new file mode 100644
index 000000000..47adfbefb
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/configure.zcml
@@ -0,0 +1,18 @@
+<configure xmlns="http://pylonshq.com/pyramid">
+
+ <!-- this must be included for the view declarations to work -->
+ <include package="pyramid.includes" />
+
+ <route
+ pattern=""
+ name="home"
+ view=".views.my_view"
+ view_renderer="templates/mytemplate.pt"
+ />
+
+ <static
+ name="static"
+ path="templates/static"
+ />
+
+</configure>
diff --git a/docs/tutorials/wiki2/src/models/tutorial/models.py b/docs/tutorials/wiki2/src/models/tutorial/models.py
new file mode 100644
index 000000000..8c3f14915
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/models.py
@@ -0,0 +1,43 @@
+import transaction
+
+from sqlalchemy import create_engine
+from sqlalchemy import Column
+from sqlalchemy import Integer
+from sqlalchemy import Text
+
+from sqlalchemy.exc import IntegrityError
+from sqlalchemy.ext.declarative import declarative_base
+
+from sqlalchemy.orm import scoped_session
+from sqlalchemy.orm import sessionmaker
+
+from zope.sqlalchemy import ZopeTransactionExtension
+
+DBSession = scoped_session(sessionmaker(
+ extension=ZopeTransactionExtension()))
+Base = declarative_base()
+
+class Page(Base):
+ """ The SQLAlchemy declarative model class for a Page object. """
+ __tablename__ = 'pages'
+ id = Column(Integer, primary_key=True)
+ name = Column(Text, unique=True)
+ data = Column(Text)
+
+ def __init__(self, name, data):
+ self.name = name
+ self.data = data
+
+def initialize_sql(db_string, echo=False):
+ engine = create_engine(db_string, echo=echo)
+ DBSession.configure(bind=engine)
+ Base.metadata.bind = engine
+ Base.metadata.create_all(engine)
+ try:
+ session = DBSession()
+ page = Page('FrontPage', 'initial data')
+ session.add(page)
+ transaction.commit()
+ except IntegrityError:
+ # already created
+ pass
diff --git a/docs/tutorials/wiki2/src/models/tutorial/run.py b/docs/tutorials/wiki2/src/models/tutorial/run.py
new file mode 100644
index 000000000..7225987ee
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/run.py
@@ -0,0 +1,24 @@
+from pyramid.configuration import Configurator
+from paste.deploy.converters import asbool
+
+from tutorial.models import initialize_sql
+
+def app(global_config, **settings):
+ """ This function returns a WSGI application.
+
+ It is usually called by the PasteDeploy framework during
+ ``paster serve``.
+ """
+ zcml_file = settings.get('configure_zcml', 'configure.zcml')
+ db_string = settings.get('db_string')
+ if db_string is None:
+ raise ValueError(
+ "No 'db_string' value in application configuration.")
+ db_echo = settings.get('db_echo', 'false')
+ initialize_sql(db_string, asbool(db_echo))
+ config = Configurator(settings=settings)
+ config.begin()
+ config.load_zcml(zcml_file)
+ config.end()
+ return config.make_wsgi_app()
+
diff --git a/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt
new file mode 100644
index 000000000..9178b5866
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt
@@ -0,0 +1,99 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<title>${project} Application</title>
+<meta name="keywords" content="python web application" />
+<meta name="description" content="pyramid web application" />
+<link href="${request.application_url}/static/default.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+<!-- start header -->
+<div id="logo">
+ <h2><code>${project}</code>, a <code>Pyramid</code> application</h2>
+</div>
+<div id="header">
+ <div id="menu">
+ </div>
+</div>
+<!-- end header -->
+<div id="wrapper">
+ <!-- start page -->
+ <div id="page">
+ <!-- start content -->
+ <div id="content">
+ <div class="post">
+ <h1 class="title">Welcome to <code>${project}</code>, an
+ application generated by the <a
+ href="http://pylonshq.com/pyramid">Pyramid</a> web
+ application framework.</h1>
+ </div>
+ </div>
+ <!-- end content -->
+ <!-- start sidebar -->
+ <div id="sidebar">
+ <ul>
+ <li id="search">
+ <h2>Search<br/> <code>Pyramid</code> Documentation</h2>
+ <form method="get"
+ action="http://pylonshq.com/docs/pyramid/current/searchresults">
+ <fieldset>
+ <input type="text" id="q" name="text" value="" />
+ <input type="submit" id="x" value="Search" />
+ </fieldset>
+ </form>
+ </li>
+ <li>
+ <h2><code>Pyramid</code> links</h2>
+ <ul>
+ <li><a
+ href="http://pylonshq.com/docs/pyramid/current/#narrative-documentation">Narrative
+ Documentation</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#api-documentation">API
+ Documentation</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#tutorials">Tutorials</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#change-history">Change
+ History</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#sample-applications">Sample
+ Applications</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#support-and-development">Support
+ and Development</a>
+ </li>
+ <li>
+ <a
+ href="irc://irc.freenode.net#pylons">IRC Channel</a>
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+ <!-- end sidebar -->
+ <div style="clear: both;">&nbsp;</div>
+ </div>
+</div>
+<!-- end page -->
+<!-- start footer -->
+<div id="footer">
+ <p id="legal">( c ) 2008. All Rights Reserved. Template design
+ by <a href="http://www.freecsstemplates.org/">Free CSS
+ Templates</a>.</p>
+</div>
+<!-- end footer -->
+</body>
+</html>
diff --git a/docs/tutorials/wiki2/src/models/tutorial/templates/static/default.css b/docs/tutorials/wiki2/src/models/tutorial/templates/static/default.css
new file mode 100644
index 000000000..41b3debde
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/templates/static/default.css
@@ -0,0 +1,380 @@
+/*
+Design by Free CSS Templates
+http://www.freecsstemplates.org
+Released for free under a Creative Commons Attribution 2.5 License
+*/
+
+body {
+ margin: 0;
+ padding: 0;
+ background: url(images/img01.gif) repeat-x left top;
+ font-size: 13px;
+ font-family: "Trebuchet MS", Georgia, "Times New Roman", Times, serif;
+ text-align: justify;
+ color: #FFFFFF;
+}
+
+h1, h2, h3 {
+ margin: 0;
+ text-transform: lowercase;
+ font-weight: normal;
+ color: #FFFFFF;
+}
+
+h1 {
+ letter-spacing: -1px;
+ font-size: 32px;
+}
+
+h2 {
+ font-size: 23px;
+}
+
+p, ul, ol {
+ margin: 0 0 2em 0;
+ text-align: justify;
+ line-height: 26px;
+}
+
+a:link {
+ color: #8BD80E;
+}
+
+a:hover, a:active {
+ text-decoration: none;
+ color: #8BD80E;
+}
+
+a:visited {
+ color: #8BD80E;
+}
+
+img {
+ border: none;
+}
+
+img.left {
+ float: left;
+ margin-right: 15px;
+}
+
+img.right {
+ float: right;
+ margin-left: 15px;
+}
+
+/* Form */
+
+form {
+ margin: 0;
+ padding: 0;
+}
+
+fieldset {
+ margin: 0;
+ padding: 0;
+ border: none;
+}
+
+legend {
+ display: none;
+}
+
+input, textarea, select {
+ font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
+ font-size: 13px;
+ color: #333333;
+}
+
+#wrapper {
+ margin: 0;
+ padding: 0;
+ background: #000000;
+}
+
+/* Header */
+
+#header {
+ width: 713px;
+ margin: 0 auto;
+ height: 42px;
+}
+
+/* Menu */
+
+#menu {
+ float: left;
+ width: 713px;
+ height: 50px;
+ background: url(images/img02.gif) no-repeat left top;
+}
+
+#menu ul {
+ margin: 0;
+ padding: 0px 0 0 10px;
+ list-style: none;
+ line-height: normal;
+}
+
+#menu li {
+ display: block;
+ float: left;
+}
+
+#menu a {
+ display: block;
+ float: left;
+ background: url(images/img04.gif) no-repeat right 55%;
+ margin-top: 5px;
+ margin-right: 3px;
+ padding: 8px 17px;
+ text-decoration: none;
+ font-size: 13px;
+ color: #000000;
+}
+
+#menu a:hover {
+ color: #000000;
+}
+
+#menu .current_page_item a {
+ color: #000000;
+}
+
+/** LOGO */
+
+#logo {
+ width: 713px;
+ height: 80px;
+ margin: 0 auto;
+}
+
+#logo h1, #logo h2 {
+ float: left;
+ margin: 0;
+ padding: 30px 0 0 0px;
+ line-height: normal;
+}
+
+#logo h1 {
+ font-family: Georgia, "Times New Roman", Times, serif;
+ font-size:40px;
+}
+
+#logo h1 a {
+ text-decoration: none;
+ color: #4C4C4C;
+}
+
+#logo h1 a:hover { text-decoration: underline; }
+
+#logo h2 {
+ float: left;
+ padding: 45px 0 0 18px;
+ font: 18px Georgia, "Times New Roman", Times, serif;
+ color: #8BD80E;
+}
+
+#logo p a {
+ text-decoration: none;
+ color: #8BD80E;
+}
+
+#logo p a:hover { text-decoration: underline; }
+
+
+
+/* Page */
+
+#page {
+ width: 663px;
+ margin: 0 auto;
+ background: #4C4C4C url(images/img03.gif) no-repeat left bottom;
+ padding: 0 25px;
+}
+
+/* Content */
+
+#content {
+ float: left;
+ width: 410px;
+
+}
+
+/* Post */
+
+.post {
+ padding: 15px 0px;
+ margin-bottom: 20px;
+}
+
+.post .title {
+ margin-bottom: 20px;
+ padding-bottom: 5px;
+}
+
+.post h1 {
+ padding: 0px 0 0 0px;
+ background: url(images/img08.jpg) no-repeat left top;
+ font-size: 24px;
+ color: #FFFFFF;
+}
+
+.post h2 {
+ padding: 0px 0 0 0px;
+ font-size: 22px;
+ color: #FFFFFF;
+}
+
+.post .entry {
+}
+
+.post .meta {
+ padding: 15px 15px 30px 0px;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 11px;
+}
+
+.post .meta p {
+ margin: 0;
+ padding-top: 15px;
+ line-height: normal;
+ color: #FFFFFF;
+}
+
+.post .meta .byline {
+ float: left;
+}
+
+.post .meta .links {
+ float: right;
+}
+
+.post .meta .more {
+ padding: 0 10px 0 18px;
+}
+
+.post .meta .comments {
+}
+
+.post .meta b {
+ display: none;
+}
+
+
+/* Sidebar */
+
+#sidebar {
+ width: 210px;
+ float: right;
+ margin: 0;
+ padding: 0;
+}
+
+#sidebar ul {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+#sidebar li {
+ margin-bottom: 40px;
+}
+
+#sidebar li ul {
+}
+
+#sidebar li li {
+ margin: 0;
+}
+
+#sidebar h2 {
+ width: 250px;
+ padding: 8px 0 0 0px;
+ margin-bottom: 10px;
+ background: url(images/img07.jpg) no-repeat left top;
+ font-size: 20px;
+ color: #FFFFFF;
+}
+
+/* Search */
+
+#search {
+
+}
+
+#search h2 {
+ margin-bottom: 20px;
+}
+
+#s {
+ width: 140px;
+ margin-right: 5px;
+ padding: 3px;
+ border: 1px solid #BED99C;
+}
+
+#x {
+ padding: 3px;
+ border: none;
+ background: #8BD80E;
+ text-transform: lowercase;
+ font-size: 11px;
+ color: #FFFFFF;
+}
+
+/* Boxes */
+
+.box1 {
+ padding: 20px;
+}
+
+.box2 {
+ color: #BABABA;
+}
+
+.box2 h2 {
+ margin-bottom: 15px;
+ font-size: 16px;
+ color: #FFFFFF;
+}
+
+.box2 ul {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+.box2 a:link, .box2 a:hover, .box2 a:active, .box2 a:visited {
+ color: #EDEDED;
+}
+
+/* Footer */
+#footer-wrap {
+}
+
+#footer {
+ margin: 0 auto;
+ padding: 20px 0 10px 0;
+ background: #000000;
+}
+
+html>body #footer {
+ height: auto;
+}
+
+#footer p {
+ font-size: 11px;
+}
+
+#legal {
+ clear: both;
+ padding-top: 17px;
+ text-align: center;
+ color: #FFFFFF;
+}
+
+#legal a {
+ font-weight: normal;
+ color: #FFFFFF;
+}
diff --git a/docs/tutorials/wiki2/src/models/tutorial/templates/static/images/img01.gif b/docs/tutorials/wiki2/src/models/tutorial/templates/static/images/img01.gif
new file mode 100644
index 000000000..5f082bd99
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/templates/static/images/img01.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/models/tutorial/templates/static/images/img02.gif b/docs/tutorials/wiki2/src/models/tutorial/templates/static/images/img02.gif
new file mode 100644
index 000000000..45a3ae976
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/templates/static/images/img02.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/models/tutorial/templates/static/images/img03.gif b/docs/tutorials/wiki2/src/models/tutorial/templates/static/images/img03.gif
new file mode 100644
index 000000000..d92ea38f9
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/templates/static/images/img03.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/models/tutorial/templates/static/images/img04.gif b/docs/tutorials/wiki2/src/models/tutorial/templates/static/images/img04.gif
new file mode 100644
index 000000000..950c4af9d
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/templates/static/images/img04.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/models/tutorial/templates/static/images/spacer.gif b/docs/tutorials/wiki2/src/models/tutorial/templates/static/images/spacer.gif
new file mode 100644
index 000000000..5bfd67a2d
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/templates/static/images/spacer.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/models/tutorial/templates/static/templatelicense.txt b/docs/tutorials/wiki2/src/models/tutorial/templates/static/templatelicense.txt
new file mode 100644
index 000000000..ccb6b06ab
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/templates/static/templatelicense.txt
@@ -0,0 +1,243 @@
+Creative Commons </>
+
+Creative Commons Legal Code
+
+*Attribution 2.5*
+
+CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
+ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION
+ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE
+INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ITS USE.
+
+/License/
+
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
+COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
+COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
+AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
+TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE
+RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS
+AND CONDITIONS.
+
+*1. Definitions*
+
+ 1. *"Collective Work"* means a work, such as a periodical issue,
+ anthology or encyclopedia, in which the Work in its entirety in
+ unmodified form, along with a number of other contributions,
+ constituting separate and independent works in themselves, are
+ assembled into a collective whole. A work that constitutes a
+ Collective Work will not be considered a Derivative Work (as
+ defined below) for the purposes of this License.
+ 2. *"Derivative Work"* means a work based upon the Work or upon the
+ Work and other pre-existing works, such as a translation, musical
+ arrangement, dramatization, fictionalization, motion picture
+ version, sound recording, art reproduction, abridgment,
+ condensation, or any other form in which the Work may be recast,
+ transformed, or adapted, except that a work that constitutes a
+ Collective Work will not be considered a Derivative Work for the
+ purpose of this License. For the avoidance of doubt, where the
+ Work is a musical composition or sound recording, the
+ synchronization of the Work in timed-relation with a moving image
+ ("synching") will be considered a Derivative Work for the purpose
+ of this License.
+ 3. *"Licensor"* means the individual or entity that offers the Work
+ under the terms of this License.
+ 4. *"Original Author"* means the individual or entity who created the
+ Work.
+ 5. *"Work"* means the copyrightable work of authorship offered under
+ the terms of this License.
+ 6. *"You"* means an individual or entity exercising rights under this
+ License who has not previously violated the terms of this License
+ with respect to the Work, or who has received express permission
+ from the Licensor to exercise rights under this License despite a
+ previous violation.
+
+*2. Fair Use Rights.* Nothing in this license is intended to reduce,
+limit, or restrict any rights arising from fair use, first sale or other
+limitations on the exclusive rights of the copyright owner under
+copyright law or other applicable laws.
+
+*3. License Grant.* Subject to the terms and conditions of this License,
+Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
+perpetual (for the duration of the applicable copyright) license to
+exercise the rights in the Work as stated below:
+
+ 1. to reproduce the Work, to incorporate the Work into one or more
+ Collective Works, and to reproduce the Work as incorporated in the
+ Collective Works;
+ 2. to create and reproduce Derivative Works;
+ 3. to distribute copies or phonorecords of, display publicly, perform
+ publicly, and perform publicly by means of a digital audio
+ transmission the Work including as incorporated in Collective Works;
+ 4. to distribute copies or phonorecords of, display publicly, perform
+ publicly, and perform publicly by means of a digital audio
+ transmission Derivative Works.
+ 5.
+
+ For the avoidance of doubt, where the work is a musical composition:
+
+ 1. *Performance Royalties Under Blanket Licenses*. Licensor
+ waives the exclusive right to collect, whether individually
+ or via a performance rights society (e.g. ASCAP, BMI,
+ SESAC), royalties for the public performance or public
+ digital performance (e.g. webcast) of the Work.
+ 2. *Mechanical Rights and Statutory Royalties*. Licensor waives
+ the exclusive right to collect, whether individually or via
+ a music rights agency or designated agent (e.g. Harry Fox
+ Agency), royalties for any phonorecord You create from the
+ Work ("cover version") and distribute, subject to the
+ compulsory license created by 17 USC Section 115 of the US
+ Copyright Act (or the equivalent in other jurisdictions).
+ 6. *Webcasting Rights and Statutory Royalties*. For the avoidance of
+ doubt, where the Work is a sound recording, Licensor waives the
+ exclusive right to collect, whether individually or via a
+ performance-rights society (e.g. SoundExchange), royalties for the
+ public digital performance (e.g. webcast) of the Work, subject to
+ the compulsory license created by 17 USC Section 114 of the US
+ Copyright Act (or the equivalent in other jurisdictions).
+
+The above rights may be exercised in all media and formats whether now
+known or hereafter devised. The above rights include the right to make
+such modifications as are technically necessary to exercise the rights
+in other media and formats. All rights not expressly granted by Licensor
+are hereby reserved.
+
+*4. Restrictions.*The license granted in Section 3 above is expressly
+made subject to and limited by the following restrictions:
+
+ 1. You may distribute, publicly display, publicly perform, or
+ publicly digitally perform the Work only under the terms of this
+ License, and You must include a copy of, or the Uniform Resource
+ Identifier for, this License with every copy or phonorecord of the
+ Work You distribute, publicly display, publicly perform, or
+ publicly digitally perform. You may not offer or impose any terms
+ on the Work that alter or restrict the terms of this License or
+ the recipients' exercise of the rights granted hereunder. You may
+ not sublicense the Work. You must keep intact all notices that
+ refer to this License and to the disclaimer of warranties. You may
+ not distribute, publicly display, publicly perform, or publicly
+ digitally perform the Work with any technological measures that
+ control access or use of the Work in a manner inconsistent with
+ the terms of this License Agreement. The above applies to the Work
+ as incorporated in a Collective Work, but this does not require
+ the Collective Work apart from the Work itself to be made subject
+ to the terms of this License. If You create a Collective Work,
+ upon notice from any Licensor You must, to the extent practicable,
+ remove from the Collective Work any credit as required by clause
+ 4(b), as requested. If You create a Derivative Work, upon notice
+ from any Licensor You must, to the extent practicable, remove from
+ the Derivative Work any credit as required by clause 4(b), as
+ requested.
+ 2. If you distribute, publicly display, publicly perform, or publicly
+ digitally perform the Work or any Derivative Works or Collective
+ Works, You must keep intact all copyright notices for the Work and
+ provide, reasonable to the medium or means You are utilizing: (i)
+ the name of the Original Author (or pseudonym, if applicable) if
+ supplied, and/or (ii) if the Original Author and/or Licensor
+ designate another party or parties (e.g. a sponsor institute,
+ publishing entity, journal) for attribution in Licensor's
+ copyright notice, terms of service or by other reasonable means,
+ the name of such party or parties; the title of the Work if
+ supplied; to the extent reasonably practicable, the Uniform
+ Resource Identifier, if any, that Licensor specifies to be
+ associated with the Work, unless such URI does not refer to the
+ copyright notice or licensing information for the Work; and in the
+ case of a Derivative Work, a credit identifying the use of the
+ Work in the Derivative Work (e.g., "French translation of the Work
+ by Original Author," or "Screenplay based on original Work by
+ Original Author"). Such credit may be implemented in any
+ reasonable manner; provided, however, that in the case of a
+ Derivative Work or Collective Work, at a minimum such credit will
+ appear where any other comparable authorship credit appears and in
+ a manner at least as prominent as such other comparable authorship
+ credit.
+
+*5. Representations, Warranties and Disclaimer*
+
+UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
+OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
+KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
+INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
+FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
+LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
+WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE
+EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
+
+*6. Limitation on Liability.* EXCEPT TO THE EXTENT REQUIRED BY
+APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL
+THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY
+DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF
+LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+*7. Termination*
+
+ 1. This License and the rights granted hereunder will terminate
+ automatically upon any breach by You of the terms of this License.
+ Individuals or entities who have received Derivative Works or
+ Collective Works from You under this License, however, will not
+ have their licenses terminated provided such individuals or
+ entities remain in full compliance with those licenses. Sections
+ 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
+ 2. Subject to the above terms and conditions, the license granted
+ here is perpetual (for the duration of the applicable copyright in
+ the Work). Notwithstanding the above, Licensor reserves the right
+ to release the Work under different license terms or to stop
+ distributing the Work at any time; provided, however that any such
+ election will not serve to withdraw this License (or any other
+ license that has been, or is required to be, granted under the
+ terms of this License), and this License will continue in full
+ force and effect unless terminated as stated above.
+
+*8. Miscellaneous*
+
+ 1. Each time You distribute or publicly digitally perform the Work or
+ a Collective Work, the Licensor offers to the recipient a license
+ to the Work on the same terms and conditions as the license
+ granted to You under this License.
+ 2. Each time You distribute or publicly digitally perform a
+ Derivative Work, Licensor offers to the recipient a license to the
+ original Work on the same terms and conditions as the license
+ granted to You under this License.
+ 3. If any provision of this License is invalid or unenforceable under
+ applicable law, it shall not affect the validity or enforceability
+ of the remainder of the terms of this License, and without further
+ action by the parties to this agreement, such provision shall be
+ reformed to the minimum extent necessary to make such provision
+ valid and enforceable.
+ 4. No term or provision of this License shall be deemed waived and no
+ breach consented to unless such waiver or consent shall be in
+ writing and signed by the party to be charged with such waiver or
+ consent.
+ 5. This License constitutes the entire agreement between the parties
+ with respect to the Work licensed here. There are no
+ understandings, agreements or representations with respect to the
+ Work not specified here. Licensor shall not be bound by any
+ additional provisions that may appear in any communication from
+ You. This License may not be modified without the mutual written
+ agreement of the Licensor and You.
+
+Creative Commons is not a party to this License, and makes no warranty
+whatsoever in connection with the Work. Creative Commons will not be
+liable to You or any party on any legal theory for any damages
+whatsoever, including without limitation any general, special,
+incidental or consequential damages arising in connection to this
+license. Notwithstanding the foregoing two (2) sentences, if Creative
+Commons has expressly identified itself as the Licensor hereunder, it
+shall have all rights and obligations of Licensor.
+
+Except for the limited purpose of indicating to the public that the Work
+is licensed under the CCPL, neither party will use the trademark
+"Creative Commons" or any related trademark or logo of Creative Commons
+without the prior written consent of Creative Commons. Any permitted use
+will be in compliance with Creative Commons' then-current trademark
+usage guidelines, as may be published on its website or otherwise made
+available upon request from time to time.
+
+Creative Commons may be contacted at http://creativecommons.org/
+<http://creativecommons.org>.
+
+« Back to Commons Deed <./>
diff --git a/docs/tutorials/wiki2/src/models/tutorial/tests.py b/docs/tutorials/wiki2/src/models/tutorial/tests.py
new file mode 100644
index 000000000..72f0c89d8
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/tests.py
@@ -0,0 +1,24 @@
+import unittest
+from pyramid.configuration import Configurator
+from pyramid import testing
+
+def _initTestingDB():
+ from tutorial.models import initialize_sql
+ session = initialize_sql('sqlite://')
+ return session
+
+class TestMyView(unittest.TestCase):
+ def setUp(self):
+ self.config = Configurator()
+ self.config.begin()
+ _initTestingDB()
+
+ def tearDown(self):
+ self.config.end()
+
+ def test_it(self):
+ from tutorial.views import my_view
+ request = testing.DummyRequest()
+ info = my_view(request)
+ self.assertEqual(info['root'].name, 'root')
+ self.assertEqual(info['project'], 'tutorial')
diff --git a/docs/tutorials/wiki2/src/models/tutorial/views.py b/docs/tutorials/wiki2/src/models/tutorial/views.py
new file mode 100644
index 000000000..e550e3257
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/views.py
@@ -0,0 +1,7 @@
+from tutorial.models import DBSession
+from tutorial.models import MyModel
+
+def my_view(request):
+ dbsession = DBSession()
+ root = dbsession.query(MyModel).filter(MyModel.name==u'root').first()
+ return {'root':root, 'project':'tutorial'}
diff --git a/docs/tutorials/wiki2/src/views/CHANGES.txt b/docs/tutorials/wiki2/src/views/CHANGES.txt
new file mode 100644
index 000000000..35a34f332
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/CHANGES.txt
@@ -0,0 +1,4 @@
+0.0
+---
+
+- Initial version
diff --git a/docs/tutorials/wiki2/src/views/README.txt b/docs/tutorials/wiki2/src/views/README.txt
new file mode 100644
index 000000000..d41f7f90f
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/README.txt
@@ -0,0 +1,4 @@
+tutorial README
+
+
+
diff --git a/docs/tutorials/wiki2/src/views/setup.cfg b/docs/tutorials/wiki2/src/views/setup.cfg
new file mode 100644
index 000000000..23b2ad983
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/setup.cfg
@@ -0,0 +1,27 @@
+[nosetests]
+match=^test
+nocapture=1
+cover-package=tutorial
+with-coverage=1
+cover-erase=1
+
+[compile_catalog]
+directory = tutorial/locale
+domain = tutorial
+statistics = true
+
+[extract_messages]
+add_comments = TRANSLATORS:
+output_file = tutorial/locale/tutorial.pot
+width = 80
+
+[init_catalog]
+domain = tutorial
+input_file = tutorial/locale/tutorial.pot
+output_dir = tutorial/locale
+
+[update_catalog]
+domain = tutorial
+input_file = tutorial/locale/tutorial.pot
+output_dir = tutorial/locale
+previous = true
diff --git a/docs/tutorials/wiki2/src/views/setup.py b/docs/tutorials/wiki2/src/views/setup.py
new file mode 100644
index 000000000..eeac5c397
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/setup.py
@@ -0,0 +1,46 @@
+import os
+import sys
+
+from setuptools import setup, find_packages
+
+here = os.path.abspath(os.path.dirname(__file__))
+README = open(os.path.join(here, 'README.txt')).read()
+CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
+
+requires = [
+ 'pyramid',
+ 'SQLAlchemy',
+ 'transaction',
+ 'repoze.tm2',
+ 'zope.sqlalchemy',
+ 'docutils'
+ ]
+
+if sys.version_info[:3] < (2,5,0):
+ requires.append('pysqlite')
+
+setup(name='tutorial',
+ version='0.0',
+ description='tutorial',
+ long_description=README + '\n\n' + CHANGES,
+ classifiers=[
+ "Programming Language :: Python",
+ "Framework :: Pylons",
+ "Topic :: Internet :: WWW/HTTP",
+ "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
+ ],
+ author='',
+ author_email='',
+ url='',
+ keywords='web wsgi pylons pyramid bfg',
+ packages=find_packages(),
+ include_package_data=True,
+ zip_safe=False,
+ test_suite='tutorial',
+ install_requires = requires,
+ entry_points = """\
+ [paste.app_factory]
+ app = tutorial.run:app
+ """
+ )
+
diff --git a/docs/tutorials/wiki2/src/views/tutorial.ini b/docs/tutorials/wiki2/src/views/tutorial.ini
new file mode 100644
index 000000000..85f131c2e
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial.ini
@@ -0,0 +1,23 @@
+[DEFAULT]
+debug = true
+
+[app:sqlalchemy]
+use = egg:tutorial#app
+reload_templates = true
+debug_authorization = false
+debug_notfound = false
+debug_templates = true
+default_locale_name = en
+db_string = sqlite:///%(here)s/tutorial.db
+db_echo = false
+
+[pipeline:main]
+pipeline =
+ egg:Paste#evalerror
+ egg:repoze.tm2#tm
+ sqlalchemy
+
+[server:main]
+use = egg:Paste#http
+host = 0.0.0.0
+port = 6543
diff --git a/docs/tutorials/wiki2/src/views/tutorial/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/__init__.py
new file mode 100644
index 000000000..cbdfd3ac6
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/__init__.py
@@ -0,0 +1,2 @@
+# A package
+
diff --git a/docs/tutorials/wiki2/src/views/tutorial/configure.zcml b/docs/tutorials/wiki2/src/views/tutorial/configure.zcml
new file mode 100644
index 000000000..3a2e13a7a
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/configure.zcml
@@ -0,0 +1,38 @@
+<configure xmlns="http://pylonshq.com/pyramid">
+
+ <!-- this must be included for the view declarations to work -->
+ <include package="pyramid.includes" />
+
+ <static
+ path="templates/static"
+ name="static"
+ />
+
+ <route
+ pattern=""
+ name="view_wiki"
+ view=".views.view_wiki"
+ />
+
+ <route
+ pattern=":pagename"
+ name="view_page"
+ view=".views.view_page"
+ view_renderer="templates/view.pt"
+ />
+
+ <route
+ pattern="add_page/:pagename"
+ name="add_page"
+ view=".views.add_page"
+ view_renderer="templates/edit.pt"
+ />
+
+ <route
+ pattern=":pagename/edit_page"
+ name="edit_page"
+ view=".views.edit_page"
+ view_renderer="templates/edit.pt"
+ />
+
+</configure>
diff --git a/docs/tutorials/wiki2/src/views/tutorial/models.py b/docs/tutorials/wiki2/src/views/tutorial/models.py
new file mode 100644
index 000000000..8c3f14915
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/models.py
@@ -0,0 +1,43 @@
+import transaction
+
+from sqlalchemy import create_engine
+from sqlalchemy import Column
+from sqlalchemy import Integer
+from sqlalchemy import Text
+
+from sqlalchemy.exc import IntegrityError
+from sqlalchemy.ext.declarative import declarative_base
+
+from sqlalchemy.orm import scoped_session
+from sqlalchemy.orm import sessionmaker
+
+from zope.sqlalchemy import ZopeTransactionExtension
+
+DBSession = scoped_session(sessionmaker(
+ extension=ZopeTransactionExtension()))
+Base = declarative_base()
+
+class Page(Base):
+ """ The SQLAlchemy declarative model class for a Page object. """
+ __tablename__ = 'pages'
+ id = Column(Integer, primary_key=True)
+ name = Column(Text, unique=True)
+ data = Column(Text)
+
+ def __init__(self, name, data):
+ self.name = name
+ self.data = data
+
+def initialize_sql(db_string, echo=False):
+ engine = create_engine(db_string, echo=echo)
+ DBSession.configure(bind=engine)
+ Base.metadata.bind = engine
+ Base.metadata.create_all(engine)
+ try:
+ session = DBSession()
+ page = Page('FrontPage', 'initial data')
+ session.add(page)
+ transaction.commit()
+ except IntegrityError:
+ # already created
+ pass
diff --git a/docs/tutorials/wiki2/src/views/tutorial/run.py b/docs/tutorials/wiki2/src/views/tutorial/run.py
new file mode 100644
index 000000000..7225987ee
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/run.py
@@ -0,0 +1,24 @@
+from pyramid.configuration import Configurator
+from paste.deploy.converters import asbool
+
+from tutorial.models import initialize_sql
+
+def app(global_config, **settings):
+ """ This function returns a WSGI application.
+
+ It is usually called by the PasteDeploy framework during
+ ``paster serve``.
+ """
+ zcml_file = settings.get('configure_zcml', 'configure.zcml')
+ db_string = settings.get('db_string')
+ if db_string is None:
+ raise ValueError(
+ "No 'db_string' value in application configuration.")
+ db_echo = settings.get('db_echo', 'false')
+ initialize_sql(db_string, asbool(db_echo))
+ config = Configurator(settings=settings)
+ config.begin()
+ config.load_zcml(zcml_file)
+ config.end()
+ return config.make_wsgi_app()
+
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.pt b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.pt
new file mode 100644
index 000000000..047a64eb3
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.pt
@@ -0,0 +1,32 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal">
+
+<head>
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
+ <title>Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)
+ Editing: ${page.name}</title>
+ <link rel="stylesheet" type="text/css"
+ href="${request.application_url}/static/style.css" />
+</head>
+
+<body>
+
+<div class="main_content">
+ <div style="float:right; width: 10em;"> Viewing
+ <span tal:replace="page.name">Page Name Goes Here</span> <br/>
+ You can return to the <a href="${request.application_url}"
+ >FrontPage</a>.
+ </div>
+
+ <div>
+ <form action="${save_url}" method="post">
+ <textarea name="body" tal:content="page.data" rows="10" cols="60"/>
+ <input type="submit" name="form.submitted" value="Save"/>
+ </form>
+ </div>
+</div>
+</body>
+</html>
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt
new file mode 100644
index 000000000..9178b5866
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt
@@ -0,0 +1,99 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<title>${project} Application</title>
+<meta name="keywords" content="python web application" />
+<meta name="description" content="pyramid web application" />
+<link href="${request.application_url}/static/default.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+<!-- start header -->
+<div id="logo">
+ <h2><code>${project}</code>, a <code>Pyramid</code> application</h2>
+</div>
+<div id="header">
+ <div id="menu">
+ </div>
+</div>
+<!-- end header -->
+<div id="wrapper">
+ <!-- start page -->
+ <div id="page">
+ <!-- start content -->
+ <div id="content">
+ <div class="post">
+ <h1 class="title">Welcome to <code>${project}</code>, an
+ application generated by the <a
+ href="http://pylonshq.com/pyramid">Pyramid</a> web
+ application framework.</h1>
+ </div>
+ </div>
+ <!-- end content -->
+ <!-- start sidebar -->
+ <div id="sidebar">
+ <ul>
+ <li id="search">
+ <h2>Search<br/> <code>Pyramid</code> Documentation</h2>
+ <form method="get"
+ action="http://pylonshq.com/docs/pyramid/current/searchresults">
+ <fieldset>
+ <input type="text" id="q" name="text" value="" />
+ <input type="submit" id="x" value="Search" />
+ </fieldset>
+ </form>
+ </li>
+ <li>
+ <h2><code>Pyramid</code> links</h2>
+ <ul>
+ <li><a
+ href="http://pylonshq.com/docs/pyramid/current/#narrative-documentation">Narrative
+ Documentation</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#api-documentation">API
+ Documentation</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#tutorials">Tutorials</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#change-history">Change
+ History</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#sample-applications">Sample
+ Applications</a>
+ </li>
+ <li>
+ <a
+ href="http://pylonshq.com/docs/pyramid/current/#support-and-development">Support
+ and Development</a>
+ </li>
+ <li>
+ <a
+ href="irc://irc.freenode.net#pylons">IRC Channel</a>
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+ <!-- end sidebar -->
+ <div style="clear: both;">&nbsp;</div>
+ </div>
+</div>
+<!-- end page -->
+<!-- start footer -->
+<div id="footer">
+ <p id="legal">( c ) 2008. All Rights Reserved. Template design
+ by <a href="http://www.freecsstemplates.org/">Free CSS
+ Templates</a>.</p>
+</div>
+<!-- end footer -->
+</body>
+</html>
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/static/default.css b/docs/tutorials/wiki2/src/views/tutorial/templates/static/default.css
new file mode 100644
index 000000000..41b3debde
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/static/default.css
@@ -0,0 +1,380 @@
+/*
+Design by Free CSS Templates
+http://www.freecsstemplates.org
+Released for free under a Creative Commons Attribution 2.5 License
+*/
+
+body {
+ margin: 0;
+ padding: 0;
+ background: url(images/img01.gif) repeat-x left top;
+ font-size: 13px;
+ font-family: "Trebuchet MS", Georgia, "Times New Roman", Times, serif;
+ text-align: justify;
+ color: #FFFFFF;
+}
+
+h1, h2, h3 {
+ margin: 0;
+ text-transform: lowercase;
+ font-weight: normal;
+ color: #FFFFFF;
+}
+
+h1 {
+ letter-spacing: -1px;
+ font-size: 32px;
+}
+
+h2 {
+ font-size: 23px;
+}
+
+p, ul, ol {
+ margin: 0 0 2em 0;
+ text-align: justify;
+ line-height: 26px;
+}
+
+a:link {
+ color: #8BD80E;
+}
+
+a:hover, a:active {
+ text-decoration: none;
+ color: #8BD80E;
+}
+
+a:visited {
+ color: #8BD80E;
+}
+
+img {
+ border: none;
+}
+
+img.left {
+ float: left;
+ margin-right: 15px;
+}
+
+img.right {
+ float: right;
+ margin-left: 15px;
+}
+
+/* Form */
+
+form {
+ margin: 0;
+ padding: 0;
+}
+
+fieldset {
+ margin: 0;
+ padding: 0;
+ border: none;
+}
+
+legend {
+ display: none;
+}
+
+input, textarea, select {
+ font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
+ font-size: 13px;
+ color: #333333;
+}
+
+#wrapper {
+ margin: 0;
+ padding: 0;
+ background: #000000;
+}
+
+/* Header */
+
+#header {
+ width: 713px;
+ margin: 0 auto;
+ height: 42px;
+}
+
+/* Menu */
+
+#menu {
+ float: left;
+ width: 713px;
+ height: 50px;
+ background: url(images/img02.gif) no-repeat left top;
+}
+
+#menu ul {
+ margin: 0;
+ padding: 0px 0 0 10px;
+ list-style: none;
+ line-height: normal;
+}
+
+#menu li {
+ display: block;
+ float: left;
+}
+
+#menu a {
+ display: block;
+ float: left;
+ background: url(images/img04.gif) no-repeat right 55%;
+ margin-top: 5px;
+ margin-right: 3px;
+ padding: 8px 17px;
+ text-decoration: none;
+ font-size: 13px;
+ color: #000000;
+}
+
+#menu a:hover {
+ color: #000000;
+}
+
+#menu .current_page_item a {
+ color: #000000;
+}
+
+/** LOGO */
+
+#logo {
+ width: 713px;
+ height: 80px;
+ margin: 0 auto;
+}
+
+#logo h1, #logo h2 {
+ float: left;
+ margin: 0;
+ padding: 30px 0 0 0px;
+ line-height: normal;
+}
+
+#logo h1 {
+ font-family: Georgia, "Times New Roman", Times, serif;
+ font-size:40px;
+}
+
+#logo h1 a {
+ text-decoration: none;
+ color: #4C4C4C;
+}
+
+#logo h1 a:hover { text-decoration: underline; }
+
+#logo h2 {
+ float: left;
+ padding: 45px 0 0 18px;
+ font: 18px Georgia, "Times New Roman", Times, serif;
+ color: #8BD80E;
+}
+
+#logo p a {
+ text-decoration: none;
+ color: #8BD80E;
+}
+
+#logo p a:hover { text-decoration: underline; }
+
+
+
+/* Page */
+
+#page {
+ width: 663px;
+ margin: 0 auto;
+ background: #4C4C4C url(images/img03.gif) no-repeat left bottom;
+ padding: 0 25px;
+}
+
+/* Content */
+
+#content {
+ float: left;
+ width: 410px;
+
+}
+
+/* Post */
+
+.post {
+ padding: 15px 0px;
+ margin-bottom: 20px;
+}
+
+.post .title {
+ margin-bottom: 20px;
+ padding-bottom: 5px;
+}
+
+.post h1 {
+ padding: 0px 0 0 0px;
+ background: url(images/img08.jpg) no-repeat left top;
+ font-size: 24px;
+ color: #FFFFFF;
+}
+
+.post h2 {
+ padding: 0px 0 0 0px;
+ font-size: 22px;
+ color: #FFFFFF;
+}
+
+.post .entry {
+}
+
+.post .meta {
+ padding: 15px 15px 30px 0px;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 11px;
+}
+
+.post .meta p {
+ margin: 0;
+ padding-top: 15px;
+ line-height: normal;
+ color: #FFFFFF;
+}
+
+.post .meta .byline {
+ float: left;
+}
+
+.post .meta .links {
+ float: right;
+}
+
+.post .meta .more {
+ padding: 0 10px 0 18px;
+}
+
+.post .meta .comments {
+}
+
+.post .meta b {
+ display: none;
+}
+
+
+/* Sidebar */
+
+#sidebar {
+ width: 210px;
+ float: right;
+ margin: 0;
+ padding: 0;
+}
+
+#sidebar ul {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+#sidebar li {
+ margin-bottom: 40px;
+}
+
+#sidebar li ul {
+}
+
+#sidebar li li {
+ margin: 0;
+}
+
+#sidebar h2 {
+ width: 250px;
+ padding: 8px 0 0 0px;
+ margin-bottom: 10px;
+ background: url(images/img07.jpg) no-repeat left top;
+ font-size: 20px;
+ color: #FFFFFF;
+}
+
+/* Search */
+
+#search {
+
+}
+
+#search h2 {
+ margin-bottom: 20px;
+}
+
+#s {
+ width: 140px;
+ margin-right: 5px;
+ padding: 3px;
+ border: 1px solid #BED99C;
+}
+
+#x {
+ padding: 3px;
+ border: none;
+ background: #8BD80E;
+ text-transform: lowercase;
+ font-size: 11px;
+ color: #FFFFFF;
+}
+
+/* Boxes */
+
+.box1 {
+ padding: 20px;
+}
+
+.box2 {
+ color: #BABABA;
+}
+
+.box2 h2 {
+ margin-bottom: 15px;
+ font-size: 16px;
+ color: #FFFFFF;
+}
+
+.box2 ul {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+.box2 a:link, .box2 a:hover, .box2 a:active, .box2 a:visited {
+ color: #EDEDED;
+}
+
+/* Footer */
+#footer-wrap {
+}
+
+#footer {
+ margin: 0 auto;
+ padding: 20px 0 10px 0;
+ background: #000000;
+}
+
+html>body #footer {
+ height: auto;
+}
+
+#footer p {
+ font-size: 11px;
+}
+
+#legal {
+ clear: both;
+ padding-top: 17px;
+ text-align: center;
+ color: #FFFFFF;
+}
+
+#legal a {
+ font-weight: normal;
+ color: #FFFFFF;
+}
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/static/images/img01.gif b/docs/tutorials/wiki2/src/views/tutorial/templates/static/images/img01.gif
new file mode 100644
index 000000000..5f082bd99
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/static/images/img01.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/static/images/img02.gif b/docs/tutorials/wiki2/src/views/tutorial/templates/static/images/img02.gif
new file mode 100644
index 000000000..45a3ae976
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/static/images/img02.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/static/images/img03.gif b/docs/tutorials/wiki2/src/views/tutorial/templates/static/images/img03.gif
new file mode 100644
index 000000000..d92ea38f9
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/static/images/img03.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/static/images/img04.gif b/docs/tutorials/wiki2/src/views/tutorial/templates/static/images/img04.gif
new file mode 100644
index 000000000..950c4af9d
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/static/images/img04.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/static/images/spacer.gif b/docs/tutorials/wiki2/src/views/tutorial/templates/static/images/spacer.gif
new file mode 100644
index 000000000..5bfd67a2d
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/static/images/spacer.gif
Binary files differ
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/static/style.css b/docs/tutorials/wiki2/src/views/tutorial/templates/static/style.css
new file mode 100644
index 000000000..cad87e0d4
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/static/style.css
@@ -0,0 +1,109 @@
+html, body {
+ color: black;
+ background-color: #ddd;
+ font: x-small "Lucida Grande", "Lucida Sans Unicode", geneva, sans-serif;
+ margin: 0;
+ padding: 0;
+}
+
+td, th {padding:3px;border:none;}
+tr th {text-align:left;background-color:#f0f0f0;color:#333;}
+tr.odd td {background-color:#edf3fe;}
+tr.even td {background-color:#fff;}
+
+#header {
+ height: 80px;
+ width: 777px;
+ background: blue URL('../images/header_inner.png') no-repeat;
+ border-left: 1px solid #aaa;
+ border-right: 1px solid #aaa;
+ margin: 0 auto 0 auto;
+}
+
+a.link, a, a.active {
+ color: #369;
+}
+
+
+#main_content {
+ color: black;
+ font-size: 127%;
+ background-color: white;
+ width: 757px;
+ margin: 0 auto 0 auto;
+ border-left: 1px solid #aaa;
+ border-right: 1px solid #aaa;
+ padding: 10px;
+}
+
+#sidebar {
+ border: 1px solid #aaa;
+ background-color: #eee;
+ margin: 0.5em;
+ padding: 1em;
+ float: right;
+ width: 200px;
+ font-size: 88%;
+}
+
+#sidebar h2 {
+ margin-top: 0;
+}
+
+#sidebar ul {
+ margin-left: 1.5em;
+ padding-left: 0;
+}
+
+h1,h2,h3,h4,h5,h6,#getting_started_steps {
+ font-family: "Century Schoolbook L", Georgia, serif;
+ font-weight: bold;
+}
+
+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/templates/static/templatelicense.txt b/docs/tutorials/wiki2/src/views/tutorial/templates/static/templatelicense.txt
new file mode 100644
index 000000000..ccb6b06ab
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/static/templatelicense.txt
@@ -0,0 +1,243 @@
+Creative Commons </>
+
+Creative Commons Legal Code
+
+*Attribution 2.5*
+
+CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
+ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION
+ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE
+INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ITS USE.
+
+/License/
+
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
+COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
+COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
+AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
+TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE
+RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS
+AND CONDITIONS.
+
+*1. Definitions*
+
+ 1. *"Collective Work"* means a work, such as a periodical issue,
+ anthology or encyclopedia, in which the Work in its entirety in
+ unmodified form, along with a number of other contributions,
+ constituting separate and independent works in themselves, are
+ assembled into a collective whole. A work that constitutes a
+ Collective Work will not be considered a Derivative Work (as
+ defined below) for the purposes of this License.
+ 2. *"Derivative Work"* means a work based upon the Work or upon the
+ Work and other pre-existing works, such as a translation, musical
+ arrangement, dramatization, fictionalization, motion picture
+ version, sound recording, art reproduction, abridgment,
+ condensation, or any other form in which the Work may be recast,
+ transformed, or adapted, except that a work that constitutes a
+ Collective Work will not be considered a Derivative Work for the
+ purpose of this License. For the avoidance of doubt, where the
+ Work is a musical composition or sound recording, the
+ synchronization of the Work in timed-relation with a moving image
+ ("synching") will be considered a Derivative Work for the purpose
+ of this License.
+ 3. *"Licensor"* means the individual or entity that offers the Work
+ under the terms of this License.
+ 4. *"Original Author"* means the individual or entity who created the
+ Work.
+ 5. *"Work"* means the copyrightable work of authorship offered under
+ the terms of this License.
+ 6. *"You"* means an individual or entity exercising rights under this
+ License who has not previously violated the terms of this License
+ with respect to the Work, or who has received express permission
+ from the Licensor to exercise rights under this License despite a
+ previous violation.
+
+*2. Fair Use Rights.* Nothing in this license is intended to reduce,
+limit, or restrict any rights arising from fair use, first sale or other
+limitations on the exclusive rights of the copyright owner under
+copyright law or other applicable laws.
+
+*3. License Grant.* Subject to the terms and conditions of this License,
+Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
+perpetual (for the duration of the applicable copyright) license to
+exercise the rights in the Work as stated below:
+
+ 1. to reproduce the Work, to incorporate the Work into one or more
+ Collective Works, and to reproduce the Work as incorporated in the
+ Collective Works;
+ 2. to create and reproduce Derivative Works;
+ 3. to distribute copies or phonorecords of, display publicly, perform
+ publicly, and perform publicly by means of a digital audio
+ transmission the Work including as incorporated in Collective Works;
+ 4. to distribute copies or phonorecords of, display publicly, perform
+ publicly, and perform publicly by means of a digital audio
+ transmission Derivative Works.
+ 5.
+
+ For the avoidance of doubt, where the work is a musical composition:
+
+ 1. *Performance Royalties Under Blanket Licenses*. Licensor
+ waives the exclusive right to collect, whether individually
+ or via a performance rights society (e.g. ASCAP, BMI,
+ SESAC), royalties for the public performance or public
+ digital performance (e.g. webcast) of the Work.
+ 2. *Mechanical Rights and Statutory Royalties*. Licensor waives
+ the exclusive right to collect, whether individually or via
+ a music rights agency or designated agent (e.g. Harry Fox
+ Agency), royalties for any phonorecord You create from the
+ Work ("cover version") and distribute, subject to the
+ compulsory license created by 17 USC Section 115 of the US
+ Copyright Act (or the equivalent in other jurisdictions).
+ 6. *Webcasting Rights and Statutory Royalties*. For the avoidance of
+ doubt, where the Work is a sound recording, Licensor waives the
+ exclusive right to collect, whether individually or via a
+ performance-rights society (e.g. SoundExchange), royalties for the
+ public digital performance (e.g. webcast) of the Work, subject to
+ the compulsory license created by 17 USC Section 114 of the US
+ Copyright Act (or the equivalent in other jurisdictions).
+
+The above rights may be exercised in all media and formats whether now
+known or hereafter devised. The above rights include the right to make
+such modifications as are technically necessary to exercise the rights
+in other media and formats. All rights not expressly granted by Licensor
+are hereby reserved.
+
+*4. Restrictions.*The license granted in Section 3 above is expressly
+made subject to and limited by the following restrictions:
+
+ 1. You may distribute, publicly display, publicly perform, or
+ publicly digitally perform the Work only under the terms of this
+ License, and You must include a copy of, or the Uniform Resource
+ Identifier for, this License with every copy or phonorecord of the
+ Work You distribute, publicly display, publicly perform, or
+ publicly digitally perform. You may not offer or impose any terms
+ on the Work that alter or restrict the terms of this License or
+ the recipients' exercise of the rights granted hereunder. You may
+ not sublicense the Work. You must keep intact all notices that
+ refer to this License and to the disclaimer of warranties. You may
+ not distribute, publicly display, publicly perform, or publicly
+ digitally perform the Work with any technological measures that
+ control access or use of the Work in a manner inconsistent with
+ the terms of this License Agreement. The above applies to the Work
+ as incorporated in a Collective Work, but this does not require
+ the Collective Work apart from the Work itself to be made subject
+ to the terms of this License. If You create a Collective Work,
+ upon notice from any Licensor You must, to the extent practicable,
+ remove from the Collective Work any credit as required by clause
+ 4(b), as requested. If You create a Derivative Work, upon notice
+ from any Licensor You must, to the extent practicable, remove from
+ the Derivative Work any credit as required by clause 4(b), as
+ requested.
+ 2. If you distribute, publicly display, publicly perform, or publicly
+ digitally perform the Work or any Derivative Works or Collective
+ Works, You must keep intact all copyright notices for the Work and
+ provide, reasonable to the medium or means You are utilizing: (i)
+ the name of the Original Author (or pseudonym, if applicable) if
+ supplied, and/or (ii) if the Original Author and/or Licensor
+ designate another party or parties (e.g. a sponsor institute,
+ publishing entity, journal) for attribution in Licensor's
+ copyright notice, terms of service or by other reasonable means,
+ the name of such party or parties; the title of the Work if
+ supplied; to the extent reasonably practicable, the Uniform
+ Resource Identifier, if any, that Licensor specifies to be
+ associated with the Work, unless such URI does not refer to the
+ copyright notice or licensing information for the Work; and in the
+ case of a Derivative Work, a credit identifying the use of the
+ Work in the Derivative Work (e.g., "French translation of the Work
+ by Original Author," or "Screenplay based on original Work by
+ Original Author"). Such credit may be implemented in any
+ reasonable manner; provided, however, that in the case of a
+ Derivative Work or Collective Work, at a minimum such credit will
+ appear where any other comparable authorship credit appears and in
+ a manner at least as prominent as such other comparable authorship
+ credit.
+
+*5. Representations, Warranties and Disclaimer*
+
+UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
+OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
+KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
+INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
+FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
+LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
+WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE
+EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
+
+*6. Limitation on Liability.* EXCEPT TO THE EXTENT REQUIRED BY
+APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL
+THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY
+DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF
+LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+*7. Termination*
+
+ 1. This License and the rights granted hereunder will terminate
+ automatically upon any breach by You of the terms of this License.
+ Individuals or entities who have received Derivative Works or
+ Collective Works from You under this License, however, will not
+ have their licenses terminated provided such individuals or
+ entities remain in full compliance with those licenses. Sections
+ 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
+ 2. Subject to the above terms and conditions, the license granted
+ here is perpetual (for the duration of the applicable copyright in
+ the Work). Notwithstanding the above, Licensor reserves the right
+ to release the Work under different license terms or to stop
+ distributing the Work at any time; provided, however that any such
+ election will not serve to withdraw this License (or any other
+ license that has been, or is required to be, granted under the
+ terms of this License), and this License will continue in full
+ force and effect unless terminated as stated above.
+
+*8. Miscellaneous*
+
+ 1. Each time You distribute or publicly digitally perform the Work or
+ a Collective Work, the Licensor offers to the recipient a license
+ to the Work on the same terms and conditions as the license
+ granted to You under this License.
+ 2. Each time You distribute or publicly digitally perform a
+ Derivative Work, Licensor offers to the recipient a license to the
+ original Work on the same terms and conditions as the license
+ granted to You under this License.
+ 3. If any provision of this License is invalid or unenforceable under
+ applicable law, it shall not affect the validity or enforceability
+ of the remainder of the terms of this License, and without further
+ action by the parties to this agreement, such provision shall be
+ reformed to the minimum extent necessary to make such provision
+ valid and enforceable.
+ 4. No term or provision of this License shall be deemed waived and no
+ breach consented to unless such waiver or consent shall be in
+ writing and signed by the party to be charged with such waiver or
+ consent.
+ 5. This License constitutes the entire agreement between the parties
+ with respect to the Work licensed here. There are no
+ understandings, agreements or representations with respect to the
+ Work not specified here. Licensor shall not be bound by any
+ additional provisions that may appear in any communication from
+ You. This License may not be modified without the mutual written
+ agreement of the Licensor and You.
+
+Creative Commons is not a party to this License, and makes no warranty
+whatsoever in connection with the Work. Creative Commons will not be
+liable to You or any party on any legal theory for any damages
+whatsoever, including without limitation any general, special,
+incidental or consequential damages arising in connection to this
+license. Notwithstanding the foregoing two (2) sentences, if Creative
+Commons has expressly identified itself as the Licensor hereunder, it
+shall have all rights and obligations of Licensor.
+
+Except for the limited purpose of indicating to the public that the Work
+is licensed under the CCPL, neither party will use the trademark
+"Creative Commons" or any related trademark or logo of Creative Commons
+without the prior written consent of Creative Commons. Any permitted use
+will be in compliance with Creative Commons' then-current trademark
+usage guidelines, as may be published on its website or otherwise made
+available upon request from time to time.
+
+Creative Commons may be contacted at http://creativecommons.org/
+<http://creativecommons.org>.
+
+« Back to Commons Deed <./>
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/view.pt b/docs/tutorials/wiki2/src/views/tutorial/templates/view.pt
new file mode 100644
index 000000000..86fcc914e
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/view.pt
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal">
+
+<head>
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
+ <title>${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" />
+</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>
diff --git a/docs/tutorials/wiki2/src/views/tutorial/tests.py b/docs/tutorials/wiki2/src/views/tutorial/tests.py
new file mode 100644
index 000000000..40336fca4
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/tests.py
@@ -0,0 +1,140 @@
+import unittest
+
+from pyramid.configuration import Configurator
+from pyramid import testing
+
+def _initTestingDB():
+ from tutorial.models import DBSession
+ from tutorial.models import Base
+ from sqlalchemy import create_engine
+ engine = create_engine('sqlite://')
+ DBSession.configure(bind=engine)
+ Base.metadata.bind = engine
+ Base.metadata.create_all(engine)
+ return DBSession
+
+def _registerRoutes(config):
+ config.add_route('view_page', ':pagename')
+ config.add_route('edit_page', ':pagename/edit_page')
+ config.add_route('add_page', 'add_page/:pagename')
+
+class ViewWikiTests(unittest.TestCase):
+ def setUp(self):
+ self.config = Configurator()
+ self.config.begin()
+
+ def tearDown(self):
+ self.config.end()
+
+ def test_it(self):
+ from tutorial.views import view_wiki
+ self.config.add_route('view_page', ':pagename')
+ request = testing.DummyRequest()
+ response = view_wiki(request)
+ self.assertEqual(response.location, 'http://example.com/FrontPage')
+
+class ViewPageTests(unittest.TestCase):
+ def setUp(self):
+ self.session = _initTestingDB()
+ self.config = Configurator()
+ self.config.begin()
+
+ def tearDown(self):
+ self.session.remove()
+ self.config.end()
+
+ def _callFUT(self, request):
+ from tutorial.views import view_page
+ return view_page(request)
+
+ def test_it(self):
+ from tutorial.models import Page
+ request = testing.DummyRequest()
+ request.matchdict['pagename'] = 'IDoExist'
+ page = Page('IDoExist', 'Hello CruelWorld IDoExist')
+ self.session.add(page)
+ _registerRoutes(self.config)
+ info = self._callFUT(request)
+ self.assertEqual(info['page'], page)
+ self.assertEqual(
+ info['content'],
+ '<div class="document">\n'
+ '<p>Hello <a href="http://example.com/add_page/CruelWorld">'
+ 'CruelWorld</a> '
+ '<a href="http://example.com/IDoExist">'
+ 'IDoExist</a>'
+ '</p>\n</div>\n')
+ self.assertEqual(info['edit_url'],
+ 'http://example.com/IDoExist/edit_page')
+
+
+class AddPageTests(unittest.TestCase):
+ def setUp(self):
+ self.session = _initTestingDB()
+ self.config = Configurator()
+ self.config.begin()
+
+ def tearDown(self):
+ self.session.remove()
+ self.config.end()
+
+ def _callFUT(self, request):
+ from tutorial.views import add_page
+ return add_page(request)
+
+ def test_it_notsubmitted(self):
+ _registerRoutes(self.config)
+ request = testing.DummyRequest()
+ request.matchdict = {'pagename':'AnotherPage'}
+ info = self._callFUT(request)
+ self.assertEqual(info['page'].data,'')
+ self.assertEqual(info['save_url'],
+ 'http://example.com/add_page/AnotherPage')
+
+ def test_it_submitted(self):
+ from tutorial.models import Page
+ _registerRoutes(self.config)
+ request = testing.DummyRequest({'form.submitted':True,
+ 'body':'Hello yo!'})
+ request.matchdict = {'pagename':'AnotherPage'}
+ self._callFUT(request)
+ page = self.session.query(Page).filter_by(name='AnotherPage').one()
+ self.assertEqual(page.data, 'Hello yo!')
+
+class EditPageTests(unittest.TestCase):
+ def setUp(self):
+ self.session = _initTestingDB()
+ self.config = Configurator()
+ self.config.begin()
+
+ def tearDown(self):
+ self.session.remove()
+ self.config.end()
+
+ def _callFUT(self, request):
+ from tutorial.views import edit_page
+ return edit_page(request)
+
+ def test_it_notsubmitted(self):
+ from tutorial.models import Page
+ _registerRoutes(self.config)
+ request = testing.DummyRequest()
+ request.matchdict = {'pagename':'abc'}
+ page = Page('abc', 'hello')
+ self.session.add(page)
+ info = self._callFUT(request)
+ self.assertEqual(info['page'], page)
+ self.assertEqual(info['save_url'],
+ 'http://example.com/abc/edit_page')
+
+ def test_it_submitted(self):
+ from tutorial.models import Page
+ _registerRoutes(self.config)
+ request = testing.DummyRequest({'form.submitted':True,
+ 'body':'Hello yo!'})
+ request.matchdict = {'pagename':'abc'}
+ page = Page('abc', 'hello')
+ self.session.add(page)
+ response = self._callFUT(request)
+ self.assertEqual(response.location, 'http://example.com/abc')
+ self.assertEqual(page.data, 'Hello yo!')
diff --git a/docs/tutorials/wiki2/src/views/tutorial/views.py b/docs/tutorials/wiki2/src/views/tutorial/views.py
new file mode 100644
index 000000000..c0d793d38
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/views.py
@@ -0,0 +1,65 @@
+import re
+
+from docutils.core import publish_parts
+
+from webob.exc import HTTPFound
+
+from pyramid.url import route_url
+
+from tutorial.models import DBSession
+from tutorial.models import Page
+
+# regular expression used to find WikiWords
+wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)")
+
+def view_wiki(request):
+ return HTTPFound(location = route_url('view_page', request,
+ pagename='FrontPage'))
+
+def view_page(request):
+ matchdict = request.matchdict
+ session = DBSession()
+ page = session.query(Page).filter_by(name=matchdict['pagename']).one()
+
+ def check(match):
+ word = match.group(1)
+ exists = session.query(Page).filter_by(name=word).all()
+ if exists:
+ view_url = route_url('view_page', request, pagename=word)
+ return '<a href="%s">%s</a>' % (view_url, word)
+ else:
+ add_url = route_url('add_page', request, pagename=word)
+ return '<a href="%s">%s</a>' % (add_url, word)
+
+ content = publish_parts(page.data, writer_name='html')['html_body']
+ content = wikiwords.sub(check, content)
+ edit_url = route_url('edit_page', request,
+ pagename=matchdict['pagename'])
+ return dict(page=page, content=content, edit_url=edit_url)
+
+def add_page(request):
+ name = request.matchdict['pagename']
+ if 'form.submitted' in request.params:
+ session = DBSession()
+ body = request.params['body']
+ page = Page(name, body)
+ session.add(page)
+ return HTTPFound(location = route_url('view_page', request,
+ pagename=name))
+ save_url = route_url('add_page', request, pagename=name)
+ page = Page('', '')
+ return dict(page=page, save_url=save_url)
+
+def edit_page(request):
+ name = request.matchdict['pagename']
+ session = DBSession()
+ page = session.query(Page).filter_by(name=name).one()
+ if 'form.submitted' in request.params:
+ page.data = request.params['body']
+ session.add(page)
+ return HTTPFound(location = route_url('view_page', request,
+ pagename=name))
+ return dict(
+ page=page,
+ save_url = route_url('edit_page', request, pagename=name),
+ )