Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(128)

Side by Side Diff: third_party/twisted_8_1/twisted/web/woven/form.py

Issue 12261012: Remove third_party/twisted_8_1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # -*- test-case-name: twisted.web.test.test_woven -*-
2 #
3 # WORK IN PROGRESS: HARD HAT REQUIRED
4 #
5
6 from __future__ import nested_scopes
7
8 # Twisted Imports
9
10 from twisted.python import formmethod, failure
11 from twisted.python.components import registerAdapter
12 from twisted.web import domhelpers, resource, util
13 from twisted.internet import defer
14
15 # Sibling Imports
16 from twisted.web.woven import model, view, controller, widgets, input, interface s
17
18 from twisted.web.microdom import parseString, lmx, Element
19
20
21 #other imports
22 import math
23
24 # map formmethod.Argument to functions that render them:
25 _renderers = {}
26
27 def registerRenderer(argumentClass, renderer):
28 """Register a renderer for a given argument class.
29
30 The renderer function should act in the same way
31 as the 'input_XXX' methods of C{FormFillerWidget}.
32 """
33 assert callable(renderer)
34 global _renderers
35 _renderers[argumentClass] = renderer
36
37
38 class FormFillerWidget(widgets.Widget):
39
40 SPANNING_TYPES = ["hidden", "submit"]
41
42 def getValue(self, request, argument):
43 """Return value for form input."""
44 if not self.model.alwaysDefault:
45 values = request.args.get(argument.name, None)
46 if values:
47 try:
48 return argument.coerce(values[0])
49 except formmethod.InputError:
50 return values[0]
51 return argument.default
52
53 def getValues(self, request, argument):
54 """Return values for form input."""
55 if not self.model.alwaysDefault:
56 values = request.args.get(argument.name, None)
57 if values:
58 try:
59 return argument.coerce(values)
60 except formmethod.InputError:
61 return values
62 return argument.default
63
64 def createShell(self, request, node, data):
65 """Create a `shell' node that will hold the additional form
66 elements, if one is required.
67 """
68 return lmx(node).table(border="0")
69
70 def input_single(self, request, content, model, templateAttributes={}):
71 """
72 Returns a text input node built based upon the node model.
73 Optionally takes an already-coded DOM node merges that
74 information with the model's information. Returns a new (??)
75 lmx node.
76 """
77 #in a text field, only the following options are allowed (well, more
78 #are, but they're not supported yet - can add them in later)
79 attribs = ['type', 'name', 'value', 'size', 'maxlength',
80 'readonly'] #only MSIE recognizes readonly and disabled
81
82 arguments = {}
83 for attrib in attribs:
84 #model hints and values override anything in the template
85 val = model.getHint(attrib, templateAttributes.get(attrib, None))
86 if val:
87 arguments[attrib] = str(val)
88
89 value = self.getValue(request, model)
90 if value:
91 arguments["value"] = str(value)
92
93 arguments["type"] = "text" #these are default
94 arguments["name"] = model.name
95
96 return content.input(**arguments)
97
98 def input_string(self, request, content, model, templateAttributes={}):
99 if not templateAttributes.has_key("size"):
100 templateAttributes["size"] = '60'
101 return self.input_single(request, content, model, templateAttributes)
102
103 input_integer = input_single
104 input_integerrange = input_single
105 input_float = input_single
106
107 def input_text(self, request, content, model, templateAttributes={}):
108 r = content.textarea(
109 cols=str(model.getHint('cols',
110 templateAttributes.get('cols', '60'))),
111 rows=str(model.getHint('rows',
112 templateAttributes.get('rows', '10'))),
113 name=model.name,
114 wrap=str(model.getHint('wrap',
115 templateAttributes.get('wrap', "virtual"))))
116 r.text(str(self.getValue(request, model)))
117 return r
118
119 def input_hidden(self, request, content, model, templateAttributes={}):
120 return content.input(type="hidden",
121 name=model.name,
122 value=str(self.getValue(request, model)))
123
124 def input_submit(self, request, content, model, templateAttributes={}):
125 arguments = {}
126 val = model.getHint("onClick", templateAttributes.get("onClick", None))
127 if val:
128 arguments["onClick"] = val
129 arguments["type"] = "submit"
130 arguments["name"] = model.name
131 div = content.div()
132 for tag, value, desc in model.choices:
133 args = arguments.copy()
134 args["value"] = tag
135 div.input(**args)
136 div.text(" ")
137 if model.reset:
138 div.input(type="reset")
139 return div
140
141 def input_choice(self, request, content, model, templateAttributes={}):
142 # am I not evil? allow onChange js events
143 arguments = {}
144 val = model.getHint("onChange", templateAttributes.get("onChange", None) )
145 if val:
146 arguments["onChange"] = val
147 arguments["name"] = model.name
148 s = content.select(**arguments)
149 default = self.getValues(request, model)
150 for tag, value, desc in model.choices:
151 kw = {}
152 if value in default:
153 kw = {'selected' : '1'}
154 s.option(value=tag, **kw).text(desc)
155 return s
156
157 def input_group(self, request, content, model, groupValues, inputType,
158 templateAttributes={}):
159 """
160 Base code for a group of objects. Checkgroup will use this, as
161 well as radiogroup. In the attributes, rows means how many rows
162 the group should be arranged into, cols means how many cols the
163 group should be arranged into. Columns take precedence over
164 rows: if both are specified, the output will always generate the
165 correct number of columns. However, if the number of elements
166 in the group exceed (or is smaller than) rows*cols, then the
167 number of rows will be off. A cols attribute of 1 will mean that
168 all the elements will be listed one underneath another. The
169 default is a rows attribute of 1: everything listed next to each
170 other.
171 """
172 rows = model.getHint('rows', templateAttributes.get('rows', None))
173 cols = model.getHint('cols', templateAttributes.get('cols', None))
174 if rows:
175 rows = int(rows)
176 if cols:
177 cols = int(cols)
178
179 defaults = self.getValues(request, model)
180 if (rows and rows>1) or (cols and cols>1): #build a table
181 s = content.table(border="0")
182 if cols:
183 breakat = cols
184 else:
185 breakat = math.ceil(float(len(groupValues))/rows)
186 for i in range(0, len(groupValues), breakat):
187 tr = s.tr()
188 for j in range(0, breakat):
189 if i+j >= len(groupValues):
190 break
191 tag, value, desc = groupValues[i+j]
192 kw = {}
193 if value in defaults:
194 kw = {'checked' : '1'}
195 tr.td().input(type=inputType, name=model.name,
196 value=tag, **kw).text(desc)
197
198 else:
199 s = content.div()
200 for tag, value, desc in groupValues:
201 kw = {}
202 if value in defaults:
203 kw = {'checked' : '1'}
204 s.input(type=inputType, name=model.name,
205 value=tag, **kw).text(desc)
206 if cols:
207 s.br()
208
209 return s
210
211 def input_checkgroup(self, request, content, model, templateAttributes={}):
212 return self.input_group(request, content, model, model.flags,
213 "checkbox", templateAttributes)
214
215 def input_radiogroup(self, request, content, model, templateAttributes={}):
216 return self.input_group(request, content, model, model.choices,
217 "radio", templateAttributes)
218
219 #I don't know why they're the same, but they were. So I removed the
220 #excess code. Maybe someone should look into removing it entirely.
221 input_flags = input_checkgroup
222
223 def input_boolean(self, request, content, model, templateAttributes={}):
224 kw = {}
225 if self.getValue(request, model):
226 kw = {'checked' : '1'}
227 return content.input(type="checkbox", name=model.name, **kw)
228
229 def input_file(self, request, content, model, templateAttributes={}):
230 kw = {}
231 for attrib in ['size', 'accept']:
232 val = model.getHint(attrib, templateAttributes.get(attrib, None))
233 if val:
234 kw[attrib] = str(val)
235 return content.input(type="file", name=model.name, **kw)
236
237 def input_date(self, request, content, model, templateAttributes={}):
238 breakLines = model.getHint('breaklines', 1)
239 date = self.getValues(request, model)
240 if date == None:
241 year, month, day = "", "", ""
242 else:
243 year, month, day = date
244 div = content.div()
245 div.text("Year: ")
246 div.input(type="text", size="4", maxlength="4", name=model.name, value=s tr(year))
247 if breakLines:
248 div.br()
249 div.text("Month: ")
250 div.input(type="text", size="2", maxlength="2", name=model.name, value=s tr(month))
251 if breakLines:
252 div.br()
253 div.text("Day: ")
254 div.input(type="text", size="2", maxlength="2", name=model.name, value=s tr(day))
255 return div
256
257 def input_password(self, request, content, model, templateAttributes={}):
258 return content.input(
259 type="password",
260 size=str(templateAttributes.get('size', "60")),
261 name=model.name)
262
263 def input_verifiedpassword(self, request, content, model, templateAttributes ={}):
264 breakLines = model.getHint('breaklines', 1)
265 values = self.getValues(request, model)
266 if isinstance(values, (str, unicode)):
267 values = (values, values)
268 if not values:
269 p1, p2 = "", ""
270 elif len(values) == 1:
271 p1, p2 = values, ""
272 elif len(values) == 2:
273 p1, p2 = values
274 else:
275 p1, p2 = "", ""
276 div = content.div()
277 div.text("Password: ")
278 div.input(type="password", size="20", name=model.name, value=str(p1))
279 if breakLines:
280 div.br()
281 div.text("Verify: ")
282 div.input(type="password", size="20", name=model.name, value=str(p2))
283 return div
284
285
286 def convergeInput(self, request, content, model, templateNode):
287 name = model.__class__.__name__.lower()
288 if _renderers.has_key(model.__class__):
289 imeth = _renderers[model.__class__]
290 else:
291 imeth = getattr(self,"input_"+name)
292
293 return imeth(request, content, model, templateNode.attributes).node
294
295 def createInput(self, request, shell, model, templateAttributes={}):
296 name = model.__class__.__name__.lower()
297 if _renderers.has_key(model.__class__):
298 imeth = _renderers[model.__class__]
299 else:
300 imeth = getattr(self,"input_"+name)
301 if name in self.SPANNING_TYPES:
302 td = shell.tr().td(valign="top", colspan="2")
303 return (imeth(request, td, model).node, shell.tr().td(colspan="2").n ode)
304 else:
305 if model.allowNone:
306 required = ""
307 else:
308 required = " *"
309 tr = shell.tr()
310 tr.td(align="right", valign="top").text(model.getShortDescription()+ ":"+required)
311 content = tr.td(valign="top")
312 return (imeth(request, content, model).node,
313 content.div(_class="formDescription"). # because class is a keyword
314 text(model.getLongDescription()).node)
315
316 def setUp(self, request, node, data):
317 # node = widgets.Widget.generateDOM(self,request,node)
318 lmn = lmx(node)
319 if not node.hasAttribute('action'):
320 lmn['action'] = (request.prepath+request.postpath)[-1]
321 if not node.hasAttribute("method"):
322 lmn['method'] = 'post'
323 lmn['enctype'] = 'multipart/form-data'
324 self.errorNodes = errorNodes = {} # name: nodes whic h trap errors
325 self.inputNodes = inputNodes = {}
326 for errorNode in domhelpers.findElementsWithAttribute(node, 'errorFor'):
327 errorNodes[errorNode.getAttribute('errorFor')] = errorNode
328 argz={}
329 # list to figure out which nodes are in the template already and which a ren't
330 hasSubmit = 0
331 argList = self.model.fmethod.getArgs()
332 for arg in argList:
333 if isinstance(arg, formmethod.Submit):
334 hasSubmit = 1
335 argz[arg.name] = arg
336 inNodes = domhelpers.findElements(
337 node,
338 lambda n: n.tagName.lower() in ('textarea', 'select', 'input',
339 'div'))
340 for inNode in inNodes:
341 t = inNode.getAttribute("type")
342 if t and t.lower() == "submit":
343 hasSubmit = 1
344 if not inNode.hasAttribute("name"):
345 continue
346 nName = inNode.getAttribute("name")
347 if argz.has_key(nName):
348 #send an empty content shell - we just want the node
349 inputNodes[nName] = self.convergeInput(request, lmx(),
350 argz[nName], inNode)
351 inNode.parentNode.replaceChild(inputNodes[nName], inNode)
352 del argz[nName]
353 # TODO:
354 # * some arg types should only have a single node (text, string, etc )
355 # * some should have multiple nodes (choice, checkgroup)
356 # * some have a bunch of ancillary nodes that are possible values (m enu, radiogroup)
357 # these should all be taken into account when walking through the te mplate
358 if argz:
359 shell = self.createShell(request, node, data)
360 # create inputs, in the same order they were passed to us:
361 for remArg in [arg for arg in argList if argz.has_key(arg.name)]:
362 inputNode, errorNode = self.createInput(request, shell, remArg)
363 errorNodes[remArg.name] = errorNode
364 inputNodes[remArg.name] = inputNode
365
366 if not hasSubmit:
367 lmn.input(type="submit")
368
369
370 class FormErrorWidget(FormFillerWidget):
371 def setUp(self, request, node, data):
372 FormFillerWidget.setUp(self, request, node, data)
373 for k, f in self.model.err.items():
374 en = self.errorNodes[k]
375 tn = self.inputNodes[k]
376 en.setAttribute('class', 'formError')
377 tn.setAttribute('class', 'formInputError')
378 en.childNodes[:]=[] # gurfle, CLEAR IT NOW!@#
379 if isinstance(f, failure.Failure):
380 f = f.getErrorMessage()
381 lmx(en).text(str(f))
382
383
384 class FormDisplayModel(model.MethodModel):
385 def initialize(self, fmethod, alwaysDefault=False):
386 self.fmethod = fmethod
387 self.alwaysDefault = alwaysDefault
388
389 class FormErrorModel(FormDisplayModel):
390 def initialize(self, fmethod, args, err):
391 FormDisplayModel.initialize(self, fmethod)
392 self.args = args
393 if isinstance(err, failure.Failure):
394 err = err.value
395 if isinstance(err, Exception):
396 self.err = getattr(err, "descriptions", {})
397 self.desc = err
398 else:
399 self.err = err
400 self.desc = "Please try again"
401
402 def wmfactory_description(self, request):
403 return str(self.desc)
404
405 class _RequestHack(model.MethodModel):
406 def wmfactory_hack(self, request):
407 rv = [[str(a), repr(b)] for (a, b)
408 in request._outDict.items()]
409 #print 'hack', rv
410 return rv
411
412 class FormProcessor(resource.Resource):
413 def __init__(self, formMethod, callback=None, errback=None):
414 resource.Resource.__init__(self)
415 self.formMethod = formMethod
416 if callback is None:
417 callback = self.viewFactory
418 self.callback = callback
419 if errback is None:
420 errback = self.errorViewFactory
421 self.errback = errback
422
423 def getArgs(self, request):
424 """Return the formmethod.Arguments.
425
426 Overridable hook to allow pre-processing, e.g. if we want to enable
427 on them depending on one of the inputs.
428 """
429 return self.formMethod.getArgs()
430
431 def render(self, request):
432 outDict = {}
433 errDict = {}
434 for methodArg in self.getArgs(request):
435 valmethod = getattr(self,"mangle_"+
436 (methodArg.__class__.__name__.lower()), None)
437 tmpval = request.args.get(methodArg.name)
438 if valmethod:
439 # mangle the argument to a basic datatype that coerce will like
440 tmpval = valmethod(tmpval)
441 # coerce it
442 try:
443 cv = methodArg.coerce(tmpval)
444 outDict[methodArg.name] = cv
445 except:
446 errDict[methodArg.name] = failure.Failure()
447 if errDict:
448 # there were problems processing the form
449 return self.errback(self.errorModelFactory(
450 request.args, outDict, errDict)).render(request)
451 else:
452 try:
453 if self.formMethod.takesRequest:
454 outObj = self.formMethod.call(request=request, **outDict)
455 else:
456 outObj = self.formMethod.call(**outDict)
457 except formmethod.FormException, e:
458 err = request.errorInfo = self.errorModelFactory(
459 request.args, outDict, e)
460 return self.errback(err).render(request)
461 else:
462 request._outDict = outDict # CHOMP CHOMP!
463 # I wanted better default behavior for debugging, so I could
464 # see the arguments passed, but there is no channel for this in
465 # the existing callback structure. So, here it goes.
466 if isinstance(outObj, defer.Deferred):
467 def _ebModel(err):
468 if err.trap(formmethod.FormException):
469 mf = self.errorModelFactory(request.args, outDict,
470 err.value)
471 return self.errback(mf)
472 raise err
473 (outObj
474 .addCallback(self.modelFactory)
475 .addCallback(self.callback)
476 .addErrback(_ebModel))
477 return util.DeferredResource(outObj).render(request)
478 else:
479 return self.callback(self.modelFactory(outObj)).render(
480 request)
481
482 def errorModelFactory(self, args, out, err):
483 return FormErrorModel(self.formMethod, args, err)
484
485 def errorViewFactory(self, m):
486 v = view.View(m)
487 v.template = '''
488 <html>
489 <head>
490 <title> Form Error View </title>
491 <style>
492 .formDescription {color: green}
493 .formError {color: red; font-weight: bold}
494 .formInputError {color: #900}
495 </style>
496 </head>
497 <body>
498 Error: <span model="description" />
499 <form model=".">
500 </form>
501 </body>
502 </html>
503 '''
504 return v
505
506 def modelFactory(self, outObj):
507 adapt = interfaces.IModel(outObj, outObj)
508 # print 'factorizing', adapt
509 return adapt
510
511 def viewFactory(self, model):
512 # return interfaces.IView(model)
513 if model is None:
514 bodyStr = '''
515 <table model="hack" style="background-color: #99f">
516 <tr pattern="listItem" view="Widget">
517 <td model="0" style="font-weight: bold">
518 </td>
519 <td model="1">
520 </td>
521 </tr>
522 </table>
523 '''
524 model = _RequestHack()
525 else:
526 bodyStr = '<div model="." />'
527 v = view.View(model)
528 v.template = '''
529 <html>
530 <head>
531 <title> Thank You </title>
532 </head>
533 <body>
534 <h1>Thank You for Using Woven</h1>
535 %s
536 </body>
537 </html>
538 ''' % bodyStr
539 return v
540
541 # manglizers
542
543 def mangle_single(self, args):
544 if args:
545 return args[0]
546 else:
547 return ''
548
549 mangle_string = mangle_single
550 mangle_text = mangle_single
551 mangle_integer = mangle_single
552 mangle_password = mangle_single
553 mangle_integerrange = mangle_single
554 mangle_float = mangle_single
555 mangle_choice = mangle_single
556 mangle_boolean = mangle_single
557 mangle_hidden = mangle_single
558 mangle_submit = mangle_single
559 mangle_file = mangle_single
560 mangle_radiogroup = mangle_single
561
562 def mangle_multi(self, args):
563 if args is None:
564 return []
565 return args
566
567 mangle_checkgroup = mangle_multi
568 mangle_flags = mangle_multi
569
570 from twisted.python.formmethod import FormMethod
571
572 view.registerViewForModel(FormFillerWidget, FormDisplayModel)
573 view.registerViewForModel(FormErrorWidget, FormErrorModel)
574 registerAdapter(FormDisplayModel, FormMethod, interfaces.IModel)
575
OLDNEW
« no previous file with comments | « third_party/twisted_8_1/twisted/web/woven/flashconduit.py ('k') | third_party/twisted_8_1/twisted/web/woven/guard.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698