blob: 35d2c5062bf699d27513c54872e133e49e864ed0 [file] [log] [blame]
#!/usr/bin/env python2.5
#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Module containing the boiler plate required to construct views. This
module is largely based on appengine's webapp framework's code.
"""
import urllib
from google.appengine.ext import db
from django.utils import simplejson
from django.template import loader
from soc.logic.exceptions import LoginRequest
from soc.logic.exceptions import RedirectRequest
from soc.logic.exceptions import AccessViolation
from soc.logic.exceptions import GDocsLoginRequest
from soc.logic.exceptions import MaintainceMode
from soc.logic.exceptions import Error
from soc.views.helper import access_checker
from soc.views.helper.response import Response
from soc.views.helper import context as context_helper
from soc.views.helper.request_data import RequestData
from soc.views.helper.request_data import RedirectHelper
class RequestHandler(object):
"""Base class managing HTTP Requests.
"""
def context(self):
return {}
def get(self):
"""Handler for HTTP GET request.
Default implementation calls templatePath and context and passes
those to render to construct the page.
"""
context = self.context()
self.render(self.templatePath(), context)
def json(self):
"""Handler for HTTP GET request with a 'fmt=json' parameter.
"""
if not self.request.GET.get('plain'):
self.response['Content-Type'] = 'application/json'
# if the browser supports HTTP/1.1
# post-check and pre-check and no-store for IE7
self.response['Cache-Control'] = 'no-store, no-cache, must-revalidate, ' \
'post-check=0, pre-check=0' # HTTP/1.1, IE7
self.response['Pragma'] = 'no-cache'
context = self.jsonContext()
if self.request.GET.get('marker'):
# allow the django test framework to capture the context dictionary
loader.render_to_string('json_marker.html', dictionary=context)
if isinstance(context, unicode) or isinstance(context, str):
data = context
else:
data = simplejson.dumps(context)
self.response.write(data)
def jsonContext(self):
"""Defines the JSON object to be dumped and returned on a HTTP GET request
with 'fmt=json' parameter.
"""
return {
'error': "json() method not implemented",
}
def post(self):
"""Handler for HTTP POST request.
"""
self.error(405)
def head(self):
"""Handler for HTTP HEAD request.
"""
self.error(405)
def options(self):
"""Handler for HTTP OPTIONS request.
"""
self.error(405)
def put(self):
"""Handler for HTTP PUT request.
"""
self.error(405)
def delete(self):
"""Handler for HTTP DELETE request.
"""
self.error(405)
def trace(self):
"""Handler for HTTP TRACE request.
"""
self.error(405)
def error(self, status, message=None):
"""Sets the error response code and message when an error is encountered.
Args:
status: the HTTP status error code
message: the message to set, uses default if None
"""
self.response.set_status(status, message=message)
template_path = "error.html"
context = {
'page_name': self.response.content,
'message': self.response.content,
}
self.response.content = ''
self.render(template_path, context)
def djangoURLPatterns(self):
"""Returns a list of Django URL pattern tuples.
"""
patterns = []
return patterns
def checkAccess(self):
"""Raise an exception if the user doesn't have access to the
requested URL.
"""
self.error(401, "checkAccess in base RequestHandler has not been changed "
"to grant access")
def render(self, template_path, render_context):
"""Renders the page using the specified context.
The page is rendered using the template and context specified and
is written to the response object.
The context object is extended with the values from helper.context.default.
Args:
template_path: the path of the template that should be used
render_context: the context that should be used
"""
context = context_helper.default(self.data)
context.update(render_context)
rendered = loader.render_to_string(template_path, dictionary=context)
self.response.write(rendered)
def templatePath(self):
"""Returns the path to the template that should be used in render().
Subclasses should override this method.
"""
raise NotImplementedError()
def accessViolation(self, status, message):
"""Default access violation handler.
"""
self.error(status, message)
def _dispatch(self):
"""Dispatches the HTTP request to its respective handler method.
"""
if self.request.method == 'GET':
if self.request.GET.get('fmt') == 'json':
self.json()
else:
self.get()
elif self.request.method == 'POST':
if db.WRITE_CAPABILITY.is_enabled():
self.post()
else:
referrer = self.request.META.get('HTTP_REFERER', '')
params = urllib.urlencode({'dsw_disabled': 1})
url_with_params = '%s?%s' % (referrer, params)
self.redirect.toUrl(url_with_params)
elif self.request.method == 'HEAD':
self.head()
elif self.request.method == 'OPTIONS':
self.options()
elif self.request.method == 'PUT':
self.put()
elif self.request.method == 'DELETE':
self.delete()
elif self.request.method == 'TRACE':
self.trace()
else:
self.error(501)
def init(self, request, args, kwargs):
"""Initializes the RequestHandler.
Sets the data and check fields.
"""
if self.data.site.maintenance_mode and not self.data.is_developer:
raise MaintainceMode(
'The site is currently in maintenance mode. Please try again later.')
def __call__(self, request, *args, **kwargs):
"""Returns the response object for the requested URL.
In detail, this method does the following:
1. Initialize request, arguments and keyword arguments as instance variables
2. Construct the response object.
3. Calls the access check.
4. Delegates dispatching to the handler to the _dispatch method.
5. Returns the response.
"""
self.request = request
self.args = args
self.kwargs = kwargs
self.response = Response()
try:
self.init(request, args, kwargs)
self.checkAccess()
self._dispatch()
except LoginRequest, e:
request.get_full_path().encode('utf-8')
self.redirect.login().to()
except RedirectRequest, e:
self.redirect.toUrl(e.url)
except AccessViolation, e:
self.accessViolation(e.status, e.args[0])
except GDocsLoginRequest, e:
self.redirect.toUrl('%s?%s' % (self.redirect.urlOf(e.url_name),
urllib.urlencode({'next':e.next})))
except Error, e:
self.error(e.status, message=e.args[0])
finally:
response = self.response
self.response = None
self.request = None
self.args = None
self.kwargs = None
self.data = None
self.check = None
self.mutator = None
self.redirect = None
return response
class SiteRequestHandler(RequestHandler):
"""Customization required by global site pages to handle HTTP requests.
"""
def init(self, request, args, kwargs):
self.data = RequestData()
self.redirect = RedirectHelper(self.data, self.response)
self.data.populate(None, request, args, kwargs)
if self.data.is_developer:
self.mutator = access_checker.DeveloperMutator(self.data)
self.check = access_checker.DeveloperAccessChecker(self.data)
else:
self.mutator = access_checker.Mutator(self.data)
self.check = access_checker.AccessChecker(self.data)
super(SiteRequestHandler, self).init(request, args, kwargs)