view.py: fix a docstring
[invirt/packages/invirt-web.git] / code / view.py
1 import os, sys
2
3 import cherrypy
4 from mako.template import Template
5 from mako.lookup import TemplateLookup
6 import simplejson
7 import datetime, decimal
8 from StringIO import StringIO
9 from invirt.config import structs as config
10 from webcommon import State
11
12 class MakoHandler(cherrypy.dispatch.LateParamPageHandler):
13     """Callable which processes a dictionary, returning the rendered body."""
14     
15     def __init__(self, template, next_handler, content_type='text/html; charset=utf-8'):
16         self.template = template
17         self.next_handler = next_handler
18         self.content_type = content_type
19     
20     def __call__(self):
21         env = globals().copy()
22         env.update(self.next_handler())
23         cherrypy.response.headers['Content-Type'] = self.content_type
24         return self.template.render(**env)
25         
26
27 class MakoLoader(object):
28     
29     def __init__(self):
30         self.lookups = {}
31
32     def get_lookup(self, directories, module_directory=None,
33                      collection_size=-1, imports=[], **kwargs):
34         # Find the appropriate template lookup.
35         key = (tuple(directories), module_directory)
36         try:
37             lookup = self.lookups[key]
38         except KeyError:
39             lookup = TemplateLookup(directories=directories,
40                                     module_directory=module_directory,
41                                     collection_size=collection_size,
42                                     default_filters=['decode.utf8'],
43                                     input_encoding='utf-8',
44                                     output_encoding='utf-8',
45                                     imports=imports,
46                                     )
47             self.lookups[key] = lookup
48         return lookup
49
50     def __call__(self, filename, directories, module_directory=None,
51                  collection_size=-1, content_type='text/html; charset=utf-8',
52                  imports=[]):
53         cherrypy.request.lookup = lookup = self.get_lookup(directories, module_directory,
54                                                            collection_size, imports)
55         cherrypy.request.template = t = lookup.get_template(filename)
56         cherrypy.request.handler = MakoHandler(t, cherrypy.request.handler, content_type)
57
58 cherrypy.tools.mako = cherrypy.Tool('on_start_resource', MakoLoader())
59
60 def revertStandardError():
61     """Move stderr to stdout, and return the contents of the old stderr."""
62     errio = sys.stderr
63     if not isinstance(errio, StringIO):
64         return ''
65     sys.stderr = sys.stdout
66     errio.seek(0)
67     return errio.read()
68
69 def catchStderr():
70     old_handler = cherrypy.request.handler
71     def wrapper(*args, **kwargs):
72         sys.stderr = StringIO()
73         ret = old_handler(*args, **kwargs)
74         e = revertStandardError()
75         if e:
76             if isinstance(ret, dict):
77                 ret["error_text"] = e
78         return ret
79     if old_handler:
80         cherrypy.request.handler = wrapper
81
82 cherrypy.tools.catch_stderr = cherrypy.Tool('before_handler', catchStderr)
83
84 class JSONEncoder(simplejson.JSONEncoder):
85         def default(self, obj):
86                 if isinstance(obj, datetime.datetime):
87                         return str(obj)
88                 elif isinstance(obj, decimal.Decimal):
89                         return float(obj)
90                 else:
91                         return simplejson.JSONEncoder.default(self, obj)
92
93 def jsonify_tool_callback(*args, **kwargs):
94     if not cherrypy.request.cached:
95         response = cherrypy.response
96         response.headers['Content-Type'] = 'text/javascript'
97         response.body = JSONEncoder().iterencode(response.body)
98
99 cherrypy.tools.jsonify = cherrypy.Tool('before_finalize', jsonify_tool_callback, priority=30)
100
101 def require_login():
102     """If the user isn't logged in, raise 403 with an error."""
103     if cherrypy.request.login is False:
104         raise cherrypy.HTTPError(403,
105             "You are not authorized to access that resource")
106
107 cherrypy.tools.require_login = cherrypy.Tool('on_start_resource', require_login, priority=150)
108
109 def require_POST():
110     """If the request isn't a POST request, raise 405 Method Not Allowed"""
111     if cherrypy.request.method != "POST":
112         raise cherrypy.HTTPError(405,
113                                  "You must submit this request with POST")
114
115 cherrypy.tools.require_POST = cherrypy.Tool('on_start_resource', require_POST, priority=150)
116
117 def remote_user_login():
118     """Get the current user based on the SSL or GSSAPI environment
119 variables and store it in the request object's login variable. This
120 conforms to the CherryPy API:
121 http://www.cherrypy.org/wiki/RequestObject#login
122
123 If the user is logged in successfully, cherrypy.request.login is set
124 to the username. If the user failed to log in, cherrypy.request.login
125 is set to False. If the user did not attempt authentication,
126 cherrypy.request.login is set to None."""
127     environ = cherrypy.request.wsgi_environ
128     user = environ.get('REMOTE_USER')
129     if user is None:
130         return
131     else:
132         cherrypy.request.login = None # clear what cherrypy put there
133
134     if environ.get('AUTH_TYPE') == 'Negotiate':
135         # Convert the krb5 principal into a krb4 username
136         if not user.endswith('@%s' % config.kerberos.realm):
137             cherrypy.request.login = False # failed to login
138         else:
139             cherrypy.request.login = user.split('@')[0].replace('/', '.')
140     else:
141         cherrypy.request.login = user
142
143 cherrypy.tools.remote_user_login = cherrypy.Tool('on_start_resource', remote_user_login, priority=50)
144
145 def invirtwebstate_init():
146     """Initialize the cherrypy.request.state object from Invirt"""
147     if not hasattr(cherrypy.request, "state"):
148         cherrypy.request.state = State(cherrypy.request.login)
149
150 cherrypy.tools.invirtwebstate = cherrypy.Tool('on_start_resource', invirtwebstate_init, priority=100)
151
152 class View(object):
153     _cp_config = {'tools.mako.directories': [os.path.join(os.path.dirname(__file__),'templates')]}