class LiveFeedWidget(LiveWidget): """ A live streaming feed widget """ url = twc.Param("The feed URL") topic = twc.Param("A topic or list of topics to subscribe to") feed = twc.Param("A moksha Feed object") d = twc.Param("The widget data") limit = twc.Param("The number of entries to display") template = "mako:mako:moksha.feeds.widgets.templates.live" onmessage = """ $.each(json, function() { $("#${id} ul li:last").remove(); $("<li/>").html( $("<a/>") .attr("href", this.link) .text(this.title)) .prependTo($("#${id} ul")); }); """ feed = Feed() topic = None limit = 10 def prepare(self): if not getattr(self, 'topic', None): self.topic = 'feed.%s' % d.get('url', self.url) super(LiveFeedWidget, self).prepare() self.d = self # Wha?
class InputField(FormField): """A generic <input> field. Generally you won't use this one, but will rely on one of its specialised subclasses like :class:`.TextField` or :class:`Checkbox`. """ type = twc.Variable('Type of input field', default=twc.Required, attribute=True) #: Input type value = twc.Param(attribute=True) #: Current value of the input required = twc.Param( 'Input field is required', attribute=True, default=None) #: Add required attributed to the input. autofocus = twc.Param( 'Autofocus form field (HTML5 only)', attribute=True, default=None) #: Add autofocus attributed to the input. template = "tw2.forms.templates.input_field" def prepare(self): super(InputField, self).prepare() self.safe_modify('attrs') self.attrs['required'] = 'required' if self.required in [ True, 'required' ] else None self.required = None # Needed because self.required would otherwise overwrite self.attrs['required'] again
class ReleaseFilter(twc.Widget): on_change = twc.Param( 'The name of the javascript function to call upon change') package = twc.Param('The name of the package') template = 'mako:fedoracommunity.widgets.package.templates.release_filter' def prepare(self): super(ReleaseFilter, self).prepare() releases = [] top_repo = os.path.join(config.get('git_repo_path'), self.package) pkgdb = get_connector('pkgdb') collections = pkgdb.get_collection_table(active_only=True) for id, collection in collections.iteritems(): name = collection['name'] ver = collection['version'] label = "%s %s" % (name, ver) value = "" branchname = collection['gitbranchname'] if branchname: repo_path = os.path.join(top_repo, branchname) if not os.path.isdir(repo_path): continue value = branchname if label != 'Fedora devel' and name in ('Fedora', 'Fedora EPEL'): releases.append({ 'label': label, 'value': value, 'version': ver, }) self.releases_table = sorted( releases, reverse=True, cmp=lambda x, y: cmp(x['version'], y['version'])) self.releases_table.insert(0, {'label': 'Rawhide', 'value': 'master'})
class ButtonSetRadio(uibase.JQueryUIWidget): """ Styles a group of radio buttons as a 'button set' by calling ``.buttonset()`` on a common container. See the wrapped library's documentation for more information: http://jqueryui.com/demos/button/#radio """ template = 'tw2.jqplugins.ui.templates.buttonset_radio' items = twc.Param("a list of dicts - required keys are 'id' and 'label'", default=[]) checked_item = twc.Param( "a single button may be marked as 'checked' by " + "passing its 'id' [as a string] to this parameter", default=None) jqmethod = "buttonset" def prepare(self): super(ButtonSetRadio, self).prepare() if not isinstance(self.items, list): raise ValueError, "'items' must be of type list" ids = [i['id'] for i in self.items] if self.checked_item and self.checked_item not in ids: raise ValueError, "A 'checked_item' has been passed in but " + \ "the id to which it refers is not in the " + \ "'items' list"
class WebSocketWidget(AbstractMokshaSocket): __shorthand__ = 'WebSocket' ws_host = twc.Param(default=twc.Required) ws_port = twc.Param(default=twc.Required) template = "mako:moksha.wsgi.widgets.api.websocket.templates.websocket"
class CustomisedForm(twf.Form): """A form that allows specification of several useful client-side behaviours.""" blank_deleted = twc.Param( 'Blank out any invisible form fields before submitting. This is needed for GrowingGrid.', default=True) disable_enter = twc.Param( 'Disable the enter button (except with textarea fields). This reduces the chance of users accidentally submitting the form.', default=True) prevent_multi_submit = twc.Param( 'When the user clicks the submit button, disable it, to prevent the user causing multiple submissions.', default=True) resources = [twc.JSLink(modname=__name__, filename="static/dynforms.js")] def prepare(self): super(CustomisedForm, self).prepare() if self.blank_deleted: self.safe_modify('attrs') self.attrs['onsubmit'] = 'twd_blank_deleted()' if self.disable_enter: self.safe_modify('resources') self.resources.append( twc.JSSource(src='document.onkeypress = twd_suppress_enter;')) if self.prevent_multi_submit: self.submit.safe_modify('attrs') self.submit.attrs['onclick'] = 'return twd_no_multi_submit(this);'
class JQueryUIWidget(twc.Widget): """ Base JQueryUIWidget """ _hide_docs = False resources = [jquery_ui_js, jquery_ui_css] jqmethod = twc.Variable("(str) Name of this widget's jQuery init method") selector = twc.Variable("(str) Escaped id. jQuery selector.") options = twc.Param('(dict) A dict of options to pass to the widget', default={}) # TODO -- add all the events http://api.jquery.com/category/events/ # TODO -- try to automatically generate IDs if not specified # TODO -- TBD, figure out if this actually makes sense for all ui things. events = twc.Param('(dict) (BETA) javascript callbacks for events', default={}) def prepare(self): if self.events is not None and not isinstance(self.events, dict): raise ValueError, 'Events parameter must be a dict' self.resources.append(jquery_ui_css(name=get_ui_theme_name())) if self.options is not None and not isinstance(self.options, dict): raise ValueError, 'Options parameter must be a dict' self.options = encoder.encode(self.options) super(JQueryUIWidget, self).prepare() if not hasattr(self, 'id') or 'id' not in self.attrs: raise ValueError, 'JQueryWidget must be supplied an id' self.selector = self.attrs['id'].replace(':', '\\\\:')
class Details(twc.Widget): template = 'mako:fedoracommunity.widgets.package.templates.details' kwds = twc.Param('Data passed in from the tabs') package_info = twc.Param('A dict containing package details from xapian') active_releases_widget = ActiveReleasesGrid def prepare(self): super(Details, self).prepare() package_name = self.kwds['package_name'] xapian_conn = get_connector('xapian') result = xapian_conn.get_package_info(package_name) if result['name'] == package_name: self.summary = result['summary'] self.description = result['description'] else: for subpkg in result['sub_pkgs']: if subpkg['name'] == package_name: self.summary = subpkg['summary'] self.description = subpkg['description'] break self.package_info = result def __repr__(self): return "<Details %s>" % self.kwds
class TextArea(FormField): rows = twc.Param('Number of rows', default=None, attribute=True) cols = twc.Param('Number of columns', default=None, attribute=True) placeholder = twc.Param('Placeholder text (HTML5 Only)', attribute=True, default=None) template = "tw2.forms.templates.textarea"
class AttributeDetails(twc.Widget): """ Widget to present the details of the given Attribute """ template = 'rnms.templates.widgets.attribute_details' attribute = twc.Param('The attribute record out of the DB query') extra = twc.Param('Additional data outside the DB query', default=None)
class Column(twc.Widget): """A column in a DataTable. The TW id becomes the YUI key, and the TW key becomes the YUI field.""" options = twc.Param( 'Configuration options for the widget. See the YUI docs for available options.', default={}) sortable = twc.Param('Is the column sortable?', default=True) resizeable = twc.Param('Is the column resizeable?', default=True) label = twc.Param('Label for the field. Auto generates this from the id', default=twc.Auto) formatter = twc.Param( 'Formatter function. Use a string like "currency" for a YUI built-in, or JSSymbol for a custom function.', default=None) def prepare(self): super(Column, self).prepare() self.safe_modify('options') self.options['sortable'] = self.sortable self.options['resizeable'] = self.resizeable if self.label is twc.Auto: self.label = twc.util.name2label(self.id) self.options['label'] = self.label self.options['key'] = self.id self.options['field'] = self.key if self.formatter: self.options['formatter'] = self.formatter
class HTML5MinMaxMixin(twc.Widget): '''HTML5 mixin for input field value limits TODO: Configure server-side validator ''' min = twc.Param('Minimum value for field', attribute=True, default=None) max = twc.Param('Maximum value for field', attribute=True, default=None)
class PostlabeledInputField(InputField): """ Inherits InputField, but with a text label that follows the input field """ text = twc.Param('Text to display after the field.') text_attrs = twc.Param('Dict of attributes to inject into the label.', default={}) template = "tw2.forms.templates.postlabeled_input_field"
class LinkField(twc.Widget): """ A dynamic link based on the value of a field. If either *link* or *text* contain a $, it is replaced with the field value. If the value is None, and there is no default, the entire link is hidden. """ template = "tw2.forms.templates.link_field" link = twc.Param('Link target', default='') text = twc.Param('Link text', default='') value = twc.Param("Value to replace $ with in the link/text") escape = twc.Param('Whether text shall be html-escaped or not', default=True) validator = twc.BlankValidator def prepare(self): super(LinkField, self).prepare() self.safe_modify('attrs') self.attrs['href'] = self.link.replace('$', six.text_type(self.value or '')) if '$' in self.text: self.text = \ self.value and \ self.text.replace('$', six.text_type(self.value)) or \ ''
class FlatRRDProtoBarChart(tw2.protovis.conventional.BarChart, RRDFlatMixin): series_sorter = twc.Param("function to compare to data points for sorting", default=None) prune_zeroes = twc.Param("hide zero-valued series?", default=False) p_data = twc.Variable("Internally produced") p_labels = twc.Variable("Internally produced") method = twc.Param( "Method for consolidating values. Either 'sum' or 'average'", default='average') def prepare(self): data = self.flat_fetch() if self.series_sorter: data.sort(self.series_sorter) if not self.method in ['sum', 'average']: raise ValueError, "Illegal value '%s' for method" % self.method self.p_labels = [series['label'] for series in data] if self.method == 'sum': self.p_data = [ sum([d[1] for d in series['data']]) for series in data ] elif self.method == 'average': self.p_data = [ sum([d[1] for d in series['data']]) / len(series['data']) for series in data ] super(FlatRRDProtoBarChart, self).prepare()
class ReleaseFilter(twc.Widget): on_change = twc.Param('The name of the javascript function to call upon change') package = twc.Param('The name of the package') template = 'mako:fedoracommunity.widgets.package.templates.release_filter' def prepare(self): super(ReleaseFilter, self).prepare() releases = [] top_repo = os.path.join(config.get('git_repo_path'), self.package) bodhi = get_connector('bodhi') for collection in bodhi.get_all_releases(): if collection['state'] != 'current': continue name = collection['id_prefix'] ver = collection['version'] label = collection['long_name'] value = "" branchname = collection['branch'] if branchname: repo_path = os.path.join(top_repo, branchname) if not os.path.isdir(repo_path): continue value = branchname if label != 'Fedora devel' and name in ('FEDORA', 'FEDORA-EPEL'): releases.append({ 'label': label, 'value': value, 'version': ver, }) self.releases_table = sorted(releases, reverse=True, cmp=lambda x, y: cmp(x['version'], y['version'])) self.releases_table.insert(0, {'label': 'Rawhide', 'value': 'master'})
class Wysihtml5(twbf.TextArea): resources = [bootstrap_wysihtml5_js, bootstrap_wysihtml5_css] parser = twc.Param( 'The set of parser rules to use. ' 'The simple parser contains only basic html5 tags, while the ' 'advanced parser contains more html5 tags and preserves some ' 'css classes. If you use the simple parser, the editor css is ' 'not strictly needed. ' 'If you set the parser to False, you completely disable parsing, ' 'which is potentially unsafe, but a lot more convenient.', default=True) # TODO: Color support # Explicitly disable stylesheets, because wysiwyg-color.css is fetched otherwise stylesheets = twc.Param(default=list()) wysihtml5_args = twc.Param(default=dict()) def prepare(self): super(Wysihtml5, self).prepare() wysihtml5_args = self.wysihtml5_args.copy() wysihtml5_args.update(stylesheets=self.stylesheets) if self.parser and self.parser in parsers: self.safe_modify('resources') self.resources.append(parsers[self.parser]) wysihtml5_args.update( parserRules=twc.js_symbol('wysihtml5ParserRules')) if self.parser is False: wysihtml5_args.update(parser=twc.js_symbol( 'function(elementOrHtml, rules, context, cleanUp) { return elementOrHtml; }' )) self.add_call(twj.jQuery(self.selector).wysihtml5(wysihtml5_args))
class MenuWidget(tw2.jqplugins.ui.base.JQueryUIWidget, twc.DisplayOnlyWidget): """ Slidey menu that I made just for you! This is *quite* alpha still. Please play with it and enhance it if you like. options:: - width: -- the width of the menu. """ resources = [ tw2.jquery.jquery_js, tw2.jqplugins.ui.jquery_ui_js, tw2.jqplugins.ui.jquery_ui_css, base.slidey_js, base.slidey_css, ] template = "mako:tw2.slideymenu.templates.menu" items = twc.Param('A recursive dictionary of menu entries', default=[]) label = twc.Param('Label for the menu-show button', default='Menu') options = twc.Param('Options.', default={}) def prepare(self): super(MenuWidget, self).prepare() self._options = encoder.encode(self.options)
class ChosenMixin(twc.Widget): '''Mixin for Chosen SelectFields''' resources = [chosen_js, chosen_css] selector = twc.Variable("Escaped id. jQuery selector.") opts = twc.Variable( 'Arguments for the javascript init function. ' 'See http://harvesthq.github.io/chosen/options.html', default=dict()) placeholder = twc.Param('Placeholder text, prompting user for selection', default='') no_results_text = twc.Param( 'Text shown when the search term returned no results', default='') search_contains = twc.Param( 'Allow matches starting from anywhere within a word.', default=False) def prepare(self): super(ChosenMixin, self).prepare() # put code here to run just before the widget is displayed if 'id' in self.attrs: self.selector = "#" + self.attrs['id'].replace(':', '\\:') if self.placeholder: self.attrs['data-placeholder'] = self.placeholder if self.no_results_text: self.opts['no_results_text'] = self.no_results_text if self.search_contains: self.opts['search_contains'] = True self.add_call(twj.jQuery(self.selector).chosen(self.opts))
class HostDetails(twc.Widget): """ Widget to present the details of the given host """ template = 'rnms.templates.widgets.host_details' host = twc.Param('The host record out of the DB query') extra = twc.Param('Additional data outside the DB query', default=None)
class OrbitedWidget(twc.Widget): onopen = twc.Param("A javascript callback for when the connection opens", default=twc.js_callback('function(){}')) onread = twc.Param("A javascript callback for when new data is read", default=twc.js_callback('function(){}')) onclose = twc.Param("A javascript callback for when the connection closes", default=twc.js_callback('function(){}')) resources = [orbited_js] template = """ <script type="text/javascript"> Orbited.settings.port = %(port)s Orbited.settings.hostname = '%(host)s' document.domain = document.domain TCPSocket = Orbited.TCPSocket connect = function() { conn = new TCPSocket() conn.onread = ${onread} conn.onopen = ${onopen} conn.onclose = ${onclose} conn.open('%(host)s', %(port)s) } $(document).ready(function() { connect() }) </script> """ % { 'port': orbited_port, 'host': orbited_host }
class TextFieldMixin(twc.Widget): '''Misc mixin class with attributes for textual input fields''' maxlength = twc.Param('Maximum length of field', attribute=True, default=None) #: Maximum length of the field placeholder = twc.Param( 'Placeholder text (HTML5 only)', attribute=True, default=None) #: Placeholder text, until user writes something.
class TextFieldMixin(twc.Widget): '''Misc mixin class with attributes for textual input fields''' maxlength = twc.Param('Maximum length of field', attribute=True, default=None) placeholder = twc.Param('Placeholder text (HTML5 only)', attribute=True, default=None)
class Form(twc.DisplayOnlyWidget): """ A form, with a submit button. It's common to pass a TableLayout or ListLayout widget as the child. """ template = "tw2.forms.templates.form" help_msg = twc.Param('This message displays as a div inside the form', default=None) action = twc.Param( 'URL to submit form data to. If this is None, the form ' + 'submits to the same URL it was displayed on.', default=None, attribute=True) method = twc.Param('HTTP method used for form submission.', default='post', attribute=True) submit = twc.Param('Submit button widget. If this is None, no submit ' + 'button is generated.', default=SubmitButton(value='Save')) buttons = twc.Param('List of additional buttons to be placed at the ' + 'bottom of the form', default=[]) attrs = {'enctype': 'multipart/form-data'} id_suffix = 'form' @classmethod def post_define(cls): if not cls.buttons: cls.buttons = [] else: for b in range(0, len(cls.buttons)): if callable(cls.buttons[b]): cls.buttons[b] = cls.buttons[b](parent=cls) if cls.submit: cls.submit = cls.submit(parent=cls) def __init__(self, **kw): super(Form, self).__init__(**kw) if self.buttons: for b in range(0, len(self.buttons)): self.buttons[b] = self.buttons[b].req() if self.submit: self.submit = self.submit.req() def prepare(self): super(Form, self).prepare() if self.buttons and not isinstance(self.buttons, list): raise AttributeError("buttons parameter must be a list or None") if self.submit and not \ ['SubmitButton' in repr(b) for b in self.buttons]: self.buttons.append(self.submit) for b in self.buttons: b.prepare()
class HBarGraph(twc.Widget): """ Small Horizontal Bar Graph Chart graph_data should be a list of 3 item list (label, percent, value label) """ template = 'rnms.templates.widgets.hbargraph' title = twc.Param('Title of the Chart', default='') graph_data = twc.Param('List of (label,pct,val)')
class TextArea(TextFieldMixin, FormField): """A multiline text area""" rows = twc.Param( 'Number of rows', default=None, attribute=True) #: Add a rows= attribute to the HTML textarea cols = twc.Param( 'Number of columns', default=None, attribute=True) #: Add a cols= attribute to the HTML textarea template = "tw2.forms.templates.textarea"
class PanelTile(twc.CompoundWidget): """ Compound Widget that creates a panel tile that holds other widgets """ template = 'rnms.templates.widgets.panel_tile' title = twc.Param('Title for this panel tile', default='Title') subtitle = twc.Param('Subtitle for this panel tile', default=None) fullwidth = twc.Param('Full width? True/False', default=False) fillrow = twc.Param('3/4 to fill row True/False', default=False) fullheight = twc.Param('Full height? True/False', default=False)
class LineChart(twc.Widget): """ Line Charts """ template = 'rnms.templates.widgets.line_chart' data_url = twc.Param('URL for fetching the JSON data for graph') show_legend = twc.Param('Boolean: show the legend or not', default=False) def prepare(self): self.resources.append(chart_min_js) self.resources.append(jquery_js)
class IFrameWidget(twc.Widget): template = "tw2.etc.templates.iframe" title = twc.Param("Title (optional)", default=None) height = twc.Param(default='100%', attribute=True) width = twc.Param(default='100%', attribute=True) src = twc.Param("Source for the iframe", attribute=True) url = twc.Param("Synonym for src", default=None) def prepare(self): if self.url: self.src = self.url super(IFrameWidget, self).prepare()
class TabWidget(twc.Widget): template = "mako:fedoracommunity.widgets.package.templates.tabs" base_url = twc.Param(default='/') args = twc.Param(default=None) kwds = twc.Param(default=None) tabs = twc.Variable(default=None) _uuid = twc.Param(default=None) widget = twc.Variable(default=None) active_tab = twc.Variable(default=None) tabs = twc.Variable(default=None) default_tab = None def __init__(self, *args, **kw): super(TabWidget, self).__init__(*args, **kw) self._uuid = str(uuid.uuid4()) self._expanded_tabs = OrderedDict() for key, widget_key in self.tabs.items(): display_name = key key = key.lower().replace(' ', '_') self._expanded_tabs[key] = { 'display_name': display_name, 'widget_key': widget_key } def prepare(self): super(TabWidget, self).prepare() if not self.args: self.args = [] if not self.kwds: self.kwds = {} if isinstance(self.args, mako.runtime.Undefined): self.args = [] if isinstance(self.kwds, mako.runtime.Undefined): self.kwds = {} if len(self.args) > 0: active_tab = self.args.pop(0).lower() else: active_tab = self.default_tab.lower() try: self.widget = moksha.common.utils.get_widget( self._expanded_tabs[active_tab]['widget_key']) except KeyError: self.widget = None self.tabs = self._expanded_tabs self.active_tab = active_tab if isinstance(self.base_url, Template): self.base_url = tg.url(self.base_url.render(**self.__dict__))