moksha/api/widgets/__init__.py | 1 moksha/api/widgets/containers/dashboardcontainer.py | 2 moksha/api/widgets/containers/tabbedcontainer.py | 14 + moksha/api/widgets/containers/templates/layout_applist.mak | 4 moksha/api/widgets/containers/templates/tabbedcontainer_panes.mak | 7 moksha/api/widgets/containers/templates/tabbedcontainer_tabs.mak | 9 moksha/api/widgets/placeholder.py | 11 + moksha/lib/helpers.py | 103 +++++++--- moksha/middleware/middleware.py | 5 setup.py | 2 10 files changed, 124 insertions(+), 34 deletions(-)
New commits: commit 474f4570361b47ff61d22dcba70c0f840f614e26 Author: John (J5) Palmieri johnp@redhat.com Date: Wed Feb 4 16:05:00 2009 -0500
add placeholders and fix up containers to be more consitant and easier to use
* added the TabbedContainerPanes and TabbedContainerTabs widgets to abstract out displaying apps inside of containers. Now if the internal config format changes a bit apps won't break if they use the standard display widgets
* fixed a few bugs with displaying widgets that had parameters
diff --git a/moksha/api/widgets/__init__.py b/moksha/api/widgets/__init__.py index 38b79ba..6449275 100644 --- a/moksha/api/widgets/__init__.py +++ b/moksha/api/widgets/__init__.py @@ -1,4 +1,5 @@ from live import LiveWidget from util import * from grid import Grid +from placeholder import Placeholder
diff --git a/moksha/api/widgets/containers/dashboardcontainer.py b/moksha/api/widgets/containers/dashboardcontainer.py index 41732bf..6892d6f 100644 --- a/moksha/api/widgets/containers/dashboardcontainer.py +++ b/moksha/api/widgets/containers/dashboardcontainer.py @@ -7,7 +7,7 @@ from tg import config
class AppListWidget(Widget): template = 'mako:moksha.api.widgets.containers.templates.layout_applist' - properties = ['layout', 'category'] + properties = ['category']
def update_params(self, d): super(AppListWidget, self).update_params(d) diff --git a/moksha/api/widgets/containers/tabbedcontainer.py b/moksha/api/widgets/containers/tabbedcontainer.py index 1111084..00e5898 100644 --- a/moksha/api/widgets/containers/tabbedcontainer.py +++ b/moksha/api/widgets/containers/tabbedcontainer.py @@ -1,10 +1,20 @@ from tw.jquery.ui_tabs import JQueryUITabs +from tw.api import Widget from pylons import config, request from repoze.what import predicates from moksha.lib.helpers import eval_app_config, ConfigWrapper
import urllib
+class TabbedContainerTabs(Widget): + template = 'mako:moksha.api.widgets.containers.templates.tabbedcontainer_tabs' + +class TabbedContainerPanes(Widget): + template = 'mako:moksha.api.widgets.containers.templates.tabbedcontainer_panes' + +tabwidget = TabbedContainerTabs('tabs') +panewidget = TabbedContainerPanes('panes') + """ :Name: TabbedContainer :Type: Container @@ -49,3 +59,7 @@ class TabbedContainer(JQueryUITabs): # not allowed to run with the current session's authorization level tabs = ConfigWrapper.process_wrappers(tabs) d['tabs'] = tabs + d['tabwidget'] = tabwidget + d['panewidget'] = panewidget + d['root_id'] = d['id'] + diff --git a/moksha/api/widgets/containers/templates/layout_applist.mak b/moksha/api/widgets/containers/templates/layout_applist.mak index 0fa4464..af2021a 100644 --- a/moksha/api/widgets/containers/templates/layout_applist.mak +++ b/moksha/api/widgets/containers/templates/layout_applist.mak @@ -6,11 +6,11 @@ % endif <dd id="${app['id']}"> % if app.has_key('widget'): - ${app['widget'](app['params'])} + ${app['widget'](**app['params'])} % endif </dd>
- % if app.has_key('url'): + % if not app.has_key('widget'): <script type="text/javascript"> $("#${app['id']}").load("${app['url']}"); </script> diff --git a/moksha/api/widgets/containers/templates/tabbedcontainer_panes.mak b/moksha/api/widgets/containers/templates/tabbedcontainer_panes.mak new file mode 100644 index 0000000..5bc58b7 --- /dev/null +++ b/moksha/api/widgets/containers/templates/tabbedcontainer_panes.mak @@ -0,0 +1,7 @@ +% for t in tabs: + <div id="${t['content_id']}"> + % if t.has_key('widget'): + ${t['widget'](**t['params'])} + % endif + </div> +% endfor \ No newline at end of file diff --git a/moksha/api/widgets/containers/templates/tabbedcontainer_tabs.mak b/moksha/api/widgets/containers/templates/tabbedcontainer_tabs.mak new file mode 100644 index 0000000..6bcaab3 --- /dev/null +++ b/moksha/api/widgets/containers/templates/tabbedcontainer_tabs.mak @@ -0,0 +1,9 @@ +<ul id="${root_id}"> + % for t in tabs: + <li> + <a href="${t['url']}" title="${t['content_id']}"> + ${t['label']} + </a> + </li> + % endfor +</ul> diff --git a/moksha/api/widgets/placeholder.py b/moksha/api/widgets/placeholder.py new file mode 100644 index 0000000..656580e --- /dev/null +++ b/moksha/api/widgets/placeholder.py @@ -0,0 +1,11 @@ +from tw.api import Widget + +class Placeholder(Widget): + engine_name = 'mako' + template = """ +<div class='placeholder'> + Moksha application <strong>${appname}</strong> is not registered yet. This is a placeholder + for testing purposes. When the app is registered it will appear in the + layout once the server is restarted. +</div> +""" diff --git a/moksha/lib/helpers.py b/moksha/lib/helpers.py index 7d9982b..e08b732 100644 --- a/moksha/lib/helpers.py +++ b/moksha/lib/helpers.py @@ -117,25 +117,34 @@ class App(ConfigWrapper): :Example: hello_app = App('Hello World App', '/appz/moksha.helloworld', - {'greeter_name': 'J5'}, - not_anonymous()) + params={'greeter_name': 'J5'}, + auth=not_anonymous()) """ - def __init__(self, label="", url="", req_params=None, auth=None): + def __init__(self, label="", url="", content_id="", + params=None, auth=None): """ :label: The title the application should use when rendering in a container. Leave this blank if you do not wish the application to be visibly labeled when rendered :url: the url to load this application from - :req_params: A dict of request parameters to send to the application - when loading it from a url + :content_id: the id to use for url navigation and associating the + content with a control such as tabs needing to know + which div to add the content to when selected. If + this is not set we scrub the label of illegal characters + lower case it and use that as the content_id + :params: A dict of request parameters to send to the application + when loading it from a url :auth: A list of predicates which are evaluate before the wrapper is sent to the container. If it evaluates to False it does not get sent. """ self.label = label self.url = url - self.req_params = req_params or {} + self.params = params or {} self.auth = auth or [] + self.content_id = content_id + if self.label and not self.content_id: + self.content_id = scrub_filter.sub('_', self.label.lower())
def process(self): """Check the predicates and construct the dict @@ -146,12 +155,13 @@ class App(ConfigWrapper): return None
query_str = "" - if self.req_params: - query_str = "?" + urllib.urlencode(self.req_params) + if self.params: + query_str = "?" + urllib.urlencode(self.params)
id = uuid.uuid4()
- return {'label': self.label, 'url': self.url + query_str, 'id': id} + return {'label': self.label, 'url': self.url + query_str, 'id': id, + 'content_id': self.content_id}
class MokshaApp(App): """A configuration wrapper class that displays a Moksa application @@ -163,26 +173,44 @@ class MokshaApp(App): :Example: hello_app = App('Hello World Moksha App', 'moksha.helloworld', - {'greeter_name': 'J5'}, - not_anonymous()) + params={'greeter_name': 'J5'}, + auth=not_anonymous())
""" - - def __init__(self, label="", moksha_app="", req_params=None, auth=None): + def __init__(self, label="", moksha_app="", content_id="", + params=None, auth=None): """ :label: The title the application should use when rendering in a container. Leave this blank if you do not wish the application to be visibly labeled when rendered :moksha_app: the name of the entry point registered under the moksha.application section - :req_params: A dict of request parameters to send to the application - when loading it + :content_id: the id to use for url navigation and associating the + content with a control such as tabs needing to know + which div to add the content to when selected. If + this is not set we scrub the label of illegal characters + lower case it and use that as the content_id + :params: A dict of request parameters to send to the application + when loading it :auth: A list of predicates which are evaluate before the wrapper is sent to the container. If it evaluates to False it does not get sent. """ # FIXME figure out how to pull auth info from an app - super(MokshaApp, self).__init__(label, '/appz/' + moksha_app, req_params, auth) + self.app = moksha_app + super(MokshaApp, self).__init__(label, + '/appz/' + moksha_app, + content_id, + params, auth) + + def process(self): + # We return a placeholder if the app is not registered + if not moksha._apps.has_key(self.app): + return MokshaWidget(self.label, 'placeholder', + self.content_id, + {'appname':self.app}, self.auth).process() + + return super(MokshaApp, self).process()
class Widget(ConfigWrapper): """A configuration wrapper class that displays a ToscaWidget. Use this @@ -196,16 +224,22 @@ class Widget(ConfigWrapper):
hello_widget = Widget('Hello World Widget', HelloWidget(), - {'greeter_name': 'J5'}, - not_anonymous()) + params={'greeter_name': 'J5'}, + auth=not_anonymous())
""" - def __init__(self, label="", widget="", params=None, auth=None): + def __init__(self, label="", widget=None, content_id="", + params=None, auth=None): """ :label: The title the widget should use when rendering in a container. Leave this blank if you do not wish the widget to be visibly labeled when rendered :widget: the ToscaWidget to be rendered + :content_id: the id to use for url navigation and associating the + content with a control such as tabs needing to know + which div to add the content to when selected. If + this is not set we scrub the label of illegal characters + lower case it and use that as the content_id :params: A dict of parameters to send to the widgets update_params method when rendering it :auth: A list of predicates which are evaluate before the wrapper @@ -217,13 +251,18 @@ class Widget(ConfigWrapper): self.params = params or {} self.auth = auth or []
+ self.content_id = content_id + if self.label and not content_id: + self.content_id = scrub_filter.sub('_', self.label.lower()) + def process(self): if not check_predicates(self.auth): return None
id = uuid.uuid4() - - return {'label': self.label, 'widget': self.widget , 'params':self.params, 'id': id} + url = '#' + self.content_id + return {'label': self.label, 'url': url,'widget': self.widget , + 'params':self.params, 'id': id, 'content_id': self.content_id}
class MokshaWidget(Widget): """A configuration wrapper class that displays a ToscaWidget registered @@ -232,25 +271,33 @@ class MokshaWidget(Widget): :Example: hello_moksha_widget = Widget('Hello Moksha Widget', 'moksha.hello', - {'greeter_name': 'J5'}, - not_anonymous()) + params={'greeter_name': 'J5'}, + auth=not_anonymous())
""" - def __init__(self, label="", name="", params=None, auth=None): + def __init__(self, label="", moksha_widget="", content_id="", + params=None, auth=None): """ :label: The title the widget should use when rendering in a container. Leave this blank if you do not wish the widget to be visibly labeled when rendered - :name: the name of the entry point registered under the - moksha.widget section + :content_id: the id to use for url navigation and associating the + content with a control such as tabs needing to know + which div to add the content to when selected. If + this is not set we scrub the label of illegal characters + lower case it and use that as the content_id + :moksha_widget: the name of the entry point registered under the + moksha.widget section :params: A dict of parameters to send to the widgets update_params method when rendering it :auth: A list of predicates which are evaluate before the wrapper is sent to the container. If it evaluates to False it does not get sent. """ - widget = moksha._widgets[name]['widget'] - super(MokshaWidget, self).__init__(label=label, widget=widget, params=params, auth=auth) + widget = moksha._widgets[moksha_widget]['widget'] + super(MokshaWidget, self).__init__(label=label, widget=widget, + content_id=content_id, params=params, + auth=auth)
# setup the dictionary of acceptable callables when eval'ing predicates from diff --git a/moksha/middleware/middleware.py b/moksha/middleware/middleware.py index 701f7d5..aee95e0 100644 --- a/moksha/middleware/middleware.py +++ b/moksha/middleware/middleware.py @@ -70,8 +70,7 @@ class MokshaMiddleware(object): self.feed_cache = Cache(self.feed_storage)
def __call__(self, environ, start_response): - environ['paste.registry'].register(moksha.apps, self.apps) - + # environ['paste.registry'].register(moksha.apps, self.apps) # environ['paste.registry'].register(moksha._widgets, self.widgets) environ['paste.registry'].register(moksha.feed_cache, self.feed_cache) environ['paste.registry'].register(moksha.menus, self.menus) @@ -148,6 +147,8 @@ class MokshaMiddleware(object): except ImportError: pass
+ moksha._apps = self.apps + def load_widgets(self): log.info('Loading moksha widgets') for widget_entry in pkg_resources.iter_entry_points('moksha.widget'): diff --git a/setup.py b/setup.py index 2f17bc3..6ae206c 100644 --- a/setup.py +++ b/setup.py @@ -64,7 +64,7 @@ setup( #livegraph = moksha.examples.livegraph:LiveGraphWidget #grid = moksha.api.widgets:Grid ptd = moksha.widgets.misc.ptd:ProcessedTowerDefense - + placeholder = moksha.api.widgets:Placeholder
[moksha.global] # The pipeline for our live widgets