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}}
+
+
+{% 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.