diff options
| -rw-r--r-- | examples/settings.py | 4 | ||||
| -rw-r--r-- | examples/urls.py | 2 | ||||
| -rw-r--r-- | flywheel/emitters.py | 15 | ||||
| -rw-r--r-- | flywheel/resource.py | 2 | ||||
| -rw-r--r-- | flywheel/templates/emitter.html | 150 | ||||
| -rw-r--r-- | flywheel/utils.py | 10 |
6 files changed, 109 insertions, 74 deletions
diff --git a/examples/settings.py b/examples/settings.py index f85e75f1..ea97d192 100644 --- a/examples/settings.py +++ b/examples/settings.py @@ -1,4 +1,7 @@ # Django settings for src project. +import os + +BASE_DIR = os.path.dirname(__file__) DEBUG = True TEMPLATE_DEBUG = DEBUG @@ -81,6 +84,7 @@ TEMPLATE_DIRS = ( # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". # Always use forward slashes, even on Windows. # Don't forget to use absolute paths, not relative paths. + os.path.join(BASE_DIR, 'templates') ) INSTALLED_APPS = ( diff --git a/examples/urls.py b/examples/urls.py index ef55e2b5..5e308f00 100644 --- a/examples/urls.py +++ b/examples/urls.py @@ -7,6 +7,8 @@ urlpatterns = patterns('', (r'pygments-example/', include('pygments_api.urls')), (r'^blog-post-example/', include('blogpost.urls')), (r'^object-store-example/', include('objectstore.urls')), + (r'^accounts/login/$', 'django.contrib.auth.views.login'), + (r'^accounts/logout/$', 'django.contrib.auth.views.logout'), (r'^admin/doc/', include('django.contrib.admindocs.urls')), (r'^admin/', include(admin.site.urls)), ) diff --git a/flywheel/emitters.py b/flywheel/emitters.py index 4ba2d92d..458fa68c 100644 --- a/flywheel/emitters.py +++ b/flywheel/emitters.py @@ -1,9 +1,10 @@ +from django.conf import settings from django.template import RequestContext, loader from django import forms from flywheel.response import NoContent -from utils import dict2xml +from utils import dict2xml, url_resolves import string try: import json @@ -12,6 +13,7 @@ except ImportError: + class BaseEmitter(object): media_type = None @@ -118,13 +120,22 @@ class DocumentingTemplateEmitter(BaseEmitter): content = self._get_content(self.resource, output) form_instance = self._get_form_instance(self.resource) + if url_resolves(settings.LOGIN_URL) and url_resolves(settings.LOGOUT_URL): + login_url = "%s?next=%s" % (settings.LOGIN_URL, self.resource.request.path) + logout_url = "%s?next=%s" % (settings.LOGOUT_URL, self.resource.request.path) + else: + login_url = None + logout_url = None + template = loader.get_template(self.template) context = RequestContext(self.resource.request, { 'content': content, 'resource': self.resource, 'request': self.resource.request, 'response': self.resource.response, - 'form': form_instance + 'form': form_instance, + 'login_url': login_url, + 'logout_url': logout_url, }) ret = template.render(context) diff --git a/flywheel/resource.py b/flywheel/resource.py index 36f49792..cc566752 100644 --- a/flywheel/resource.py +++ b/flywheel/resource.py @@ -7,9 +7,7 @@ from flywheel.response import status, Response, ResponseException from decimal import Decimal import re -from itertools import chain -# TODO: Display user login in top panel: http://stackoverflow.com/questions/806835/django-redirect-to-previous-page-after-login # TODO: Figure how out references and named urls need to work nicely # TODO: POST on existing 404 URL, PUT on existing 404 URL # diff --git a/flywheel/templates/emitter.html b/flywheel/templates/emitter.html index 93f4cb49..7078eafd 100644 --- a/flywheel/templates/emitter.html +++ b/flywheel/templates/emitter.html @@ -5,6 +5,11 @@ <head> <style> pre {border: 1px solid black; padding: 1em; background: #ffd} + body {margin: 0; border:0; padding: 0;} + span.api {margin: 0.5em 1em} + span.auth {float: right; margin-right: 1em} + div.header {margin: 0; border:0; padding: 0.25em 0; background: #ddf} + div.content {margin: 0 1em;} div.action {border: 1px solid black; padding: 0.5em 1em; margin-bottom: 0.5em; background: #ddf} ul.accepttypes {float: right; list-style-type: none; margin: 0; padding: 0} ul.accepttypes li {display: inline;} @@ -17,82 +22,87 @@ <title>API - {{ resource.name }}</title> </head> <body> - <h1>{{ resource.name }}</h1> - <p>{{ resource.description|linebreaksbr }}</p> - <pre><b>{{ response.status }} {{ response.status_text }}</b>{% autoescape off %} -{% for key, val in response.headers.items %}<b>{{ key }}:</b> {{ val|urlize_quoted_links }} -{% endfor %} -{{ content|urlize_quoted_links }} </pre>{% endautoescape %} - -{% if 'GET' in resource.allowed_methods %} - <div class='action'> - <a href='{{ request.path }}'>GET</a> - <ul class="accepttypes"> - {% for media_type in resource.emitted_media_types %} - {% with resource.ACCEPT_QUERY_PARAM|add:"="|add:media_type as param %} - <li>[<a href='{{ request.path|add_query_param:param }}'>{{ media_type }}</a>]</li> - {% endwith %} - {% endfor %} - </ul> - <div class="clearing"></div> + <div class='header'> + <span class='api'><a href='http://www.thewebhaswon.com/flywheel/'>FlyWheel API</a></span> + <span class='auth'>{% if user.is_active %}Welcome, {{ user }}.{% if logout_url %} <a href='{{ logout_url }}'>Log out</a>{% endif %}{% else %}Not logged in {% if login_url %}<a href='{{ login_url }}'>Log in</a>{% endif %}{% endif %}</span> </div> -{% endif %} - -{% comment %} *** Only display the POST/PUT/DELETE forms if we have a bound form, and if method *** - *** tunneling via POST forms is enabled. *** - *** (We could display only the POST form if method tunneling is disabled, but I think *** - *** the user experience would be confusing, so we simply turn all forms off. *** {% endcomment %} - -{% if resource.METHOD_PARAM and form %} - {% if 'POST' in resource.allowed_methods %} - <div class='action'> - <form action="{{ request.path }}" method="post"> - {% csrf_token %} - {{ form.non_field_errors }} - {% for field in form %} - <div> - {{ field.label_tag }}: - {{ field }} - {{ field.help_text }} - {{ field.errors }} - </div> - {% endfor %} - <div class="clearing"></div> - <input type="submit" value="POST" /> - </form> - </div> - {% endif %} + <div class='content'> + <h1>{{ resource.name }}</h1> + <p>{{ resource.description|linebreaksbr }}</p> + <pre><b>{{ response.status }} {{ response.status_text }}</b>{% autoescape off %} + {% for key, val in response.headers.items %}<b>{{ key }}:</b> {{ val|urlize_quoted_links }} + {% endfor %} + {{ content|urlize_quoted_links }}</pre>{% endautoescape %} - {% if 'PUT' in resource.allowed_methods %} + {% if 'GET' in resource.allowed_methods %} <div class='action'> - <form action="{{ request.path }}" method="post"> - <input type="hidden" name="{{ resource.METHOD_PARAM }}" value="PUT" /> - {% csrf_token %} - {{ form.non_field_errors }} - {% for field in form %} - <div> - {{ field.label_tag }}: - {{ field }} - {{ field.help_text }} - {{ field.errors }} - </div> - {% endfor %} - <div class="clearing"></div> - <input type="submit" value="PUT" /> - </form> + <a href='{{ request.path }}'>GET</a> + <ul class="accepttypes"> + {% for media_type in resource.emitted_media_types %} + {% with resource.ACCEPT_QUERY_PARAM|add:"="|add:media_type as param %} + <li>[<a href='{{ request.path|add_query_param:param }}'>{{ media_type }}</a>]</li> + {% endwith %} + {% endfor %} + </ul> + <div class="clearing"></div> </div> {% endif %} - {% if 'DELETE' in resource.allowed_methods %} - <div class='action'> - <form action="{{ request.path }}" method="post"> - {% csrf_token %} - <input type="hidden" name="{{ resource.METHOD_PARAM }}" value="DELETE" /> - <input type="submit" value="DELETE" /> - </form> - </div> + {% comment %} *** Only display the POST/PUT/DELETE forms if we have a bound form, and if method *** + *** tunneling via POST forms is enabled. *** + *** (We could display only the POST form if method tunneling is disabled, but I think *** + *** the user experience would be confusing, so we simply turn all forms off. *** {% endcomment %} + + {% if resource.METHOD_PARAM and form %} + {% if 'POST' in resource.allowed_methods %} + <div class='action'> + <form action="{{ request.path }}" method="post"> + {% csrf_token %} + {{ form.non_field_errors }} + {% for field in form %} + <div> + {{ field.label_tag }}: + {{ field }} + {{ field.help_text }} + {{ field.errors }} + </div> + {% endfor %} + <div class="clearing"></div> + <input type="submit" value="POST" /> + </form> + </div> + {% endif %} + + {% if 'PUT' in resource.allowed_methods %} + <div class='action'> + <form action="{{ request.path }}" method="post"> + <input type="hidden" name="{{ resource.METHOD_PARAM }}" value="PUT" /> + {% csrf_token %} + {{ form.non_field_errors }} + {% for field in form %} + <div> + {{ field.label_tag }}: + {{ field }} + {{ field.help_text }} + {{ field.errors }} + </div> + {% endfor %} + <div class="clearing"></div> + <input type="submit" value="PUT" /> + </form> + </div> + {% endif %} + + {% if 'DELETE' in resource.allowed_methods %} + <div class='action'> + <form action="{{ request.path }}" method="post"> + {% csrf_token %} + <input type="hidden" name="{{ resource.METHOD_PARAM }}" value="DELETE" /> + <input type="submit" value="DELETE" /> + </form> + </div> + {% endif %} {% endif %} -{% endif %} - + </div> </body> </html>
\ No newline at end of file diff --git a/flywheel/utils.py b/flywheel/utils.py index 98d8e1ae..c0386871 100644 --- a/flywheel/utils.py +++ b/flywheel/utils.py @@ -2,11 +2,21 @@ import re import xml.etree.ElementTree as ET from django.utils.encoding import smart_unicode from django.utils.xmlutils import SimplerXMLGenerator +from django.core.urlresolvers import resolve try: import cStringIO as StringIO except ImportError: import StringIO + +def url_resolves(url): + """Return True if the given URL is mapped to a view in the urlconf, False otherwise.""" + try: + resolve(url) + except: + return False + return True + # From piston def coerce_put_post(request): """ |
