comparison martini/web.py @ 0:3c3522ce6e3a

initial import of martINI from https://svn.openplans.org/svn/standalone/martINI/
author k0s <k0scist@gmail.com>
date Tue, 08 Dec 2009 15:13:28 -0500
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:3c3522ce6e3a
1 """
2 a view with webob + genshi for editing and viewing .ini files
3 """
4 import os
5 import re
6
7 from genshi.template import TemplateLoader
8 from martini.config import ConfigMunger
9 from martini.utils import getbool
10 from martini.utils import getlist
11 from paste.httpexceptions import HTTPExceptionHandler
12 from paste.urlparser import StaticURLParser
13 from pkg_resources import resource_filename
14 from webob import Request, Response, exc
15
16 class MartiniWebView(object):
17
18 ### class level variables
19 defaults = { 'auto_reload': 'True',
20 'files': None,
21 'directories': None }
22
23 def __init__(self, **kw):
24
25 # set instance parameters from kw and defaults
26 for key in self.defaults:
27 setattr(self, key, kw.get(key, self.defaults[key]))
28 self.auto_reload = getbool(self.auto_reload)
29
30 # files
31 self.files = getlist(self.files)
32 if self.files:
33 assert [ f for f in self.files if os.path.isabs(f) ]
34 self.files = dict([(os.path.basename(f), f)
35 for f in self.files])
36 else:
37 self.files = {}
38
39 # directories
40 self.directories = getlist(self.directories)
41 if self.directories:
42 assert [ d for d in self.directories if os.path.isabs(d) ]
43 if len(self.directories) > 1 or self.files:
44 # namespace directories
45 self.directories = dict([(os.path.basename(d), d)
46 for d in self.directories])
47 else:
48 # don't namespace a single directory
49 self.directories = { '': self.directories[0] }
50 else:
51 self.directories = {}
52
53 # have to have something to serve!
54 assert self.files or self.directories
55
56 # methods to respond to
57 self.response_functions = { 'GET': self.get,
58 'POST': self.post,
59 }
60
61 # template loader
62 templates_dir = resource_filename(__name__, 'templates')
63 self.loader = TemplateLoader(templates_dir,
64 auto_reload=self.auto_reload)
65
66 # fileserver
67 self.fileserver = StaticURLParser(resource_filename(__name__, 'static'))
68
69 def file(self, path):
70 path = path.strip('/')
71 if not path:
72 return None
73 f = self.files.get(path, None)
74 if f is None:
75 if path in self.directories:
76 return self.directories[path]
77 if '/' in path:
78 path = path.split('/', 1)[-1]
79 for d in self.directories.values():
80 filename = os.path.join(d, path)
81 if os.path.exists(filename):
82 return filename
83 else:
84 return f
85
86 ### methods dealing with HTTP
87 def __call__(self, environ, start_response):
88 request = Request(environ)
89
90 if request.path_info.strip('/') and os.path.exists(os.path.join(resource_filename(__name__, 'static'), request.path_info.strip('/'))):
91 return self.fileserver(environ, start_response)
92
93 if request.path_info.endswith('/'):
94 if request.path_info.strip('/'):
95 raise exc.HTTPFound(location=request.path_info.rstrip('/'))
96 else:
97 if request.path_info != '/':
98 raise exc.HTTPFound(location='/')
99 res = self.make_response(request)
100 return res(environ, start_response)
101
102 def make_response(self, request):
103 return self.response_functions.get(request.method, self.error)(request)
104
105 def get_response(self, text, content_type='text/html'):
106 """make an HTTP response from text"""
107 res = Response(content_type=content_type, body=text)
108 return res
109
110 def get(self, request):
111 """
112 return response to a GET requst
113 """
114
115 # index of all resources
116 if not request.path_info.strip('/'):
117 template = self.loader.load('index.html')
118 items = self.files.keys() + self.directories.keys()
119 res = template.generate(request=request, items=items).render('html', doctype='html')
120 return self.get_response(res)
121
122 # get the file
123 f = self.file(request.path_info)
124 if not f:
125 raise exc.HTTPNotFound()
126
127 # index page of a directory
128 if os.path.isdir(f):
129 template = self.loader.load('index.html')
130 items = os.listdir(f)
131 res = template.generate(request=request, items=items).render('html', doctype='html')
132 return self.get_response(res)
133
134 # get configuration
135 conf = ConfigMunger(f)
136
137 # render the template
138 template = self.loader.load('table.html')
139 res = template.generate(request=request, sections=conf.dict()).render('html', doctype='html')
140 # generate the response
141 return self.get_response(res)
142
143 def post(self, request):
144 """
145 return response to a POST request
146 """
147
148 if not request.path_info.strip('/'):
149 raise exc.HTTPMethodNotAllowed()
150
151 # get the file
152 f = self.file(request.path_info)
153 if not f:
154 raise exc.HTTPNotFound()
155 if os.path.isdir(f):
156 raise exc.HTTPMethodNotAllowed()
157
158 regex = '\[([^\]]+)\](.*)'
159
160 conf = ConfigMunger(f)
161
162 delete = request.POST.getall('delete')
163 if delete:
164 del request.POST['delete']
165
166 for key, value in request.POST.items():
167 value = ' '.join(value.strip().split())
168 match = re.match(regex, key)
169 section, option = match.groups()
170 if option:
171 conf.set(section, option, value)
172 else:
173 if value:
174 conf.move_section(section, value)
175 else:
176 conf.add_section(section)
177
178 for d in delete:
179 match = re.match(regex, d)
180 section, option = match.groups()
181 if conf.has_section(section):
182 if option:
183 conf.remove_option(section, option)
184 else:
185 conf.remove_section(section)
186
187 output = file(f, 'w')
188 conf.write(output)
189
190 return exc.HTTPSeeOther(location=request.path_info)
191
192 def error(self, request):
193 """deal with non-supported methods"""
194 return exc.HTTPMethodNotAllowed("Only %r operations are allowed" % self.response_functions.keys())
195
196
197 ### paste factory
198
199 def factory(global_conf, **app_conf):
200 """create a webob view and wrap it in middleware"""
201 keystr = 'martini.'
202 args = dict([(key.split(keystr, 1)[-1], value)
203 for key, value in app_conf.items()
204 if key.startswith(keystr) ])
205 app = MartiniWebView(**args)
206 return HTTPExceptionHandler(app)