You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bloodhound.apache.org by gj...@apache.org on 2012/11/30 18:32:57 UTC

svn commit: r1415756 - in /incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard: api.py util.py widgets/templates/widget_doc.html

Author: gjm
Date: Fri Nov 30 17:32:56 2012
New Revision: 1415756

URL: http://svn.apache.org/viewvc?rev=1415756&view=rev
Log:
adding a widget to display widgets docs - towards #139 (from olemis)

Added:
    incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/templates/widget_doc.html   (with props)
Modified:
    incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/api.py
    incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/util.py

Modified: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/api.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/api.py?rev=1415756&r1=1415755&r2=1415756&view=diff
==============================================================================
--- incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/api.py (original)
+++ incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/api.py Fri Nov 30 17:32:56 2012
@@ -28,9 +28,12 @@ __metaclass__ = type
 from datetime import date, time, datetime, timedelta
 from sys import version_info
 
+from genshi.builder import tag
+
 from trac.core import Component, ExtensionPoint, implements, \
         Interface, TracError
 from trac.perm import IPermissionRequestor
+from trac.resource import get_resource_url, Resource, resource_exists
 from trac.util.compat import set
 from trac.util.datefmt import parse_date
 from trac.util.translation import _
@@ -95,7 +98,7 @@ class ILayoutProvider(Interface):
         """
 
 class DashboardSystem(Component):
-    implements(IPermissionRequestor)
+    implements(IPermissionRequestor, IWidgetProvider)
 
     widget_providers = ExtensionPoint(IWidgetProvider)
     layout_providers = ExtensionPoint(ILayoutProvider)
@@ -106,7 +109,136 @@ class DashboardSystem(Component):
                 # 'DASHBOARD_CREATE', 'DASHBOARD_EDIT' <= Coming soon ;)
                ]
 
+    # IWidgetProvider methods
+
+    def get_widgets(self):
+        """List the name of the widgets that will always be available
+        """
+        yield 'WidgetDoc'
+
+    def get_widget_description(self, name):
+        """Return plain text description of the widget with specified name.
+        """
+        try:
+            return {
+                    'WidgetDoc' : """Display widget documentation"""
+                    }[name]
+        except KeyError:
+            raise InvalidIdentifier('Widget name MUST match any of ' +
+                        ', '.join(self.get_widgets()),
+                    title='Invalid widget identifier')
+
+    def get_widget_params(self, name):
+        """Return a dictionary describing wigdet preference for the widget 
+        with specified name. Used to customize widget behavior.
+        """
+        try:
+            return {
+                    'WidgetDoc' : {
+                        'urn' : {
+                                'desc' : """Widget name. If missing then """
+                                        """documentation for all widgets """
+                                        """will be displayed."""
+                                }
+                        }
+                   }[name]
+        except KeyError:
+            raise InvalidIdentifier('Widget name MUST match any of ' +
+                        ', '.join(self.get_widgets()),
+                    title='Invalid widget identifier')
+
+    def render_widget(self, name, context, options):
+        """Render widget considering given options.
+        """
+        if name == 'WidgetDoc':
+            widget_name, = self.bind_params(options,
+                    self.get_widget_params(name), 'urn')
+            if widget_name is not None:
+                try:
+                    providers = [([widget_name],
+                            self.resolve_widget(widget_name))]
+                except LookupError:
+                    return 'widget_alert.html', {
+                            'title' : _('Widget documentation'),
+                            'data' : {
+                                    'msglabel' : 'Alert',
+                                    'msgbody' : 'Unknown identifier',
+                                    'msgdetails' : [
+                                            ('Widget name', widget_name)
+                                        ]
+                                  }
+                        }, context
+            else:
+                providers = [(provider.get_widgets(), provider) \
+                        for provider in self.widget_providers]
+            metadata = [self._prepare_doc_metadata(self.widget_metadata(wnm, p)) \
+                    for widgets, p in providers for wnm in widgets]
+            docs_resource = Resource('wiki', 'BloodhoundWidgets')
+            insert_docs = resource_exists(self.env, docs_resource) and \
+                    not (context.resource and \
+                    docs_resource == context.resource)
+            return 'widget_doc.html', {
+                        'title' : _('Widget documentation'),
+                        'data' : {
+                                'items' : metadata
+                            },
+                        'ctxtnav' : [tag.a(tag.i(class_='icon-info-sign'),
+                                    ' ', _('Help'),
+                                    href=get_resource_url(
+                                            self.env, docs_resource,
+                                            context.href)
+                                )] if insert_docs else [],
+                    }, context
+        else:
+            raise InvalidIdentifier('Widget name MUST match any of ' +
+                        ', '.join(self.get_widgets()),
+                    title='Invalid widget identifier')
+
     # Public API
+    def widget_metadata(self, nm, provider=None):
+        """Retrieve widget metadata.
+
+        :param nm:        Widget name
+        :param provider:  Widget provider. If omitted it will be resolved.
+        """
+        if provider is None:
+            provider = self.resolve_widget(nm)
+        return {
+                'urn' : nm,
+                'desc' : provider.get_widget_description(nm),
+                'params' : provider.get_widget_params(nm),
+            }
+
+    def _prepare_doc_metadata(self, spec):
+        """Transform widget metadata into a format suitable to render
+        documentation.
+        """
+        return {
+                'id' : "%s-widget" % (spec['urn'],),
+                'title' : tag.code(spec['urn']),
+                'desc' : '\n'.join(l.strip() 
+                                   for l in spec['desc'].splitlines()),
+                'sections' : [
+                        {
+                            'title' : _('Parameters'),
+                            'entries' : [
+                                    {
+                                        'caption' : pnm,
+                                        'summary' : '\n'.join(
+                                                l.strip() for l in \
+                                                p.get('desc').splitlines()),
+                                        'details' : [
+                                                ('Type', p.get('type', str)),
+                                                ('Required', p.get('required',
+                                                                   False)),
+                                                ('Default', p.get('default')),
+                                            ]
+                                    }
+                                for pnm, p in spec['params'].iteritems()]
+                        }
+                    ]
+            }
+
     def bind_params(self, options, spec, *params):
         """Extract values for widget arguments from `options` and ensure 
         they are valid and properly formatted.

Modified: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/util.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/util.py?rev=1415756&r1=1415755&r2=1415756&view=diff
==============================================================================
--- incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/util.py (original)
+++ incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/util.py Fri Nov 30 17:32:56 2012
@@ -31,6 +31,7 @@ from urlparse import urlparse
 from wsgiref.util import setup_testing_defaults
 
 from trac.core import Component, implements, ExtensionPoint
+from trac.util.text import to_unicode
 from trac.web.api import Request
 from trac.web.chrome import add_link, Chrome
 from trac.web.main import RequestDispatcher

Added: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/templates/widget_doc.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/templates/widget_doc.html?rev=1415756&view=auto
==============================================================================
--- incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/templates/widget_doc.html (added)
+++ incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/templates/widget_doc.html Fri Nov 30 17:32:56 2012
@@ -0,0 +1,54 @@
+<!--!
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+
+<div
+    xmlns="http://www.w3.org/1999/xhtml"
+    xmlns:py="http://genshi.edgewall.org/"
+    xmlns:xi="http://www.w3.org/2001/XInclude"
+    py:strip="">
+
+  <py:for each="item in items">
+    <h3 py:attrs="{ 'id' : item.get('id')}">${item.title}</h3>
+    ${wiki_to_html(context, item.desc)}
+    <py:if test="item.sections">
+      <py:for each="section in item.sections">
+        <h6>${section.title}</h6>
+        <py:for each="subitem in section.entries">
+          <div class="row">
+            <div class="span2">
+              <span class="label label-inverse">${subitem.caption}</span>
+            </div>
+            <div class="offset2">
+              ${wiki_to_html(context, subitem.summary)}
+            </div>
+          </div>
+          <dl class="dl-horizontal" py:if="subitem.details">
+            <py:for each="row in subitem.details">
+              <py:if test="row[1]">
+                <dt style="line-height: 21px"><span class="label">${row[0]}</span></dt>
+                <dd style="line-height: 21px">${row[1]}</dd>
+              </py:if>
+            </py:for>
+          </dl>
+        </py:for>
+      </py:for>
+    </py:if>
+  </py:for>
+</div>
+

Propchange: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/templates/widget_doc.html
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/templates/widget_doc.html
------------------------------------------------------------------------------
    svn:mime-type = text/html