You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by tv...@apache.org on 2012/12/18 05:29:28 UTC

[1/41] git commit: [#4957] Group multiple tools of same type in navbar

Updated Branches:
  refs/heads/tv/4957 1e34809b3 -> 506a731e7 (forced update)


[#4957] Group multiple tools of same type in navbar


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/506a731e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/506a731e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/506a731e

Branch: refs/heads/tv/4957
Commit: 506a731e7f67251f58fa731f81ff8dbaf714ffd3
Parents: 0a7b185
Author: Tim Van Steenburgh <tv...@gmail.com>
Authored: Fri Dec 14 22:42:50 2012 +0000
Committer: Tim Van Steenburgh <tv...@gmail.com>
Committed: Tue Dec 18 04:27:01 2012 +0000

----------------------------------------------------------------------
 Allura/allura/app.py                              |    9 +++-
 Allura/allura/controllers/project.py              |   11 +++++
 Allura/allura/model/project.py                    |   37 +++++++++++++++-
 Allura/allura/nf/allura/css/site_style.css        |   23 ++++++++++
 Allura/allura/templates/jinja_master/top_nav.html |    8 +++-
 Allura/allura/templates/tool_list.html            |   21 +++++++++
 Allura/allura/tests/functional/test_admin.py      |   14 +++---
 Allura/allura/tests/functional/test_tool_list.py  |   13 ++++++
 Allura/allura/tests/unit/test_project.py          |   30 +++++++++++++
 Allura/allura/tests/unit/test_sitemapentry.py     |   16 +++++++
 10 files changed, 171 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/506a731e/Allura/allura/app.py
----------------------------------------------------------------------
diff --git a/Allura/allura/app.py b/Allura/allura/app.py
index 613e88d..d691bf2 100644
--- a/Allura/allura/app.py
+++ b/Allura/allura/app.py
@@ -33,7 +33,8 @@ class ConfigOption(object):
 
 class SitemapEntry(object):
 
-    def __init__(self, label, url=None, children=None, className=None, ui_icon=None, small=None):
+    def __init__(self, label, url=None, children=None, className=None,
+            ui_icon=None, small=None, tool_name=None):
         self.label = label
         self.className = className
         if url is not None:
@@ -44,6 +45,8 @@ class SitemapEntry(object):
         if children is None:
             children = []
         self.children = children
+        self.tool_name = tool_name
+        self.matching_urls = []
 
     def __getitem__(self, x):
         """
@@ -92,6 +95,10 @@ class SitemapEntry(object):
                 self.children.append(e)
                 child_index[lbl] = e
 
+    def matches_url(self, request):
+        """Return true if this SitemapEntry 'matches' the url of `request`."""
+        return self.url in request.upath_info or any([
+            url in request.upath_info for url in self.matching_urls])
 
 class Application(object):
     """

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/506a731e/Allura/allura/controllers/project.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/project.py b/Allura/allura/controllers/project.py
index 9c3c5c5..a420117 100644
--- a/Allura/allura/controllers/project.py
+++ b/Allura/allura/controllers/project.py
@@ -254,6 +254,16 @@ class HostNeighborhoodController(WsgiDispatchController, NeighborhoodController)
     nf = NewForgeController()
     search = SearchController()
 
+class ToolListController(object):
+    """Renders a list of all tools of a given type in the current project."""
+
+    @expose('jinja:allura:templates/tool_list.html')
+    def _default(self, tool_name, *args, **kw):
+        tool_name = tool_name.lower()
+        entries = [e for e in c.project.sitemap()
+                if e.tool_name and e.tool_name.lower() == tool_name]
+        return dict(entries=entries, type=entries[0].tool_name if entries else None)
+
 class ProjectController(object):
 
     def __init__(self):
@@ -261,6 +271,7 @@ class ProjectController(object):
         setattr(self, 'feed.atom', self.feed)
         setattr(self, '_nav.json', self._nav)
         self.screenshot = ScreenshotsController()
+        self._list = ToolListController()
 
     @expose('json:')
     def _nav(self):

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/506a731e/Allura/allura/model/project.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/project.py b/Allura/allura/model/project.py
index c3ac033..454850c 100644
--- a/Allura/allura/model/project.py
+++ b/Allura/allura/model/project.py
@@ -1,4 +1,5 @@
 import logging
+from collections import Counter, OrderedDict
 from datetime import datetime
 
 from tg import config
@@ -418,7 +419,8 @@ class Project(MappedClass, ActivityNode, ActivityObject):
             if app.is_visible_to(c.user):
                 for sm in app.main_menu():
                     entry = sm.bind_app(app)
-                    entry.ui_icon='tool-%s' % ac.tool_name.lower()
+                    entry.tool_name = ac.tool_name
+                    entry.ui_icon = 'tool-%s' % entry.tool_name.lower()
                     ordinal = int(ac.options.get('ordinal', 0)) + delta_ordinal
                     if ordinal > max_ordinal:
                         max_ordinal = ordinal
@@ -431,6 +433,39 @@ class Project(MappedClass, ActivityNode, ActivityObject):
         entries = sorted(entries, key=lambda e: e['ordinal'])
         return [e['entry'] for e in entries]
 
+    def grouped_navbar_entries(self):
+        """Return a ``allura.app.SitemapEntry`` list suitable for rendering
+        the project navbar with tools grouped together by tool type.
+        """
+        # get orginal (non-grouped) navbar entries
+        sitemap = self.sitemap()
+        # ordered dict to preserve the orginal ordering of tools
+        grouped_nav = OrderedDict()
+        # count how many tools of each type we have
+        counts = Counter([e.tool_name.lower() for e in sitemap if e.tool_name])
+        for e in sitemap:
+            # if it's not a tool, add to navbar and continue
+            if not e.tool_name:
+                grouped_nav[id(e)] = e
+                continue
+            tool_name = e.tool_name.lower()
+            # tool of a type we don't have in the navbar yet
+            if tool_name not in grouped_nav:
+                # there's more than one tool of this type
+                if counts.get(tool_name, 1) > 1:
+                    # change label to be the tool name (type)
+                    e.label = e.tool_name
+                    # add tool url to list of urls that will match this nav entry
+                    e.matching_urls.append(e.url)
+                    # change url to point to tool list page
+                    e.url = self.url() + '_list/' + tool_name
+                grouped_nav[tool_name] = e
+            else:
+                # already have a tool of this type in the nav; add this tool's
+                # url to the list of urls that match this nav entry
+                grouped_nav[tool_name].matching_urls.append(e.url)
+        return grouped_nav.values()
+
     def parent_iter(self):
         yield self
         pp = self.parent_project

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/506a731e/Allura/allura/nf/allura/css/site_style.css
----------------------------------------------------------------------
diff --git a/Allura/allura/nf/allura/css/site_style.css b/Allura/allura/nf/allura/css/site_style.css
index c5dec9e..ebe842a 100644
--- a/Allura/allura/nf/allura/css/site_style.css
+++ b/Allura/allura/nf/allura/css/site_style.css
@@ -370,6 +370,29 @@ td, td img {
   transform: rotate(45deg);
 }
 
+.tool-count {
+  display: block;
+  position: absolute;
+  z-index: 2;
+  height: 13px;
+  width: 13px;
+  line-height: 13px;
+  bottom: 17px;
+  left: 64%;
+  font-size: 11px;
+  font-weight: bold;
+  color: white;
+  padding: 1px;
+  border: 1px solid #9F0000;
+  background-color: #A70000;
+  -moz-border-radius: 2px;
+  -webkit-border-radius: 2px;
+  -o-border-radius: 2px;
+  -ms-border-radius: 2px;
+  -khtml-border-radius: 2px;
+  border-radius: 2px;
+}
+
 .nowrap {
   white-space: nowrap;
 }

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/506a731e/Allura/allura/templates/jinja_master/top_nav.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/jinja_master/top_nav.html b/Allura/allura/templates/jinja_master/top_nav.html
index dfa288a..0ba753d 100644
--- a/Allura/allura/templates/jinja_master/top_nav.html
+++ b/Allura/allura/templates/jinja_master/top_nav.html
@@ -1,5 +1,5 @@
 {% if c.project %}
-  {% for s in c.project.sitemap() %}
+  {% for s in c.project.grouped_navbar_entries() %}
     <a href="{{s.url}}" class="ui-icon-{{s.ui_icon or 'admin'}}">
       {{s.label}}
       {% if s.label == 'Home' %}
@@ -8,10 +8,14 @@
           <span class="diamond"></span>
         {% endif %}
       {% else %}
-        {% if s.url in request.upath_info or c.project.neighborhood.url()+'_admin' in request.upath_info %}
+        {% if s.matches_url(request) or c.project.neighborhood.url()+'_admin' in request.upath_info %}
           <span class="diamond"></span>
         {% endif %}
       {% endif %}
+      {% set grouped_tool_count = s.matching_urls|length %}
+      {% if grouped_tool_count %}
+        <span class="tool-count">{{grouped_tool_count}}</span>
+      {% endif %}
     </a>
 	{% endfor %}
 	<div style="clear:both"></div>

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/506a731e/Allura/allura/templates/tool_list.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/tool_list.html b/Allura/allura/templates/tool_list.html
new file mode 100644
index 0000000..67ab8df
--- /dev/null
+++ b/Allura/allura/templates/tool_list.html
@@ -0,0 +1,21 @@
+{% set hide_left_bar = True %}
+{% extends g.theme.master %}
+
+{% block title %}{{c.project.name}} / {{type}} tools{% endblock %}
+
+{% block extra_css %}
+{% endblock %}
+
+{% block inner_grid %}{% endblock %}
+{% block header_classes %} colored title{% endblock %}
+{% block header %}{{type}} tools{% endblock %}
+
+{% block content %}
+<div>
+  <ul>
+  {% for e in entries %}
+    <li><a href="{{e.url}}">{{e.label}}</a></li>
+  {% endfor %}
+  </ul>
+</div>
+{% endblock %}

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/506a731e/Allura/allura/tests/functional/test_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_admin.py b/Allura/allura/tests/functional/test_admin.py
index 656f1c9..a049e5b 100644
--- a/Allura/allura/tests/functional/test_admin.py
+++ b/Allura/allura/tests/functional/test_admin.py
@@ -99,15 +99,15 @@ class TestProjectAdmin(TestController):
                     'new.mount_point':'test-tool2',
                     'new.mount_label':'Test Tool2'})
         assert 'error' not in self.webflash(r)
-        # check the nav - the similarly named tool should NOT be active
+        # check the nav - tools of same type are grouped
         r = self.app.get('/p/test/test-tool/Home/')
-        active_link = r.html.findAll('span',{'class':'diamond'})
-        assert len(active_link) == 1
-        assert active_link[0].parent['href'] == '/p/test/test-tool/'
-        r = self.app.get('/p/test/test-tool2/Home/')
-        active_link = r.html.findAll('span',{'class':'diamond'})
+        active_link = r.html.findAll('span', {'class':'diamond'})
         assert len(active_link) == 1
-        assert active_link[0].parent['href'] == '/p/test/test-tool2/'
+        assert active_link[0].parent['href'] == '/p/test/_list/wiki'
+        # check tool-count of grouped tools
+        tool_count = active_link[0].findNextSibling('span')['class']
+        assert tool_count['class'] == u'tool-count', tool_count['class']
+        assert tool_count.text == u'2', tool_count.text
         # check can't create dup tool
         r = self.app.post('/admin/update_mounts', params={
                 'new.install':'install',

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/506a731e/Allura/allura/tests/functional/test_tool_list.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_tool_list.py b/Allura/allura/tests/functional/test_tool_list.py
new file mode 100644
index 0000000..43810b6
--- /dev/null
+++ b/Allura/allura/tests/functional/test_tool_list.py
@@ -0,0 +1,13 @@
+from allura.tests import TestController
+from allura.tests import decorators as td
+
+
+class TestToolListController(TestController):
+
+    @td.with_wiki
+    @td.with_tool('test', 'Wiki', 'wiki2')
+    def test_default(self):
+        """Test that list page contains a link to all tools of that type."""
+        r = self.app.get('/p/test/_list/wiki')
+        assert len(r.html.find('a', dict(href='/p/test/wiki/'))) == 1, r
+        assert len(r.html.find('a', dict(href='/p/test/wiki2/'))) == 1, r

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/506a731e/Allura/allura/tests/unit/test_project.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/unit/test_project.py b/Allura/allura/tests/unit/test_project.py
new file mode 100644
index 0000000..b59d04f
--- /dev/null
+++ b/Allura/allura/tests/unit/test_project.py
@@ -0,0 +1,30 @@
+import unittest
+from mock import Mock
+
+from allura import model as M
+from allura.app import SitemapEntry
+
+
+class TestProject(unittest.TestCase):
+    def test_grouped_navbar_entries(self):
+        p = M.Project()
+        sitemap_entries = [
+            SitemapEntry('bugs', url='bugs url', tool_name='Tickets'),
+            SitemapEntry('wiki', url='wiki url', tool_name='Wiki'),
+            SitemapEntry('discuss', url='discuss url', tool_name='Discussion'),
+            SitemapEntry('subproject', url='subproject url'),
+            SitemapEntry('features', url='features url', tool_name='Tickets'),
+            SitemapEntry('help', url='help url', tool_name='Discussion'),
+            SitemapEntry('support reqs', url='support url', tool_name='Tickets'),
+        ]
+        p.url = Mock(return_value='proj_url/')
+        p.sitemap = Mock(return_value=sitemap_entries)
+        expected = [
+            ('Tickets', 'proj_url/_list/tickets', 3),
+            ('wiki', 'wiki url', 0),
+            ('Discussion', 'proj_url/_list/discussion', 2),
+            ('subproject', 'subproject url', 0),
+        ]
+        actual = [(e.label, e.url, len(e.matching_urls))
+                for e in p.grouped_navbar_entries()]
+        self.assertEqual(expected, actual)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/506a731e/Allura/allura/tests/unit/test_sitemapentry.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/unit/test_sitemapentry.py b/Allura/allura/tests/unit/test_sitemapentry.py
new file mode 100644
index 0000000..42fcc80
--- /dev/null
+++ b/Allura/allura/tests/unit/test_sitemapentry.py
@@ -0,0 +1,16 @@
+import unittest
+from mock import Mock
+
+from allura.app import SitemapEntry
+
+
+class TestSitemapEntry(unittest.TestCase):
+    def test_matches_url(self):
+        request = Mock(upath_info='/p/project/tool/artifact')
+        s1 = SitemapEntry('tool', url='/p/project/tool')
+        s2 = SitemapEntry('tool2', url='/p/project/tool2')
+        s3 = SitemapEntry('Tool', url='/p/project/_list/tool')
+        s3.matching_urls.append('/p/project/tool')
+        self.assertTrue(s1.matches_url(request))
+        self.assertFalse(s2.matches_url(request))
+        self.assertTrue(s3.matches_url(request))