Add breadcrumbs to final details pages

This patch adds the breadcrumb navigation to the final few details
pages: Users, Load Balancers, Projects, Hypervisors.

- Small redesigns the Load Balancer page to make the Member/Monitor
listings more readable
- Small redesign of Monitor Details to make Pools list more readable
- Adds missing translation wrapper to subnets breadcrumb

At this point, all Details pages should now use the breadcrumb format,
with the exception of Network Details, which is waiting on
https://bugs.launchpad.net/horizon/+bug/1416838

Closes-Bug: 1413823
Closes-Bug: 1100318
Change-Id: I3a1de6d24dfd49ae2e28107e9de76bbd9d972162
This commit is contained in:
Rob Cresswell 2015-10-30 11:17:52 +09:00
parent 08f7f87cac
commit d861d6848b
18 changed files with 154 additions and 89 deletions

View File

@ -1,11 +1,18 @@
{% extends 'base.html' %}
{% load i18n %}
{% load breadcrumb_nav %}
{% block title %}{% trans "Hypervisor Servers" %}{% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{{ table.render }}
{% block page_header %}
<div class='page-header'>
{% breadcrumb_nav %}
</div>
{% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{{ table.render }}
</div>
</div>
</div>
{% endblock %}

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
@ -56,7 +57,7 @@ class AdminIndexView(tabs.TabbedTableView):
class AdminDetailView(tables.DataTableView):
table_class = project_tables.AdminHypervisorInstancesTable
template_name = 'admin/hypervisors/detail.html'
page_title = _("Hypervisor Servers")
page_title = _("Servers")
def get_data(self):
instances = []
@ -75,3 +76,12 @@ class AdminDetailView(tables.DataTableView):
self.request,
_('Unable to retrieve hypervisor instances list.'))
return instances
def get_context_data(self, **kwargs):
context = super(AdminDetailView, self).get_context_data(**kwargs)
hypervisor_name = self.kwargs['hypervisor'].split('_', 1)[1]
breadcrumb = [
(_("Hypervisors"), reverse('horizon:admin:hypervisors:index')),
(hypervisor_name,), ]
context['custom_breadcrumb'] = breadcrumb
return context

View File

@ -1,10 +1,6 @@
{% load i18n %}
<h3>{% trans "Project Overview" %}</h3>
<div class="info detail">
<h4>{% trans "Information" %}</h4>
<hr class="header_rule">
<div class="detail tab-content">
<dl class="dl-horizontal">
<dt>{% trans "Project Name" %}</dt>
<dd>{{ project.name }}</dd>

View File

@ -1,10 +1,14 @@
{% extends 'base.html' %}
{% load i18n %}
{% load breadcrumb_nav %}
{% block title %}{% trans "Project Details" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=page_title %}
{% endblock page_header %}
<div class='page-header'>
{% breadcrumb_nav %}
</div>
{% endblock %}
{% block main %}
<div class="row">

View File

@ -1586,8 +1586,6 @@ class DetailProjectViewTests(test.BaseAdminViewTests):
self.assertTemplateUsed(res, 'identity/projects/detail.html')
self.assertEqual(res.context['project'].name, project.name)
self.assertEqual(res.context['project'].id, project.id)
self.assertContains(res, "Project Details: %s" % project.name,
1, 200)
@test.create_stubs({api.keystone: ('tenant_get',)})
def test_detail_view_with_exception(self):

View File

@ -18,12 +18,12 @@
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from django.views import generic
from horizon import exceptions
from horizon import messages
from horizon import tables
from horizon.utils import memoized
from horizon import views
from horizon import workflows
from openstack_dashboard import api
@ -206,15 +206,15 @@ class UpdateProjectView(workflows.WorkflowView):
return initial
class DetailProjectView(generic.TemplateView):
class DetailProjectView(views.HorizonTemplateView):
template_name = 'identity/projects/detail.html'
page_title = "{{ project.name }}"
def get_context_data(self, **kwargs):
context = super(DetailProjectView, self).get_context_data(**kwargs)
project = self.get_data()
table = project_tables.TenantsTable(self.request)
context["project"] = project
context["page_title"] = _("Project Details: %s") % project.name
context["url"] = reverse(INDEX_URL)
context["actions"] = table.render_row_actions(project)
return context

View File

@ -1,10 +1,6 @@
{% load i18n %}
<h3>{% trans "User Overview" %}</h3>
<div class="info detail">
<h4>{% trans "Information" %}</h4>
<hr class="header_rule">
<div class="detail tab-content">
<dl class="dl-horizontal">
{% if domain_id %}
<dt>{% trans "Domain ID" %}</dt>

View File

@ -1,7 +1,15 @@
{% extends 'base.html' %}
{% load i18n %}
{% load breadcrumb_nav %}
{% block title %}{% trans "User Details" %}{% endblock %}
{% block page_header %}
<div class='page-header'>
{% breadcrumb_nav %}
</div>
{% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">

View File

@ -600,8 +600,6 @@ class UsersViewTests(test.BaseAdminViewTests):
self.assertTemplateUsed(res, 'identity/users/detail.html')
self.assertEqual(res.context['user'].name, user.name)
self.assertEqual(res.context['user'].id, user.id)
self.assertContains(res, "<h1>User Details: %s</h1>" % user.name,
1, 200)
self.assertEqual(res.context['tenant_name'], tenant.name)
@test.create_stubs({api.keystone: ('user_get',)})

View File

@ -164,7 +164,7 @@ class CreateView(forms.ModalFormView):
class DetailView(views.HorizonTemplateView):
template_name = 'identity/users/detail.html'
page_title = _("User Details: {{ user.name }}")
page_title = "{{ user.name }}"
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)

View File

@ -12,14 +12,12 @@
# License for the specific language governing permissions and limitations
# under the License.
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import tabs
from openstack_dashboard import api
from openstack_dashboard.dashboards.project.loadbalancers import tables
@ -109,18 +107,7 @@ class VipDetailsTab(tabs.Tab):
template_name = "project/loadbalancers/_vip_details.html"
def get_context_data(self, request):
vid = self.tab_group.kwargs['vip_id']
vip = []
try:
vip = api.lbaas.vip_get(request, vid)
fips = api.network.tenant_floating_ip_list(self.tab_group.request)
vip_fip = [fip for fip in fips
if fip.port_id == vip.port.id]
if vip_fip:
vip.fip = vip_fip[0]
except Exception:
exceptions.handle(self.tab_group.request,
_('Unable to retrieve VIP details.'))
vip = self.tab_group.kwargs['vip']
return {'vip': vip}

View File

@ -1,7 +1,6 @@
{% load i18n sizeformat parse_date %}
<div class="info row detail">
<hr class="header_rule">
<div class="detail">
<dl class="dl-horizontal">
<dt>{% trans "ID" %}</dt>
<dd>{{ member.id }}</dd>

View File

@ -1,7 +1,6 @@
{% load i18n sizeformat parse_date %}
<div class="info row detail">
<hr class="header_rule">
<div class="detail">
<dl class="dl-horizontal">
<dt>{% trans "ID" %}</dt>
<dd>{{ monitor.id }}</dd>
@ -34,15 +33,19 @@
<dt>{% trans "Admin State Up" %}</dt>
<dd>{{ monitor.admin_state_up|yesno|capfirst }}</dd>
</dl>
<dt>{% trans "Pools" %}</dt>
<dl class="dl-horizontal">
<h4>{% trans "Pools" %}</h4>
<hr class="header_rule">
{% if monitor.pools %}
{% for pool in monitor.pools %}
{% url 'horizon:project:loadbalancers:pooldetails' pool.id as pool_url %}
<dd><a href="{{ pool_url }}">{{ pool.name_or_id }}</a></dd>
{% endfor %}
<ul>
{% for pool in monitor.pools %}
{% url 'horizon:project:loadbalancers:pooldetails' pool.id as pool_url %}
<li><a href="{{ pool_url }}">{{ pool.name_or_id }}</a></li>
{% endfor %}
{% else %}
<dd>{% trans "None" %}</dd>
{% trans "None" %}
{% endif %}
</dl>
</div>

View File

@ -1,7 +1,6 @@
{% load i18n sizeformat parse_date %}
<div class="info row detail">
<hr class="header_rule">
<div class="detail">
<dl class="dl-horizontal">
<dt>{% trans "ID" %}</dt>
<dd>{{ pool.id }}</dd>
@ -20,7 +19,7 @@
{% url 'horizon:project:loadbalancers:vipdetails' pool.vip_id as vip_url %}
<dd><a href="{{ vip_url }}">{{ pool.vip.name_or_id }}</a></dd>
{% else %}
<dd>{% trans "-" %}</dd>
<dd>{% trans "None" %}</dd>
{% endif %}
<dt>{% trans "Provider" %}</dt>
@ -36,34 +35,40 @@
<dt>{% trans "Load Balancing Method" %}</dt>
<dd>{{ pool.lb_method }}</dd>
<dt>{% trans "Members" %}</dt>
<dd>
{% if pool.members %}
{% for member in pool.members %}
{% url 'horizon:project:loadbalancers:memberdetails' member.id as member_url %}
<a href="{{ member_url }}">{{ member.address }}:{{ member.protocol_port }}</a><br>
{% endfor %}
{% else %}
<dd>{% trans "-" %}</dd>
{% endif %}
</dd>
<dt>{% trans "Health Monitors" %}</dt>
<dd>
{% if pool.health_monitors %}
{% for monitor in pool.health_monitors %}
{% url 'horizon:project:loadbalancers:monitordetails' monitor.id as monitor_url %}
<a href="{{ monitor_url }}">{{ monitor.display_name }}</a><br>
{% endfor %}
{% else %}
<dd>{% trans "-" %}</dd>
{% endif %}
</dd>
<dt>{% trans "Admin State Up" %}</dt>
<dd>{{ pool.admin_state_up|yesno|capfirst }}</dd>
<dt>{% trans "Status" %}</dt>
<dd>{{ pool.status }}</dd>
</dl>
<dl class="dl-horizontal">
<h4>{% trans "Members" %}</h4>
<hr class="header_rule">
{% if pool.members %}
<ul>
{% for member in pool.members %}
{% url 'horizon:project:loadbalancers:memberdetails' member.id as member_url %}
<li><a href="{{ member_url }}">{{ member.address }}:{{ member.protocol_port }}</a></li>
{% endfor %}
<ul>
{% else %}
{% trans "None" %}
{% endif %}
</dl>
<dl class="dl-horizontal">
<h4>{% trans "Health Monitors" %}</h4>
<hr class="header_rule">
{% if pool.health_monitors %}
<ul>
{% for monitor in pool.health_monitors %}
{% url 'horizon:project:loadbalancers:monitordetails' monitor.id as monitor_url %}
<li><a href="{{ monitor_url }}">{{ monitor.display_name }}</a></li>
{% endfor %}
</ul>
{% else %}
{% trans "None" %}
{% endif %}
</dl>
</div>

View File

@ -1,7 +1,6 @@
{% load i18n sizeformat parse_date %}
<div class="info row detail">
<hr class="header_rule">
<div class="detail">
<dl class="dl-horizontal">
<dt>{% trans "ID" %}</dt>
<dd>{{ vip.id }}</dd>

View File

@ -21,6 +21,9 @@ from openstack_dashboard.dashboards.project.loadbalancers import views
urlpatterns = patterns(
'openstack_dashboard.dashboards.project.loadbalancers.views',
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^\?tab=lbtabs__members$', views.IndexView.as_view(), name='members'),
url(r'^\?tab=lbtabs__monitors$',
views.IndexView.as_view(), name='monitors'),
url(r'^addpool$', views.AddPoolView.as_view(), name='addpool'),
url(r'^updatepool/(?P<pool_id>[^/]+)/$',
views.UpdatePoolView.as_view(), name='updatepool'),

View File

@ -71,8 +71,8 @@ class AddMonitorView(workflows.WorkflowView):
class PoolDetailsView(tabs.TabView):
tab_group_class = project_tabs.PoolDetailsTabs
template_name = 'project/loadbalancers/details_tabs.html'
page_title = _("Pool Details")
template_name = 'horizon/common/_detail.html'
page_title = "{{ pool.name|default:pool.id }}"
@memoized.memoized_method
def get_data(self):
@ -111,14 +111,52 @@ class PoolDetailsView(tabs.TabView):
class VipDetailsView(tabs.TabView):
tab_group_class = project_tabs.VipDetailsTabs
template_name = 'project/loadbalancers/details_tabs.html'
page_title = _("VIP Details")
template_name = 'horizon/common/_detail.html'
page_title = "{{ vip.name|default:vip_id }}"
@memoized.memoized_method
def get_data(self):
vid = self.kwargs['vip_id']
vip = []
try:
vip = api.lbaas.vip_get(self.request, vid)
fips = api.network.tenant_floating_ip_list(self.request)
vip_fip = [fip for fip in fips
if fip.port_id == vip.port.id]
if vip_fip:
vip.fip = vip_fip[0]
except Exception:
exceptions.handle(self.request,
_('Unable to retrieve VIP details.'))
return vip
def get_context_data(self, **kwargs):
context = super(VipDetailsView, self).get_context_data(**kwargs)
vip = self.get_data()
context['vip'] = vip
vip_nav = vip.pool.name_or_id
breadcrumb = [
(_("Load Balancers"), self.get_redirect_url()),
(vip_nav,
reverse('horizon:project:loadbalancers:vipdetails',
args=(vip.id,))),
(_("VIP"),), ]
context["custom_breadcrumb"] = breadcrumb
return context
def get_tabs(self, request, *args, **kwargs):
vip = self.get_data()
return self.tab_group_class(request, vip=vip, **kwargs)
@staticmethod
def get_redirect_url():
return reverse("horizon:project:loadbalancers:index")
class MemberDetailsView(tabs.TabView):
tab_group_class = project_tabs.MemberDetailsTabs
template_name = 'project/loadbalancers/details_tabs.html'
page_title = _("Member Details")
template_name = 'horizon/common/_detail.html'
page_title = "{{ member.name|default:member.id }}"
@memoized.memoized_method
def get_data(self):
@ -133,6 +171,15 @@ class MemberDetailsView(tabs.TabView):
context = super(MemberDetailsView, self).get_context_data(**kwargs)
member = self.get_data()
context['member'] = member
member_nav = member.pool.name_or_id
breadcrumb = [
(_("Load Balancers"), self.get_redirect_url()),
(member_nav,
reverse('horizon:project:loadbalancers:pooldetails',
args=(member.pool.id,))),
(_("Members"), reverse('horizon:project:loadbalancers:members')),
]
context["custom_breadcrumb"] = breadcrumb
table = project_tables.MembersTable(self.request)
context["url"] = self.get_redirect_url()
context["actions"] = table.render_row_actions(member)
@ -149,8 +196,8 @@ class MemberDetailsView(tabs.TabView):
class MonitorDetailsView(tabs.TabView):
tab_group_class = project_tabs.MonitorDetailsTabs
template_name = 'project/loadbalancers/details_tabs.html'
page_title = _("Monitor Details")
template_name = 'horizon/common/_detail.html'
page_title = "{{ monitor.name|default:monitor.id }}"
@memoized.memoized_method
def get_data(self):
@ -165,6 +212,11 @@ class MonitorDetailsView(tabs.TabView):
context = super(MonitorDetailsView, self).get_context_data(**kwargs)
monitor = self.get_data()
context['monitor'] = monitor
breadcrumb = [
(_("Load Balancers"), self.get_redirect_url()),
(_("Monitors"), reverse('horizon:project:loadbalancers:monitors')),
]
context["custom_breadcrumb"] = breadcrumb
table = project_tables.MonitorsTable(self.request)
context["url"] = self.get_redirect_url()
context["actions"] = table.render_row_actions(monitor)

View File

@ -149,10 +149,10 @@ class DetailView(tabs.TabView):
network_id=subnet.network_id)
# TODO(robcresswell) Add URL for "Subnets" crumb after bug/1416838
breadcrumb = [
("Networks", self.get_redirect_url()),
(_("Networks"), self.get_redirect_url()),
(network_nav, reverse('horizon:project:networks:detail',
args=(subnet.network_id,))),
("Subnets",), ]
(_("Subnets"),), ]
context["custom_breadcrumb"] = breadcrumb
context["subnet"] = subnet
context["url"] = self.get_redirect_url()