Adds breadcrumb to resource browser.

Fixes bug #1037012

Change-Id: I09247ae2e30261989118c37de8ec33d90c8b4100
This commit is contained in:
Ke Wu 2012-08-13 11:53:51 -07:00
parent c00496e1d1
commit 156a368c63
10 changed files with 173 additions and 27 deletions

View File

@ -19,6 +19,7 @@ from django.utils.translation import ugettext_lazy as _
from horizon.tables import DataTable
from horizon.utils import html
from .breadcrumb import Breadcrumb
class ResourceBrowser(html.HTMLElement):
@ -48,6 +49,18 @@ class ResourceBrowser(html.HTMLElement):
This table class must set browser_table attribute in Meta to
``"content"``.
.. attribute:: navigation_kwarg_name
This attribute represents the key of the navigatable items in the
kwargs property of this browser's view.
Defaults to ``"navigation_kwarg"``.
.. attribute:: content_kwarg_name
This attribute represents the key of the content items in the
kwargs property of this browser's view.
Defaults to ``"content_kwarg"``.
.. attribute:: template
String containing the template which should be used to render
@ -57,20 +70,43 @@ class ResourceBrowser(html.HTMLElement):
The name of the context variable which will contain the browser when
it is rendered. Defaults to ``"browser"``.
.. attribute:: has_breadcrumb
Indicates if the content table of the browser would have breadcrumb.
Defaults to false.
.. attribute:: breadcrumb_template
This is a template used to render the breadcrumb.
Defaults to ``"horizon/common/_breadcrumb.html"``.
"""
name = None
verbose_name = None
navigation_table_class = None
content_table_class = None
navigation_kwarg_name = "navigation_kwarg"
content_kwarg_name = "content_kwarg"
navigable_item_name = _("Navigation Item")
template = "horizon/common/_resource_browser.html"
context_var_name = "browser"
has_breadcrumb = False
breadcrumb_template = "horizon/common/_breadcrumb.html"
breadcrumb_url = None
def __init__(self, request, tables_dict=None, attrs=None, **kwargs):
super(ResourceBrowser, self).__init__()
self.name = self.name or self.__class__.__name__
self.verbose_name = self.verbose_name or self.name.title()
self.request = request
self.kwargs = kwargs
self.has_breadcrumb = getattr(self, "has_breadcrumb")
if self.has_breadcrumb:
self.breadcrumb_template = getattr(self, "breadcrumb_template")
self.breadcrumb_url = getattr(self, "breadcrumb_url")
if not self.breadcrumb_url:
raise ValueError("You must specify a breadcrumb_url "
"if the has_breadcrumb is set to True.")
self.attrs.update(attrs or {})
self.check_table_class(self.content_table_class, "content_table_class")
self.check_table_class(self.navigation_table_class,
@ -91,6 +127,19 @@ class ResourceBrowser(html.HTMLElement):
"""
self.navigation_table = tables[self.navigation_table_class._meta.name]
self.content_table = tables[self.content_table_class._meta.name]
if self.has_breadcrumb:
self.prepare_breadcrumb(tables)
def prepare_breadcrumb(self, tables):
navigation_item = self.kwargs.get(self.navigation_kwarg_name)
content_path = self.kwargs.get(self.content_kwarg_name)
if self.has_breadcrumb and navigation_item and content_path:
for table in tables.values():
table.breadcrumb = Breadcrumb(self.request,
self.breadcrumb_template,
navigation_item,
content_path,
self.breadcrumb_url)
def render(self):
browser_template = template.loader.get_template(self.template)

View File

@ -0,0 +1,48 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 Nebula, Inc.
#
# Licensed 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.
from django import template
from horizon.utils import html
class Breadcrumb(html.HTMLElement):
def __init__(self, request, template, root,
subfolder_path, url, attr=None):
super(Breadcrumb, self).__init__()
self.template = template
self.request = request
self.root = root
self.subfolder_path = subfolder_path
self.url = url
self._subfolders = []
def get_subfolders(self):
if self.subfolder_path and not self._subfolders:
(parent, slash, folder) = self.subfolder_path.strip('/') \
.rpartition('/')
while folder:
path = "%s%s%s/" % (parent, slash, folder)
self._subfolders.insert(0, (folder, path))
(parent, slash, folder) = parent.rpartition('/')
return self._subfolders
def render(self):
""" Renders the table using the template from the table options. """
breadcrumb_template = template.loader.get_template(self.template)
extra_context = {"breadcrumb": self}
context = template.RequestContext(self.request, extra_context)
return breadcrumb_template.render(context)

View File

@ -14,8 +14,6 @@
# License for the specific language governing permissions and limitations
# under the License.
from collections import defaultdict
from django.utils.translation import ugettext_lazy as _
from horizon.tables import MultiTableView
@ -31,8 +29,8 @@ class ResourceBrowserView(MultiTableView):
% self.__class__.__name__)
self.table_classes = (self.browser_class.navigation_table_class,
self.browser_class.content_table_class)
super(ResourceBrowserView, self).__init__(*args, **kwargs)
self.navigation_selection = False
super(ResourceBrowserView, self).__init__(*args, **kwargs)
def get_browser(self):
if not hasattr(self, "browser"):

View File

@ -31,3 +31,7 @@ class ContainerBrowser(browsers.ResourceBrowser):
navigation_table_class = ContainersTable
content_table_class = ObjectsTable
navigable_item_name = _("Container")
navigation_kwarg_name = "container_name"
content_kwarg_name = "subfolder_path"
has_breadcrumb = True
breadcrumb_url = "horizon:nova:containers:index"

View File

@ -4,21 +4,7 @@
{% block page_header %}
<div class='page-header'>
<h2>{% trans "Container" %}
{% if subfolders %}
: <a href="{% url horizon:nova:containers:index container_name|add:'/' %}">{{container_name}}</a>
<small>/</small>
{% elif container_name %}
: {{container_name}}
{% endif %}
{% for subfolder, path in subfolders %}
<small>
{% if not forloop.last %}
<a href="{% url horizon:nova:containers:index container_name|add:'/' path %}">
{% endif %}{{ subfolder }}{% if not forloop.last %}</a> /{% endif %}
</small>
{% endfor %}
</h2>
<h2>{% trans "Containers" %}
</div>
{% endblock page_header %}

View File

@ -361,7 +361,7 @@ class FilterAction(BaseAction):
def data_type_filter(self, table, data, filter_string):
filtered_data = []
for data_type in table.data_types:
for data_type in table._meta.data_types:
func_name = "filter_%s_data" % data_type
filter_func = getattr(self, func_name, None)
if not filter_func and not callable(filter_func):

View File

@ -881,6 +881,7 @@ class DataTable(object):
self.kwargs = kwargs
self._needs_form_wrapper = needs_form_wrapper
self._no_data_message = self._meta.no_data_message
self.breadcrumb = None
# Create a new set
columns = []

View File

@ -0,0 +1,20 @@
{% load url from future %}
{% load i18n %}
{% with subfolders=breadcrumb.get_subfolders %}
<ul class="breadcrumb">
<li>
Folder Path: <a href="{% url breadcrumb.url breadcrumb.root|add:'/' %}">{{ breadcrumb.root }}</a> <span class="divider">/</span>
</li>
{% for subfolder, path in subfolders %}
<li>
{% if not forloop.last %}
<a href="{% url breadcrumb.url breadcrumb.root|add:'/' path %}">
{% endif %}
{{ subfolder }}
{% if not forloop.last %}
</a> <span class="divider">/</span>
{% endif %}
</li>
{% endfor %}
</ul>
{% endwith %}

View File

@ -11,6 +11,13 @@
{{ table.render_table_actions }}
</th>
</tr>
{% if table.breadcrumb %}
<tr>
<td class="breadcrumb_td" colspan="{{ table.get_columns|length }}">
{{ table.breadcrumb.render }}
</td>
</tr>
{% endif %}
{% if not table.is_browser_table %}
<tr>
{% for column in columns %}

View File

@ -1410,6 +1410,7 @@ label.log-length {
/* ResourceBrowser style */
#browser_wrapper {
width: @browserWrapperWidth;
min-width: 1000px;
background-color: @grayLighter;
border: @dataTableBorderWidth solid @dataTableBorderColor;
.border-radius(4px);
@ -1435,21 +1436,36 @@ label.log-length {
float: left;
}
div.navigation_wrapper {
z-index: 10;
width: @navigationTableWidth;
div.table_wrapper,
thead th.table_header {
border-right: 0 none;
border-top-right-radius: 0;
}
td.normal_column{
td {
&:first-child {
border-left: 0 none;
}
&.breadcrumb_td {
padding-right: 0px;
max-width: 200px;
}
}
tfoot td {
border-right: 0 none;
border-bottom-right-radius: 0;
}
ul.breadcrumb {
padding-right: 0px;
border-top-right-radius: 0px;
border-bottom-right-radius: 0px;
border-right: none;
white-space: nowrap;
}
tbody td {
background-color: @white;
}
}
div.content_wrapper {
width: @contentTableWidth;
@ -1462,13 +1478,31 @@ label.log-length {
&:last-child {
border-right: 0 none;
}
&.breadcrumb_td {
padding-left: 0px;
}
}
tfoot td {
border-left: 0 none;
border-bottom-left-radius: 0;
}
/* FIXME(Ke Wu): for now there are two breadcrumb tr in both table
* and this one in the content table is hidden. This hack is made to
* fix the alignment of two table, needs a better solution in the
* future.
*/
ul.breadcrumb {
padding-left: 0px;
border-top-left-radius: 0px;
border-bottom-left-radius: 0px;
border-left: none;
li {
visibility: hidden;
}
}
}
table {
border-collapse: collapse;
thead {
tr th {
border-bottom: none;
@ -1484,16 +1518,15 @@ label.log-length {
height: @tdHeight;
padding: @actionsColumnPadding;
}
}
&.table-striped {
tbody {
tr:nth-child(even) td,
tr:nth-child(even) th {
background-color: @white;
}
td.actions_column {
position: static;
}
}
}
.breadcrumb{
padding: 6px;
margin: 0 0 1px 0;
}
}
/* Styling for inline object creation buttons */