aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/settings.py4
-rw-r--r--examples/urls.py2
-rw-r--r--flywheel/emitters.py15
-rw-r--r--flywheel/resource.py2
-rw-r--r--flywheel/templates/emitter.html150
-rw-r--r--flywheel/utils.py10
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):
"""