Update action for namespaces table public/protected attributes

With the inline edit deprecation the ability to edit the
public/protected attributes for a namespace was also removed.

This patch adds an update action to the namespaces table so users
can edit these attributes.

Change-Id: I7476b49a5bfe161c000046dfb5d622bfec924da7
Closes-Bug: #1617353
This commit is contained in:
Luis Daniel Castellanos
2016-09-01 09:37:20 -05:00
parent aff061da1f
commit 6fb02c5268
8 changed files with 198 additions and 1 deletions

View File

@@ -14,6 +14,8 @@
METADATA_CREATE_TEMPLATE = 'admin/metadata_defs/create.html'
METADATA_CREATE_URL = "horizon:admin:metadata_defs:create"
METADATA_UPDATE_TEMPLATE = 'admin/metadata_defs/update.html'
METADATA_UPDATE_URL = "horizon:admin:metadata_defs:update"
METADATA_DETAIL_OVERVIEW_TEMPLATE = "admin/metadata_defs/_detail_overview.html"
METADATA_DETAIL_CONTENTS_TEMPLATE = "admin/metadata_defs/_detail_contents.html"
METADATA_DETAIL_TEMPLATE = 'horizon/common/_detail.html'

View File

@@ -18,6 +18,7 @@ Forms for managing metadata.
"""
import json
from django.core.urlresolvers import reverse
from django.forms import ValidationError # noqa
from django.utils.translation import ugettext_lazy as _
@@ -146,3 +147,30 @@ class ManageResourceTypesForm(forms.SelfHandlingForm):
def get_names(self, items):
return [item['name'] for item in items]
class UpdateNamespaceForm(forms.SelfHandlingForm):
public = forms.BooleanField(label=_("Public"), required=False)
protected = forms.BooleanField(label=_("Protected"), required=False)
def __init__(self, request, *args, **kwargs):
super(UpdateNamespaceForm, self).__init__(request, *args, **kwargs)
def handle(self, request, data):
try:
params = {
'visibility': 'public' if data['public'] else 'private',
'protected': data['protected']
}
glance.metadefs_namespace_update(request,
self.initial['namespace_id'],
**params)
msg = _('Namespace successfully updated.')
messages.success(request, msg)
except Exception:
msg = _('Error updating attributes for namespace.')
redirect = reverse(constants.METADATA_INDEX_URL)
exceptions.handle(request, msg, redirect=redirect)
return False
return True

View File

@@ -33,6 +33,13 @@ class ImportNamespace(tables.LinkAction):
policy_rules = (("image", "add_metadef_namespace"),)
class EditNamespace(tables.LinkAction):
name = 'update'
verbose_name = _('Edit Namespace')
url = constants.METADATA_UPDATE_URL
classes = ('ajax-modal',)
class DeleteNamespace(tables.DeleteAction):
@staticmethod
def action_present(count):
@@ -138,6 +145,7 @@ class AdminNamespacesTable(tables.DataTable):
table_actions = (AdminMetadataFilterAction,
ImportNamespace,
DeleteNamespace,)
row_actions = (ManageResourceTypeAssociations,
row_actions = (EditNamespace,
ManageResourceTypeAssociations,
DeleteNamespace,)
pagination_param = "namespace_marker"

View File

@@ -0,0 +1,18 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-header %}{% trans "Edit Namespace Attributes" %}{% endblock %}
{% block modal-body %}
<div class="left">
<fieldset>
{% include "horizon/common/_form_fields.html" %}
</fieldset>
</div>
<div class="right">
<h3>{% trans "Description:" %}</h3>
<p>
{% trans "Edit Public/Protected attributes for the namespace." %}
</p>
</div>
{% endblock %}

View File

@@ -0,0 +1,7 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Update a Metadata Namespace" %}{% endblock %}
{% block main %}
{% include 'admin/metadata_defs/_update.html' %}
{% endblock %}

View File

@@ -305,3 +305,99 @@ class MetadataDefinitionsCreateViewTest(test.BaseAdminViewTests):
self.assertFormError(res, "form", None, ['No input was provided for '
'the namespace content.'])
class MetadataDefinitionsUpdateViewTest(test.BaseAdminViewTests):
@test.create_stubs({api.glance: ('metadefs_namespace_get', )})
def test_admin_metadata_defs_update_namespace_get(self):
namespace = self.metadata_defs.first()
api.glance.metadefs_namespace_get(
IsA(http.HttpRequest),
namespace["namespace"]).AndReturn(namespace)
self.mox.ReplayAll()
res = self.client.get(reverse(
constants.METADATA_UPDATE_URL,
args=[namespace['namespace']]))
self.assertTemplateUsed(res, constants.METADATA_UPDATE_TEMPLATE)
@test.create_stubs({api.glance: ('metadefs_namespace_get',)})
def test_admin_metadata_defs_update_namespace_get_exception(self):
namespace = self.metadata_defs.first()
api.glance.metadefs_namespace_get(
IsA(http.HttpRequest),
namespace["namespace"]).AndRaise(self.exceptions.glance)
self.mox.ReplayAll()
res = self.client.get(reverse(
constants.METADATA_UPDATE_URL,
args=[namespace['namespace']]))
self.assertRedirectsNoFollow(res,
reverse(constants.METADATA_INDEX_URL))
@test.create_stubs({api.glance: ('metadefs_namespace_get',
'metadefs_namespace_update',)})
def test_admin_metadata_defs_update_namespace_post(self):
namespace = self.metadata_defs.first()
params = {
'visibility': namespace.visibility,
'protected': namespace.protected
}
api.glance.metadefs_namespace_update(
IsA(http.HttpRequest),
namespace.namespace,
**params).AndReturn(namespace)
api.glance.metadefs_namespace_get(
IsA(http.HttpRequest),
namespace["namespace"]).AndReturn(namespace)
self.mox.ReplayAll()
form_data = {
'namespace_id': namespace.namespace,
'public': True,
'protected': True
}
url = reverse(constants.METADATA_UPDATE_URL,
args=[namespace.namespace])
res = self.client.post(url, form_data)
self.assertRedirectsNoFollow(res,
reverse(constants.METADATA_INDEX_URL))
@test.create_stubs({api.glance: ('metadefs_namespace_get',
'metadefs_namespace_update',)})
def test_admin_metadata_defs_update_namespace_post_exception(self):
namespace = self.metadata_defs.first()
params = {
'visibility': namespace.visibility,
'protected': namespace.protected
}
api.glance.metadefs_namespace_update(
IsA(http.HttpRequest),
namespace.namespace,
**params).AndRaise(self.exceptions.glance)
api.glance.metadefs_namespace_get(
IsA(http.HttpRequest),
namespace["namespace"]).AndReturn(namespace)
self.mox.ReplayAll()
form_data = {
'namespace_id': namespace.namespace,
'public': True,
'protected': True
}
url = reverse(constants.METADATA_UPDATE_URL,
args=[namespace.namespace])
res = self.client.post(url, form_data)
self.assertRedirectsNoFollow(res,
reverse(constants.METADATA_INDEX_URL))

View File

@@ -23,6 +23,7 @@ NAMESPACES = r'^(?P<namespace_id>[^/]+)/%s$'
urlpatterns = [
url(r'^$', views.AdminIndexView.as_view(), name='index'),
url(r'^create/$', views.CreateView.as_view(), name='create'),
url(NAMESPACES % 'update', views.UpdateView.as_view(), name='update'),
url(NAMESPACES % 'detail', views.DetailView.as_view(), name='detail'),
url(r'^(?P<id>[^/]+)/resource_types/$',
views.ManageResourceTypes.as_view(), name='resource_types'),

View File

@@ -14,6 +14,7 @@
import json
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse_lazy
from django.utils.translation import ugettext_lazy as _
@@ -89,6 +90,42 @@ class CreateView(forms.ModalFormView):
submit_label = _("Import Namespace")
class UpdateView(forms.ModalFormView):
form_class = admin_forms.UpdateNamespaceForm
form_id = 'update_namespace_attributes_form'
template_name = constants.METADATA_UPDATE_TEMPLATE
context_object_name = 'namespace'
success_url = reverse_lazy(constants.METADATA_INDEX_URL)
submit_url = constants.METADATA_UPDATE_URL
page_title = _("Edit Metadata Namespace")
submit_label = _("Save Changes")
def get_context_data(self, **kwargs):
context = super(UpdateView, self).get_context_data(**kwargs)
args = (self.kwargs['namespace_id'],)
context["namespace_id"] = self.kwargs['namespace_id']
context["submit_url"] = reverse(self.submit_url, args=args)
return context
def _get_object(self, *args, **kwargs):
namespace_id = self.kwargs['namespace_id']
try:
return glance.metadefs_namespace_get(self.request, namespace_id)
except Exception:
redirect = self.success_url
msg = _('Unable to retrieve namespace details.')
exceptions.handle(self.request, msg, redirect=redirect)
def get_initial(self):
namespace = self._get_object()
visibility = \
True if namespace['visibility'] == 'public' else False
return {'namespace_id': namespace['namespace'],
'public': visibility,
'protected': namespace['protected'],
}
class DetailView(tabs.TabView):
redirect_url = constants.METADATA_INDEX_URL
tab_group_class = admin_tabs.NamespaceDetailTabs