summaryrefslogtreecommitdiff
path: root/homepage
diff options
context:
space:
mode:
authorMarcel Hellkamp <marc@gsites.de>2009-11-03 09:07:19 +0100
committerMarcel Hellkamp <marc@gsites.de>2009-11-03 09:07:19 +0100
commita71a1d0b36eacc9be6f99700a68f4a3e7760e705 (patch)
treedda86058be1ab34176144fe231748f2399d2137c /homepage
parent83853f64b98b6dd736272213ee48a610b0c6d26e (diff)
downloadbottle-a71a1d0b36eacc9be6f99700a68f4a3e7760e705.tar.gz
Added homepage and docs to main repository
Diffstat (limited to 'homepage')
-rwxr-xr-xhomepage/app.py41
-rw-r--r--homepage/pages/contact.md15
-rw-r--r--homepage/pages/docs.md461
-rw-r--r--homepage/pages/faq.md49
-rw-r--r--homepage/pages/logos.md31
-rw-r--r--homepage/pages/release.md17
-rw-r--r--homepage/pages/start.md218
-rw-r--r--homepage/static/blockquote.pngbin0 -> 321 bytes
-rw-r--r--homepage/static/bottle-logo.pngbin0 -> 10628 bytes
-rw-r--r--homepage/static/bottle-sig.pngbin0 -> 1793 bytes
-rw-r--r--homepage/static/bottle.pngbin0 -> 12514 bytes
-rw-r--r--homepage/static/favicon.icobin0 -> 686 bytes
-rw-r--r--homepage/static/logo.pngbin0 -> 43166 bytes
-rw-r--r--homepage/static/logo_10.pngbin0 -> 45165 bytes
-rw-r--r--homepage/static/logo_100.pngbin0 -> 43288 bytes
-rw-r--r--homepage/static/logo_110.pngbin0 -> 45558 bytes
-rw-r--r--homepage/static/logo_120.pngbin0 -> 45468 bytes
-rw-r--r--homepage/static/logo_130.pngbin0 -> 43884 bytes
-rw-r--r--homepage/static/logo_140.pngbin0 -> 44459 bytes
-rw-r--r--homepage/static/logo_150.pngbin0 -> 44727 bytes
-rw-r--r--homepage/static/logo_160.pngbin0 -> 44755 bytes
-rw-r--r--homepage/static/logo_170.pngbin0 -> 44344 bytes
-rw-r--r--homepage/static/logo_180.pngbin0 -> 45441 bytes
-rw-r--r--homepage/static/logo_190.pngbin0 -> 45168 bytes
-rw-r--r--homepage/static/logo_20.pngbin0 -> 45433 bytes
-rw-r--r--homepage/static/logo_200.pngbin0 -> 42791 bytes
-rw-r--r--homepage/static/logo_30.pngbin0 -> 44391 bytes
-rw-r--r--homepage/static/logo_40.pngbin0 -> 44705 bytes
-rw-r--r--homepage/static/logo_50.pngbin0 -> 45298 bytes
-rw-r--r--homepage/static/logo_60.pngbin0 -> 44490 bytes
-rw-r--r--homepage/static/logo_70.pngbin0 -> 44027 bytes
-rw-r--r--homepage/static/logo_80.pngbin0 -> 45349 bytes
-rw-r--r--homepage/static/logo_90.pngbin0 -> 45477 bytes
-rw-r--r--homepage/static/logo_bg.pngbin0 -> 16589 bytes
-rw-r--r--homepage/static/main.css138
-rw-r--r--homepage/static/news.html5
-rw-r--r--homepage/static/pygments.css100
-rw-r--r--homepage/static/robots.txt0
-rw-r--r--homepage/views/footer.tpl9
-rw-r--r--homepage/views/header.tpl25
-rw-r--r--homepage/views/pagemask.tpl5
41 files changed, 1114 insertions, 0 deletions
diff --git a/homepage/app.py b/homepage/app.py
new file mode 100755
index 0000000..5cf94e9
--- /dev/null
+++ b/homepage/app.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python2.6
+# -*- coding: utf-8 -*-
+from bottle import route, abort, view
+import bottle
+import markdown
+import os.path
+import sys
+import re
+import codecs
+
+# Static files
+
+@route('/:filename#.+\.(css|js|ico|png|txt|html)#')
+def static(filename):
+ bottle.send_file(filename, root='./static/')
+
+# Bottle Pages
+
+pagedir = './pages'
+cachedir = './cache'
+
+@route('/')
+@route('/page/:name#[a-zA-Z0-9_]{3,65}#')
+@view('pagemask')
+def page(name='start'):
+ orig = os.path.join(pagedir, name+'.md')
+ cache = os.path.join(cachedir, name+'.html')
+ if not os.path.exists(orig):
+ abort(404, 'Page %s not found.' % name)
+ if not os.path.exists(cache) \
+ or os.path.getmtime(orig) > os.path.getmtime(cache):
+ with codecs.open(orig, encoding='utf8') as f:
+ html = markdown.markdown(f.read(), ['codehilite(force_linenos=True)','wikilink(base_url=/page/)','toc'])
+ with open(cache, 'w') as f:
+ f.write(html.encode('utf-8'))
+ with open(cache) as f:
+ return dict(content=f.read(), pagename=name.title())
+
+# Start server
+#bottle.debug(True)
+bottle.run(host='0.0.0.0', reloader=True, port=int(sys.argv[1]), server=bottle.PasteServer)
diff --git a/homepage/pages/contact.md b/homepage/pages/contact.md
new file mode 100644
index 0000000..c89b504
--- /dev/null
+++ b/homepage/pages/contact.md
@@ -0,0 +1,15 @@
+Kontakt und Impressum
+=====================
+
+Impressum und Kontaktdaten nach [§5 TMG](http://bundesrecht.juris.de/tmg/__5.html).
+Die Nutzung der folgenden Kontaktdaten ist ausschließlich für die
+Kontaktaufnahme mit dem Betreiber dieser Webseite bei rechtlichen
+Problemen vorgesehen. Insbesondere die Nutzung zu Werbe- oder ähnlichen
+Zwecken ist ausdrücklich untersagt.
+
+ * **Betreiber**: Marcel Hellkamp
+ * **Ort**: D - 37075 Göttingen
+ * **Strasse**: Theodor-Heuss Strasse 13
+ * **Telefon**: +49 (0) 551 2509854
+ * **E-Mail**: bottle [ät] paws.de
+
diff --git a/homepage/pages/docs.md b/homepage/pages/docs.md
new file mode 100644
index 0000000..b755670
--- /dev/null
+++ b/homepage/pages/docs.md
@@ -0,0 +1,461 @@
+[TOC]
+
+Bottle Documentation
+====================
+
+This document is a work in progress. If you have questions not answered here,
+please file a ticket at bottle's [issue
+tracker](http://github.com/defnull/bottle/issues).
+
+## Basic Routes
+
+Routes are used to map request URLs to callables that generate the response content. Bottle has a `route()` decorator to do that.
+
+ #!Python
+ from bottle import route, run
+ @route('/hello')
+ def hello():
+ return "Hello World!"
+ run() # This starts the HTTP server
+
+Run this script, visit <http://localhost:8080/hello> and you will see "Hello World!" in your Browser.
+
+
+
+### GET, POST, HEAD, ...
+
+The route decorator has an optional keyword argument `method` which defaults to `method='GET'`.
+Possible values are `POST`, `PUT`, `DELETE`, `HEAD` or any other HTTP request method you want to listen to.
+
+ #!Python
+ from bottle import route, request
+ @route('/form/submit', method='POST')
+ def form_submit():
+ form_data = request.POST
+ do_something(form_data)
+ return "Done"
+
+
+
+
+
+
+
+
+## Dynamic Routes
+
+You can extract parts of the URL and create dynamic routes with an easy syntax.
+
+ #!Python
+ @route('/hello/:name')
+ def hello(name):
+ return "Hello %s!" % name
+
+By default, a `:placeholder` matches everything up to the next
+slash. To change that, you can add some regular expression in between `#`s:
+
+ #!Python
+ @route('/get_object/:id#[0-9]+#')
+ def get(id):
+ return "Object ID: %d" % int(id)
+
+or even use full features regular expressions with named groups:
+
+ #!Python
+ @route('/get_object/(?P<id>[0-9]+)')
+ def get(id):
+ return "Object ID: %d" % int(id)
+
+As you can see, URL parameters remain strings, even if they are
+configured to only match digits. You have to explicitly cast them into
+the type you need.
+
+### The @validate() decorator
+
+Bottle offers a handy decorator called `validate()` to check and manipulate URL parameters.
+It takes callables as keyword arguments and filters every URL parameter
+through the corresponding callable before they are passed to your
+request handler.
+
+ #!Python
+ from bottle import route, validate
+ # /test/validate/1/2.3/4,5,6,7
+ @route('/test/validate/:i/:f/:csv')
+ @validate(i=int, f=float, csv=lambda x: map(int, x.split(',')))
+ def validate_test(i, f, csv):
+ return "Int: %d, Float:%f, List:%s" % (i, f, repr(csv))
+
+You may raise `ValueError` in your custom callable if the parameter
+does not validate.
+
+
+
+
+
+## Returning files and JSON
+
+The WSGI specification expects an iterable list of strings and can't handle file
+objects or plain strings. Bottle automatically converts them to iterables, so
+you don't have to. The following example will work with Bottle, but won't work
+with pure WSGI.
+
+ #!Python
+ @route('/get_string')
+ def get_string():
+ return "This is not a list of strings, but a single string"
+ @route('/file')
+ def get_file():
+ return open('some/file.txt','r')
+
+Even dictionaries are allowed. They are converted to
+[json](http://de.wikipedia.org/wiki/JavaScript_Object_Notation) and returned
+as `Content-Type: application/json`.
+
+ #!Python
+ @route('/api/status')
+ def api_status():
+ return {'status':'online', 'servertime':time.time()}
+
+You can turn off this feature: `bottle.default_app().autojson = False`
+
+## HTTP errors and redirects
+
+ #!Python
+ from bottle import redirect, abort
+
+ @route('/wrong/url')
+ def wrong():
+ redirect("/right/url")
+
+ @route('/restricted')
+ def restricted():
+ abort(401, "Sorry, access denied.")
+
+
+## Static files
+
+ #!Python
+ from bottle import send_file
+
+ @route('/static/:filename')
+ def static_file(filename):
+ send_file(filename, root='/path/to/static/files')
+
+
+## Cookies
+
+Bottle stores cookies sent by the client in a dictionary called `request.COOKIES`. To create new cookies,
+the method `response.set_cookie(name, value[, **params])` is used. It accepts additional parameters as long as they are valid
+cookie attributes supported by [SimpleCookie](http://docs.python.org/library/cookie.html#morsel-objects).
+
+ #!Python
+ from bottle import response
+ response.set_cookie('key','value', path='/', domain='example.com', secure=True, expires=+500, ...)
+
+To set the `max-age` attribute (which is not a valid Python parameter name) you can directly access an instance of
+[cookie.SimpleCookie](http://docs.python.org/library/cookie.html#Cookie.SimpleCookie) in `response.COOKIES`.
+
+ #!Python
+ from bottle import response
+ response.COOKIES['key'] = 'value'
+ response.COOKIES['key']['max-age'] = 500
+
+
+
+
+
+
+
+
+
+
+
+## Templates
+
+Bottle uses its own little template engine by default. You can use a template by
+calling `template(template_name, **template_arguments)` and returning
+the result.
+
+ #!Python
+ @route('/hello/:name')
+ def hello(name):
+ return template('hello_template', username=name)
+
+This will load the template `hello_template.tpl` with the `username` variable set to the URL `:name` part and return the result as a string.
+
+The `hello_template.tpl` file could look like this:
+
+ #!html
+ <h1>Hello {{username}}</h1>
+ <p>How are you?</p>
+
+### Template search path
+
+The list `bottle.TEMPLATE_PATH` is used to map template names to actual
+file names. By default, this list contains `['./%s.tpl', './views/%s.tpl']`.
+
+### Template caching
+
+Templates are cached in memory after compilation. Modifications made to
+the template file will have no affect until you clear the template
+cache. Call `bottle.TEMPLATES.clear()` to do so.
+
+
+
+
+
+
+
+
+
+
+
+## Template Syntax
+
+The template syntax is a very thin layer around the Python language.
+It's main purpose is to ensure correct indention of blocks, so you
+can format your template without worrying about indentions. Here is the
+complete syntax description:
+
+ * `%...` starts a line of python code. You don't have to worry about indentions. Bottle handles that for you.
+ * `%end` closes a Python block opened by `%if ...`, `%for ...` or other block statements. Explicitly closing of blocks is required.
+ * `{{...}}` prints the result of the included python statement.
+ * `%include template_name optional_arguments` allows you to include other templates.
+ * Every other line is returned as text.
+
+Example:
+
+ #!html
+ %header = 'Test Template'
+ %items = [1,2,3,'fly']
+ %include http_header title=header, use_js=['jquery.js', 'default.js']
+ <h1>{{header.title()}}</h1>
+ <ul>
+ %for item in items:
+ <li>
+ %if isinstance(item, int):
+ Zahl: {{item}}
+ %else:
+ %try:
+ Other type: ({{type(item).__name__}}) {{repr(item)}}
+ %except:
+ Error: Item has no string representation.
+ %end try-block (yes, you may add comments here)
+ %end
+ </li>
+ %end
+ </ul>
+ %include http_footer
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Key/Value Databases
+
+Bottle (>0.4.6) offers a persistent key/value database accessible through the
+`bottle.db` module variable. You can use key or attribute syntax to store or
+fetch any pickle-able object to the database. Both
+`bottle.db.bucket_name.key_name` and `bottle.db[bucket_name][key_name]`
+will work.
+
+Missing buckets are created on demand. You don't have to check for
+their existence before using them. Just be sure to use alphanumeric
+bucket-names.
+
+The bucket objects behave like mappings (dictionaries), except that
+only strings are allowed for keys and values must be pickle-able.
+Printing a bucket object doesn't print the keys and values, and the
+`items()` and `values()` methods are not supported. Missing keys will raise
+`KeyError` as expected.
+
+### Persistence
+During a request live-cycle, all changes are cached in thread-local memory. At
+the end of the request, the changes are saved automatically so the next request
+will have access to the updated values. Each bucket is stored in a separate file
+in `bottle.DB_PATH`. Be sure to allow write-access to this path and use bucket
+names that are allowed in filenames.
+
+### Race conditions
+You don't have do worry about file corruption but race conditions are still a
+problem in multi-threaded or forked environments. You can call
+`bottle.db.save()` or `botle.db.bucket_name.save()` to flush the thread-local
+memory cache to disk, but there is no way to detect database changes made in
+other threads until these threads call `bottle.db.save()` or leave the current
+request cycle.
+
+### Example
+
+ #!Python
+ from bottle import route, db
+ @route('/db/counter')
+ def db_counter():
+ if 'hits' not in db.counter:
+ db.counter.hits = 0
+ db['counter']['hits'] += 1
+ return "Total hits: %d!" % db.counter.hits
+
+
+
+
+
+
+
+
+
+
+## Using WSGI and Middleware
+
+A call to `bottle.default_app()` returns your WSGI application. After applying as many WSGI middleware modules as you like, you can tell
+`bottle.run()` to use your wrapped application, instead of the default one.
+
+ #!Python
+ from bottle import default_app, run
+ app = default_app()
+ newapp = YourMiddleware(app)
+ run(app=newapp)
+
+### How default_app() works
+
+Bottle creates a single instance of `bottle.Bottle()` and uses it as a default for most of the modul-level decorators and the `bottle.run()` routine.
+`bottle.default_app()` returns (or changes) this default. You may, however, create your own instances of `bottle.Bottle()`.
+
+ #!Python
+ from bottle import Bottle, run
+ mybottle = Bottle()
+ @mybottle.route('/')
+ def index():
+ return 'default_app'
+ run(app=mybottle)
+
+
+## Deployment
+
+Bottle uses the build-in `wsgiref.SimpleServer` by default. This non-threading
+HTTP server is perfectly fine for development and early production,
+but may become a performance bottleneck when server load increases.
+
+There are three ways to eliminate this bottleneck:
+
+ * Use a multi-threaded server adapter
+ * Spread the load between multiple bottle instances
+ * Do both
+
+### Multi-Threaded Server
+
+The easiest way to increase performance is to install a multi-threaded and
+WSGI-capable HTTP server like [Paste][paste], [flup][flup], [cherrypy][cherrypy]
+or [fapws3][fapws3] and use the corresponding bottle server-adapter.
+
+ #!Python
+ from bottle import PasteServer, FlupServer, FapwsServer, CherryPyServer
+ bottle.run(server=PasteServer) # Example
+
+If bottle is missing an adapter for your favorite server or you want to tweak
+the server settings, you may want to manually set up your HTTP server and use
+`bottle.default_app()` to access your WSGI application.
+
+ #!Python
+ def run_custom_paste_server(self, host, port):
+ myapp = bottle.default_app()
+ from paste import httpserver
+ httpserver.serve(myapp, host=host, port=port)
+
+### Multiple Server Processes
+
+A single Python process can only utilize one CPU at a time, even if
+there are more CPU cores available. The trick is to balance the load
+between multiple independent Python processes to utilize all of your
+CPU cores.
+
+Instead of a single Bottle application server, you start one instances
+of your server for each CPU core available using different local port
+(localhost:8080, 8081, 8082, ...). Then a high performance load
+balancer acts as a reverse proxy and forwards each new requests to
+a random Bottle processes, spreading the load between all available
+backed server instances. This way you can use all of your CPU cores and
+even spread out the load between different physical servers.
+
+But there are a few drawbacks:
+
+ * You can't easily share data between multiple Python processes.
+ * It takes a lot of memory to run several copies of Python and Bottle
+at the same time.
+
+One of the fastest load balancer available is [pound](http://www.apsis.ch/pound/) but most common web servers have a proxy-module that can do the work just fine.
+
+I'll add examples for [lighttpd](http://www.lighttpd.net/) and
+[Apache](http://www.apache.org/) web servers soon.
+
+### Apache mod_wsgi
+
+Instead of running your own HTTP server from within Bottle, you can
+attach Bottle applications to an [Apache server][apache] using
+[mod_wsgi][] and Bottles WSGI interface.
+
+All you need is an `app.wsgi` file that provides an
+`application` object. This object is used by mod_wsgi to start your
+application and should be a WSGI conform Python callable.
+
+ #!Python
+ # File: /var/www/yourapp/app.wsgi
+
+ # Change working directory so relative paths (and template lookup) work again
+ os.chdir(os.path.dirname(__file__))
+
+ import bottle
+ # ... add or import your bottle app code here ...
+ # Do NOT use bottle.run() with mod_wsgi
+ application = bottle.default_app()
+
+The Apache configuration may look like this:
+
+ #!ApacheConf
+ <VirtualHost *>
+ ServerName example.com
+
+ WSGIDaemonProcess yourapp user=www-data group=www-data processes=1 threads=5
+ WSGIScriptAlias / /var/www/yourapp/app.wsgi
+
+ <Directory /var/www/yourapp>
+ WSGIProcessGroup yourapp
+ WSGIApplicationGroup %{GLOBAL}
+ Order deny,allow
+ Allow from all
+ </Directory>
+ </VirtualHost>
+
+
+### Google AppEngine
+
+ #!Python
+ import bottle
+ from google.appengine.ext.webapp import util
+ # ... add or import your bottle app code here ...
+ # Do NOT use bottle.run() with AppEngine
+ util.run_wsgi_app(bottle.default_app())
+
+### Good old CGI
+
+CGI is slow as hell, but it works.
+
+ #!Python
+ import bottle
+ # ... add or import your bottle app code here ...
+ bottle.run(server=bottle.CGIServer)
+
+[mako]: http://www.makotemplates.org/
+[cherrypy]: http://www.cherrypy.org/
+[flup]: http://trac.saddi.com/flup
+[paste]: http://pythonpaste.org/
+[fapws3]: http://github.com/william-os4y/fapws3
+[apache]: http://www.apache.org/
+[mod_wsgi]: http://code.google.com/p/modwsgi/
diff --git a/homepage/pages/faq.md b/homepage/pages/faq.md
new file mode 100644
index 0000000..1aa114d
--- /dev/null
+++ b/homepage/pages/faq.md
@@ -0,0 +1,49 @@
+[TOC]
+
+Frequently Asked Questions
+==========================
+
+
+## How to Ignore Tailing Slashes?
+
+Bottle does not ignore tailing slashes by default.
+To handle URLs like `/example` and `/example/` the same,
+you could add two `@route` decorators (fast when using static routes)
+
+ #!Python
+ @route('/test')
+ @route('/test/')
+ def test(): pass
+
+or use dynamic routes
+
+ #!Python
+ @route('/test/?')
+ def test(): pass
+
+or add a WSGI middleware that strips tailing '/' from URLs
+
+ #!Python
+ class StripPathMiddleware(object):
+ def __init__(self, app):
+ self.app = app
+ def __call__(self, e, h):
+ e['PATH_INFO'] = e['PATH_INFO'].rstrip('/')
+ return self.app(e,h)
+
+ app = bottle.default_app()
+ app = StripPathMiddleware(app)
+ bottle.run(app=app)
+
+
+
+
+
+## Apache/mod_python and Template Not Found?
+
+Templates are searched in "./" or "./views/" but Apaches mod_python
+changes your working directory. You can use
+
+ bottle.TEMPLATE_PATH.insert(0,'/absolut/path/to/templates/%s.tpl')
+
+to add an absolute search path.
diff --git a/homepage/pages/logos.md b/homepage/pages/logos.md
new file mode 100644
index 0000000..b484a09
--- /dev/null
+++ b/homepage/pages/logos.md
@@ -0,0 +1,31 @@
+Bottle Logo
+===========
+
+The official color is:
+
+<img src="/logo_130.png" />
+
+But ...
+
+<div style="text-align: center">
+<img src="/logo_10.png" alt='color logo 1' width='10%' />
+<img src="/logo_20.png" alt='color logo 2' width='10%' />
+<img src="/logo_30.png" alt='color logo 3' width='10%' />
+<img src="/logo_40.png" alt='color logo 4' width='10%' />
+<img src="/logo_50.png" alt='color logo 5' width='10%' />
+<img src="/logo_60.png" alt='color logo 6' width='10%' />
+<img src="/logo_70.png" alt='color logo 7' width='10%' />
+<img src="/logo_80.png" alt='color logo 8' width='10%' />
+<img src="/logo_90.png" alt='color logo 9' width='10%' />
+<img src="/logo_100.png" alt='color logo 10' width='10%' />
+<img src="/logo_110.png" alt='color logo 11' width='10%' />
+<img src="/logo_120.png" alt='color logo 12' width='10%' />
+<img src="/logo_130.png" alt='color logo 14' width='10%' />
+<img src="/logo_140.png" alt='color logo 14' width='10%' />
+<img src="/logo_150.png" alt='color logo 15' width='10%' />
+<img src="/logo_160.png" alt='color logo 16' width='10%' />
+<img src="/logo_170.png" alt='color logo 17' width='10%' />
+<img src="/logo_180.png" alt='color logo 18' width='10%' />
+<img src="/logo_190.png" alt='color logo 19' width='10%' />
+<img src="/logo_200.png" alt='color logo 20' width='10%' />
+</div>
diff --git a/homepage/pages/release.md b/homepage/pages/release.md
new file mode 100644
index 0000000..4a3d62c
--- /dev/null
+++ b/homepage/pages/release.md
@@ -0,0 +1,17 @@
+# Bottle Release Strategy
+
+## Version Numbers and Releases
+
+Bottles version number breaks into three parts: Major release, minor release and revision.
+Major releases are very rare and only happen on significant jumps in functionality.
+Minor releases introduce new functionality and may break compatibility with previous releases in some places, but are mostly API compatible.
+Revisions may fix bugs, improve performance or introduce minor new features, but (hopefully) never break compatibility.
+
+It is save and recommended to update to new revisions. You should consider updateng to new releases as well, because I don't have the time and enegry to support old releases.
+
+In all three parts, a zero indicates 'beta' status. Bottle-X.X.0 is a release candidate. Bottle-X.0.X is a preview for the next major release. Use them with care.
+
+## Development
+
+The 'master' branch on github always contains the latest release candidate. New features and bugfixes are developed and tested in separate branches or forks until they are merged into bottle/master.
+You can use 'master' for testing. It should work most of the time.
diff --git a/homepage/pages/start.md b/homepage/pages/start.md
new file mode 100644
index 0000000..dffd638
--- /dev/null
+++ b/homepage/pages/start.md
@@ -0,0 +1,218 @@
+Bottle Web Framework
+====================
+
+<div style="float: right; padding: 0px 0px 2em 2em"><img src="/bottle-logo.png" alt="Botle Logo" /></div>
+
+Bottle is a fast and simple [WSGI][wsgi]-framework for the [Python Programming Language][py]. It
+offers request dispatching with url parameter support ([routes](/page/docs#basic_routes)), [templates](/page/docs#templates), key/value
+[databases](/page/docs#key_value_databases), a build-in HTTP server and adapters for many third party
+WSGI/HTTP-server and template engines. All in a single file and with no dependencies other than the Python standard library.
+
+ [wsgi]: http://www.wsgi.org/wsgi/
+ [py]: http://python.org/
+ [bottle-dl]: http://github.com/defnull/bottle/raw/master/bottle.py
+
+### Installation and Dependencies
+
+You can install the latest stable release with `easy_install -U bottle` or just download the newest [bottle.py][bottle-dl] and place it in your project directory. There are no (hard) dependencies other than the Python standard library. Bottle runs with **Python 2.5+ and 3.x** (using 2to3)
+
+<!--
+
+## News
+
+<ul id='newshere'><li><i>Loading...</i></li><li>&nbsp;</li><li>&nbsp;</li><li>&nbsp;</li><li>&nbsp;</li></ul>
+<script type="text/javascript">
+ $('#newshere').load('http://bottle.paws.de/news.html')
+</script>
+
+-->
+
+## Features and Examples
+
+### Small and Lightweight
+
+No installation or configuration required. No dependencies other than
+the Python standard library. Just get a copy of bottle.py and start
+coding! A basic "Hello World" application in Bottle looks like this:
+
+ #!Python
+ from bottle import route, run
+
+ @route('/')
+ def index():
+ return 'Hello World!'
+
+ run(host='localhost', port=8080)
+
+That's it. Start it up and go to <http://localhost:8080/>.
+
+### Nice looking URLs
+
+Extract data out of dynamic URLs with a simple route syntax.
+
+ #!Python
+ @route('/hello/:name')
+ def hello(name):
+ return 'Hello, %s' % name
+
+Or use full featured regular expressions to do so.
+
+ #!Python
+ @route('/friends/(?<name>(Alice|Bob))')
+ def friends(name):
+ return 'Hello, %s! Good to see you :)' % name
+
+### Static Files, Redirects and HTTP Errors
+
+Use these handy helpers for regular tasks.
+
+ #!Python
+ from bottle import send_file, redirect, abort
+
+ @route('/static/:filename')
+ def static_file(filename):
+ send_file(filename, root='/path/to/static/files')
+
+ @route('/wrong/url')
+ def wrong():
+ redirect("/right/url")
+
+ @route('/restricted')
+ def restricted():
+ abort(401, "Sorry, access denied.")
+
+### POST, GET, Header and Cookies
+
+As easy as using a `dict()`
+
+ #!Python
+ from bottle import request, response
+
+ @route('/hello/cookie')
+ def cookie():
+ name = request.COOKIES.get('name', 'Stranger')
+ response.header['Content-Type'] = 'text/plain'
+ return 'Hello, %s' % name
+
+ @route('/hello/cookie', method='POST')
+ def set_cookie():
+ if 'name' in request.POST:
+ name = request.POST['name']
+ response.COOKIES['name'] = name
+ return 'OK'
+
+### Templates
+
+Bottle includes a simple and lightning fast template engine
+
+ #!Python
+ @get('/hello/template/:names')
+ def pretty_hello(names):
+ names = names.split(',')
+ return template('hello', title='Hello World', names=names)
+
+And here is the template:
+
+ #!html
+ <html>
+ <head>
+ <title>{{title}}</title>
+ </head>
+ <body>
+ %for name in names:
+ <p>Hello, <strong>{{name}}</strong></p>
+ %end
+ </body>
+ </html>
+
+Use [mako][] it you need more features
+
+ #!Python
+ from bottle import mako_template as template
+
+### HTTP Server
+
+Bottle has a HTTP Server build in but also supports [cherrypy][],
+[flup][], [paste][] and [fapws3][] as alternatives.
+
+ #!Python
+ from bottle import PasteServer
+ run(server=PasteServer)
+
+
+### Non-Features and Known Bugs
+
+Bottle does **not** include (yet):
+
+ * Models and ORMs: Choose your own (SQLAlchemy, Elixir)
+ * HTML-Helper, Session, Identification and Authentication: Do it yourself
+ * Scaffolding: No, sorry
+
+Some things don't work (yet):
+
+ * Multipart File Uploads do not work with Python 3.x because the cgi.FileStorage is broken.
+
+[mako]: http://www.makotemplates.org/
+[cherrypy]: http://www.cherrypy.org/
+[flup]: http://trac.saddi.com/flup
+[paste]: http://pythonpaste.org/
+[fapws3]: http://github.com/william-os4y/fapws3
+
+
+
+
+
+## Voices
+
+[Kaelin](http://bitbucket.org/kaelin), 2009-10-22, [PyPi Comment](http://pypi.python.org/pypi/bottle):
+
+> Bottle rocks! The fastest path I've found between idea and implementation for simple Web applications.
+
+[Seth](http://blog.curiasolutions.com/about/) in his [blog](http://blog.curiasolutions.com/2009/09/the-great-web-development-shootout/) [posts](http://blog.curiasolutions.com/2009/10/the-great-web-technology-shootout-round-3-better-faster-and-shinier/) about common web framework performance:
+
+> As you can see, there was practically no difference in speed between Bottle and pure WSGI in a basic “hello world” test. Even with the addition of Mako and SQLAlchemy, Bottle performed significantly faster than a bare Pylons or Django setup. On a side note, adding a sample template using Bottle’s default templating package didn’t seem to change these numbers at all.
+
+## Projects using Bottle
+
+ * [flugzeit-rechner.de](http://www.flugzeit-rechner.de/) runs on Bottle and Jinja2.
+ * [Cuttlefish](http://bitbucket.org/kaelin/cuttlefish/) is a browser-based search tool for quickly `grep`ing source code.
+ * [Torque](http://github.com/jreid42/torque) is a multiuser collaborative interface for torrenting.
+ * [Message in a Bottle](http://github.com/kennyshen/MIAB) - a simple community messaging app using Bottle and Cassandra.
+
+
+## Thanks to
+
+In chronological order of their last contribution (DESC).
+
+ * [Damien Degois](http://github.com/babs) for his `If-Modified-Since` support in `send_file()` and his excellent bug reports
+ * [Stefan Matthias Aust](http://github.com/sma) for his contribution to `SimpleTemplate` and `Jinja2Template`
+ * [DauerBaustelle](http://github.com/dauerbaustelle) for his ideas
+ * [smallfish](http://pynotes.appspot.com/) for his chinese translation of the bottle documentation
+ * [Johannes Schönberger](http://www.python-forum.de/user-6026.html) for his auto reloading code
+ * [clutchski](http://github.com/clutchski) for his `CGIAdapter` and CGI support
+ * huanguan1978 for his windows `send_file()` bug report and patch
+ * The [German Python Community](http://www.python-forum.de/topic-19451.html) for their support and motivation
+
+
+## Licence (MIT)
+
+ Copyright (c) 2009, Marcel Hellkamp.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
diff --git a/homepage/static/blockquote.png b/homepage/static/blockquote.png
new file mode 100644
index 0000000..74d7cdd
--- /dev/null
+++ b/homepage/static/blockquote.png
Binary files differ
diff --git a/homepage/static/bottle-logo.png b/homepage/static/bottle-logo.png
new file mode 100644
index 0000000..9afb050
--- /dev/null
+++ b/homepage/static/bottle-logo.png
Binary files differ
diff --git a/homepage/static/bottle-sig.png b/homepage/static/bottle-sig.png
new file mode 100644
index 0000000..6d3a625
--- /dev/null
+++ b/homepage/static/bottle-sig.png
Binary files differ
diff --git a/homepage/static/bottle.png b/homepage/static/bottle.png
new file mode 100644
index 0000000..0b54911
--- /dev/null
+++ b/homepage/static/bottle.png
Binary files differ
diff --git a/homepage/static/favicon.ico b/homepage/static/favicon.ico
new file mode 100644
index 0000000..ad2abbe
--- /dev/null
+++ b/homepage/static/favicon.ico
Binary files differ
diff --git a/homepage/static/logo.png b/homepage/static/logo.png
new file mode 100644
index 0000000..29d2467
--- /dev/null
+++ b/homepage/static/logo.png
Binary files differ
diff --git a/homepage/static/logo_10.png b/homepage/static/logo_10.png
new file mode 100644
index 0000000..c79c516
--- /dev/null
+++ b/homepage/static/logo_10.png
Binary files differ
diff --git a/homepage/static/logo_100.png b/homepage/static/logo_100.png
new file mode 100644
index 0000000..d2914cc
--- /dev/null
+++ b/homepage/static/logo_100.png
Binary files differ
diff --git a/homepage/static/logo_110.png b/homepage/static/logo_110.png
new file mode 100644
index 0000000..81f8317
--- /dev/null
+++ b/homepage/static/logo_110.png
Binary files differ
diff --git a/homepage/static/logo_120.png b/homepage/static/logo_120.png
new file mode 100644
index 0000000..3f8062f
--- /dev/null
+++ b/homepage/static/logo_120.png
Binary files differ
diff --git a/homepage/static/logo_130.png b/homepage/static/logo_130.png
new file mode 100644
index 0000000..8cfc1b4
--- /dev/null
+++ b/homepage/static/logo_130.png
Binary files differ
diff --git a/homepage/static/logo_140.png b/homepage/static/logo_140.png
new file mode 100644
index 0000000..33bc394
--- /dev/null
+++ b/homepage/static/logo_140.png
Binary files differ
diff --git a/homepage/static/logo_150.png b/homepage/static/logo_150.png
new file mode 100644
index 0000000..b088667
--- /dev/null
+++ b/homepage/static/logo_150.png
Binary files differ
diff --git a/homepage/static/logo_160.png b/homepage/static/logo_160.png
new file mode 100644
index 0000000..06b3384
--- /dev/null
+++ b/homepage/static/logo_160.png
Binary files differ
diff --git a/homepage/static/logo_170.png b/homepage/static/logo_170.png
new file mode 100644
index 0000000..ecbcb7b
--- /dev/null
+++ b/homepage/static/logo_170.png
Binary files differ
diff --git a/homepage/static/logo_180.png b/homepage/static/logo_180.png
new file mode 100644
index 0000000..4506202
--- /dev/null
+++ b/homepage/static/logo_180.png
Binary files differ
diff --git a/homepage/static/logo_190.png b/homepage/static/logo_190.png
new file mode 100644
index 0000000..3b32256
--- /dev/null
+++ b/homepage/static/logo_190.png
Binary files differ
diff --git a/homepage/static/logo_20.png b/homepage/static/logo_20.png
new file mode 100644
index 0000000..23e7fd6
--- /dev/null
+++ b/homepage/static/logo_20.png
Binary files differ
diff --git a/homepage/static/logo_200.png b/homepage/static/logo_200.png
new file mode 100644
index 0000000..c2c483d
--- /dev/null
+++ b/homepage/static/logo_200.png
Binary files differ
diff --git a/homepage/static/logo_30.png b/homepage/static/logo_30.png
new file mode 100644
index 0000000..883a818
--- /dev/null
+++ b/homepage/static/logo_30.png
Binary files differ
diff --git a/homepage/static/logo_40.png b/homepage/static/logo_40.png
new file mode 100644
index 0000000..00b32ae
--- /dev/null
+++ b/homepage/static/logo_40.png
Binary files differ
diff --git a/homepage/static/logo_50.png b/homepage/static/logo_50.png
new file mode 100644
index 0000000..a9d1a76
--- /dev/null
+++ b/homepage/static/logo_50.png
Binary files differ
diff --git a/homepage/static/logo_60.png b/homepage/static/logo_60.png
new file mode 100644
index 0000000..d0d6548
--- /dev/null
+++ b/homepage/static/logo_60.png
Binary files differ
diff --git a/homepage/static/logo_70.png b/homepage/static/logo_70.png
new file mode 100644
index 0000000..31d4137
--- /dev/null
+++ b/homepage/static/logo_70.png
Binary files differ
diff --git a/homepage/static/logo_80.png b/homepage/static/logo_80.png
new file mode 100644
index 0000000..9fe5601
--- /dev/null
+++ b/homepage/static/logo_80.png
Binary files differ
diff --git a/homepage/static/logo_90.png b/homepage/static/logo_90.png
new file mode 100644
index 0000000..408a222
--- /dev/null
+++ b/homepage/static/logo_90.png
Binary files differ
diff --git a/homepage/static/logo_bg.png b/homepage/static/logo_bg.png
new file mode 100644
index 0000000..b664b06
--- /dev/null
+++ b/homepage/static/logo_bg.png
Binary files differ
diff --git a/homepage/static/main.css b/homepage/static/main.css
new file mode 100644
index 0000000..32a68cc
--- /dev/null
+++ b/homepage/static/main.css
@@ -0,0 +1,138 @@
+* {
+ margin: 0px;
+ padding: 0px;
+}
+
+html {
+ background-color: #fff;
+ background: #eee url('/logo_bg.png') no-repeat 0px 0px;
+ padding: 20px 20px 20px 200px;
+}
+
+body {
+ border: 1px solid #bbb;
+ background-color: #eee;
+ background-color: #fff;
+ padding: 25px 20px 20px 20px;
+ font-size: 14px;
+ font-family: sans-serif;
+ line-height: 1.5em;
+ color:#000;
+}
+
+h1, h2, h3, h4, h5 {
+ margin: 1em 0em 0.5em 0em;
+ padding: 2px 0px;
+}
+
+h1 {
+ margin: 0em 0em 0.5em 0em;
+ border-bottom: 3px solid #033;
+}
+
+h2 {
+ margin: 2em 0em 0.5em 0em;
+ border-bottom: 3px solid #033;
+}
+
+p {
+ margin-bottom: 1em;
+ text-align: justify;
+}
+
+ul, ol {
+ margin: 0 0 1em 1.5em;
+}
+
+a {
+ color: #056;
+}
+
+a:hover {
+ color: #000;
+}
+
+img {
+ border: 0;
+}
+
+form div {
+ clear:left;
+}
+
+form div label {
+ display:block;
+ float:left;
+ width:150px;
+}
+
+input, textarea {
+ border:1px solid #bbb;
+ background-color:#eee;
+ padding:1px;
+ margin:1px;
+ vertical-align:middle;
+}
+
+input:hover, textarea:hover {
+ background-color:white;
+}
+
+input:focus, textarea:focus {
+ border:1px solid #055;
+ background-color:white;
+}
+
+ul.error {
+ color:red;
+}
+
+blockquote {
+ margin: 22px 40px;
+ upadding: 3px;
+ color: #575757;
+ padding: 0 50px;
+ background: transparent url("blockquote.png") no-repeat 0 0;
+}
+
+.small {
+ font-size:75%;
+}
+
+#footer {
+ border-top: 3px solid #033;
+ text-align: center;
+ padding-top: 1em;
+}
+
+#navigation {
+ position: absolute;
+ width: 150px;
+ left: 25px;
+}
+
+#navigation h1 {
+ font-size: 1em;
+}
+
+div.toc {
+ float: right;
+ border: 1px solid #ddd;
+ background-color: #fff;
+ padding: 1em;
+ margin: 0 0 2em 2em;
+ line-height: 1.4em;
+}
+
+div.toc ul {
+ margin: 0 0 0 1em;
+}
+
+div.toc ul ul {
+ font-size: 0.8em;
+}
+
+div.toc ul ul ul {
+ display: none;
+}
+
diff --git a/homepage/static/news.html b/homepage/static/news.html
new file mode 100644
index 0000000..2e44f32
--- /dev/null
+++ b/homepage/static/news.html
@@ -0,0 +1,5 @@
+<li><a href="https://twitter.com/bottlepy/status/5068803778">bottlepy</a>: Bottle 0.6.4 released! http://bottle.paws.de (13:16 today)</li>
+<li><a href="https://twitter.com/bottlepy/status/5068781146">bottlepy</a>: git commit: Version bump. This is a bugfix release. (13:15 today)</li>
+<li><a href="https://twitter.com/bottlepy/status/5068752312">bottlepy</a>: git commit: HEAD should never return content. Removed the clearhead parameter (13:13 today)</li>
+<li><a href="https://twitter.com/bottlepy/status/5068561951">bottlepy</a>: By the way: A new bottle release is coming. Mostly bug fixes this time. (13:03 today)</li>
+<li><a href="https://twitter.com/bottlepy/status/5068529786">bottlepy</a>: Just uploaded a new profile picture. This twitter account should be more than just a #git commit message log. What do you think? (13:01 today)</li>
diff --git a/homepage/static/pygments.css b/homepage/static/pygments.css
new file mode 100644
index 0000000..f5d5647
--- /dev/null
+++ b/homepage/static/pygments.css
@@ -0,0 +1,100 @@
+table.codehilitetable {
+ margin: 1em;
+}
+
+table.codehilitetable td.linenos {
+ width: 2em;
+ color: #ccc;
+ text-align: right;
+}
+
+table.codehilitetable td.linenos pre {
+ padding-right: 0.5em;
+ background-color: #fff;
+}
+
+table.codehilitetable td.code pre {
+ border: 1px solid #ccc;
+ background-color: #eee;
+ padding: 0 3em 0 1em;
+ overflow: auto;
+}
+
+
+
+code {
+ font-family: Monaco, "Courier New", monospace;
+ background-color: #eee;
+ padding: 0px 0.1em;
+}
+
+table.codehilitetable {
+# border: 1px solid #ccc;
+# background-color: #eee;
+# margin: 1em 2em;
+# overflow: auto;
+# line-height: 125%;
+}
+
+
+div.codehilite .hll { background-color: #ffffcc }
+div.codehilite .c { color: #408080; font-style: italic } /* Comment */
+div.codehilite .err { border: 1px solid #FF0000 } /* Error */
+div.codehilite .k { color: #008000; font-weight: bold } /* Keyword */
+div.codehilite .o { color: #666666 } /* Operator */
+div.codehilite .cm { color: #408080; font-style: italic } /* Comment.Multiline */
+div.codehilite .cp { color: #BC7A00 } /* Comment.Preproc */
+div.codehilite .c1 { color: #408080; font-style: italic } /* Comment.Single */
+div.codehilite .cs { color: #408080; font-style: italic } /* Comment.Special */
+div.codehilite .gd { color: #A00000 } /* Generic.Deleted */
+div.codehilite .ge { font-style: italic } /* Generic.Emph */
+div.codehilite .gr { color: #FF0000 } /* Generic.Error */
+div.codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */
+div.codehilite .gi { color: #00A000 } /* Generic.Inserted */
+div.codehilite .go { color: #808080 } /* Generic.Output */
+div.codehilite .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
+div.codehilite .gs { font-weight: bold } /* Generic.Strong */
+div.codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+div.codehilite .gt { color: #0040D0 } /* Generic.Traceback */
+div.codehilite .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
+div.codehilite .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
+div.codehilite .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
+div.codehilite .kp { color: #008000 } /* Keyword.Pseudo */
+div.codehilite .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
+div.codehilite .kt { color: #B00040 } /* Keyword.Type */
+div.codehilite .m { color: #666666 } /* Literal.Number */
+div.codehilite .s { color: #BA2121 } /* Literal.String */
+div.codehilite .na { color: #7D9029 } /* Name.Attribute */
+div.codehilite .nb { color: #008000 } /* Name.Builtin */
+div.codehilite .nc { color: #0000FF; font-weight: bold } /* Name.Class */
+div.codehilite .no { color: #880000 } /* Name.Constant */
+div.codehilite .nd { color: #AA22FF } /* Name.Decorator */
+div.codehilite .ni { color: #999999; font-weight: bold } /* Name.Entity */
+div.codehilite .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
+div.codehilite .nf { color: #0000FF } /* Name.Function */
+div.codehilite .nl { color: #A0A000 } /* Name.Label */
+div.codehilite .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
+div.codehilite .nt { color: #008000; font-weight: bold } /* Name.Tag */
+div.codehilite .nv { color: #19177C } /* Name.Variable */
+div.codehilite .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
+div.codehilite .w { color: #bbbbbb } /* Text.Whitespace */
+div.codehilite .mf { color: #666666 } /* Literal.Number.Float */
+div.codehilite .mh { color: #666666 } /* Literal.Number.Hex */
+div.codehilite .mi { color: #666666 } /* Literal.Number.Integer */
+div.codehilite .mo { color: #666666 } /* Literal.Number.Oct */
+div.codehilite .sb { color: #BA2121 } /* Literal.String.Backtick */
+div.codehilite .sc { color: #BA2121 } /* Literal.String.Char */
+div.codehilite .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
+div.codehilite .s2 { color: #BA2121 } /* Literal.String.Double */
+div.codehilite .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
+div.codehilite .sh { color: #BA2121 } /* Literal.String.Heredoc */
+div.codehilite .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
+div.codehilite .sx { color: #008000 } /* Literal.String.Other */
+div.codehilite .sr { color: #BB6688 } /* Literal.String.Regex */
+div.codehilite .s1 { color: #BA2121 } /* Literal.String.Single */
+div.codehilite .ss { color: #19177C } /* Literal.String.Symbol */
+div.codehilite .bp { color: #008000 } /* Name.Builtin.Pseudo */
+div.codehilite .vc { color: #19177C } /* Name.Variable.Class */
+div.codehilite .vg { color: #19177C } /* Name.Variable.Global */
+div.codehilite .vi { color: #19177C } /* Name.Variable.Instance */
+div.codehilite .il { color: #666666 } /* Literal.Number.Integer.Long */
diff --git a/homepage/static/robots.txt b/homepage/static/robots.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/homepage/static/robots.txt
diff --git a/homepage/views/footer.tpl b/homepage/views/footer.tpl
new file mode 100644
index 0000000..63d176a
--- /dev/null
+++ b/homepage/views/footer.tpl
@@ -0,0 +1,9 @@
+<div id='footer'>
+<div>Powered by <a href="/"><img src="/bottle-sig.png" /></a></div>
+<div>Browse sources at <a href="http://github.com/defnull/bottle">GitHub</a></div>
+<div><a href="/page/contact"><small>Kontakt und Impressum</small></a></div>
+
+
+</div>
+</body>
+</html>
diff --git a/homepage/views/header.tpl b/homepage/views/header.tpl
new file mode 100644
index 0000000..f01e51a
--- /dev/null
+++ b/homepage/views/header.tpl
@@ -0,0 +1,25 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+ <title>{{title or 'Homepage'}} - Bottle Web Framework</title>
+ <link type="text/css" rel="stylesheet" href="/main.css" />
+ <link type="text/css" rel="stylesheet" href="/pygments.css" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" >
+ <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript"></script>
+</head>
+<body>
+<div id="navigation">
+ <h1>Pages</h1>
+ <ul>
+ <li><a href="/">Home</a></li>
+ <li><a href="/page/docs">Documentation</a></li>
+ </ul>
+ <h1>Links</h1>
+ <ul>
+ <li><a target="_blank" href="http://pypi.python.org/pypi/bottle">Download</a></li>
+ <li><a target="_blank" href="http://github.com/defnull/bottle">GitHub Repository</a></li>
+ <li><a target="_blank" href="http://github.com/defnull/bottle/issues">Issue Tracker</a></li>
+ <li><a target="_blank" href="http://groups.google.de/group/bottlepy">Google Groups</a></li>
+ <li><a target="_blank" href="http://twitter.com/bottlepy">Twitter</a></li>
+ </ul>
+</div>
diff --git a/homepage/views/pagemask.tpl b/homepage/views/pagemask.tpl
new file mode 100644
index 0000000..07ac18b
--- /dev/null
+++ b/homepage/views/pagemask.tpl
@@ -0,0 +1,5 @@
+%include header title=pagename.title()
+<div id='mdpage'>
+{{content}}
+</div>
+%include footer