From 4040cf7ef5a9843e25db69b3a17b3796f3a39fb8 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Mon, 16 Nov 2015 00:17:20 -0800 Subject: - complete rewrite of wiki2/authorization.rst - add wiki2/src/authorization/ files - improve tag in views/tutorial/templates/edit.jinja2 --- docs/tutorials/wiki2/authorization.rst | 137 ++++++++-------- .../wiki2/src/authorization/development.ini | 4 +- .../wiki2/src/authorization/production.ini | 14 +- docs/tutorials/wiki2/src/authorization/setup.py | 2 +- .../wiki2/src/authorization/tutorial/__init__.py | 18 +-- .../wiki2/src/authorization/tutorial/models.py | 37 ----- .../src/authorization/tutorial/models/__init__.py | 7 + .../src/authorization/tutorial/models/meta.py | 46 ++++++ .../src/authorization/tutorial/models/mymodel.py | 26 +++ .../authorization/tutorial/scripts/initializedb.py | 25 +-- .../wiki2/src/authorization/tutorial/security.py | 7 - .../authorization/tutorial/security/__init__.py | 1 + .../src/authorization/tutorial/security/default.py | 7 + .../authorization/tutorial/static/theme.min.css | 2 +- .../authorization/tutorial/templates/edit.jinja2 | 73 +++++++++ .../src/authorization/tutorial/templates/edit.pt | 72 --------- .../authorization/tutorial/templates/layout.jinja2 | 66 ++++++++ .../authorization/tutorial/templates/login.jinja2 | 74 +++++++++ .../src/authorization/tutorial/templates/login.pt | 74 --------- .../tutorial/templates/mytemplate.jinja2 | 8 + .../authorization/tutorial/templates/mytemplate.pt | 66 -------- .../authorization/tutorial/templates/view.jinja2 | 71 +++++++++ .../src/authorization/tutorial/templates/view.pt | 72 --------- .../wiki2/src/authorization/tutorial/tests.py | 175 ++++++--------------- .../wiki2/src/authorization/tutorial/views.py | 124 --------------- .../src/authorization/tutorial/views/__init__.py | 0 .../src/authorization/tutorial/views/default.py | 120 ++++++++++++++ .../wiki2/src/views/tutorial/templates/edit.jinja2 | 2 +- 28 files changed, 648 insertions(+), 682 deletions(-) delete mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/models.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py delete mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/security.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/security/__init__.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/security/default.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 delete mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja2 delete mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.jinja2 delete mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 delete mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt delete mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/views.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/views/__init__.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/views/default.py (limited to 'docs/tutorials') diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst index 98e6110f3..e40433497 100644 --- a/docs/tutorials/wiki2/authorization.rst +++ b/docs/tutorials/wiki2/authorization.rst @@ -104,10 +104,8 @@ Open ``tutorial/tutorial/__init__.py`` and add a ``root_factory`` parameter to our :term:`Configurator` constructor, that points to the class we created above: -.. TODO update the lines to include, linenos, lineno-start - .. literalinclude:: src/authorization/tutorial/__init__.py - :lines: 24-25 + :lines: 13-14 :emphasize-lines: 2 :language: python @@ -128,18 +126,18 @@ Open ``tutorial/tutorial/__init__.py`` and add the highlighted import statements: .. literalinclude:: src/authorization/tutorial/__init__.py - :lines: 1-7 + :lines: 1-5 :linenos: - :emphasize-lines: 2-3,7 + :emphasize-lines: 2-5 :language: python Now add those policies to the configuration: .. literalinclude:: src/authorization/tutorial/__init__.py - :lines: 21-27 + :lines: 7-16 :linenos: - :lineno-start: 21 - :emphasize-lines: 1-3,6-7 + :lineno-start: 7 + :emphasize-lines: 4-6,9-10 :language: python Only the highlighted lines need to be added. @@ -152,47 +150,50 @@ ticket that may be included in the request. We are also enabling an Note that the :class:`pyramid.authentication.AuthTktAuthenticationPolicy` constructor accepts two arguments: ``secret`` and ``callback``. ``secret`` is a string representing an encryption key used by the "authentication ticket" -machinery represented by this policy: it is required. The ``callback`` is the +machinery represented by this policy; it is required. The ``callback`` is the ``groupfinder()`` function that we created before. + Add permission declarations ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Open ``tutorial/tutorial/views.py`` and add a ``permission='edit'`` parameter -to the ``@view_config`` decorators for ``add_page()`` and ``edit_page()``: -.. literalinclude:: src/authorization/tutorial/views.py - :lines: 60-61 +Open ``tutorial/tutorial/views/default.py`` and add a ``permission='view'`` +parameter to the ``@view_config`` decorator for ``view_wiki()`` and +``view_page()`` as follows: + +.. literalinclude:: src/authorization/tutorial/views/default.py + :lines: 27-29 :emphasize-lines: 1-2 :language: python -.. literalinclude:: src/authorization/tutorial/views.py - :lines: 75-76 +.. literalinclude:: src/authorization/tutorial/views/default.py + :lines: 33-35 :emphasize-lines: 1-2 :language: python Only the highlighted lines, along with their preceding commas, need to be edited and added. -The result is that only users who possess the ``edit`` permission at the time -of the request may invoke those two views. +This allows anyone to invoke these two views. -Add a ``permission='view'`` parameter to the ``@view_config`` decorator for -``view_wiki()`` and ``view_page()`` as follows: +Add a ``permission='edit'`` parameter to the ``@view_config`` decorators for +``add_page()`` and ``edit_page()``: -.. literalinclude:: src/authorization/tutorial/views.py - :lines: 30-31 +.. literalinclude:: src/authorization/tutorial/views/default.py + :lines: 57-59 :emphasize-lines: 1-2 :language: python -.. literalinclude:: src/authorization/tutorial/views.py - :lines: 36-37 +.. literalinclude:: src/authorization/tutorial/views/default.py + :lines: 72-74 :emphasize-lines: 1-2 :language: python Only the highlighted lines, along with their preceding commas, need to be edited and added. -This allows anyone to invoke these two views. +The result is that only users who possess the ``edit`` permission at the time +of the request may invoke those two views. We are done with the changes needed to control access. The changes that follow will add the login and logout feature. @@ -206,7 +207,7 @@ Go back to ``tutorial/tutorial/__init__.py`` and add these two routes as highlighted: .. literalinclude:: src/authorization/tutorial/__init__.py - :lines: 30-33 + :lines: 20-23 :emphasize-lines: 2-3 :language: python @@ -214,7 +215,7 @@ highlighted: ``view_page`` route definition: .. literalinclude:: src/authorization/tutorial/__init__.py - :lines: 33 + :lines: 23 :language: python This is because ``view_page``'s route definition uses a catch-all @@ -234,11 +235,11 @@ We'll also add a ``logout`` view callable to our application and provide a link to it. This view will clear the credentials of the logged in user and redirect back to the front page. -Add the following import statements to the head of -``tutorial/tutorial/views.py``: +Add the following import statements to ``tutorial/tutorial/views/default.py`` +after the import from ``pyramid.httpexceptions``: -.. literalinclude:: src/authorization/tutorial/views.py - :lines: 9-19 +.. literalinclude:: src/authorization/tutorial/views/default.py + :lines: 10-20 :emphasize-lines: 1-11 :language: python @@ -251,18 +252,18 @@ cookie. Now add the ``login`` and ``logout`` views at the end of the file: -.. literalinclude:: src/authorization/tutorial/views.py - :lines: 91-123 +.. literalinclude:: src/authorization/tutorial/views/default.py + :lines: 88-121 :language: python ``login()`` has two decorators: - a ``@view_config`` decorator which associates it with the ``login`` route - and makes it visible when we visit ``/login``, + and makes it visible when we visit ``/login``, and - a ``@forbidden_view_config`` decorator which turns it into a :term:`forbidden view`. ``login()`` will be invoked when a user tries to execute a view callable for which they lack authorization. For example, if - a user has not logged in and tries to add or edit a Wiki page, they will be + a user has not logged in and tries to add or edit a wiki page, they will be shown the login form before being allowed to continue. The order of these two :term:`view configuration` decorators is unimportant. @@ -270,36 +271,36 @@ The order of these two :term:`view configuration` decorators is unimportant. ``logout()`` is decorated with a ``@view_config`` decorator which associates it with the ``logout`` route. It will be invoked when we visit ``/logout``. -Add the ``login.pt`` Template -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Add the ``login.jinja2`` template +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Create ``tutorial/tutorial/templates/login.pt`` with the following content: +Create ``tutorial/tutorial/templates/login.jinja2`` with the following content: -.. literalinclude:: src/authorization/tutorial/templates/login.pt +.. literalinclude:: src/authorization/tutorial/templates/login.jinja2 :language: html The above template is referenced in the login view that we just added in -``views.py``. +``views/default.py``. Return a ``logged_in`` flag to the renderer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Open ``tutorial/tutorial/views.py`` again. Add a ``logged_in`` parameter to -the return value of ``view_page()``, ``edit_page()``, and ``add_page()`` as -follows: +Open ``tutorial/tutorial/views/default.py`` again. Add a ``logged_in`` +parameter to the return value of ``view_page()``, ``add_page()``, and +``edit_page()`` as follows: -.. literalinclude:: src/authorization/tutorial/views.py - :lines: 57-58 +.. literalinclude:: src/authorization/tutorial/views/default.py + :lines: 54-55 :emphasize-lines: 1-2 :language: python -.. literalinclude:: src/authorization/tutorial/views.py - :lines: 72-73 +.. literalinclude:: src/authorization/tutorial/views/default.py + :lines: 69-70 :emphasize-lines: 1-2 :language: python -.. literalinclude:: src/authorization/tutorial/views.py - :lines: 85-89 +.. literalinclude:: src/authorization/tutorial/views/default.py + :lines: 82-86 :emphasize-lines: 3-4 :language: python @@ -311,19 +312,19 @@ the user is not authenticated, or a userid if the user is authenticated. Add a "Logout" link when logged in ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Open ``tutorial/tutorial/templates/edit.pt`` and -``tutorial/tutorial/templates/view.pt`` and add the following code as +Open ``tutorial/tutorial/templates/edit.jinja2`` and +``tutorial/tutorial/templates/view.jinja2`` and add the following code as indicated by the highlighted lines. -.. literalinclude:: src/authorization/tutorial/templates/edit.pt - :lines: 34-38 - :emphasize-lines: 3-5 +.. literalinclude:: src/authorization/tutorial/templates/edit.jinja2 + :lines: 34-40 + :emphasize-lines: 3-7 :language: html -The attribute ``tal:condition="logged_in"`` will make the element be included -when ``logged_in`` is any user id. The link will invoke the logout view. The -above element will not be included if ``logged_in`` is ``None``, such as when -a user is not authenticated. +The attribute ``logged_in`` will make the element be included when +``logged_in`` is any user id. The link will invoke the logout view. The above +element will not be included if ``logged_in`` is ``None``, such as when a user +is not authenticated. Reviewing our changes --------------------- @@ -332,7 +333,7 @@ Our ``tutorial/tutorial/__init__.py`` will look like this when we're done: .. literalinclude:: src/authorization/tutorial/__init__.py :linenos: - :emphasize-lines: 2-3,7,21-23,25-27,31-32 + :emphasize-lines: 2-3,5,10-12,14-16,21-22 :language: python Only the highlighted lines need to be added or edited. @@ -346,31 +347,31 @@ Our ``tutorial/tutorial/models/mymodel.py`` will look like this when we're done: Only the highlighted lines need to be added or edited. -Our ``tutorial/tutorial/views.py`` will look like this when we're done: +Our ``tutorial/tutorial/views/default.py`` will look like this when we're done: -.. literalinclude:: src/authorization/tutorial/views.py +.. literalinclude:: src/authorization/tutorial/views/default.py :linenos: - :emphasize-lines: 9-11,14-19,25,31,37,58,61,73,76,88,91-117,119-123 + :emphasize-lines: 10-20,27-28,33-34,54-55,57-58,69-70,72-73,84-85,88-121 :language: python Only the highlighted lines need to be added or edited. -Our ``tutorial/tutorial/templates/edit.pt`` template will look like this when +Our ``tutorial/tutorial/templates/edit.jinja2`` template will look like this when we're done: -.. literalinclude:: src/authorization/tutorial/templates/edit.pt +.. literalinclude:: src/authorization/tutorial/templates/edit.jinja2 :linenos: - :emphasize-lines: 36-38 + :emphasize-lines: 36-40 :language: html Only the highlighted lines need to be added or edited. -Our ``tutorial/tutorial/templates/view.pt`` template will look like this when +Our ``tutorial/tutorial/templates/view.jinja2`` template will look like this when we're done: -.. literalinclude:: src/authorization/tutorial/templates/view.pt +.. literalinclude:: src/authorization/tutorial/templates/view.jinja2 :linenos: - :emphasize-lines: 36-38 + :emphasize-lines: 36-40 :language: html Only the highlighted lines need to be added or edited. @@ -405,5 +406,3 @@ following URLs, checking that the result is as expected: the login form with the ``editor`` credentials), we'll see a Logout link in the upper right hand corner. When we click it, we're logged out, and redirected back to the front page. - -.. TODO update the lines to include in src/authorization/tutorial/__init__.py diff --git a/docs/tutorials/wiki2/src/authorization/development.ini b/docs/tutorials/wiki2/src/authorization/development.ini index a9d53b296..99c4ff0fe 100644 --- a/docs/tutorials/wiki2/src/authorization/development.ini +++ b/docs/tutorials/wiki2/src/authorization/development.ini @@ -27,7 +27,7 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -host = 0.0.0.0 +host = 127.0.0.1 port = 6543 ### @@ -68,4 +68,4 @@ level = NOTSET formatter = generic [formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/docs/tutorials/wiki2/src/authorization/production.ini b/docs/tutorials/wiki2/src/authorization/production.ini index 4684d2f7a..97acfbd7d 100644 --- a/docs/tutorials/wiki2/src/authorization/production.ini +++ b/docs/tutorials/wiki2/src/authorization/production.ini @@ -1,3 +1,8 @@ +### +# app configuration +# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html +### + [app:main] use = egg:tutorial @@ -16,7 +21,10 @@ use = egg:waitress#main host = 0.0.0.0 port = 6543 -# Begin logging configuration +### +# logging configuration +# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html +### [loggers] keys = root, tutorial, sqlalchemy @@ -51,6 +59,4 @@ level = NOTSET formatter = generic [formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s - -# End logging configuration +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/docs/tutorials/wiki2/src/authorization/setup.py b/docs/tutorials/wiki2/src/authorization/setup.py index 09bd63d33..d4e5a4072 100644 --- a/docs/tutorials/wiki2/src/authorization/setup.py +++ b/docs/tutorials/wiki2/src/authorization/setup.py @@ -10,7 +10,7 @@ with open(os.path.join(here, 'CHANGES.txt')) as f: requires = [ 'pyramid', - 'pyramid_chameleon', + 'pyramid_jinja2', 'pyramid_debugtoolbar', 'pyramid_tm', 'SQLAlchemy', diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py index 2ada42171..084fee19f 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py @@ -2,30 +2,20 @@ from pyramid.config import Configurator from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy -from sqlalchemy import engine_from_config - -from tutorial.security import groupfinder - -from .models import ( - DBSession, - Base, - ) - +from security.default import groupfinder def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ - engine = engine_from_config(settings, 'sqlalchemy.') - DBSession.configure(bind=engine) - Base.metadata.bind = engine authn_policy = AuthTktAuthenticationPolicy( 'sosecret', callback=groupfinder, hashalg='sha512') authz_policy = ACLAuthorizationPolicy() config = Configurator(settings=settings, - root_factory='tutorial.models.RootFactory') + root_factory='tutorial.models.mymodel.RootFactory') config.set_authentication_policy(authn_policy) config.set_authorization_policy(authz_policy) - config.include('pyramid_chameleon') + config.include('pyramid_jinja2') + config.include('.models.meta') config.add_static_view('static', 'static', cache_max_age=3600) config.add_route('view_wiki', '/') config.add_route('login', '/login') diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models.py b/docs/tutorials/wiki2/src/authorization/tutorial/models.py deleted file mode 100644 index 4f7e1e024..000000000 --- a/docs/tutorials/wiki2/src/authorization/tutorial/models.py +++ /dev/null @@ -1,37 +0,0 @@ -from pyramid.security import ( - Allow, - Everyone, - ) - -from sqlalchemy import ( - Column, - Integer, - Text, - ) - -from sqlalchemy.ext.declarative import declarative_base - -from sqlalchemy.orm import ( - scoped_session, - 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) - - -class RootFactory(object): - __acl__ = [ (Allow, Everyone, 'view'), - (Allow, 'group:editors', 'edit') ] - def __init__(self, request): - pass diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py new file mode 100644 index 000000000..7b1c62867 --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py @@ -0,0 +1,7 @@ +from sqlalchemy.orm import configure_mappers +# import all models classes here for sqlalchemy mappers +# to pick up +from .mymodel import Page # flake8: noqa + +# run configure mappers to ensure we avoid any race conditions +configure_mappers() diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py new file mode 100644 index 000000000..b72b45f9f --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py @@ -0,0 +1,46 @@ +from sqlalchemy import engine_from_config +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +from sqlalchemy.schema import MetaData +import zope.sqlalchemy + +NAMING_CONVENTION = { + "ix": 'ix_%(column_0_label)s', + "uq": "uq_%(table_name)s_%(column_0_name)s", + "ck": "ck_%(table_name)s_%(constraint_name)s", + "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", + "pk": "pk_%(table_name)s" +} + +metadata = MetaData(naming_convention=NAMING_CONVENTION) +Base = declarative_base(metadata=metadata) + + +def includeme(config): + settings = config.get_settings() + dbmaker = get_dbmaker(get_engine(settings)) + + config.add_request_method( + lambda r: get_session(r.tm, dbmaker), + 'dbsession', + reify=True + ) + + config.include('pyramid_tm') + + +def get_session(transaction_manager, dbmaker): + dbsession = dbmaker() + zope.sqlalchemy.register(dbsession, + transaction_manager=transaction_manager) + return dbsession + + +def get_engine(settings, prefix='sqlalchemy.'): + return engine_from_config(settings, prefix) + + +def get_dbmaker(engine): + dbmaker = sessionmaker() + dbmaker.configure(bind=engine) + return dbmaker diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py new file mode 100644 index 000000000..03e2f90ca --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py @@ -0,0 +1,26 @@ +from .meta import Base + +from pyramid.security import ( + Allow, + Everyone, + ) + +from sqlalchemy import ( + Column, + Integer, + Text, + ) + + +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(Integer) + +class RootFactory(object): + __acl__ = [ (Allow, Everyone, 'view'), + (Allow, 'group:editors', 'edit') ] + def __init__(self, request): + pass \ No newline at end of file diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py index 23a5f13f4..4aac4a848 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py @@ -2,36 +2,41 @@ import os import sys import transaction -from sqlalchemy import engine_from_config - from pyramid.paster import ( get_appsettings, setup_logging, ) -from ..models import ( - DBSession, - Page, +from ..models.meta import ( Base, + get_session, + get_engine, + get_dbmaker, ) +from ..models.mymodel import Page def usage(argv): cmd = os.path.basename(argv[0]) - print('usage: %s <config_uri>\n' + print('usage: %s <config_uri> [var=value]\n' '(example: "%s development.ini")' % (cmd, cmd)) sys.exit(1) def main(argv=sys.argv): - if len(argv) != 2: + if len(argv) < 2: usage(argv) config_uri = argv[1] setup_logging(config_uri) settings = get_appsettings(config_uri) - engine = engine_from_config(settings, 'sqlalchemy.') - DBSession.configure(bind=engine) + + engine = get_engine(settings) + dbmaker = get_dbmaker(engine) + + dbsession = get_session(transaction.manager, dbmaker) + Base.metadata.create_all(engine) + with transaction.manager: model = Page(name='FrontPage', data='This is the front page') - DBSession.add(model) + dbsession.add(model) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/security.py b/docs/tutorials/wiki2/src/authorization/tutorial/security.py deleted file mode 100644 index d88c9c71f..000000000 --- a/docs/tutorials/wiki2/src/authorization/tutorial/security.py +++ /dev/null @@ -1,7 +0,0 @@ -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/security/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/security/__init__.py new file mode 100644 index 000000000..5bb534f79 --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/security/__init__.py @@ -0,0 +1 @@ +# package diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/security/default.py b/docs/tutorials/wiki2/src/authorization/tutorial/security/default.py new file mode 100644 index 000000000..d88c9c71f --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/security/default.py @@ -0,0 +1,7 @@ +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/static/theme.min.css b/docs/tutorials/wiki2/src/authorization/tutorial/static/theme.min.css index 2f924bcc5..0d25de5b6 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/static/theme.min.css +++ b/docs/tutorials/wiki2/src/authorization/tutorial/static/theme.min.css @@ -1 +1 @@ -@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a{color:#fff}.starter-template .links ul li a:hover{text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} \ No newline at end of file +@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a,a{color:#f2b7bd;text-decoration:underline}.starter-template .links ul li a:hover,a:hover{color:#fff;text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 new file mode 100644 index 000000000..c4f3a2c93 --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 @@ -0,0 +1,73 @@ +<!DOCTYPE html> +<html lang="{{request.locale_name}}"> + <head> + <meta charset="utf-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <meta name="description" content="pyramid web application"> + <meta name="author" content="Pylons Project"> + <link rel="shortcut icon" href="{{request.static_url('tutorial:static/pyramid-16x16.png')}}"> + + <title>Edit{% if page.name %} {{page.name}}{% endif %} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) + + + + + + + + + + + + + +
+
+
+
+ +
+
+
+ {% if logged_in %} +

+ Logout +

+ {% endif %} +

+ Editing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} +

+

You can return to the + FrontPage. +

+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt deleted file mode 100644 index ed355434d..000000000 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - ${page.name} - Pyramid tutorial wiki (based on - TurboGears 20-Minute Wiki) - - - - - - - - - - - - -
-
-
-
- -
-
-
-

- Logout -

-

- Editing Page Name Goes - Here -

-

You can return to the - FrontPage. -

-
-
- -
-
- -
-
-
-
-
-
- -
-
-
- - - - - - - - diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 new file mode 100644 index 000000000..ff624c65b --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 @@ -0,0 +1,66 @@ + + + + + + + + + + + Alchemy Scaffold for The Pyramid Web Framework + + + + + + + + + + + + + +
+
+
+
+ +
+
+ {% block content %} +

No content

+ {% endblock content %} +
+
+
+ +
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja2 new file mode 100644 index 000000000..a80a2a165 --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja2 @@ -0,0 +1,74 @@ + + + + + + + + + + + Login - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) + + + + + + + + + + + + + +
+
+
+
+ +
+
+
+

+ + Login +
+ {{ message }} +

+
+ +
+ + +
+
+ + +
+
+ +
+
+
+
+
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt deleted file mode 100644 index 4a938e9bb..000000000 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - Login - Pyramid tutorial wiki (based on - TurboGears 20-Minute Wiki) - - - - - - - - - - - - -
-
-
-
- -
-
-
-

- - Login -
- -

-
- -
- - -
-
- - -
-
- -
-
-
-
-
-
- -
-
-
- - - - - - - - diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.jinja2 new file mode 100644 index 000000000..bb622bf5a --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.jinja2 @@ -0,0 +1,8 @@ +{% extends "layout.jinja2" %} + +{% block content %} +
+

Pyramid Alchemy scaffold

+

Welcome to {{project}}, an application generated by
the Pyramid Web Framework 1.7.dev0.

+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt deleted file mode 100644 index c9b0cec21..000000000 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - Alchemy Scaffold for The Pyramid Web Framework - - - - - - - - - - - - - -
-
-
-
- -
-
-
-

Pyramid Alchemy scaffold

-

Welcome to ${project}, an application generated by
the Pyramid Web Framework.

-
-
-
- -
- -
-
-
- - - - - - - - diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 new file mode 100644 index 000000000..a7afc66fc --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 @@ -0,0 +1,71 @@ + + + + + + + + + + + {{page.name}} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) + + + + + + + + + + + + + +
+
+
+
+ +
+
+
+ {% if logged_in %} +

+ Logout +

+ {% endif %} +

{{ content|safe }}

+

+ + Edit this page + +

+

+ Viewing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} +

+

You can return to the + FrontPage. +

+
+
+
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt deleted file mode 100644 index 02cb8e73b..000000000 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - ${page.name} - Pyramid tutorial wiki (based on - TurboGears 20-Minute Wiki) - - - - - - - - - - - - -
-
-
-
- -
-
-
-

- Logout -

-
- Page text goes here. -
-

- - Edit this page - -

-

- Viewing - Page Name Goes Here -

-

You can return to the - FrontPage. -

-
-
-
-
- -
-
-
- - - - - - - - diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/tests.py b/docs/tutorials/wiki2/src/authorization/tutorial/tests.py index 9f01d2da5..b947e3bb1 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/tests.py @@ -3,144 +3,63 @@ import transaction from pyramid import testing -def _initTestingDB(): - from sqlalchemy import create_engine - from tutorial.models import ( - DBSession, - Page, - Base - ) - engine = create_engine('sqlite://') - Base.metadata.create_all(engine) - DBSession.configure(bind=engine) - with transaction.manager: - model = Page(name='FrontPage', data='This is the front page') - DBSession.add(model) - 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 dummy_request(dbsession): + return testing.DummyRequest(dbsession=dbsession) + + +class BaseTest(unittest.TestCase): def setUp(self): - self.config = testing.setUp() - self.session = _initTestingDB() + self.config = testing.setUp(settings={ + 'sqlalchemy.url': 'sqlite:///:memory:' + }) + self.config.include('.models.meta') + settings = self.config.get_settings() - def tearDown(self): - self.session.remove() - testing.tearDown() + from .models.meta import ( + get_session, + get_engine, + get_dbmaker, + ) - def _callFUT(self, request): - from tutorial.views import view_wiki - return view_wiki(request) + self.engine = get_engine(settings) + dbmaker = get_dbmaker(self.engine) - def test_it(self): - _registerRoutes(self.config) - request = testing.DummyRequest() - response = self._callFUT(request) - self.assertEqual(response.location, 'http://example.com/FrontPage') + self.session = get_session(transaction.manager, dbmaker) -class ViewPageTests(unittest.TestCase): - def setUp(self): - self.session = _initTestingDB() - self.config = testing.setUp() + def init_database(self): + from .models.meta import Base + Base.metadata.create_all(self.engine) def tearDown(self): - self.session.remove() - testing.tearDown() - - 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(name='IDoExist', data='Hello CruelWorld IDoExist') - self.session.add(page) - _registerRoutes(self.config) - info = self._callFUT(request) - self.assertEqual(info['page'], page) - self.assertEqual( - info['content'], - '
\n' - '

Hello ' - 'CruelWorld ' - '' - 'IDoExist' - '

\n
\n') - self.assertEqual(info['edit_url'], - 'http://example.com/IDoExist/edit_page') - - -class AddPageTests(unittest.TestCase): - def setUp(self): - self.session = _initTestingDB() - self.config = testing.setUp() + from .models.meta import Base - def tearDown(self): - self.session.remove() testing.tearDown() + transaction.abort() + Base.metadata.create_all(self.engine) + + +class TestMyViewSuccessCondition(BaseTest): - 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 = testing.setUp() + super(TestMyViewSuccessCondition, self).setUp() + self.init_database() - def tearDown(self): - self.session.remove() - testing.tearDown() + from .models.mymodel import MyModel + + model = MyModel(name='one', value=55) + self.session.add(model) + + def test_passing_view(self): + from .views.default import my_view + info = my_view(dummy_request(self.session)) + self.assertEqual(info['one'].name, 'one') + self.assertEqual(info['project'], 'tutorial') + + +class TestMyViewFailureCondition(BaseTest): - 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(name='abc', data='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(name='abc', data='hello') - self.session.add(page) - response = self._callFUT(request) - self.assertEqual(response.location, 'http://example.com/abc') - self.assertEqual(page.data, 'Hello yo!') + def test_failing_view(self): + from .views.default import my_view + info = my_view(dummy_request(self.session)) + self.assertEqual(info.status_int, 500) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views.py b/docs/tutorials/wiki2/src/authorization/tutorial/views.py deleted file mode 100644 index e954d5a31..000000000 --- a/docs/tutorials/wiki2/src/authorization/tutorial/views.py +++ /dev/null @@ -1,124 +0,0 @@ -import re -from docutils.core import publish_parts - -from pyramid.httpexceptions import ( - HTTPFound, - HTTPNotFound, - ) - -from pyramid.view import ( - view_config, - forbidden_view_config, - ) - -from pyramid.security import ( - remember, - forget, - ) - -from .security import USERS - -from .models import ( - DBSession, - Page, - ) - - -# regular expression used to find WikiWords -wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") - -@view_config(route_name='view_wiki', - permission='view') -def view_wiki(request): - return HTTPFound(location = request.route_url('view_page', - pagename='FrontPage')) - -@view_config(route_name='view_page', renderer='templates/view.pt', - permission='view') -def view_page(request): - pagename = request.matchdict['pagename'] - page = DBSession.query(Page).filter_by(name=pagename).first() - if page is None: - return HTTPNotFound('No such page') - - def check(match): - word = match.group(1) - exists = DBSession.query(Page).filter_by(name=word).all() - if exists: - view_url = request.route_url('view_page', pagename=word) - return '%s' % (view_url, word) - else: - add_url = request.route_url('add_page', pagename=word) - return '%s' % (add_url, word) - - content = publish_parts(page.data, writer_name='html')['html_body'] - content = wikiwords.sub(check, content) - edit_url = request.route_url('edit_page', pagename=pagename) - return dict(page=page, content=content, edit_url=edit_url, - logged_in=request.authenticated_userid) - -@view_config(route_name='add_page', renderer='templates/edit.pt', - permission='edit') -def add_page(request): - pagename = request.matchdict['pagename'] - if 'form.submitted' in request.params: - body = request.params['body'] - page = Page(name=pagename, data=body) - DBSession.add(page) - return HTTPFound(location = request.route_url('view_page', - pagename=pagename)) - save_url = request.route_url('add_page', pagename=pagename) - page = Page(name='', data='') - return dict(page=page, save_url=save_url, - logged_in=request.authenticated_userid) - -@view_config(route_name='edit_page', renderer='templates/edit.pt', - permission='edit') -def edit_page(request): - pagename = request.matchdict['pagename'] - page = DBSession.query(Page).filter_by(name=pagename).one() - if 'form.submitted' in request.params: - page.data = request.params['body'] - DBSession.add(page) - return HTTPFound(location = request.route_url('view_page', - pagename=pagename)) - return dict( - page=page, - save_url=request.route_url('edit_page', pagename=pagename), - logged_in=request.authenticated_userid - ) - -@view_config(route_name='login', renderer='templates/login.pt') -@forbidden_view_config(renderer='templates/login.pt') -def login(request): - login_url = request.route_url('login') - referrer = request.url - if referrer == login_url: - referrer = '/' # never use the login form itself as came_from - came_from = request.params.get('came_from', referrer) - message = '' - login = '' - password = '' - if 'form.submitted' in request.params: - login = request.params['login'] - password = request.params['password'] - if USERS.get(login) == password: - headers = remember(request, login) - return HTTPFound(location = came_from, - headers = headers) - message = 'Failed login' - - return dict( - message = message, - url = request.application_url + '/login', - came_from = came_from, - login = login, - password = password, - ) - -@view_config(route_name='logout') -def logout(request): - headers = forget(request) - return HTTPFound(location = request.route_url('view_wiki'), - headers = headers) - diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py new file mode 100644 index 000000000..f35f041a4 --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py @@ -0,0 +1,120 @@ +import cgi +import re +from docutils.core import publish_parts + +from pyramid.httpexceptions import ( + HTTPFound, + HTTPNotFound, + ) + +from pyramid.view import ( + view_config, + forbidden_view_config, + ) + +from pyramid.security import ( + remember, + forget, + ) + +from ..security.default import USERS + +from ..models.mymodel import Page + +# regular expression used to find WikiWords +wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") + +@view_config(route_name='view_wiki', + permission='view') +def view_wiki(request): + return HTTPFound(location=request.route_url('view_page', + pagename='FrontPage')) + +@view_config(route_name='view_page', renderer='templates/view.jinja2', + permission='view') +def view_page(request): + pagename = request.matchdict['pagename'] + page = request.dbsession.query(Page).filter_by(name=pagename).first() + if page is None: + return HTTPNotFound('No such page') + + def check(match): + word = match.group(1) + exists = request.dbsession.query(Page).filter_by(name=word).all() + if exists: + view_url = request.route_url('view_page', pagename=word) + return '%s' % (view_url, cgi.escape(word)) + else: + add_url = request.route_url('add_page', pagename=word) + return '%s' % (add_url, cgi.escape(word)) + + content = publish_parts(page.data, writer_name='html')['html_body'] + content = wikiwords.sub(check, content) + edit_url = request.route_url('edit_page', pagename=pagename) + return dict(page=page, content=content, edit_url=edit_url, + logged_in=request.authenticated_userid) + +@view_config(route_name='add_page', renderer='templates/edit.jinja2', + permission='edit') +def add_page(request): + pagename = request.matchdict['pagename'] + if 'form.submitted' in request.params: + body = request.params['body'] + page = Page(name=pagename, data=body) + request.dbsession.add(page) + return HTTPFound(location = request.route_url('view_page', + pagename=pagename)) + save_url = request.route_url('add_page', pagename=pagename) + page = Page(name='', data='') + return dict(page=page, save_url=save_url, + logged_in=request.authenticated_userid) + +@view_config(route_name='edit_page', renderer='templates/edit.jinja2', + permission='edit') +def edit_page(request): + pagename = request.matchdict['pagename'] + page = request.dbsession.query(Page).filter_by(name=pagename).one() + if 'form.submitted' in request.params: + page.data = request.params['body'] + request.dbsession.add(page) + return HTTPFound(location = request.route_url('view_page', + pagename=pagename)) + return dict( + page=page, + save_url = request.route_url('edit_page', pagename=pagename), + logged_in=request.authenticated_userid + ) + +@view_config(route_name='login', renderer='templates/login.jinja2') +@forbidden_view_config(renderer='templates/login.jinja2') +def login(request): + login_url = request.route_url('login') + referrer = request.url + if referrer == login_url: + referrer = '/' # never use the login form itself as came_from + came_from = request.params.get('came_from', referrer) + message = '' + login = '' + password = '' + if 'form.submitted' in request.params: + login = request.params['login'] + password = request.params['password'] + if USERS.get(login) == password: + headers = remember(request, login) + return HTTPFound(location = came_from, + headers = headers) + message = 'Failed login' + + return dict( + message = message, + url = request.application_url + '/login', + came_from = came_from, + login = login, + password = password, + ) + +@view_config(route_name='logout') +def logout(request): + headers = forget(request) + return HTTPFound(location = request.route_url('view_wiki'), + headers = headers) diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 index ad4cd17e1..b3aadfc2e 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 +++ b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 @@ -8,7 +8,7 @@ - {{page.name}} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) + Edit{% if page.name %} {{page.name}}{% endif %} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) -- cgit v1.2.3