diff --git a/conntrackt/templates/conntrackt/entity_detail.html b/conntrackt/templates/conntrackt/entity_detail.html --- a/conntrackt/templates/conntrackt/entity_detail.html +++ b/conntrackt/templates/conntrackt/entity_detail.html @@ -11,9 +11,18 @@

{{entity.name}}

-
+ +{% if entity.description %}
+ {{entity.description}} +
+
+
+{% endif %} +
+
+ {% html_link "Edit" "entity_update" entity.id class="btn btn-primary" %} {% html_link "Remove" "entity_delete" entity.id class="btn btn-primary" %}
diff --git a/conntrackt/templates/conntrackt/entity_update_form.html b/conntrackt/templates/conntrackt/entity_update_form.html new file mode 100644 --- /dev/null +++ b/conntrackt/templates/conntrackt/entity_update_form.html @@ -0,0 +1,25 @@ +{% extends "conntrackt/base.html" %} + +{# For html_link #} +{% load conntrackt_tags %} +{# For Bootstrapped forms #} +{% load crispy_forms_tags %} + +{% block content %} +
+

Edit entity {{entity.name}}

+
+
+
+
+
+ {% csrf_token %} + {{ form | crispy }} +
+
+ +
+
+
+
+{% endblock content %} diff --git a/conntrackt/templates/conntrackt/location_widget.html b/conntrackt/templates/conntrackt/location_widget.html --- a/conntrackt/templates/conntrackt/location_widget.html +++ b/conntrackt/templates/conntrackt/location_widget.html @@ -4,11 +4,13 @@ {{location.name}} {% html_link '' 'project_location_iptables' project.id location.id class="btn btn-link" %} + {% for entity in entities %} {% html_link entity.name 'entity' entity.id class="btn btn-link" %} {% html_link '' 'entity_iptables' entity.id class="btn btn-link" %} + {% html_link '' 'entity_update' entity.id class="btn btn-link" %} {% html_link '' 'entity_delete' entity.id class="btn btn-link" %} {% endfor %} @@ -17,6 +19,7 @@ {% html_link "Add entity" "entity_create" class="btn btn-primary btn-mini" get="project="|add:project_id|add:"&location="|add:location_id %} + {% endwith %} diff --git a/conntrackt/tests/test_views.py b/conntrackt/tests/test_views.py --- a/conntrackt/tests/test_views.py +++ b/conntrackt/tests/test_views.py @@ -929,3 +929,53 @@ class EntityDeleteViewTest(TestCase): follow=True) self.assertEqual(response.context["request"].META["PATH_INFO"], reverse("project", args=(1,))) + + +class EntityUpdateViewTest(TestCase): + + fixtures = ['test-data.json'] + + def setUp(self): + # Set-up web client. + self.client = Client() + + # Set-up users with different view permissions. + self.user = {} + self.user["fullperms"] = User.objects.create_user("fullperms", "fullperms@example.com", "fullperms") + self.user["fullperms"].user_permissions.add(Permission.objects.get(codename="change_entity")) + self.user["noperms"] = User.objects.create_user("noperms", "noperms@example.com", "noperms") + + def test_permission_denied(self): + """ + Tests if permission will be denied for client without sufficient privileges. + """ + + self.client.login(username="noperms", password="noperms") + + response = self.client.get(reverse("entity_update", args=(1,))) + + self.assertContains(response, "You have insufficient privileges to access this resource. Please contact your local system administrator if you believe you should have been granted access.", status_code=403) + + def test_permission_granted(self): + """ + Tests if permission will be granted for user with correct privileges. + """ + + self.client.login(username="fullperms", password="fullperms") + + response = self.client.get(reverse("entity_update", args=(1,))) + + self.assertEqual(response.status_code, 200) + + def test_content(self): + """ + Tests if the form comes pre-populated with proper content. + """ + + self.client.login(username="fullperms", password="fullperms") + + response = self.client.get(reverse("entity_update", args=(1,))) + + self.assertContains(response, ">Edit entity Test Entity 1<") + self.assertContains(response, 'value="Test Entity 1"') + self.assertContains(response, "This is a test entity 1.") diff --git a/conntrackt/urls.py b/conntrackt/urls.py --- a/conntrackt/urls.py +++ b/conntrackt/urls.py @@ -6,7 +6,7 @@ from django.contrib.auth.views import lo from .views import IndexView, EntityView, entity_iptables, project_iptables from .views import ProjectView, ProjectCreateView, ProjectUpdateView, ProjectDeleteView from .views import LocationCreateView, LocationUpdateView, LocationDeleteView -from .views import EntityCreateView, EntityDeleteView +from .views import EntityCreateView, EntityUpdateView, EntityDeleteView urlpatterns = patterns( @@ -37,6 +37,8 @@ urlpatterns = patterns( name='entity'), # View for creating a new entity. url(r'^entity/add/$', EntityCreateView.as_view(), name="entity_create"), + # View for updating an existing entity. + url(r'^entity/(?P\d+)/edit/$', EntityUpdateView.as_view(), name="entity_update"), # View for deleting an entity. url(r'^entity/(?P\d+)/remove/$', EntityDeleteView.as_view(), name="entity_delete"), diff --git a/conntrackt/views.py b/conntrackt/views.py --- a/conntrackt/views.py +++ b/conntrackt/views.py @@ -475,6 +475,24 @@ class EntityCreateView(MultiplePermissio return initial +class EntityUpdateView(MultiplePermissionsRequiredMixin, UpdateView): + """ + View for updating an existing entity. + """ + + model = Entity + form_class = EntityForm + template_name_suffix = "_update_form" + + # Required permissions. + permissions = { + "all": ("conntrackt.change_entity",), + } + + # Raise authorisation denied exception for unmet permissions. + raise_exception = True + + class EntityDeleteView(MultiplePermissionsRequiredMixin, DeleteView): """ View for deleting an entity.