diff --git a/horizon/browsers/base.py b/horizon/browsers/base.py index e4a8979206..802b635364 100644 --- a/horizon/browsers/base.py +++ b/horizon/browsers/base.py @@ -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) diff --git a/horizon/browsers/breadcrumb.py b/horizon/browsers/breadcrumb.py new file mode 100644 index 0000000000..ba1ca748f5 --- /dev/null +++ b/horizon/browsers/breadcrumb.py @@ -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) diff --git a/horizon/browsers/views.py b/horizon/browsers/views.py index 933795e10b..c2e8b437d8 100644 --- a/horizon/browsers/views.py +++ b/horizon/browsers/views.py @@ -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"): diff --git a/horizon/dashboards/nova/containers/browsers.py b/horizon/dashboards/nova/containers/browsers.py index 68aab4c996..bb8de593a2 100644 --- a/horizon/dashboards/nova/containers/browsers.py +++ b/horizon/dashboards/nova/containers/browsers.py @@ -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" diff --git a/horizon/dashboards/nova/containers/templates/containers/index.html b/horizon/dashboards/nova/containers/templates/containers/index.html index 1dc45d98ea..3779a89431 100644 --- a/horizon/dashboards/nova/containers/templates/containers/index.html +++ b/horizon/dashboards/nova/containers/templates/containers/index.html @@ -4,21 +4,7 @@ {% block page_header %} {% endblock page_header %} diff --git a/horizon/tables/actions.py b/horizon/tables/actions.py index f8cc8372e3..3984b9ef04 100644 --- a/horizon/tables/actions.py +++ b/horizon/tables/actions.py @@ -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): diff --git a/horizon/tables/base.py b/horizon/tables/base.py index ce9404194d..126af4f1b1 100644 --- a/horizon/tables/base.py +++ b/horizon/tables/base.py @@ -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 = [] diff --git a/horizon/templates/horizon/common/_breadcrumb.html b/horizon/templates/horizon/common/_breadcrumb.html new file mode 100644 index 0000000000..b7e8307407 --- /dev/null +++ b/horizon/templates/horizon/common/_breadcrumb.html @@ -0,0 +1,20 @@ +{% load url from future %} +{% load i18n %} +{% with subfolders=breadcrumb.get_subfolders %} + +{% endwith %} diff --git a/horizon/templates/horizon/common/_data_table.html b/horizon/templates/horizon/common/_data_table.html index ccbdbc4b36..0560f996e1 100644 --- a/horizon/templates/horizon/common/_data_table.html +++ b/horizon/templates/horizon/common/_data_table.html @@ -11,6 +11,13 @@ {{ table.render_table_actions }} + {% if table.breadcrumb %} + + + {{ table.breadcrumb.render }} + + + {% endif %} {% if not table.is_browser_table %} {% for column in columns %} diff --git a/openstack_dashboard/static/dashboard/less/horizon.less b/openstack_dashboard/static/dashboard/less/horizon.less index 297dd5546c..7f7225ce3b 100644 --- a/openstack_dashboard/static/dashboard/less/horizon.less +++ b/openstack_dashboard/static/dashboard/less/horizon.less @@ -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 */