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

Side by Side Diff: third_party/twisted_8_1/twisted/web/widgets.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_web -*-
2 #
3 # Copyright (c) 2001-2004 Twisted Matrix Laboratories.
4 # See LICENSE for details.
5
6
7 """A twisted web component framework.
8
9 This module is DEPRECATED.
10 """
11
12 import warnings
13 warnings.warn("This module is deprecated, please use Woven instead.", Deprecatio nWarning)
14
15 # System Imports
16 import string, time, types, traceback, pprint, sys, os
17 import linecache
18 import re
19 from cStringIO import StringIO
20
21 # Twisted Imports
22 from twisted.python import failure, log, rebuild, reflect, util
23 from twisted.internet import defer
24 from twisted.web import http
25
26 # Sibling Imports
27 import html, resource, error
28 import util as webutil
29
30 #backwards compatibility
31 from util import formatFailure, htmlrepr, htmlUnknown, htmlDict, htmlList,\
32 htmlInst, htmlString, htmlReprTypes
33
34
35
36 from server import NOT_DONE_YET
37
38 True = (1==1)
39 False = not True
40
41
42 # magic value that sez a widget needs to take over the whole page.
43
44 FORGET_IT = 99
45
46 def listify(x):
47 return [x]
48 def _ellipsize(x):
49 y = repr(x)
50 if len(y) > 1024:
51 return y[:1024]+"..."
52 return y
53
54
55 class Widget:
56 """A component of a web page.
57 """
58 title = None
59 def getTitle(self, request):
60 return self.title or reflect.qual(self.__class__)
61
62 def display(self, request):
63 """Implement me to represent your widget.
64
65 I must return a list of strings and twisted.internet.defer.Deferred
66 instances.
67 """
68 raise NotImplementedError("%s.display" % reflect.qual(self.__class__))
69
70 class StreamWidget(Widget):
71 """A 'streamable' component of a webpage.
72 """
73
74 def stream(self, write, request):
75 """Call 'write' multiple times with a string argument to represent this widget.
76 """
77 raise NotImplementedError("%s.stream" % reflect.qual(self.__class__))
78
79 def display(self, request):
80 """Produce a list containing a single string.
81 """
82 l = []
83 try:
84 result = self.stream(l.append, request)
85 if result is not None:
86 return result
87 return l
88 except:
89 return [webutil.formatFailure(failure.Failure())]
90
91 class WidgetMixin(Widget):
92 """A mix-in wrapper for a Widget.
93
94 This mixin can be used to wrap functionality in any other widget with a
95 method of your choosing. It is designed to be used for mix-in classes that
96 can be mixed in to Form, StreamWidget, Presentation, etc, to augment the
97 data available to the 'display' methods of those classes, usually by adding
98 it to a Session.
99 """
100
101 def display(self):
102 raise NotImplementedError("%s.display" % self.__class__)
103
104 def displayMixedWidget(self, request):
105 for base in reflect.allYourBase(self.__class__):
106 if issubclass(base, Widget) and not issubclass(base, WidgetMixin):
107 return base.display(self, request)
108
109 class Presentation(Widget):
110 """I am a widget which formats a template with interspersed python expressio ns.
111 """
112 template = '''
113 Hello, %%%%world%%%%.
114 '''
115 world = "you didn't assign to the 'template' attribute"
116 def __init__(self, template=None, filename=None):
117 if filename:
118 self.template = open(filename).read()
119 elif template:
120 self.template = template
121 self.variables = {}
122 self.tmpl = string.split(self.template, "%%%%")
123
124 def addClassVars(self, namespace, Class):
125 for base in Class.__bases__:
126 # Traverse only superclasses that know about Presentation.
127 if issubclass(base, Presentation) and base is not Presentation:
128 self.addClassVars(namespace, base)
129 # 'lower' classes in the class heirarchy take precedence.
130 for k in Class.__dict__.keys():
131 namespace[k] = getattr(self, k)
132
133 def addVariables(self, namespace, request):
134 self.addClassVars(namespace, self.__class__)
135
136 def prePresent(self, request):
137 """Perform any tasks which must be done before presenting the page.
138 """
139
140 def formatTraceback(self, tb):
141 return [html.PRE(tb)]
142
143 def streamCall(self, call, *args, **kw):
144 """Utility: Call a method like StreamWidget's 'stream'.
145 """
146 io = StringIO()
147 apply(call, (io.write,) + args, kw)
148 return io.getvalue()
149
150 def display(self, request):
151 tm = []
152 flip = 0
153 namespace = {}
154 self.prePresent(request)
155 self.addVariables(namespace, request)
156 # This variable may not be obscured...
157 namespace['request'] = request
158 namespace['self'] = self
159 for elem in self.tmpl:
160 flip = not flip
161 if flip:
162 if elem:
163 tm.append(elem)
164 else:
165 try:
166 x = eval(elem, namespace, namespace)
167 except:
168 log.deferr()
169 tm.append(webutil.formatFailure(failure.Failure()))
170 else:
171 if isinstance(x, types.ListType):
172 tm.extend(x)
173 elif isinstance(x, Widget):
174 val = x.display(request)
175 if not isinstance(val, types.ListType):
176 raise Exception("%s.display did not return a list, i t returned %s!" % (x.__class__, repr(val)))
177 tm.extend(val)
178 else:
179 # Only two allowed types here should be deferred and
180 # string.
181 tm.append(x)
182 return tm
183
184
185 def htmlFor_hidden(write, name, value):
186 write('<INPUT TYPE="hidden" NAME="%s" VALUE="%s" />' % (name, value))
187
188 def htmlFor_file(write, name, value):
189 write('<INPUT SIZE="60" TYPE="file" NAME="%s" />' % name)
190
191 def htmlFor_string(write, name, value):
192 write('<INPUT SIZE="60" TYPE="text" NAME="%s" VALUE="%s" />' % (name, value) )
193
194 def htmlFor_password(write, name, value):
195 write('<INPUT SIZE="60" TYPE="password" NAME="%s" />' % name)
196
197 def htmlFor_text(write, name, value):
198 write('<textarea COLS="60" ROWS="10" NAME="%s" WRAP="virtual">%s</textarea>' % (name, value))
199
200 def htmlFor_menu(write, name, value, allowMultiple=False):
201 "Value of the format [(optionName, displayName[, selected]), ...]"
202
203 write(' <select NAME="%s"%s>\n' %
204 (name, (allowMultiple and " multiple") or ''))
205
206 for v in value:
207 optionName, displayName, selected = util.padTo(3, v)
208 selected = (selected and " selected") or ''
209 write(' <option VALUE="%s"%s>%s</option>\n' %
210 (optionName, selected, displayName))
211 if not value:
212 write(' <option VALUE=""></option>\n')
213 write(" </select>\n")
214
215 def htmlFor_multimenu(write, name, value):
216 "Value of the format [(optionName, displayName[, selected]), ...]"
217 return htmlFor_menu(write, name, value, True)
218
219 def htmlFor_checkbox(write, name, value):
220 "A checkbox."
221 if value:
222 value = 'checked = "1"'
223 else:
224 value = ''
225 write('<INPUT TYPE="checkbox" NAME="__checkboxes__" VALUE="%s" %s />\n' % (n ame, value))
226
227 def htmlFor_checkgroup(write, name, value):
228 "A check-group."
229 for optionName, displayName, checked in value:
230 checked = (checked and 'checked = "1"') or ''
231 write('<INPUT TYPE="checkbox" NAME="%s" VALUE="%s" %s />%s<br />\n' % (n ame, optionName, checked, displayName))
232
233 def htmlFor_radio(write, name, value):
234 "A radio button group."
235 for optionName, displayName, checked in value:
236 checked = (checked and 'checked = "1"') or ''
237 write('<INPUT TYPE="radio" NAME="%s" VALUE="%s" %s />%s<br />\n' % (name , optionName, checked, displayName))
238
239 class FormInputError(Exception):
240 pass
241
242 class Form(Widget):
243 """I am a web form.
244
245 In order to use me, you probably want to set self.formFields (or override
246 'getFormFields') and override 'process'. In order to demonstrate how this
247 is done, here is a small sample Form subclass::
248
249 | from twisted.web import widgets
250 | class HelloForm(widgets.Form):
251 | formFields = [
252 | ['string', 'Who to greet?', 'whoToGreet', 'World',
253 | 'This is for choosing who to greet.'],
254 | ['menu', 'How to greet?', 'how', [('cheerfully', 'with a smile' ),
255 | ('sullenly', 'without enthusi asm'),
256 | ('spontaneously', 'on the spu r of the moment')]]
257 | 'This is for choosing how to greet them.']
258 | def process(self, write, request, submit, whoToGreet, how):
259 | write('The web wakes up and %s says, \"Hello, %s!\"' % (how, wh oToGreet))
260
261 If you load this widget, you will see that it displays a form with 2 inputs
262 derived from data in formFields. Note the argument names to 'process':
263 after 'write' and 'request', they are the same as the 3rd elements ('Input
264 Name' parameters) of the formFields list.
265 """
266
267 formGen = {
268 'hidden': htmlFor_hidden,
269 'file': htmlFor_file,
270 'string': htmlFor_string,
271 'int': htmlFor_string,
272 'float': htmlFor_string,
273 'text': htmlFor_text,
274 'menu': htmlFor_menu,
275 'multimenu': htmlFor_multimenu,
276 'password': htmlFor_password,
277 'checkbox': htmlFor_checkbox,
278 'checkgroup': htmlFor_checkgroup,
279 'radio': htmlFor_radio,
280 }
281
282 formParse = {
283 'int': int,
284 'float': float,
285 }
286
287 formFields = [
288 ]
289
290 # do we raise an error when we get extra args or not?
291 formAcceptExtraArgs = 0
292
293 def getFormFields(self, request, fieldSet = None):
294 """I return a list of lists describing this form, or a Deferred.
295
296 This information is used both to display the form and to process it.
297 The list is in the following format::
298
299 | [['Input Type', 'Display Name', 'Input Name', 'Input Value', ' Description'],
300 | ['Input Type 2', 'Display Name 2', 'Input Name 2', 'Input Value 2', 'Description 2']
301 | ...]
302
303 Valid values for 'Input Type' are:
304
305 - 'hidden': a hidden field that contains a string that the user won't change
306
307 - 'string': a short string
308
309 - 'int': an integer, e.g. 1, 0, 25 or -23
310
311 - 'float': a float, e.g. 1.0, 2, -3.45, or 28.4324231
312
313 - 'text': a longer text field, suitable for entering paragraphs
314
315 - 'menu': an HTML SELECT input, a list of choices
316
317 - 'multimenu': an HTML SELECT input allowing multiple choices
318
319 - 'checkgroup': a group of checkboxes
320
321 - 'radio': a group of radio buttons
322
323 - 'password': a 'string' field where the contents are not visible as t he user types
324
325 - 'file': a file-upload form (EXPERIMENTAL)
326
327 'Display Name' is a descriptive string that will be used to
328 identify the field to the user.
329
330 The 'Input Name' must be a legal Python identifier that describes both
331 the value's name on the HTML form and the name of an argument to
332 'self.process()'.
333
334 The 'Input Value' is usually a string, but its value can depend on the
335 'Input Type'. 'int' it is an integer, 'menu' it is a list of pairs of
336 strings, representing (value, name) pairs for the menu options. Input
337 value for 'checkgroup' and 'radio' should be a list of ('inputName',
338 'Display Name', 'checked') triplets.
339
340 The 'Description' field is an (optional) string which describes the form
341 item to the user.
342
343 If this result is statically determined for your Form subclass, you can
344 assign it to FormSubclass.formFields; if you need to determine it
345 dynamically, you can override this method.
346
347 Note: In many cases it is desirable to use user input for defaults in
348 the form rather than those supplied by your calculations, which is what
349 this method will do to self.formFields. If this is the case for you,
350 but you still need to dynamically calculate some fields, pass your
351 results back through this method by doing::
352
353 | def getFormFields(self, request):
354 | myFormFields = [self.myFieldCalculator()]
355 | return widgets.Form.getFormFields(self, request, myFormFields)
356
357 """
358 fields = []
359 if fieldSet is None:
360 fieldSet = self.formFields
361 if not self.shouldProcess(request):
362 return fieldSet
363
364 for field in fieldSet:
365 if len(field)==5:
366 inputType, displayName, inputName, inputValue, description = fie ld
367 else:
368 inputType, displayName, inputName, inputValue = field
369 description = ""
370
371 if inputType == 'checkbox':
372 if request.args.has_key('__checkboxes__'):
373 if inputName in request.args['__checkboxes__']:
374 inputValue = 1
375 else:
376 inputValue = 0
377 else:
378 inputValue = 0
379 elif inputType in ('checkgroup', 'radio'):
380 if request.args.has_key(inputName):
381 keys = request.args[inputName]
382 else:
383 keys = []
384 iv = inputValue
385 inputValue = []
386 for optionName, optionDisplayName, checked in iv:
387 checked = optionName in keys
388 inputValue.append([optionName, optionDisplayName, checked])
389 elif request.args.has_key(inputName):
390 iv = request.args[inputName][0]
391 if inputType in ['menu', 'multimenu']:
392 if iv in inputValue:
393 inputValue.remove(iv)
394 inputValue.insert(0, iv)
395 else:
396 inputValue = iv
397 fields.append([inputType, displayName, inputName, inputValue, descri ption])
398 return fields
399
400 submitNames = ['Submit']
401 actionURI = ''
402
403 def format(self, form, write, request):
404 """I display an HTML FORM according to the result of self.getFormFields.
405 """
406 write('<form ENCTYPE="multipart/form-data" METHOD="post" ACTION="%s">\n'
407 '<table BORDER="0">\n' % (self.actionURI or request.uri))
408
409 for field in form:
410 if len(field) == 5:
411 inputType, displayName, inputName, inputValue, description = fie ld
412 else:
413 inputType, displayName, inputName, inputValue = field
414 description = ""
415 write('<tr>\n<td ALIGN="right" VALIGN="top"><B>%s</B></td>\n'
416 '<td VALIGN="%s">\n' %
417 (displayName, ((inputType == 'text') and 'top') or 'middle'))
418 self.formGen[inputType](write, inputName, inputValue)
419 write('\n<br />\n<font size="-1">%s</font></td>\n</tr>\n' % descript ion)
420
421
422 write('<tr><td></td><td ALIGN="left"><hr />\n')
423 for submitName in self.submitNames:
424 write('<INPUT TYPE="submit" NAME="submit" VALUE="%s" />\n' % submitN ame)
425 write('</td></tr>\n</table>\n'
426 '<INPUT TYPE="hidden" NAME="__formtype__" VALUE="%s" />\n'
427 % (reflect.qual(self.__class__)))
428 fid = self.getFormID()
429 if fid:
430 write('<INPUT TYPE="hidden" NAME="__formid__" VALUE="%s" />\n' % fid )
431 write("</form>\n")
432
433 def getFormID(self):
434 """Override me: I disambiguate between multiple forms of the same type.
435
436 In order to determine which form an HTTP POST request is for, you must
437 have some unique identifier which distinguishes your form from other
438 forms of the same class. An example of such a unique identifier would
439 be: on a page with multiple FrobConf forms, each FrobConf form refers
440 to a particular Frobnitz instance, which has a unique id(). The
441 FrobConf form's getFormID would probably look like this::
442
443 | def getFormID(self):
444 | return str(id(self.frobnitz))
445
446 By default, this method will return None, since distinct Form instances
447 may be identical as far as the application is concerned.
448 """
449
450 def process(self, write, request, submit, **kw):
451 """Override me: I process a form.
452
453 I will only be called when the correct form input data to process this
454 form has been received.
455
456 I take a variable number of arguments, beginning with 'write',
457 'request', and 'submit'. 'write' is a callable object that will append
458 a string to the response, 'request' is a twisted.web.request.Request
459 instance, and 'submit' is the name of the submit action taken.
460
461 The remainder of my arguments must be correctly named. They will each b e named after one of the
462
463 """
464 write("<pre>Submit: %s <br /> %s</pre>" % (submit, html.PRE(pprint.Prett yPrinter().pformat(kw))))
465
466 def _doProcess(self, form, write, request):
467 """(internal) Prepare arguments for self.process.
468 """
469 args = request.args.copy()
470 kw = {}
471 for field in form:
472 inputType, displayName, inputName, inputValue = field[:4]
473 if inputType == 'checkbox':
474 if request.args.has_key('__checkboxes__'):
475 if inputName in request.args['__checkboxes__']:
476 formData = 1
477 else:
478 formData = 0
479 else:
480 formData = 0
481 elif inputType in ['checkgroup', 'radio', 'multimenu']:
482 if args.has_key(inputName):
483 formData = args[inputName]
484 del args[inputName]
485 else:
486 formData = []
487 else:
488 if not args.has_key(inputName):
489 raise FormInputError("missing field %s." % repr(inputName))
490 formData = args[inputName]
491 del args[inputName]
492 if not len(formData) == 1:
493 raise FormInputError("multiple values for field %s." %repr(i nputName))
494 formData = formData[0]
495 method = self.formParse.get(inputType)
496 if method:
497 try:
498 formData = method(formData)
499 except:
500 raise FormInputError("%s: %s" % (displayName, "error"))
501 kw[inputName] = formData
502 submitAction = args.get('submit')
503 if submitAction:
504 submitAction = submitAction[0]
505 for field in ['submit', '__formtype__', '__checkboxes__']:
506 if args.has_key(field):
507 del args[field]
508 if args and not self.formAcceptExtraArgs:
509 raise FormInputError("unknown fields: %s" % repr(args))
510 return apply(self.process, (write, request, submitAction), kw)
511
512 def formatError(self,error):
513 """Format an error message.
514
515 By default, this will make the message appear in red, bold italics.
516 """
517 return '<font color="#f00"><b><i>%s</i></b></font><br />\n' % error
518
519 def shouldProcess(self, request):
520 args = request.args
521 fid = self.getFormID()
522 return (args and # there are arguments to the request
523 args.has_key('__formtype__') and # this is a widgets.Form reques t
524 args['__formtype__'][0] == reflect.qual(self.__class__) and # it is for a form of my type
525 ((not fid) or # I am only allowed one form per page
526 (args.has_key('__formid__') and # if I distinguish myself from others, the request must too
527 args['__formid__'][0] == fid))) # I am in fact the same
528
529 def tryAgain(self, err, req):
530 """Utility method for re-drawing the form with an error message.
531
532 This is handy in forms that process Deferred results. Normally you can
533 just raise a FormInputError() and this will happen by default.
534
535 """
536 l = []
537 w = l.append
538 w(self.formatError(err))
539 self.format(self.getFormFields(req), w, req)
540 return l
541
542 def display(self, request):
543 """Display the form."""
544 form = self.getFormFields(request)
545 if isinstance(form, defer.Deferred):
546 if self.shouldProcess(request):
547 form.addCallback(lambda form, f=self._displayProcess, r=request: f(r, form))
548 else:
549 form.addCallback(lambda form, f=self._displayFormat, r=request: f(r, form))
550 return [form]
551 else:
552 if self.shouldProcess(request):
553 return self._displayProcess(request, form)
554 else:
555 return self._displayFormat(request, form)
556
557 def _displayProcess(self, request, form):
558 l = []
559 write = l.append
560 try:
561 val = self._doProcess(form, write, request)
562 if val:
563 l.extend(val)
564 except FormInputError, fie:
565 write(self.formatError(str(fie)))
566 return l
567
568 def _displayFormat(self, request, form):
569 l = []
570 self.format(form, l.append, request)
571 return l
572
573
574
575 class DataWidget(Widget):
576 def __init__(self, data):
577 self.data = data
578 def display(self, request):
579 return [self.data]
580
581 class Time(Widget):
582 def display(self, request):
583 return [time.ctime(time.time())]
584
585 class Container(Widget):
586 def __init__(self, *widgets):
587 self.widgets = widgets
588
589 def display(self, request):
590 value = []
591 for widget in self.widgets:
592 d = widget.display(request)
593 value.extend(d)
594 return value
595
596 class _RequestDeferral:
597 def __init__(self):
598 self.deferred = defer.Deferred()
599 self.io = StringIO()
600 self.write = self.io.write
601
602 def finish(self):
603 self.deferred.callback([self.io.getvalue()])
604
605 def possiblyDeferWidget(widget, request):
606 # web in my head get it out get it out
607 try:
608 disp = widget.display(request)
609 # if this widget wants to defer anything -- well, I guess we've got to
610 # defer it.
611 for elem in disp:
612 if isinstance(elem, defer.Deferred):
613 req = _RequestDeferral()
614 RenderSession(disp, req)
615 return req.deferred
616 return string.join(disp, '')
617 except:
618 io = StringIO()
619 traceback.print_exc(file=io)
620 return html.PRE(io.getvalue())
621
622 class RenderSession:
623 """I handle rendering of a list of deferreds, outputting their
624 results in correct order."""
625
626 class Sentinel:
627 pass
628
629 def __init__(self, lst, request):
630 self.lst = lst
631 self.request = request
632 self.needsHeaders = 0
633 self.beforeBody = 1
634 self.forgotten = 0
635 self.pauseList = []
636 for i in range(len(self.lst)):
637 item = self.lst[i]
638 if isinstance(item, defer.Deferred):
639 self._addDeferred(item, self.lst, i)
640 self.keepRendering()
641
642 def _addDeferred(self, deferred, lst, idx):
643 sentinel = self.Sentinel()
644 if hasattr(deferred, 'needsHeader'):
645 # You might want to set a header from a deferred, in which
646 # case you have to set an attribute -- needsHeader.
647 self.needsHeaders = self.needsHeaders + 1
648 args = (sentinel, 1)
649 else:
650 args = (sentinel, 0)
651 lst[idx] = sentinel, deferred
652 deferred.pause()
653 self.pauseList.append(deferred)
654 deferred.addCallbacks(self.callback, self.callback,
655 callbackArgs=args, errbackArgs=args)
656
657
658 def callback(self, result, sentinel, decNeedsHeaders):
659 if self.forgotten:
660 return
661 if result != FORGET_IT:
662 self.needsHeaders = self.needsHeaders - decNeedsHeaders
663 else:
664 result = [FORGET_IT]
665
666 # Make sure result is a sequence,
667 if not type(result) in (types.ListType, types.TupleType):
668 result = [result]
669
670 # If the deferred does not wish to produce its result all at
671 # once, it can give us a partial result as
672 # (NOT_DONE_YET, partial_result)
673 ## XXX: How would a deferred go about producing the result in multiple
674 ## stages?? --glyph
675 if result[0] is NOT_DONE_YET:
676 done = 0
677 result = result[1]
678 if not type(result) in (types.ListType, types.TupleType):
679 result = [result]
680 else:
681 done = 1
682
683 for i in xrange(len(result)):
684 item = result[i]
685 if isinstance(item, defer.Deferred):
686 self._addDeferred(item, result, i)
687
688 for position in range(len(self.lst)):
689 item = self.lst[position]
690 if type(item) is types.TupleType and len(item) > 0:
691 if item[0] is sentinel:
692 break
693 else:
694 raise AssertionError('Sentinel for Deferred not found!')
695
696 if done:
697 self.lst[position:position+1] = result
698 else:
699 self.lst[position:position] = result
700
701 self.keepRendering()
702
703
704 def keepRendering(self):
705 while self.pauseList:
706 pl = self.pauseList
707 self.pauseList = []
708 for deferred in pl:
709 deferred.unpause()
710 return
711
712 if self.needsHeaders:
713 # short circuit actual rendering process until we're sure no
714 # more deferreds need to set headers...
715 return
716
717 assert self.lst is not None, "This shouldn't happen."
718 while 1:
719 item = self.lst[0]
720 if self.beforeBody and FORGET_IT in self.lst:
721 # If I haven't moved yet, and the widget wants to take
722 # over the page, let it do so!
723 self.forgotten = 1
724 return
725
726 if isinstance(item, types.StringType):
727 self.beforeBody = 0
728 self.request.write(item)
729 elif type(item) is types.TupleType and len(item) > 0:
730 if isinstance(item[0], self.Sentinel):
731 return
732 elif isinstance(item, failure.Failure):
733 self.request.write(webutil.formatFailure(item))
734 else:
735 self.beforeBody = 0
736 unknown = html.PRE(repr(item))
737 self.request.write("RENDERING UNKNOWN: %s" % unknown)
738
739 del self.lst[0]
740 if len(self.lst) == 0:
741 self.lst = None
742 self.request.finish()
743 return
744
745
746 ## XXX: is this needed?
747 class WidgetResource(resource.Resource):
748 def __init__(self, widget):
749 self.widget = widget
750 resource.Resource.__init__(self)
751
752 def render(self, request):
753 RenderSession(self.widget.display(request), request)
754 return NOT_DONE_YET
755
756
757 class Page(resource.Resource, Presentation):
758
759 def __init__(self):
760 resource.Resource.__init__(self)
761 Presentation.__init__(self)
762
763 def render(self, request):
764 displayed = self.display(request)
765 RenderSession(displayed, request)
766 return NOT_DONE_YET
767
768
769 class WidgetPage(Page):
770 """
771 I am a Page that takes a Widget in its constructor, and displays that
772 Widget wrapped up in a simple HTML template.
773 """
774 stylesheet = '''
775 a
776 {
777 font-family: Lucida, Verdana, Helvetica, Arial, sans-serif;
778 color: #369;
779 text-decoration: none;
780 }
781
782 th
783 {
784 font-family: Lucida, Verdana, Helvetica, Arial, sans-serif;
785 font-weight: bold;
786 text-decoration: none;
787 text-align: left;
788 }
789
790 pre, code
791 {
792 font-family: "Courier New", Courier, monospace;
793 }
794
795 p, body, td, ol, ul, menu, blockquote, div
796 {
797 font-family: Lucida, Verdana, Helvetica, Arial, sans-serif;
798 color: #000;
799 }
800 '''
801
802 template = '''<html>
803 <head>
804 <title>%%%%self.title%%%%</title>
805 <style>
806 %%%%self.stylesheet%%%%
807 </style>
808 <base href="%%%%request.prePathURL()%%%%">
809 </head>
810
811 <body>
812 <h1>%%%%self.title%%%%</h1>
813 %%%%self.widget%%%%
814 </body>
815 </html>
816 '''
817
818 title = 'No Title'
819 widget = 'No Widget'
820
821 def __init__(self, widget):
822 Page.__init__(self)
823 self.widget = widget
824 if hasattr(widget, 'stylesheet'):
825 self.stylesheet = widget.stylesheet
826
827 def prePresent(self, request):
828 self.title = self.widget.getTitle(request)
829
830 def render(self, request):
831 displayed = self.display(request)
832 RenderSession(displayed, request)
833 return NOT_DONE_YET
834
835 class Gadget(resource.Resource):
836 """I am a collection of Widgets, to be rendered through a Page Factory.
837 self.pageFactory should be a Resource that takes a Widget in its
838 constructor. The default is twisted.web.widgets.WidgetPage.
839 """
840
841 isLeaf = 0
842
843 def __init__(self):
844 resource.Resource.__init__(self)
845 self.widgets = {}
846 self.files = []
847 self.modules = []
848 self.paths = {}
849
850 def render(self, request):
851 #Redirect to view this entity as a collection.
852 request.setResponseCode(http.FOUND)
853 # TODO who says it's not https?
854 request.setHeader("location","http%s://%s%s/" % (
855 request.isSecure() and 's' or '',
856 request.getHeader("host"),
857 (string.split(request.uri,'?')[0])))
858 return "NO DICE!"
859
860 def putWidget(self, path, widget):
861 """
862 Gadget.putWidget(path, widget)
863 Add a Widget to this Gadget. It will be rendered through the
864 pageFactory associated with this Gadget, whenever 'path' is requested.
865 """
866 self.widgets[path] = widget
867
868 #this is an obsolete function
869 def addFile(self, path):
870 """
871 Gadget.addFile(path)
872 Add a static path to this Gadget. This method is obsolete, use
873 Gadget.putPath instead.
874 """
875
876 log.msg("Gadget.addFile() is deprecated.")
877 self.paths[path] = path
878
879 def putPath(self, path, pathname):
880 """
881 Gadget.putPath(path, pathname)
882 Add a static path to this Gadget. Whenever 'path' is requested,
883 twisted.web.static.File(pathname) is sent.
884 """
885 self.paths[path] = pathname
886
887 def getWidget(self, path, request):
888 return self.widgets.get(path)
889
890 def pageFactory(self, *args, **kwargs):
891 """
892 Gadget.pageFactory(*args, **kwargs) -> Resource
893 By default, this method returns self.page(*args, **kwargs). It
894 is only for backwards-compatibility -- you should set the 'pageFactory'
895 attribute on your Gadget inside of its __init__ method.
896 """
897 #XXX: delete this after a while.
898 if hasattr(self, "page"):
899 log.msg("Gadget.page is deprecated, use Gadget.pageFactory instead")
900 return apply(self.page, args, kwargs)
901 else:
902 return apply(WidgetPage, args, kwargs)
903
904 def getChild(self, path, request):
905 if path == '':
906 # ZOOP!
907 if isinstance(self, Widget):
908 return self.pageFactory(self)
909 widget = self.getWidget(path, request)
910 if widget:
911 if isinstance(widget, resource.Resource):
912 return widget
913 else:
914 p = self.pageFactory(widget)
915 p.isLeaf = getattr(widget,'isLeaf',0)
916 return p
917 elif self.paths.has_key(path):
918 prefix = getattr(sys.modules[self.__module__], '__file__', '')
919 if prefix:
920 prefix = os.path.abspath(os.path.dirname(prefix))
921 return static.File(os.path.join(prefix, self.paths[path]))
922
923 elif path == '__reload__':
924 return self.pageFactory(Reloader(map(reflect.namedModule, [self.__mo dule__] + self.modules)))
925 else:
926 return error.NoResource("No such child resource in gadget.")
927
928
929 class TitleBox(Presentation):
930
931 template = '''\
932 <table %%%%self.widthOption%%%% cellpadding="1" cellspacing="0" border="0"><tr>\
933 <td bgcolor="%%%%self.borderColor%%%%"><center><font color="%%%%self.titleTextCo lor%%%%">%%%%self.title%%%%</font></center>\
934 <table width="100%" cellpadding="3" cellspacing="0" border="0"><tr>\
935 <td bgcolor="%%%%self.boxColor%%%%"><font color="%%%%self.boxTextColor%%%%">%%%% self.widget%%%%</font></td>\
936 </tr></table></td></tr></table>\
937 '''
938
939 borderColor = '#000000'
940 titleTextColor = '#ffffff'
941 boxTextColor = '#000000'
942 boxColor = '#ffffff'
943 widthOption = 'width="100%"'
944
945 title = 'No Title'
946 widget = 'No Widget'
947
948 def __init__(self, title, widget):
949 """Wrap a widget with a given title.
950 """
951 self.widget = widget
952 self.title = title
953 Presentation.__init__(self)
954
955
956 class Reloader(Presentation):
957 template = '''
958 Reloading...
959 <ul>
960 %%%%reload(request)%%%%
961 </ul> ... reloaded!
962 '''
963 def __init__(self, modules):
964 Presentation.__init__(self)
965 self.modules = modules
966
967 def reload(self, request):
968 request.redirect("..")
969 x = []
970 write = x.append
971 for module in self.modules:
972 rebuild.rebuild(module)
973 write('<li>reloaded %s<br />' % module.__name__)
974 return x
975
976 class Sidebar(StreamWidget):
977 bar = [
978 ['Twisted',
979 ['mirror', 'http://coopweb.org/ssd/twisted/'],
980 ['mailing list', 'cgi-bin/mailman/listinfo/twisted-python']
981 ]
982 ]
983
984 headingColor = 'ffffff'
985 headingTextColor = '000000'
986 activeHeadingColor = '000000'
987 activeHeadingTextColor = 'ffffff'
988 sectionColor = '000088'
989 sectionTextColor = '008888'
990 activeSectionColor = '0000ff'
991 activeSectionTextColor = '00ffff'
992
993 def __init__(self, highlightHeading, highlightSection):
994 self.highlightHeading = highlightHeading
995 self.highlightSection = highlightSection
996
997 def getList(self):
998 return self.bar
999
1000 def stream(self, write, request):
1001 write("<table width=120 cellspacing=1 cellpadding=1 border=0>")
1002 for each in self.getList():
1003 if each[0] == self.highlightHeading:
1004 headingColor = self.activeHeadingColor
1005 headingTextColor = self.activeHeadingTextColor
1006 canHighlight = 1
1007 else:
1008 headingColor = self.headingColor
1009 headingTextColor = self.headingTextColor
1010 canHighlight = 0
1011 write('<tr><td colspan=2 bgcolor="#%s"><font color="%s">'
1012 '<strong>%s</strong>'
1013 '</font></td></td></tr>\n' % (headingColor, headingTextColor, each[0]))
1014 for name, link in each[1:]:
1015 if canHighlight and (name == self.highlightSection):
1016 sectionColor = self.activeSectionColor
1017 sectionTextColor = self.activeSectionTextColor
1018 else:
1019 sectionColor = self.sectionColor
1020 sectionTextColor = self.sectionTextColor
1021 write('<tr><td align=right bgcolor="#%s" width=6>-</td>'
1022 '<td bgcolor="#%s"><a href="%s"><font color="#%s">%s'
1023 '</font></a></td></tr>'
1024 % (sectionColor, sectionColor, request.sibLink(link), sec tionTextColor, name))
1025 write("</table>")
1026
1027 # moved from template.py
1028 from twisted.web.woven import template
1029 from twisted.python import components
1030
1031 class WebWidgetNodeMutator(template.NodeMutator):
1032 """A WebWidgetNodeMutator replaces the node that is passed in to generate
1033 with the result of generating the twisted.web.widget instance it adapts.
1034 """
1035 def generate(self, request, node):
1036 widget = self.data
1037 displayed = widget.display(request)
1038 try:
1039 html = string.join(displayed)
1040 except:
1041 pr = Presentation()
1042 pr.tmpl = displayed
1043 #strList = pr.display(request)
1044 html = string.join(displayed)
1045 stringMutator = template.StringNodeMutator(html)
1046 return stringMutator.generate(request, node)
1047
1048 components.registerAdapter(WebWidgetNodeMutator, Widget, template.INodeMutator)
1049
1050 import static
OLDNEW
« no previous file with comments | « third_party/twisted_8_1/twisted/web/vhost.py ('k') | third_party/twisted_8_1/twisted/web/woven/FlashConduit.fla » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698