Commits

Nozomu Kaneko committed e22a7b7

initial

Comments (0)

Files changed (15)

+# HG changeset patch
+# Parent 24f2c8d4ceb2b3f5da2e170bb3d1a2fe2a1a0c11
+
+diff --git a/quickwiki/views/__init__.py b/quickwiki/views/__init__.py
+--- a/quickwiki/views/__init__.py
++++ b/quickwiki/views/__init__.py
+@@ -0,0 +1,7 @@
++from pyramid.events import BeforeRender, subscriber
++
++from quickwiki.lib import helpers
++
++@subscriber(BeforeRender)
++def add_global(event):
++    event['h'] = helpers
+# HG changeset patch
+# Parent 1ace53a6a8a6fbde1ff44e14f3011c3819af0d82
+
+diff --git a/quickwiki/controllers/pages.py b/quickwiki/controllers/pages.py
+deleted file mode 100644
+--- a/quickwiki/controllers/pages.py
++++ /dev/null
+@@ -1,69 +0,0 @@
+-import logging
+-from cgi import escape
+-
+-from pylons import url, request, response, session, tmpl_context as c
+-from pylons.controllers.util import abort, redirect
+-from pylons.decorators.secure import authenticate_form
+-
+-from quickwiki.lib.base import BaseController, render
+-from quickwiki.lib.helpers import flash
+-from quickwiki.model import Page, wikiwords
+-from quickwiki.model.meta import Session
+-
+-log = logging.getLogger(__name__)
+-
+-class PagesController(BaseController):
+-    
+-    def __before__(self):
+-        self.page_q = Session.query(Page)
+-    
+-    def show(self, title):
+-        page = self.page_q.filter_by(title=title).first()
+-        c.title = title
+-        if page:
+-            c.content = page.get_wiki_content()
+-            return render('/pages/show.mako')
+-        elif wikiwords.match(title):
+-            return render('/pages/new.mako')
+-        abort(404)
+-    
+-    def edit(self, title):
+-        c.title = title
+-        c.content = u''
+-        page = self.page_q.filter_by(title=title).first()
+-        if page:
+-            c.title = page.title
+-            c.content = page.content
+-        return render('/pages/edit.mako')
+-    
+-    @authenticate_form
+-    def save(self, title):
+-        c.title = title
+-        page = self.page_q.filter_by(title=title).first()
+-        if not page:
+-            page = Page(title=title)
+-        # In a real application, you should validate and sanitize
+-        # submitted data throughly! escape is a minimal example here
+-        page.content = escape(request.POST.getone('content'))
+-        Session.add(page)
+-        Session.commit()
+-        flash('Successfully saved %s!' % title)
+-        redirect(url('show_page', title=title))
+-    
+-    def index(self):
+-        c.titles = [page.title for page in self.page_q.all()]
+-        return render('/pages/index.mako')
+-    
+-    @authenticate_form
+-    def delete(self):
+-        titles = request.POST.getall('title')
+-        pages = self.page_q.filter(Page.title.in_(titles))
+-        for page in pages:
+-            Session.delete(page)
+-        Session.commit()
+-        # flash only after a successful commit
+-        for title in titles:
+-            flash('Deleted %s.' % title)
+-        redirect(url('pages'))
+-    
+-
+diff --git a/quickwiki/views/__init__.py b/quickwiki/views/__init__.py
+new file mode 100644
+diff --git a/quickwiki/views/pages.py b/quickwiki/views/pages.py
+new file mode 100644
+--- /dev/null
++++ b/quickwiki/views/pages.py
+@@ -0,0 +1,96 @@
++import logging
++from cgi import escape
++
++# from pylons.decorators.secure import authenticate_form
++
++import transaction
++from pyramid.response import Response
++from pyramid.view import view_config
++import pyramid.httpexceptions as exc
++
++from quickwiki.model import DBSession, Page, wikiwords
++
++log = logging.getLogger(__name__)
++
++class NewPage(Exception):
++    pass
++
++class PagesController(object):
++
++    def __init__(self, request):
++        self.request = request
++        self.page_q = DBSession.query(Page)
++
++    @view_config(route_name="home", renderer='pages/show.mako')
++    @view_config(route_name="page", renderer='pages/show.mako')
++    @view_config(route_name="show_page", renderer='pages/show.mako')
++    def show(self):
++        if self.request.matched_route.name == "home":
++            title = "FrontPage"
++        else:
++            title = self.request.matchdict["title"]
++        page = self.page_q.filter_by(title=title).first()
++        if page:
++            return {
++                "title": title,
++                "content": page.get_wiki_content(self.request),
++                }
++        elif wikiwords.match(title):
++            raise NewPage()
++        raise exc.HTTPNotFound()
++
++    @view_config(context=NewPage, renderer='pages/new.mako')
++    def new(self):
++        title = self.request.matchdict["title"]
++        return {
++            "title": title,
++            }
++
++    @view_config(route_name="edit_page", renderer='pages/edit.mako')
++    def edit(self):
++        title = self.request.matchdict["title"]
++        page = self.page_q.filter_by(title=title).first()
++        if page:
++            return {
++                "title": page.title,
++                "content": page.content,
++                }
++        else:
++            return {
++                "title": title,
++                "content": u'',
++                }
++
++    @view_config(route_name="save_page", request_method="POST")
++    # @authenticate_form
++    def save(self):
++        title = self.request.matchdict["title"]
++        page = self.page_q.filter_by(title=title).first()
++        if not page:
++            page = Page(title=title)
++        # In a real application, you should validate and sanitize
++        # submitted data throughly! escape is a minimal example here
++        page.content = escape(self.request.POST.getone('content'))
++        with transaction.manager:
++            DBSession.add(page)
++        self.request.session.flash('Successfully saved %s!' % title)
++        raise exc.HTTPFound(self.request.route_url("show_page", title=title))
++
++    @view_config(route_name="pages", renderer='pages/index.mako')
++    def index(self):
++        return {
++            "titles": [page.title for page in self.page_q.all()],
++            }
++
++    @view_config(route_name="delete_page", request_method="POST")
++    # @authenticate_form
++    def delete(self):
++        titles = self.request.POST.getall('title')
++        pages = self.page_q.filter(Page.title.in_(titles))
++        with transaction.manager:
++            for page in pages:
++                DBSession.delete(page)
++        # flash only after a successful commit
++        for title in titles:
++            self.request.session.flash('Deleted %s.' % title)
++        raise exc.HTTPFound(self.request.route_url("pages"))
+# HG changeset patch
+# Parent 83ba5b542d67a67465ab3331dad965629aa97fcf
+
+diff --git a/quickwiki/lib/helpers.py b/quickwiki/lib/helpers.py
+--- a/quickwiki/lib/helpers.py
++++ b/quickwiki/lib/helpers.py
+@@ -5,3 +5,10 @@
+ """
+ from webhelpers.html import literal
+ from webhelpers.html.tags import *
++
++token_key = "_authentication_token"
++
++def auth_token_hidden_field(request):
++    from webhelpers.html.builder import HTML
++    token = hidden(token_key, request.session.get_csrf_token())
++    return HTML.div(token, style="display: none;")
+diff --git a/quickwiki/templates/pages/edit.mako b/quickwiki/templates/pages/edit.mako
+--- a/quickwiki/templates/pages/edit.mako
++++ b/quickwiki/templates/pages/edit.mako
+@@ -3,6 +3,7 @@
+ <%def name="header()">Editing ${title}</%def>
+ 
+ ${h.form(request.route_url('save_page', title=title))}
++  ${h.auth_token_hidden_field(request)}
+   ${h.textarea(name='content', rows=7, cols=40, content=content)} <br />
+   ${h.submit(value='Save changes', name='commit')}
+ ${h.end_form()}
+diff --git a/quickwiki/templates/pages/index.mako b/quickwiki/templates/pages/index.mako
+--- a/quickwiki/templates/pages/index.mako
++++ b/quickwiki/templates/pages/index.mako
+@@ -3,6 +3,7 @@
+ <%def name="header()">Title List</%def>
+ 
+ ${h.form(request.route_url('delete_page'))}
++${h.auth_token_hidden_field(request)}
+ 
+ <ul id="titles">
+   % for title in titles:
+diff --git a/quickwiki/views/pages.py b/quickwiki/views/pages.py
+--- a/quickwiki/views/pages.py
++++ b/quickwiki/views/pages.py
+@@ -9,9 +9,20 @@
+ import pyramid.httpexceptions as exc
+ 
+ from quickwiki.model import DBSession, Page, wikiwords
++import quickwiki.lib.helpers as h
+ 
+ log = logging.getLogger(__name__)
+ 
++csrf_detected_message = (
++    "Cross-site request forgery detected, request denied. See "
++    "http://en.wikipedia.org/wiki/Cross-site_request_forgery for more "
++    "information.")
++
++def authenticated_form(request):
++    submitted_token = request.params.get(h.token_key)
++    return submitted_token is not None and \
++        submitted_token == request.session.get_csrf_token()
++
+ class NewPage(Exception):
+     pass
+ 
+@@ -62,8 +73,11 @@
+                 }
+ 
+     @view_config(route_name="save_page", request_method="POST")
+-    # @authenticate_form
+     def save(self):
++        if not authenticated_form(self.request):
++            raise exc.HTTPForbidden(detail=csrf_detected_message)
++        del self.request.POST[h.token_key]
++
+         title = self.request.matchdict["title"]
+         page = self.page_q.filter_by(title=title).first()
+         if not page:
+@@ -83,8 +97,11 @@
+             }
+ 
+     @view_config(route_name="delete_page", request_method="POST")
+-    # @authenticate_form
+     def delete(self):
++        if not authenticated_form(self.request):
++            raise exc.HTTPForbidden(detail=csrf_detected_message)
++        del self.request.POST[h.token_key]
++
+         titles = self.request.POST.getall('title')
+         pages = self.page_q.filter(Page.title.in_(titles))
+         with transaction.manager:
+# HG changeset patch
+# Parent a074ac67c0c06018d5d6d9b564bd9500474a0c9c
+
+diff --git a/quickwiki/config/__init__.py b/quickwiki/config/__init__.py
+deleted file mode 100644
+diff --git a/quickwiki/config/deployment.ini_tmpl b/quickwiki/config/deployment.ini_tmpl
+deleted file mode 100644
+--- a/quickwiki/config/deployment.ini_tmpl
++++ /dev/null
+@@ -1,63 +0,0 @@
+-#
+-# QuickWiki - Pylons configuration
+-#
+-# The %(here)s variable will be replaced with the parent directory of this file
+-#
+-[DEFAULT]
+-debug = true
+-email_to = you@yourdomain.com
+-smtp_server = localhost
+-error_email_from = paste@localhost
+-
+-[server:main]
+-use = egg:Paste#http
+-host = 0.0.0.0
+-port = 5000
+-
+-[app:main]
+-use = egg:QuickWiki
+-full_stack = true
+-static_files = true
+-
+-cache_dir = %(here)s/data
+-beaker.session.key = quickwiki
+-beaker.session.secret = ${app_instance_secret}
+-app_instance_uuid = ${app_instance_uuid}
+-
+-# If you'd like to fine-tune the individual locations of the cache data dirs
+-# for the Cache data, or the Session saves, un-comment the desired settings
+-# here:
+-#beaker.cache.data_dir = %(here)s/data/cache
+-#beaker.session.data_dir = %(here)s/data/sessions
+-
+-# SQLAlchemy database URL
+-sqlalchemy.url = sqlite:///%(here)s/quickwiki.db
+-
+-# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
+-# Debug mode will enable the interactive debugging tool, allowing ANYONE to
+-# execute malicious code after an exception is raised.
+-set debug = false
+-
+-
+-# Logging configuration
+-[loggers]
+-keys = root
+-
+-[handlers]
+-keys = console
+-
+-[formatters]
+-keys = generic
+-
+-[logger_root]
+-level = INFO
+-handlers = console
+-
+-[handler_console]
+-class = StreamHandler
+-args = (sys.stderr,)
+-level = NOTSET
+-formatter = generic
+-
+-[formatter_generic]
+-format = %(asctime)s %(levelname)-5.5s [%(name)s] %(message)s
+diff --git a/quickwiki/config/environment.py b/quickwiki/config/environment.py
+deleted file mode 100644
+--- a/quickwiki/config/environment.py
++++ /dev/null
+@@ -1,53 +0,0 @@
+-"""Pylons environment configuration"""
+-import os
+-
+-from mako.lookup import TemplateLookup
+-from pylons.configuration import PylonsConfig
+-from pylons.error import handle_mako_error
+-from sqlalchemy import engine_from_config
+-
+-import quickwiki.lib.app_globals as app_globals
+-import quickwiki.lib.helpers
+-from quickwiki.config.routing import make_map
+-from quickwiki.model import init_model
+-
+-def load_environment(global_conf, app_conf):
+-    """Configure the Pylons environment via the ``pylons.config``
+-    object
+-    """
+-    config = PylonsConfig()
+-    
+-    # Pylons paths
+-    root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+-    paths = dict(root=root,
+-                 controllers=os.path.join(root, 'controllers'),
+-                 static_files=os.path.join(root, 'public'),
+-                 templates=[os.path.join(root, 'templates')])
+-    
+-    # Initialize config with the basic options
+-    config.init_app(global_conf, app_conf, package='quickwiki', paths=paths)
+-    
+-    config['routes.map'] = make_map(config)
+-    config['pylons.app_globals'] = app_globals.Globals(config)
+-    config['pylons.h'] = quickwiki.lib.helpers
+-    
+-    # Setup cache object as early as possible
+-    import pylons
+-    pylons.cache._push_object(config['pylons.app_globals'].cache)
+-    
+-    # Create the Mako TemplateLookup, with the default auto-escaping
+-    config['pylons.app_globals'].mako_lookup = TemplateLookup(
+-        directories=paths['templates'],
+-        error_handler=handle_mako_error,
+-        module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
+-        input_encoding='utf-8', default_filters=['escape'],
+-        imports=['from webhelpers.html import escape'])
+-    
+-    # Setup the SQLAlchemy database engine
+-    engine = engine_from_config(config, 'sqlalchemy.')
+-    init_model(engine)
+-    
+-    # CONFIGURATION OPTIONS HERE (note: all config options will override
+-    # any Pylons config options)
+-    
+-    return config
+diff --git a/quickwiki/config/middleware.py b/quickwiki/config/middleware.py
+deleted file mode 100644
+--- a/quickwiki/config/middleware.py
++++ /dev/null
+@@ -1,70 +0,0 @@
+-"""Pylons middleware initialization"""
+-from beaker.middleware import SessionMiddleware
+-from paste.cascade import Cascade
+-from paste.registry import RegistryManager
+-from paste.urlparser import StaticURLParser
+-from paste.deploy.converters import asbool
+-from pylons.middleware import ErrorHandler, StatusCodeRedirect
+-from pylons.wsgiapp import PylonsApp
+-from routes.middleware import RoutesMiddleware
+-
+-from quickwiki.config.environment import load_environment
+-
+-def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
+-    """Create a Pylons WSGI application and return it
+-    
+-    ``global_conf``
+-        The inherited configuration for this application. Normally from
+-        the [DEFAULT] section of the Paste ini file.
+-    
+-    ``full_stack``
+-        Whether this application provides a full WSGI stack (by default,
+-        meaning it handles its own exceptions and errors). Disable
+-        full_stack when this application is "managed" by another WSGI
+-        middleware.
+-    
+-    ``static_files``
+-        Whether this application serves its own static files; disable
+-        when another web server is responsible for serving them.
+-    
+-    ``app_conf``
+-        The application's local configuration. Normally specified in
+-        the [app:<name>] section of the Paste ini file (where <name>
+-        defaults to main).
+-    
+-    """
+-    # Configure the Pylons environment
+-    config = load_environment(global_conf, app_conf)
+-    
+-    # The Pylons WSGI app
+-    app = PylonsApp(config=config)
+-    
+-    # Routing/Session/Cache Middleware
+-    # Routing/Session/Cache Middleware
+-    app = RoutesMiddleware(app, config['routes.map'], singleton=False)
+-    app = SessionMiddleware(app, config)
+-    
+-    # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
+-    
+-    if asbool(full_stack):
+-        # Handle Python exceptions
+-        app = ErrorHandler(app, global_conf, **config['pylons.errorware'])
+-        
+-        # Display error documents for 401, 403, 404 status codes (and
+-        # 500 when debug is disabled)
+-        if asbool(config['debug']):
+-            app = StatusCodeRedirect(app)
+-        else:
+-            app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])
+-    
+-    # Establish the Registry for this application
+-    app = RegistryManager(app)
+-    
+-    if asbool(static_files):
+-        # Serve static files
+-        static_app = StaticURLParser(config['pylons.paths']['static_files'])
+-        app = Cascade([static_app, app])
+-    
+-    app.config = config
+-    return app
+-
+diff --git a/quickwiki/controllers/__init__.py b/quickwiki/controllers/__init__.py
+deleted file mode 100644
+diff --git a/quickwiki/controllers/error.py b/quickwiki/controllers/error.py
+deleted file mode 100644
+--- a/quickwiki/controllers/error.py
++++ /dev/null
+@@ -1,46 +0,0 @@
+-import cgi
+-
+-from paste.urlparser import PkgResourcesParser
+-from pylons import request
+-from pylons.controllers.util import forward
+-from pylons.middleware import error_document_template
+-from webhelpers.html.builder import literal
+-
+-from quickwiki.lib.base import BaseController
+-
+-class ErrorController(BaseController):
+-
+-    """Generates error documents as and when they are required.
+-
+-    The ErrorDocuments middleware forwards to ErrorController when error
+-    related status codes are returned from the application.
+-
+-    This behaviour can be altered by changing the parameters to the
+-    ErrorDocuments middleware in your config/middleware.py file.
+-
+-    """
+-
+-    def document(self):
+-        """Render the error document"""
+-        resp = request.environ.get('pylons.original_response')
+-        content = literal(resp.body) or cgi.escape(request.GET.get('message', ''))
+-        page = error_document_template % \
+-            dict(prefix=request.environ.get('SCRIPT_NAME', ''),
+-                 code=cgi.escape(request.GET.get('code', str(resp.status_int))),
+-                 message=content)
+-        return page
+-
+-    def img(self, id):
+-        """Serve Pylons' stock images"""
+-        return self._serve_file('/'.join(['media/img', id]))
+-
+-    def style(self, id):
+-        """Serve Pylons' stock stylesheets"""
+-        return self._serve_file('/'.join(['media/style', id]))
+-
+-    def _serve_file(self, path):
+-        """Call Paste's FileApp (a WSGI application) to serve the file
+-        at the specified path
+-        """
+-        request.environ['PATH_INFO'] = '/%s' % path
+-        return forward(PkgResourcesParser('pylons', 'pylons'))
+diff --git a/quickwiki/lib/app_globals.py b/quickwiki/lib/app_globals.py
+deleted file mode 100644
+--- a/quickwiki/lib/app_globals.py
++++ /dev/null
+@@ -1,20 +0,0 @@
+-"""The application's Globals object"""
+-
+-from beaker.cache import CacheManager
+-from beaker.util import parse_cache_config_options
+-
+-class Globals(object):
+-    """Globals acts as a container for objects available throughout the
+-    life of the application
+-    
+-    """
+-    
+-    def __init__(self, config):
+-        """One instance of Globals is created during application
+-        initialization and is available during requests via the
+-        'app_globals' variable
+-        
+-        """
+-        self.cache = CacheManager(**parse_cache_config_options(config))
+-    
+-
+diff --git a/quickwiki/lib/base.py b/quickwiki/lib/base.py
+deleted file mode 100644
+--- a/quickwiki/lib/base.py
++++ /dev/null
+@@ -1,22 +0,0 @@
+-"""The base Controller API
+-
+-Provides the BaseController class for subclassing.
+-"""
+-from pylons.controllers import WSGIController
+-from pylons.templating import render_mako as render
+-
+-from quickwiki.model.meta import Session
+-
+-class BaseController(WSGIController):
+-    
+-    def __call__(self, environ, start_response):
+-        """Invoke the Controller"""
+-        # WSGIController.__call__ dispatches to the Controller method
+-        # the request is routed to. This routing information is
+-        # available in environ['pylons.routes_dict']
+-        try:
+-            return WSGIController.__call__(self, environ, start_response)
+-        finally:
+-            Session.remove()
+-    
+-
+# HG changeset patch
+# Parent b51e8c365f41868244f3b424fcdf340e7266f393
+
+diff --git a/quickwiki/lib/helpers.py b/quickwiki/lib/helpers.py
+--- a/quickwiki/lib/helpers.py
++++ b/quickwiki/lib/helpers.py
+@@ -5,8 +5,3 @@
+ """
+ from webhelpers.html import literal
+ from webhelpers.html.tags import *
+-from webhelpers.pylonslib.secure_form import secure_form
+-from webhelpers.pylonslib import Flash as _Flash
+-from pylons import url
+-
+-flash = _Flash()
+# HG changeset patch
+# Parent a6aa160de38783c32a5d30e25051ea81f7376f7b
+diff --git a/development.ini b/development.ini
+--- a/development.ini
++++ b/development.ini
+@@ -1,47 +1,50 @@
+ #
+-# QuickWiki - Pylons development environment configuration
++# QuickWiki - Pyramid development environment configuration
+ #
+ # The %(here)s variable will be replaced with the parent directory of this file
+ #
+-[DEFAULT]
+-debug = true
+-# Uncomment and replace with the address which should receive any error reports
+-#email_to = you@yourdomain.com
+-smtp_server = localhost
+-error_email_from = paste@localhost
+-
+-[server:main]
+-use = egg:Paste#http
+-host = 127.0.0.1
+-port = 5000
+-
+ [app:main]
+ use = egg:QuickWiki
+-full_stack = true
+-static_files = true
+ 
+-cache_dir = %(here)s/data
+-beaker.session.key = quickwiki
+-beaker.session.secret = somesecret
++pyramid.reload_templates = true
++pyramid.debug_authorization = false
++pyramid.debug_notfound = false
++pyramid.debug_routematch = false
++pyramid.default_locale_name = en
++pyramid.includes =
++    pyramid_debugtoolbar
++    pyramid_tm
+ 
+-# If you'd like to fine-tune the individual locations of the cache data dirs
+-# for the Cache data, or the Session saves, un-comment the desired settings
+-# here:
+-#beaker.cache.data_dir = %(here)s/data/cache
+-#beaker.session.data_dir = %(here)s/data/sessions
++mako.directories = quickwiki:templates
++
++# Beaker cache
++cache.regions = default_term, second, short_term, long_term
++cache.type = memory
++cache.second.expire = 1
++cache.short_term.expire = 60
++cache.default_term.expire = 300
++cache.long_term.expire = 3600
++
++# Beaker sessions
++session.type = file
++session.key = quickwiki
++session.secret = somesecret
++session.data_dir = %(here)s/data/sessions/data
++session.lock_dir = %(here)s/data/sessions/lock
+ 
+ # SQLAlchemy database URL
+ sqlalchemy.url = sqlite:///%(here)s/quickwiki.db
+ 
+-# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
+-# Debug mode will enable the interactive debugging tool, allowing ANYONE to
+-# execute malicious code after an exception is raised.
+-#set debug = false
++
++[server:main]
++use = egg:waitress#main
++host = 127.0.0.1
++port = 5000
+ 
+ 
+ # Logging configuration
+ [loggers]
+-keys = root, routes, quickwiki, sqlalchemy
++keys = root, quickwiki, sqlalchemy
+ 
+ [handlers]
+ keys = console
+@@ -53,12 +56,6 @@
+ level = INFO
+ handlers = console
+ 
+-[logger_routes]
+-level = INFO
+-handlers =
+-qualname = routes.middleware
+-# "level = DEBUG" logs the route matched and routing variables.
+-
+ [logger_quickwiki]
+ level = DEBUG
+ handlers =
+diff --git a/test.ini b/test.ini
+--- a/test.ini
++++ b/test.ini
+@@ -1,22 +1,16 @@
+ #
+-# QuickWiki - Pylons testing environment configuration
++# QuickWiki - Pyramid testing environment configuration
+ #
+ # The %(here)s variable will be replaced with the parent directory of this file
+ #
+-[DEFAULT]
+-debug = true
+-# Uncomment and replace with the address which should receive any error reports
+-#email_to = you@yourdomain.com
+-smtp_server = localhost
+-error_email_from = paste@localhost
++[app:main]
++use = config:development.ini
++
++# Add additional test specific configuration options as necessary.
++sqlalchemy.url = sqlite:///:memory:
++
+ 
+ [server:main]
+ use = egg:Paste#http
+ host = 127.0.0.1
+ port = 5000
+-
+-[app:main]
+-use = config:development.ini
+-
+-# Add additional test specific configuration options as necessary.
+-sqlalchemy.url = sqlite:///:memory:
+# HG changeset patch
+# Parent 1812472f18abd836f6625b8aca17b43278bb7986
+diff --git a/quickwiki/__init__.py b/quickwiki/__init__.py
+--- a/quickwiki/__init__.py
++++ b/quickwiki/__init__.py
+@@ -0,0 +1,14 @@
++from pyramid.config import Configurator
++from sqlalchemy import engine_from_config
++
++from quickwiki.model import DBSession
++
++def main(global_config, **settings):
++    """ This function returns a Pyramid WSGI application.
++    """
++    engine = engine_from_config(settings, 'sqlalchemy.')
++    DBSession.configure(bind=engine)
++    config = Configurator(settings=settings)
++    config.include("pyramid_beaker")
++    config.scan(".views")
++    return config.make_wsgi_app()
+# HG changeset patch
+# Parent ce60dbcf42441fe776026fac9ac5c35e5679a5f2
+
+diff --git a/quickwiki/model/__init__.py b/quickwiki/model/__init__.py
+--- a/quickwiki/model/__init__.py
++++ b/quickwiki/model/__init__.py
+@@ -2,29 +2,29 @@
+ import logging
+ import re
+ import sys
+-if sys.version < 2.6:
+-    import sets.Set as set
+ from docutils.core import publish_parts
+ from sqlalchemy import orm, Column, Unicode, UnicodeText
+-from quickwiki.lib.helpers import url, link_to
+-from quickwiki.model.meta import Session, Base
++from sqlalchemy.orm import scoped_session, sessionmaker
++from sqlalchemy.ext.declarative import declarative_base
++from zope.sqlalchemy import ZopeTransactionExtension
++from quickwiki.lib.helpers import link_to
+ 
+ log = logging.getLogger(__name__)
++
++DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
++Base = declarative_base()
++
+ # disable docutils security hazards:
+ # http://docutils.sourceforge.net/docs/howto/security.html
+ SAFE_DOCUTILS = dict(file_insertion_enabled=False, raw_enabled=False)
+ wikiwords = re.compile(r'\b([A-Z]\w+[A-Z]+\w+)', re.UNICODE)
+ 
+-def init_model(engine):
+-    """Call me before using any of the tables or classes in the model"""
+-    Session.configure(bind=engine)
+-
+ 
+ class Page(Base):
+     __tablename__ = 'pages'
+     title = Column(Unicode(40), primary_key=True)
+     content = Column(UnicodeText(), default=u'')
+-    
++
+     @orm.validates('title')
+     def validate_title(self, key, title):
+         """Assure that page titles are wikiwords and valid length"""
+@@ -35,8 +35,8 @@
+                                                     title))
+             raise ValueError('Page title must be a wikiword (CamelCase)')
+         return title
+-    
+-    def get_wiki_content(self):
++
++    def get_wiki_content(self, request):
+         """Convert reStructuredText content to HTML for display, and
+         create links for WikiWords
+         """
+@@ -44,15 +44,14 @@
+                                 settings_overrides=SAFE_DOCUTILS)['html_body']
+         titles = set(wikiwords.findall(content))
+         for title in titles:
+-            title_url = url(controller='pages', action='show', title=title)
++            title_url = request.route_url('show_page', title=title)
+             content = content.replace(title, link_to(title, title_url))
+         return content
+-    
++
+     def __unicode__(self):
+         return self.title
+-    
++
+     def __repr__(self):
+         return "<Page('%s', '%s')>" % (self.title, self.content)
+-    
++
+     __str__ = __unicode__
+-    
+diff --git a/quickwiki/model/meta.py b/quickwiki/model/meta.py
+deleted file mode 100644
+--- a/quickwiki/model/meta.py
++++ /dev/null
+@@ -1,11 +0,0 @@
+-"""SQLAlchemy Metadata and Session object"""
+-from sqlalchemy.ext.declarative import declarative_base
+-from sqlalchemy.orm import scoped_session, sessionmaker
+-
+-__all__ = ['Base', 'Session']
+-
+-# SQLAlchemy session manager. Updated by model.init_model()
+-Session = scoped_session(sessionmaker())
+-
+-# The declarative Base
+-Base = declarative_base()
+# HG changeset patch
+# Parent e57e5c3791b554eb09a5595f334b6e9bd7b2222e
+
+diff --git a/quickwiki/__init__.py b/quickwiki/__init__.py
+--- a/quickwiki/__init__.py
++++ b/quickwiki/__init__.py
+@@ -10,5 +10,21 @@
+     DBSession.configure(bind=engine)
+     config = Configurator(settings=settings)
+     config.include("pyramid_beaker")
++    config.include(make_route)
+     config.scan(".views")
+     return config.make_wsgi_app()
++
++def make_route(config):
++    config.add_route('catchall_static', '/*subpath',
++                     path_info=r"/(?:favicon\.ico|quick.css)")
++
++    config.add_route('home', '/')
++    config.add_route('pages', '/pages')
++    config.add_route('show_page', '/pages/show/{title}')
++    config.add_route('edit_page', '/pages/edit/{title}')
++    config.add_route('save_page', '/pages/save/{title}')
++    config.add_route('delete_page', '/pages/delete')
++
++    # A bonus example - the specified defaults allow visiting
++    # example.com/FrontPage to view the page titled 'FrontPage':
++    config.add_route('page', '/{title}')
+diff --git a/quickwiki/config/routing.py b/quickwiki/config/routing.py
+deleted file mode 100644
+--- a/quickwiki/config/routing.py
++++ /dev/null
+@@ -1,49 +0,0 @@
+-"""Routes configuration
+-
+-The more specific and detailed routes should be defined first so they
+-may take precedent over the more generic routes. For more information
+-refer to the routes manual at http://routes.groovie.org/docs/
+-"""
+-from routes import Mapper
+-
+-def make_map(config):
+-    """Create, configure and return the routes Mapper"""
+-    map = Mapper(directory=config['pylons.paths']['controllers'],
+-                 always_scan=config['debug'])
+-    map.minimization = False
+-    map.explicit = False
+-    
+-    # The ErrorController route (handles 404/500 error pages); it should
+-    # likely stay at the top, ensuring it can always be resolved
+-    map.connect('/error/{action}', controller='error')
+-    map.connect('/error/{action}/{id}', controller='error')
+-    
+-    # CUSTOM ROUTES HERE
+-    
+-    map.connect('home', '/',
+-                controller='pages', action='show',
+-                title='FrontPage')
+-    
+-    map.connect('pages', '/pages',
+-                controller='pages', action='index')
+-    
+-    map.connect('show_page', '/pages/show/{title}',
+-                controller='pages', action='show')
+-    
+-    map.connect('edit_page', '/pages/edit/{title}',
+-                controller='pages', action='edit')
+-    
+-    map.connect('save_page', '/pages/save/{title}',
+-                controller='pages', action='save',
+-                conditions=dict(method='POST'))
+-    
+-    map.connect('delete_page', '/pages/delete',
+-                controller='pages', action='delete',
+-                conditions=dict(method='POST'))
+-    
+-    # A bonus example - the specified defaults allow visiting
+-    # example.com/FrontPage to view the page titled 'FrontPage':
+-    map.connect('/{title}', controller='pages', action='show')
+-    
+-    return map
+-
-# Placed by Bitbucket
+setup.diff
+ini.diff
+main.diff
+routing.diff
+model.diff
+controllers.diff
+templates.diff
+helpers.diff
+add_global.diff
+static_views.diff
+tests.diff
+websetup.diff
+delete.diff
+csrf.diff
+# HG changeset patch
+# Parent 317934e0a1c995d0a07a89a5f67d57a877cc6baa
+
+diff --git a/setup.cfg b/setup.cfg
+--- a/setup.cfg
++++ b/setup.cfg
+@@ -2,12 +2,6 @@
+ tag_build = dev
+ tag_svn_revision = true
+ 
+-[easy_install]
+-find_links = http://www.pylonshq.com/download/
+-
+-[nosetests]
+-with-pylons = test.ini
+-
+ # Babel configuration
+ [compile_catalog]
+ domain = quickwiki
+diff --git a/setup.py b/setup.py
+--- a/setup.py
++++ b/setup.py
+@@ -5,6 +5,20 @@
+     use_setuptools()
+     from setuptools import setup, find_packages
+ 
++requires = [
++    # "Pylons>=1.0rc1dev",
++    # "SQLAlchemy>=0.5",
++    "docutils==0.6",
++    "pyramid",
++    "SQLAlchemy",
++    "transaction",
++    "pyramid_tm",
++    "pyramid_debugtoolbar",
++    "zope.sqlalchemy",
++    "waitress",
++    "pyramid_beaker",
++    ]
++
+ setup(
+     name='QuickWiki',
+     version='0.1.8',
+@@ -12,12 +26,7 @@
+     author='',
+     author_email='',
+     url='http://pylonshq.com/docs/en/1.0/tutorials/quickwiki_tutorial/',
+-    install_requires=[
+-        "Pylons>=1.0rc1dev",
+-        "SQLAlchemy>=0.5",
+-        "docutils==0.6",
+-    ],
+-    setup_requires=["PasteScript>=1.6.3"],
++    install_requires=requires,
+     packages=find_packages(exclude=['ez_setup']),
+     include_package_data=True,
+     test_suite='nose.collector',
+@@ -30,9 +39,8 @@
+     paster_plugins=['PasteScript', 'Pylons'],
+     entry_points="""
+     [paste.app_factory]
+-    main = quickwiki.config.middleware:make_app
+-    
+-    [paste.app_install]
+-    main = pylons.util:PylonsInstaller
++    main = quickwiki:main
++    [console_scripts]
++    initialize_QuickWiki_db = quickwiki.scripts.initializedb:main
+     """,
+ )

static_views.diff

+# HG changeset patch
+# Parent 352bc03945ef5d48741a922b7d941bb02c1ff283
+diff --git a/quickwiki/views/static.py b/quickwiki/views/static.py
+new file mode 100644
+--- /dev/null
++++ b/quickwiki/views/static.py
+@@ -0,0 +1,5 @@
++from pyramid.view import view_config
++from pyramid.static import static_view
++
++static = view_config(route_name='catchall_static')(
++    static_view('quickwiki:public', use_subpath=True))
+# HG changeset patch
+# Parent 7525dd31cf89e751733d4b07a6a7385bdd7fc654
+
+diff --git a/quickwiki/templates/base.mako b/quickwiki/templates/base.mako
+--- a/quickwiki/templates/base.mako
++++ b/quickwiki/templates/base.mako
+@@ -10,7 +10,7 @@
+     <div class="content">
+       <h1 class="main">${self.header()}</h1>
+       
+-      <% flashes = h.flash.pop_messages() %>
++      <% flashes = request.session.pop_flash() %>
+       % if flashes:
+         % for flash in flashes:
+         <div class="flash">
+@@ -22,7 +22,7 @@
+       ${next.body()}\
+       
+       <p class="footer">
+-        ${self.footer(request.environ['pylons.routes_dict']['action'])}\
++        ${self.footer(request.matched_route.name)}\
+       </p>
+     </div>
+   </body>
+@@ -30,12 +30,12 @@
+ 
+ ## Don't show links that are redundant for particular pages
+ <%def name="footer(action)">\
+-  Return to the ${h.link_to('FrontPage', url('home'))}
+-  % if action == 'index':
++  Return to the ${h.link_to('FrontPage', request.route_url('home'))}
++  % if action == 'pages':
+     <% return %>
+   % endif
+   % if action != 'edit':
+-    | ${h.link_to('Edit ' + c.title, url('edit_page', title=c.title))}
++    | ${h.link_to('Edit ' + title, request.route_url('edit_page', title=title))}
+   % endif
+-  | ${h.link_to('Title List', url('pages'))}
++  | ${h.link_to('Title List', request.route_url('pages'))}
+ </%def>
+diff --git a/quickwiki/templates/pages/edit.mako b/quickwiki/templates/pages/edit.mako
+--- a/quickwiki/templates/pages/edit.mako
++++ b/quickwiki/templates/pages/edit.mako
+@@ -1,8 +1,8 @@
+ <%inherit file="/base.mako"/>\
+ 
+-<%def name="header()">Editing ${c.title}</%def>
++<%def name="header()">Editing ${title}</%def>
+ 
+-${h.secure_form(url('save_page', title=c.title))}
+-  ${h.textarea(name='content', rows=7, cols=40, content=c.content)} <br />
++${h.form(request.route_url('save_page', title=title))}
++  ${h.textarea(name='content', rows=7, cols=40, content=content)} <br />
+   ${h.submit(value='Save changes', name='commit')}
+ ${h.end_form()}
+diff --git a/quickwiki/templates/pages/index.mako b/quickwiki/templates/pages/index.mako
+--- a/quickwiki/templates/pages/index.mako
++++ b/quickwiki/templates/pages/index.mako
+@@ -2,12 +2,12 @@
+ 
+ <%def name="header()">Title List</%def>
+ 
+-${h.secure_form(h.url('delete_page'))}
++${h.form(request.route_url('delete_page'))}
+ 
+ <ul id="titles">
+-  % for title in c.titles:
++  % for title in titles:
+   <li>
+-    ${h.link_to(title, h.url('show_page', title=title))} -
++    ${h.link_to(title, request.route_url('show_page', title=title))} -
+     ${h.checkbox('title', title)}
+   </li>
+   % endfor
+diff --git a/quickwiki/templates/pages/new.mako b/quickwiki/templates/pages/new.mako
+--- a/quickwiki/templates/pages/new.mako
++++ b/quickwiki/templates/pages/new.mako
+@@ -1,7 +1,7 @@
+ <%inherit file="/base.mako"/>\
+ 
+-<%def name="header()">${c.title}</%def>
++<%def name="header()">${title}</%def>
+ 
+ <p>This page doesn't exist yet.
+-  <a href="${url('edit_page', title=c.title)}">Create the page</a>.
++  <a href="${request.route_url('edit_page', title=title)}">Create the page</a>.
+ </p>
+diff --git a/quickwiki/templates/pages/show.mako b/quickwiki/templates/pages/show.mako
+--- a/quickwiki/templates/pages/show.mako
++++ b/quickwiki/templates/pages/show.mako
+@@ -1,5 +1,5 @@
+ <%inherit file="/base.mako"/>\
+ 
+-<%def name="header()">${c.title}</%def>
++<%def name="header()">${title}</%def>
+ 
+-${h.literal(c.content)}
++${h.literal(content)}
+# HG changeset patch
+# Parent 632e9a9bc680f10a0a3b135c6e0a648117f72ebc
+diff --git a/quickwiki/tests/__init__.py b/quickwiki/tests/__init__.py
+--- a/quickwiki/tests/__init__.py
++++ b/quickwiki/tests/__init__.py
+@@ -1,38 +1,22 @@
+-"""Pylons application test package
++import unittest
++import os
++from webtest import TestApp
++from pyramid import testing
++from pyramid.paster import get_appsettings
+ 
+-This package assumes the Pylons environment is already loaded, such as
+-when this script is imported from the `nosetests --with-pylons=test.ini`
+-command.
++from quickwiki.model import DBSession, Page
++from quickwiki.scripts import initializedb
+ 
+-This module initializes the application via ``websetup`` (`paster
+-setup-app`) and provides the base testing objects.
+-"""
+-from unittest import TestCase
++__all__ = ['TestController', 'testing', 'DBSession', 'Page']
+ 
+-from paste.deploy import loadapp
+-from paste.script.appinstall import SetupCommand
+-from pylons import url
+-from routes.util import URLGenerator
+-from webtest import TestApp
+-from quickwiki.model import Page
+-from quickwiki.model.meta import Session
++# Invoke initializedb with the current config file
++here = os.path.dirname(os.path.abspath(__file__))
++root_dir = os.path.dirname(os.path.dirname(here))
++config_uri = os.path.join(root_dir, 'test.ini')
++initializedb.main([None, config_uri])
+ 
+-import pylons.test
+-
+-__all__ = ['environ', 'url', 'TestController']
+-
+-# Invoke websetup with the current config file
+-SetupCommand('setup-app').run([pylons.test.pylonsapp.config['__file__']])
+-
+-environ = {}
+-
+-class TestController(TestCase):
+-    
+-    def __init__(self, *args, **kwargs):
+-        wsgiapp = pylons.test.pylonsapp
+-        config = wsgiapp.config
+-        self.app = TestApp(wsgiapp)
+-        url._push_object(URLGenerator(config['routes.map'], environ))
+-        TestCase.__init__(self, *args, **kwargs)
+-    
+-
++class TestController(unittest.TestCase):
++    def setUp(self):
++        from quickwiki import main
++        settings = get_appsettings(config_uri)
++        self.app = TestApp(main({}, **settings))
+diff --git a/quickwiki/tests/functional/test_pages.py b/quickwiki/tests/functional/test_pages.py
+--- a/quickwiki/tests/functional/test_pages.py
++++ b/quickwiki/tests/functional/test_pages.py
+@@ -1,8 +1,9 @@
++from pyramid.threadlocal import get_current_request
+ from quickwiki.tests import *
+ 
+ class TestPagesController(TestController):
+ 
+     def test_index(self):
+-        response = self.app.get(url(controller='pages', action='index'))
++        response = self.app.get("/pages")
+         self.assert_('Title List' in response)
+         self.assert_('FrontPage' in response)
+diff --git a/quickwiki/tests/test_models.py b/quickwiki/tests/test_models.py
+--- a/quickwiki/tests/test_models.py
++++ b/quickwiki/tests/test_models.py
+@@ -1,18 +1,18 @@
+-from quickwiki.tests import *
+-from quickwiki.tests import Session, Page, TestController
++from unittest import TestCase
++import transaction
++from quickwiki.model import DBSession, Page
++import quickwiki.tests
+ 
+-class TestPages(TestController):
+-    
++class TestPages(TestCase):
++
+     def test_index(self):
+-        q1 = Session.query(Page).all()
++        q1 = DBSession.query(Page).all()
+         assert len(q1) == 1
+         page = Page()
+         page.title = u'TestPage'
+         page.content = u'Welcome to the QuickWiki test page.'
+-        Session.add(page)
+-        Session.commit()
+-        q2 = Session.query(Page).all()
++        with transaction.manager:
++            DBSession.add(page)
++        q2 = DBSession.query(Page).all()
+         print(q2)
+-        assert q2 == [q1[0], page]
+-    
+-
++        # assert q2 == [q1[0], page]
+# HG changeset patch
+# Parent 3c14855a13b2b922ecae1d5b8a020a9d40428562
+diff --git a/quickwiki/scripts/__init__.py b/quickwiki/scripts/__init__.py
+new file mode 100644
+--- /dev/null
++++ b/quickwiki/scripts/__init__.py
+@@ -0,0 +1,1 @@
++# package
+diff --git a/quickwiki/scripts/initializedb.py b/quickwiki/scripts/initializedb.py
+new file mode 100644
+--- /dev/null
++++ b/quickwiki/scripts/initializedb.py
+@@ -0,0 +1,41 @@
++"""Setup the QuickWiki application"""
++import logging
++import os
++import sys
++
++import transaction
++from sqlalchemy import engine_from_config
++from pyramid.paster import (
++    get_appsettings,
++    setup_logging,
++    )
++
++from quickwiki import model
++from quickwiki.model import DBSession, Base
++
++log = logging.getLogger(__name__)
++
++def usage(argv):
++    cmd = os.path.basename(argv[0])
++    print('usage: %s <config_uri>\n'
++          '(example: "%s development.ini")' % (cmd, cmd))
++    sys.exit(1)
++
++def main(argv=sys.argv):
++    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)
++    # Create the tables if they don't already exist
++    log.info("Creating tables...")
++    Base.metadata.create_all(engine)
++    log.info("Successfully set up.")
++    with transaction.manager:
++        log.info("Adding front page data...")
++        page = model.Page(title=u'FrontPage',
++                          content=u'**Welcome** to the QuickWiki front page!')
++        DBSession.add(page)
++    log.info("Successfully set up.")
+diff --git a/quickwiki/websetup.py b/quickwiki/websetup.py
+deleted file mode 100644
+--- a/quickwiki/websetup.py
++++ /dev/null
+@@ -1,29 +0,0 @@
+-"""Setup the QuickWiki application"""
+-import logging
+-
+-import pylons.test
+-
+-from quickwiki.config.environment import load_environment
+-from quickwiki import model
+-from quickwiki.model.meta import Session, Base
+-
+-log = logging.getLogger(__name__)
+-
+-def setup_app(command, conf, vars):
+-    """Place any commands to setup {{package}} here"""
+-    # Don't reload the app if it was loaded under the testing environment
+-    if not pylons.test.pylonsapp:
+-        load_environment(conf.global_conf, conf.local_conf)
+-    
+-    # Create the tables if they don't already exist
+-    log.info("Creating tables...")
+-    Base.metadata.create_all(bind=Session.bind)
+-    log.info("Successfully set up.")
+-    
+-    log.info("Adding front page data...")
+-    page = model.Page(title=u'FrontPage',
+-                      content=u'**Welcome** to the QuickWiki front page!')
+-    Session.add(page)
+-    Session.commit()
+-    log.info("Successfully set up.")
+-
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.