Package turbogears :: Package widgets :: Module base

Source Code for Module turbogears.widgets.base

  1  import itertools 
  2  import os 
  3  import warnings 
  4   
  5  import pkg_resources 
  6   
  7  from cherrypy import request 
  8  import tgmochikit 
  9   
 10  from turbogears import config, startup, view 
 11  from turbogears.util import (setlike, to_unicode, copy_if_mutable, 
 12      get_package_name, request_available) 
 13  from turbogears.i18n.utils import get_locale 
 14  from turbogears.widgets.meta import MetaWidget, load_kid_template 
 15   
 16  __all__ = ['load_widgets', 'all_widgets', 'Widget', 'CompoundWidget', 
 17      'WidgetsList', 'register_static_directory', 'static', 
 18      'Resource', 'Link', 'CSSLink', 'JSLink', 
 19      'Source', 'CSSSource', 'JSSource', 
 20      'js_location', 'mochikit', 'jsi18nwidget', 
 21      'WidgetDescription', 'set_with_self'] 
 22   
 23   
24 -class Enum(set):
25 """Enum used at js_locations. 26 27 This is less strict than ``turbogears.utils.Enum`` and serves our 28 purposes as well as allowing any object with ``retrieve_javascript``, 29 ``retrieve_css``, and ``location`` attributes to provide resources to 30 the template when scanned in ``turbogears.controllers._process_output``. 31 32 Example: 33 34 >>> locations = Enum('bodytop', 'bodybottom', head') 35 >>> locations.head == locations.head 36 True 37 >>> locations.head == locations.bodybottom 38 False 39 >>> locations.head in locations 40 True 41 >>> locations.foo 42 Traceback (most recent call last): 43 ... 44 AttributeError 45 46 """
47 - def __init__(self, *args):
48 set.__init__(self, args)
49
50 - def __getattr__(self, name):
51 if name in self: 52 return name 53 raise AttributeError
54
55 - def __repr__(self):
56 return "%s(%s)" % (self.__class__.__name__, ', '.join(map(repr, self)))
57 58 # Keeps a count of the each declared widget in a WidgetsList instance 59 # so their order is preserved. 60 counter = itertools.count() 61 62 # Load all widgets provided by the widget entry point
63 -def load_widgets():
64 for widget_mod in pkg_resources.iter_entry_points("turbogears.widgets"): 65 try: 66 widget_mod.load() 67 except Exception, e: 68 raise ImportError, 'Error loading plugin "%s"\n%s: %s' % ( 69 widget_mod, e.__class__.__name__, e)
70 71 all_widgets = set() 72 73 PlainHTML = load_kid_template(""" 74 <html xmlns:py="http://purl.org/kid/ns#" py:replace="elements"/> 75 """, modname='turbogears.widgets.plainhtml')[0] 76 77 78 ############################################################################# 79 # Widgets base classes # 80 ############################################################################# 81
82 -class Widget(object):
83 """A TurboGears Widget. 84 85 '__init__' and 'update_params' are the only methods you might need to 86 care extending. 87 88 Attributes you should know about: 89 90 name : The name of the widget. 91 template : Kid template for the widget. 92 default : Default value for the widget. 93 css : List of CSSLinks and CSSSources for the widget. These 94 will be automatically pulled and inserted in the 95 page template that displays it. 96 javascript: List of JSLinks and JSSources for the widget. Same as css. 97 is_named : A property returning True if the widget has overrided it's 98 default name. 99 params : All parameter names listed here will be treated as special 100 parameters. This list is updated by the metaclass and 101 always contains *all* params from the widget itself and 102 all it's bases. Can be used as a quick reminder of all 103 the params your widget has at it's disposal. They all 104 behave the same and have the same priorities regarding their 105 overridal. Read on... 106 params_doc: A dictionary containing 'params' names as keys and their 107 docstring as value. For documentation at the widget browser. 108 109 All initialization parameters listed at the class attribute "params" can be 110 defined as class attributes, overriden at __init__ or at display time. They 111 will be treated as special params for the widget, which means: 112 113 1) You can fix default values for them when sublcassing Widget. 114 115 2) If passed as **params to the constructor, the will be bound automatically 116 to the widget instance, taking preference over any class attributes 117 previously defined. Mutable attributes (dicts and lists) defined as class 118 attributes are safe to modify as care is taken to copy them so the class 119 attribute remains unchanged. 120 121 3) They can be further overrided by passing them as keyword args to 122 display(). This will only affect that display() call in a thread-safe 123 way. 124 125 4) A callable can be passed and it will be called automatically when sending 126 variables to the template. This can be handy to pick up parameters which 127 change in every request or affect many widgets simultaneously. 128 129 """ 130 __metaclass__ = MetaWidget 131 132 name = "widget" 133 template = None 134 default = None 135 css = [] 136 javascript = [] 137 params = [] 138 params_doc = {} 139
140 - def __init__(self, name=None, template=None, default=None, **params):
141 """Widget initialization. 142 143 All initialization has to take place in this method. 144 It's not thread-safe to mutate widget's attributes outside this method 145 or anytime after widget's first display. 146 147 *Must* call super(MyWidget, self).__init__(*args, **kw) cooperatively, 148 unless, of course, your know what you're doing. Preferably this should 149 be done before any actual work is done in the method. 150 151 Parameters: 152 153 name : The widget's name. In input widgets, this will also be the 154 name of the variable that the form will send to the 155 controller. This is the only param that is safe to pass as a 156 positional argument to __init__. 157 template : The template that the widget should use to display itself. 158 Currently only Kid templates are supported. You can both 159 initialize with a template string or with the path to a 160 file-base template: "myapp.templates.widget_tmpl" 161 default : Default value to display when no value is passed at display 162 time. 163 **params : Keyword arguments specific to your widget or to any of it's 164 bases. If listed at class attribute 'params' the will be 165 bound automatically to the widget instance. 166 167 Note: Do not confuse these parameters with parameters listed at 168 "params". Some widgets accept parameters at the constructor which are 169 not listed params, these parameter won't be passed to the template, be 170 automatically called, etc.. 171 172 """ 173 self._declaration_counter = counter.next() 174 if name: 175 self.name = name 176 177 if template: 178 self.template_c, self.template = load_kid_template(template) 179 180 if default is not None: 181 self.default = default 182 183 # logic for managing the params attribute 184 for param in self.__class__.params: 185 if param in params: 186 # make sure we don't keep references to mutables 187 setattr(self, param, copy_if_mutable(params.pop(param))) 188 189 else: 190 # make sure we don't alter mutable class attributes 191 value, mutable = copy_if_mutable( 192 getattr(self.__class__, param), True) 193 if mutable: 194 # re-set it only if mutable 195 setattr(self, param, value) 196 197 for unused in params.iterkeys(): 198 warnings.warn('keyword argument "%s" is unused at %r instance' % ( 199 unused, self.__class__.__name__))
200
201 - def adjust_value(self, value, **params):
202 """Adjust the value sent to the template on display.""" 203 return value
204
205 - def update_params(self, params):
206 """Update the template parameters. 207 208 This method will have the last chance to update the variables sent to 209 the template for the specific request. All parameters listed at class 210 attribute 'params' will be available at the 'params' dict this method 211 receives. 212 213 *Must* call super(MyWidget, self).update_params(params) cooperatively, 214 unless, of course, your know what you're doing. Preferably this should 215 be done before any actual work is done in the method. 216 217 """ 218 pass
219 220 # The following methods are needed to be a well behaved widget, however, 221 # there is rarely the need to override or extend them if inheritting 222 # directly or indirectly from Widget. 223
224 - def __call__(self, *args, **params):
225 """Delegate to display. Used as an alias to avoid tiresome typing.""" 226 return self.display(*args, **params)
227
228 - def display(self, value=None, **params):
229 """Display the widget in a Kid template. 230 231 Returns an elementtree node instance. If you need serialized output 232 in a string, call 'render' instead. 233 234 Probably you will not need to override or extend if inheriting from 235 Widget. 236 237 @params: 238 239 value : The value to display in the widget. 240 **params: Extra parameters specific to the widget. All keyword params 241 supplied will pass through the update_params method which 242 will have a last chance to modify them before reaching the 243 template. 244 245 """ 246 if not getattr(self, 'template_c', False): 247 warnings.warn('kid' in view.engines 248 and "Widget instance %r has no template defined" % self 249 or "Trying to render a widget, but the Kid" 250 " templating engine is not installed or not yet loaded.") 251 return None 252 253 # logic for managing the params attribute 254 for param in self.__class__.params: 255 if param in params: 256 param_value = params[param] 257 if callable(param_value): 258 param_value = param_value() 259 260 else: 261 # if the param hasn't been overridden (passed as a keyword 262 # argument inside **params) put the corresponding instance 263 # value inside params. 264 param_value = getattr(self, param, None) 265 266 # make sure we don't pass a reference to mutables 267 params[param] = copy_if_mutable(param_value) 268 269 if not params.get('name'): 270 params['name'] = self.name 271 272 if value is None: 273 value = self.default 274 if callable(value): 275 value = value() 276 params['value'] = to_unicode(self.adjust_value(value, **params)) 277 278 self.update_params(params) 279 280 try: 281 transform = view.engines['kid'].transform 282 except (KeyError, AttributeError): 283 # this can happen if you render a widget before application startup 284 # when view.load_engines() has not yet been called 285 raise RuntimeError("Trying to render a widget, but the Kid" 286 " templating engine is not installed or not yet loaded.") 287 288 # If the page template is Genshi, we keep track of the nesting level, 289 # because Genshi cannot display Kid's ElementTree elements directly. 290 if request_available() and request.tg_template_enginename == 'genshi': 291 display_level = getattr(request, 'tg_widgets_display_level', 0) 292 request.tg_widgets_display_level = display_level + 1 293 else: 294 display_level = None 295 296 try: 297 output = transform(params, self.template_c) 298 if display_level == 0: 299 # On the topmost level, we create a Genshi markup stream 300 # from Kid's ElementTree element to make Genshi really happy. 301 # This automatism makes wrapping widget output with ET(...) 302 # calls in Genshi page templates unnecessary. 303 output = view.genshi_et(output) 304 finally: 305 if display_level is not None: 306 request.tg_widgets_display_level = display_level 307 308 return output
309
310 - def render(self, value=None, format="html", **params):
311 """Exactly the same as display() but return serialized output instead. 312 313 Useful for debugging or to display the widget in a non-Kid template 314 like Cheetah, STAN, ... 315 316 """ 317 elem = self.display(value, **params) 318 template = PlainHTML(elements=elem) 319 return template.serialize(output=format, fragment=True)
320
321 - def retrieve_javascript(self):
322 """Return the needed JavaScript ressources. 323 324 Return a setlike instance containing all the JSLinks and JSSources 325 the widget needs. 326 327 """ 328 scripts = setlike() 329 for script in self.javascript: 330 scripts.add(script) 331 return scripts
332
333 - def retrieve_css(self):
334 """Return the needed CSS ressources. 335 336 Return a setlike instance with all the CSSLinks and CSSSources 337 the widget needs. 338 339 """ 340 css = setlike() 341 for cssitem in self.css: 342 css.add(cssitem) 343 return css
344
345 - def _get_is_named(self):
346 """Return True if the widget has overridden its default name.""" 347 if self.name != "widget": 348 return True 349 else: 350 return False
351 is_named = property(_get_is_named) 352
353 - def __setattr__(self, key, value):
354 if self._locked: 355 raise ValueError, \ 356 "It is not threadsafe to modify widgets in a request" 357 else: 358 return super(Widget, self).__setattr__(key, value)
359
360 - def __repr__(self):
361 return "%s(%s)" % (self.__class__.__name__, ', '.join( 362 ["%s=%r" % (var, getattr(self, var)) 363 for var in ['name'] + self.__class__.params]))
364 365
366 -class CompoundWidget(Widget):
367 """A widget that can contain other widgets. 368 369 A compound widget is a widget that can group several widgets to make a 370 complex widget. Child widget names must be listed at their widget's 371 ``member_widgets`` attribute. 372 373 """ 374 compound = True 375 member_widgets = [] 376
377 - def __init__(self, *args, **kw):
378 # logic for managing the member_widgets attribute 379 for member in self.__class__.member_widgets: 380 if member in kw: 381 setattr(self, member, kw.pop(member)) 382 elif not hasattr(self, member): 383 setattr(self, member, None) 384 super(CompoundWidget, self).__init__(*args, **kw)
385
386 - def iter_member_widgets(self):
387 """Iterates over all the widget's children""" 388 for member in self.__class__.member_widgets: 389 attr = getattr(self, member, None) 390 if isinstance(attr, list): 391 for widget in attr: 392 yield widget 393 elif attr is not None: 394 yield attr
395
396 - def display(self, value=None, **params):
397 params["member_widgets_params"] = params.copy() 398 # logic for managing the member_widgets attribute 399 for member in self.__class__.member_widgets: 400 params[member] = getattr(self, member, None) 401 return super(CompoundWidget, self).display(value, **params)
402
403 - def update_params(self, d):
404 super(CompoundWidget, self).update_params(d) 405 d['value_for'] = lambda f: self.value_for(f, d['value']) 406 widgets_params = d['member_widgets_params'] 407 d['params_for'] = lambda f: self.params_for(f, **widgets_params)
408
409 - def value_for(self, item, value):
410 """Get value for member widget. 411 412 Pick up the value for a given member_widget 'item' from the 413 value dict passed to this widget. 414 415 """ 416 name = getattr(item, "name", item) 417 if isinstance(value, dict): 418 return value.get(name) 419 else: 420 return None
421
422 - def params_for(self, item, **params):
423 """Get params for member widget. 424 425 Pick up the params for the given member_widget 'item' from 426 the params dict passed to this widget. 427 428 """ 429 name = getattr(item, "name", item) 430 item_params = {} 431 for k, v in params.iteritems(): 432 if isinstance(v, dict): 433 if name in v: 434 item_params[k] = v[name] 435 return item_params
436
437 - def retrieve_javascript(self):
438 """Get JavaScript for the member widgets. 439 440 Retrieve the JavaScript for all the member widgets and 441 get an ordered union of them. 442 443 """ 444 scripts = setlike() 445 for script in self.javascript: 446 scripts.add(script) 447 for widget in self.iter_member_widgets(): 448 for script in widget.retrieve_javascript(): 449 scripts.add(script) 450 return scripts
451
452 - def retrieve_css(self):
453 """Get CSS for the member widgets. 454 455 Retrieve the CSS for all the member widgets and 456 get an ordered union of them. 457 458 """ 459 css = setlike() 460 for cssitem in self.css: 461 css.add(cssitem) 462 for widget in self.iter_member_widgets(): 463 for cssitem in widget.retrieve_css(): 464 css.add(cssitem) 465 return css
466 467 468 ############################################################################# 469 # Declarative widgets support # 470 ############################################################################# 471
472 -class MetaWidgetsList(type):
473 """Metaclass for WidgetLists. 474 475 Takes care that the resulting WidgetList has all widgets in the same order 476 as they were declared. 477 478 """
479 - def __new__(meta, class_name, bases, class_dict):
480 declared_widgets = [] 481 for base in bases: 482 declared_widgets.extend(getattr(base, 'declared_widgets', [])) 483 for name, value in class_dict.items(): 484 if isinstance(value, Widget): 485 if not value.is_named: 486 value.name = name 487 declared_widgets.append(value) 488 # we keep the widget accessible as attribute here, 489 # i.e. we don't del class_dict[name] as we did before 490 declared_widgets.sort(key=lambda w: w._declaration_counter) 491 cls = type.__new__(meta, class_name, bases, class_dict) 492 cls.declared_widgets = declared_widgets 493 return cls
494 495
496 -class WidgetsList(list):
497 """A widget list. 498 499 That's really all. A plain old list that you can declare as a classs 500 with widgets ordered as attributes. Syntactic sugar for an unsweet world. 501 502 """ 503 __metaclass__ = MetaWidgetsList 504
505 - def __init__(self, *args):
506 super(WidgetsList, self).__init__(self.declared_widgets) 507 if args: 508 if len(args) == 1: 509 args = args[0] 510 if isinstance(args, Widget): 511 args = [args] 512 self.extend(args) 513 if not self: 514 warnings.warn("You have declared an empty WidgetsList")
515 516 # Make WidgetsList instances usable as Widget containers in the dict 517 # returned by controller methods, so that the JS/CSS resources of all 518 # contained widgets get picked up by the templates: 519
520 - def retrieve_javascript(self):
521 return itertools.chain(*(w.retrieve_javascript() for w in self 522 if callable(getattr(w, 'retrieve_javascript', None))))
523
524 - def retrieve_css(self):
525 return itertools.chain(*(w.retrieve_css() for w in self 526 if callable(getattr(w, 'retrieve_css', None))))
527 528 529 ############################################################################# 530 # CSS, JS and mochikit stuff # 531 ############################################################################# 532
533 -def register_static_directory(modulename, directory):
534 """Set up a static directory for JavaScript and CSS files. 535 536 You can refer to this static directory in templates as 537 ${tg.widgets}/modulename 538 539 """ 540 directory = os.path.abspath(directory) 541 config.update({'/tg_widgets/%s' % modulename: { 542 'static_filter.on' : True, 543 'static_filter.dir': directory 544 }})
545 546 static = "turbogears.widgets" 547 548 register_static_directory(static, 549 pkg_resources.resource_filename(__name__, 'static')) 550 551 register_static_directory('turbogears', 552 pkg_resources.resource_filename('turbogears', 'static')) 553 554
555 -def set_with_self(self):
556 theset = setlike() 557 theset.add(self) 558 return theset
559 560
561 -class Resource(Widget):
562 """A resource for your widget. 563 564 For example, this can be a link to an external JavaScript/CSS file 565 or inline source to include at the template the widget is displayed. 566 567 """ 568 order = 0 569 params = ['order'] 570 params_doc = {'order': "JS and CSS are sorted in this 'order' at render time"}
571 572 591 592 608 609 610 js_location = Enum('head', 'bodytop', 'bodybottom') 611 612 637 638
639 -class TGMochiKit(JSLink):
640 """This JSLink includes MochkKit by means of the tgMochiKit project. 641 642 Depending on three config options, you can decide 643 644 ``tg_mochikit.version`` -- ``"1.3.1"`` 645 Which version of MochiKit should be used. 646 ``tg_mochikit.packed`` -- ``False`` 647 Whether to deliver the MochiKit JS code packed or unpacked. 648 ``tg_mochikit.xhtml`` -- ``False`` 649 Whether to be XHTML-compliant or not when ``tg_mochikit.packed`` is 650 ``False``, i.e. selects whether the ``turbogears.mochikit`` widget 651 should include all of MochiKit's submodules as separate JavaScript 652 resources or just the main ``MochiKit.js`` file. 653 654 For more explanation of the options, see the tgMochiKit documentation at 655 656 http://docs.turbogears.org/tgMochiKit 657 658 """ 659 660 template = """<script xmlns:py="http://purl.org/kid/ns#" 661 py:for="js in javascripts" py:replace="js.display()" 662 /> 663 """ 664
665 - def retrieve_javascript(self):
666 tgmochikit.init(register_static_directory, config) 667 if config.get('tg.mochikit_suppress', False): 668 if 'turbogears.mochikit' in config.get('tg.include_widgets', []): 669 warnings.warn("""\ 670 tg.mochikit_suppress == True, but 'turbogears.mochikit' is to be included via 671 'tg.include_widgets'. This is a contradiction, and mochikit_suppress overrides 672 the inclusion to prevent subtle errors due to version mixing of MochiKit.""") 673 return [] 674 javascripts = [JSLink("tgmochikit", path) 675 for path in tgmochikit.get_paths()] 676 return javascripts
677
678 - def update_params(self, d):
679 super(TGMochiKit, self).update_params(d) 680 d['javascripts'] = self.retrieve_javascript()
681 682 mochikit = TGMochiKit("tgmochikit") 683 684
685 -class JSI18NWidget(Widget):
686 """JavaScript i18n support widget. 687 688 This is a widget that can be used to add support for 689 internationalization of JavaScript texts. 690 691 It will basically add an implementation of the 692 _ function that then can be used to wrap text literals 693 in javascript-code. 694 695 Additionally, message-catalogs are looked up and included 696 depending on the current locale. 697 698 To include the widget, put 699 700 tg.include_widgets = ['turbogears.jsi18nwidget'] 701 702 in your application configuration. 703 704 """ 705 params = ['locale_catalog_providers'] 706 707 locale_catalog_providers = [] 708
709 - def __init__(self, *args, **kwargs):
710 super(JSI18NWidget, self).__init__(*args, **kwargs) 711 self._initialized = False
712
713 - def register_package_provider(self, 714 package=None, directory=None, domain=None):
715 716 parent = self 717 718 class PackageProvider(object): 719 def __init__(self): 720 self._initialized = False
721 722 def __call__(self, locale): 723 if not self._initialized: 724 # the leading underscore is to prevent shadowing of the 725 # register_package_provider-arguments 726 if package is None: 727 _package = get_package_name() 728 else: 729 _package = package 730 if directory is None: 731 _directory = "static/javascript" 732 else: 733 _directory = directory 734 if domain is None: 735 _domain = config.get('i18n.domain', 'messages') 736 else: 737 _domain = domain 738 js_dir = pkg_resources.resource_filename(_package, _directory) 739 register_static_directory(_package, js_dir) 740 self._package_name = _package 741 self._domain = _domain 742 self._initialized = True 743 js = [] 744 for l in locale, locale[:2]: 745 link = JSLink(self._package_name, "%s-%s.js" % (self._domain, l)) 746 if parent.linked_file_exists(link): 747 js.append(link) 748 break 749 return js
750 751 self.locale_catalog_providers.append(PackageProvider()) 752
753 - def retrieve_javascript(self):
754 if not self._initialized: 755 if not config.get('i18n.suppress_default_package_provider', False): 756 self.register_package_provider() 757 self._initialized = True 758 js = super(JSI18NWidget, self).retrieve_javascript() 759 js.add(JSLink("turbogears", 'js/i18n_base.js')) 760 locale = get_locale() 761 for pp in self.locale_catalog_providers: 762 js.extend(pp(locale)) 763 return js
764
765 - def linked_file_exists(self, widget):
766 static_dir = config.get('static_filter.dir', 767 path='/tg_widgets/%s' % widget.mod) 768 if static_dir: 769 return os.path.exists(os.path.join(static_dir, widget.name)) 770 else: 771 return False
772 773 jsi18nwidget = JSI18NWidget() 774 775
776 -class Source(Resource):
777 params = ["src"] 778
779 - def __init__(self, src, *args, **kw):
780 super(Source, self).__init__(*args, **kw) 781 self.src = src
782
783 - def __hash__(self):
784 return hash(self.src)
785
786 - def __eq__(self, other):
787 return self.src == getattr(other, "src", None)
788 789
790 -class CSSSource(Source):
791 """A CSS source snippet.""" 792 793 template = """ 794 <style type="text/css" media="$media">$src</style> 795 """ 796 params = ["media"] 797 params_doc = {'src': 'The CSS source for the link', 798 'media' : 'Specify the media the css source link is for'} 799 media = "all" 800 801 retrieve_css = set_with_self
802 803
804 -class JSSource(Source):
805 """A JavaScript source snippet.""" 806 807 template = """ 808 <script type="text/javascript" 809 defer="${defer and 'defer' or None}">$src</script> 810 """ 811 params = ["defer"] 812 params_doc = {'defer': 'If true, browser may defer execution of the script'} 813 defer = False 814 815 location = js_location.head 816
817 - def __init__(self, src, location=None, **kw):
818 if location: 819 if location not in js_location: 820 raise ValueError, "JSSource location should be in %s" % js_location 821 self.location = location 822 super(JSSource, self).__init__(src, **kw)
823 824 retrieve_javascript = set_with_self
825 826 827 ############################################################################# 828 # Classes for supporting the toolbox widget browser # 829 ############################################################################# 830 831 just_the_widget = load_kid_template(""" 832 <div xmlns:py="http://purl.org/kid/ns#" py:content="for_widget.display()"/> 833 """)[0] 834 835
836 -class MetaDescription(MetaWidget):
837 """Metaclass for widget descriptions. 838 839 Makes sure the widget browser knows about all of them as soon as they 840 come into existence. 841 842 """
843 - def __init__(cls, name, bases, dct):
844 super(MetaDescription, cls).__init__(name, bases, dct) 845 register = dct.get("register", True) 846 if name != "WidgetDescription" and register: 847 all_widgets.add(cls)
848 849
850 -class WidgetDescription(CompoundWidget):
851 """A description for a Widget. 852 853 Makes the 'for_widget' widget appear in the browser. It's a nice way to 854 show off to your friends your coolest new widgets and to have a testing 855 platform while developing them. 856 857 """ 858 __metaclass__ = MetaDescription 859 860 template = just_the_widget 861 for_widget = None 862 member_widgets = ["for_widget"] 863 show_separately = False 864
865 - def _get_name(self):
866 return self.for_widget_class.__name__
867 name = property(_get_name) 868
869 - def _get_widget_class(self):
870 return self.for_widget.__class__
871 for_widget_class = property(_get_widget_class) 872
873 - def _get_description(self):
874 return self.for_widget_class.__doc__
875 description = property(_get_description) 876
877 - def _get_full_class_name(self):
878 cls = self.for_widget_class 879 return "%s.%s" % (cls.__module__, cls.__name__)
880 full_class_name = property(_get_full_class_name) 881
882 - def _get_source(self):
883 import inspect 884 return inspect.getsource(self.__class__)
885 source = property(_get_source) 886
887 - def retrieve_css(self):
888 return self.for_widget.retrieve_css()
889
890 - def retrieve_javascript(self):
891 return self.for_widget.retrieve_javascript()
892 893
894 -class CoreWD(WidgetDescription):
895 register = False 896
897 - def _get_full_class_name(self):
898 cls = self.for_widget_class 899 return "turbogears.widgets.%s" % (cls.__name__)
900 full_class_name = property(_get_full_class_name)
901 902
903 -class RenderOnlyWD(WidgetDescription):
904 register = False 905 template = """ 906 <div> 907 This widget will render like that:<br/><br/> 908 <tt class="rendered">${for_widget.render(value)}</tt> 909 </div> 910 """ 911
912 - def retrieve_javascript(self):
913 return setlike()
914
915 - def retrieve_css(self):
916 return setlike()
917 918 919 ############################################################################# 920 # CSS and JS WidgetDescription's # 921 ############################################################################# 922
923 -class CSSLinkDesc(CoreWD, RenderOnlyWD):
924 name = "CSS Link" 925 for_widget = CSSLink("turbogears", "css/yourstyle.css")
926 927
928 -class JSLinkDesc(CoreWD, RenderOnlyWD):
929 name = "JS Link" 930 for_widget = JSLink("turbogears", "js/yourscript.js")
931 932
933 -class CSSSourceDesc(CoreWD, RenderOnlyWD):
934 name = "CSS Source" 935 for_widget = CSSSource("""body { font-size:12px; }""")
936 937
938 -class JSSourceDesc(CoreWD, RenderOnlyWD):
939 name = "JS Source" 940 for_widget = JSSource("document.title = 'Hello World';")
941