From 5e8b4880aba9a262bd95a01298acd986cc4944b7 Mon Sep 17 00:00:00 2001
From: Vincent Simonin <vincent.simonin@ext.ec.europa.eu>
Date: Mon, 3 Jul 2023 18:38:39 +0200
Subject: [PATCH] Add HTTP Header UI

* Add HTTP Header for a Mapping
* Remove / edit HTTP Header
---
 .../netbox_rps_plugin/__init__.py             |  2 +-
 .../netbox_rps_plugin/filtersets.py           | 19 +++++++-
 .../netbox_rps_plugin/forms.py                | 17 ++++++-
 .../netbox_rps_plugin/models.py               |  3 ++
 .../netbox_rps_plugin/navigation.py           |  1 -
 .../netbox_rps_plugin/tables.py               |  3 +-
 .../netbox_rps_plugin/accesslist.html         | 44 -------------------
 .../netbox_rps_plugin/httpheader/child.html   | 40 +++++++++++++++++
 .../templates/netbox_rps_plugin/mapping.html  |  6 +++
 .../netbox_rps_plugin/urls.py                 |  8 +++-
 .../netbox_rps_plugin/views.py                | 31 ++++++++++---
 plugins/netbox-rps-plugin/setup.py            |  2 +-
 12 files changed, 119 insertions(+), 57 deletions(-)
 delete mode 100644 plugins/netbox-rps-plugin/netbox_rps_plugin/templates/netbox_rps_plugin/accesslist.html
 create mode 100644 plugins/netbox-rps-plugin/netbox_rps_plugin/templates/netbox_rps_plugin/httpheader/child.html

diff --git a/plugins/netbox-rps-plugin/netbox_rps_plugin/__init__.py b/plugins/netbox-rps-plugin/netbox_rps_plugin/__init__.py
index 1cec540..c4fcc6c 100644
--- a/plugins/netbox-rps-plugin/netbox_rps_plugin/__init__.py
+++ b/plugins/netbox-rps-plugin/netbox_rps_plugin/__init__.py
@@ -5,7 +5,7 @@ class NetBoxRpsConfig(PluginConfig):
     name = 'netbox_rps_plugin'
     verbose_name = 'NetBox RPS'
     description = 'A Netbox plugin to add RPS resources'
-    version = '0.7.1'
+    version = '0.8.0'
     author = "Vincent Simonin"
     author_email = "vincent.simonin@ext.ec.europa.eu"
     base_url = 'rps'
diff --git a/plugins/netbox-rps-plugin/netbox_rps_plugin/filtersets.py b/plugins/netbox-rps-plugin/netbox_rps_plugin/filtersets.py
index f67f8cf..b7658fe 100644
--- a/plugins/netbox-rps-plugin/netbox_rps_plugin/filtersets.py
+++ b/plugins/netbox-rps-plugin/netbox_rps_plugin/filtersets.py
@@ -1,5 +1,5 @@
 from netbox.filtersets import NetBoxModelFilterSet
-from .models import Mapping
+from .models import Mapping, HttpHeader
 from django.db.models import Q
 
 class MappingFilterSet(NetBoxModelFilterSet):
@@ -9,5 +9,20 @@ class MappingFilterSet(NetBoxModelFilterSet):
         fields = ('id', 'authentication', 'source', 'target', 'Comment')
 
     def search(self, queryset, name, value):
-        return queryset.filter( Q(source__icontains=value) | Q(target__icontains=value) 
+        return queryset.filter( Q(source__icontains=value) | Q(target__icontains=value)
                                | Q(authentication__icontains=value) | Q(Comment__icontains=value))
+
+class HttpHeaderFilterSet(NetBoxModelFilterSet):
+
+    class Meta:
+        model = HttpHeader
+        fields = ('id', 'name', 'value', 'apply_to')
+
+    def search(self, queryset, name, value):
+        """Perform the filtered search."""
+        if not value.strip():
+            return queryset
+        qs_filter = (
+            Q(name__icontains=value)
+        )
+        return queryset.filter(qs_filter)
diff --git a/plugins/netbox-rps-plugin/netbox_rps_plugin/forms.py b/plugins/netbox-rps-plugin/netbox_rps_plugin/forms.py
index 379c63c..abb293e 100644
--- a/plugins/netbox-rps-plugin/netbox_rps_plugin/forms.py
+++ b/plugins/netbox-rps-plugin/netbox_rps_plugin/forms.py
@@ -1,7 +1,7 @@
 from django import forms
 from django.utils.translation import gettext as _
 from netbox.forms import NetBoxModelForm, NetBoxModelFilterSetForm, NetBoxModelImportForm
-from .models import Mapping, AuthenticationChoices
+from .models import Mapping, AuthenticationChoices, HttpHeader
 
 
 class MappingForm(NetBoxModelForm):
@@ -33,3 +33,18 @@ class MappingImportForm(NetBoxModelImportForm):
     class Meta:
         model = Mapping
         fields = ('source', 'target', 'authentication', 'testingpage', 'webdav')
+
+
+class HttpHeaderForm(NetBoxModelForm):
+
+    class Meta:
+        model = HttpHeader
+        fields = (
+            'mapping', 'name', 'value', 'apply_to'
+        )
+        labels = {
+            'mapping': 'Mapping',
+            'name': 'Name',
+            'value': 'Value',
+            'apply_to': 'Apply to',
+        }
diff --git a/plugins/netbox-rps-plugin/netbox_rps_plugin/models.py b/plugins/netbox-rps-plugin/netbox_rps_plugin/models.py
index f086f88..c678b16 100644
--- a/plugins/netbox-rps-plugin/netbox_rps_plugin/models.py
+++ b/plugins/netbox-rps-plugin/netbox_rps_plugin/models.py
@@ -101,3 +101,6 @@ class HttpHeader(NetBoxModel):
     class Meta:
         unique_together = ['mapping', 'name', 'apply_to']
         ordering = ['name']
+
+    def __str__(self):
+        return self.name
diff --git a/plugins/netbox-rps-plugin/netbox_rps_plugin/navigation.py b/plugins/netbox-rps-plugin/netbox_rps_plugin/navigation.py
index ed91ca5..f6aa3b2 100644
--- a/plugins/netbox-rps-plugin/netbox_rps_plugin/navigation.py
+++ b/plugins/netbox-rps-plugin/netbox_rps_plugin/navigation.py
@@ -32,4 +32,3 @@ menu = (
         icon_class='mdi mdi-graph-outline'
     )
 )
-
diff --git a/plugins/netbox-rps-plugin/netbox_rps_plugin/tables.py b/plugins/netbox-rps-plugin/netbox_rps_plugin/tables.py
index 9920a02..de29258 100644
--- a/plugins/netbox-rps-plugin/netbox_rps_plugin/tables.py
+++ b/plugins/netbox-rps-plugin/netbox_rps_plugin/tables.py
@@ -21,11 +21,12 @@ class HttpHeaderTable(NetBoxTable):
     mapping = tables.Column(
         linkify=True
     )
+
     class Meta(NetBoxTable.Meta):
         model = HttpHeader
         fields = (
             'pk', 'id', 'name', 'value', 'apply_to', 'mapping'
         )
         default_columns = (
-            'name', 'value'
+            'name', 'value', 'apply_to'
         )
diff --git a/plugins/netbox-rps-plugin/netbox_rps_plugin/templates/netbox_rps_plugin/accesslist.html b/plugins/netbox-rps-plugin/netbox_rps_plugin/templates/netbox_rps_plugin/accesslist.html
deleted file mode 100644
index 56972de..0000000
--- a/plugins/netbox-rps-plugin/netbox_rps_plugin/templates/netbox_rps_plugin/accesslist.html
+++ /dev/null
@@ -1,44 +0,0 @@
-{% extends 'generic/object.html' %}
-{% load render_table from django_tables2 %}
-
-{% block content %}
-  <div class="row mb-3">
-    <div class="col col-md-6">
-      <div class="card">
-        <h5 class="card-header">Access List</h5>
-        <div class="card-body">
-          <table class="table table-hover attr-table">
-            <tr>
-              <th scope="row">Name</th>
-              <td>{{ object.name }}</td>
-            </tr>
-            <tr>
-              <th scope="row">Default Action</th>
-              <td>{{ object.get_default_action_display }}</td>
-            </tr>
-            <tr>
-              <th scope="row">Rules</th>
-              <td>{{ object.rules.count }}</td>
-            </tr>
-          </table>
-        </div>
-      </div>
-      {% include 'inc/panels/custom_fields.html' %}
-    </div>
-    <div class="col col-md-6">
-      {% include 'inc/panels/tags.html' %}
-      {% include 'inc/panels/comments.html' %}
-    </div>
-  </div>
-
-  <div class="row">
-      <div class="col col-md-12">
-        <div class="card">
-          <h5 class="card-header">Rules</h5>
-          <div class="card-body table-responsive">
-            {% render_table rules_table %}
-          </div>
-        </div>
-      </div>
-    </div>
-{% endblock content %}
diff --git a/plugins/netbox-rps-plugin/netbox_rps_plugin/templates/netbox_rps_plugin/httpheader/child.html b/plugins/netbox-rps-plugin/netbox_rps_plugin/templates/netbox_rps_plugin/httpheader/child.html
new file mode 100644
index 0000000..031ee55
--- /dev/null
+++ b/plugins/netbox-rps-plugin/netbox_rps_plugin/templates/netbox_rps_plugin/httpheader/child.html
@@ -0,0 +1,40 @@
+{% extends 'generic/object.html' %}
+{% load helpers %}
+{% load render_table from django_tables2 %}
+{% load perms %}
+
+{% block extra_controls %}
+  <a href="{% url 'plugins:netbox_rps_plugin:httpheader_add' %}?mapping={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-sm btn-primary">
+    <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add HTTP Header
+  </a>
+{% endblock %}
+
+{% block content %}
+    {% include 'inc/table_controls_htmx.html' with table_modal="HttpHeaderTable_config" %}
+
+    <form method="post">
+        {% csrf_token %}
+        <input type="hidden" name="return_url" value="{% if return_url %}{{ return_url }}{% else %}{{ request.path }}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}{% endif %}" />
+
+        <div class="card">
+            <div class="card-body htmx-container table-responsive" id="object_list">
+                {% include 'htmx/table.html' %}
+            </div>
+        </div>
+        <div class="noprint bulk-buttons">
+            <div class="bulk-button-group">
+              {% if 'bulk_delete' in actions %}
+                <button type="submit" name="_delete" formaction="{% url 'plugins:netbox_rps_plugin:httpheaders_bulk_delete' %}?return_url={% url 'plugins:netbox_rps_plugin:mapping_httpheader' pk=object.pk %}" class="btn btn-danger btn-sm">
+                  <i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
+                </button>
+              {% endif %}
+            </div>
+          </div>
+    </form>
+
+{% endblock %}
+
+{% block modals %}
+    {{ block.super }}
+    {% table_config_form table %}
+{% endblock modals %}
diff --git a/plugins/netbox-rps-plugin/netbox_rps_plugin/templates/netbox_rps_plugin/mapping.html b/plugins/netbox-rps-plugin/netbox_rps_plugin/templates/netbox_rps_plugin/mapping.html
index 3f148bd..980d6d9 100644
--- a/plugins/netbox-rps-plugin/netbox_rps_plugin/templates/netbox_rps_plugin/mapping.html
+++ b/plugins/netbox-rps-plugin/netbox_rps_plugin/templates/netbox_rps_plugin/mapping.html
@@ -1,5 +1,11 @@
 {% extends 'generic/object.html' %}
 
+{% block extra_controls %}
+  <a href="{% url 'plugins:netbox_rps_plugin:httpheader_add' %}?mapping={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-sm btn-primary">
+    <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add HTTP Header
+  </a>
+{% endblock %}
+
 {% block content %}
   <div class="row mb-3">
     <div class="col col-md-6">
diff --git a/plugins/netbox-rps-plugin/netbox_rps_plugin/urls.py b/plugins/netbox-rps-plugin/netbox_rps_plugin/urls.py
index d1c7d51..47afe99 100644
--- a/plugins/netbox-rps-plugin/netbox_rps_plugin/urls.py
+++ b/plugins/netbox-rps-plugin/netbox_rps_plugin/urls.py
@@ -19,5 +19,11 @@ urlpatterns = (
     }),
 
     # HTTP Headers
-    #path('http-headers/', views.MappingHttpHeadersView.as_view(), name='httpheaders_list'),
+    path('http-headers/add/', views.HttpHeaderEditView.as_view(), name='httpheader_add'),
+    path('http-headers/delete/', views.HttpHeaderBulkDeleteView.as_view(), name='httpheaders_bulk_delete'),
+    path('http-headers/<int:pk>/edit/', views.HttpHeaderEditView.as_view(), name='httpheader_edit'),
+    path('http-headers/<int:pk>/delete/', views.HttpHeaderDeleteView.as_view(), name='httpheader_delete'),
+    path('http-headers/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='httpheader_changelog', kwargs={
+        'model': models.HttpHeader
+    }),
 )
diff --git a/plugins/netbox-rps-plugin/netbox_rps_plugin/views.py b/plugins/netbox-rps-plugin/netbox_rps_plugin/views.py
index 0c6474e..5211b30 100644
--- a/plugins/netbox-rps-plugin/netbox_rps_plugin/views.py
+++ b/plugins/netbox-rps-plugin/netbox_rps_plugin/views.py
@@ -5,7 +5,7 @@ from utilities.views import ViewTab, register_model_view
 from django.utils.translation import gettext as _
 
 class MappingView(generic.ObjectView):
-    queryset = models.Mapping.objects.all()
+    queryset = models.Mapping.objects.all().prefetch_related('http_headers')
 
 
 class MappingListView(generic.ObjectListView):
@@ -35,14 +35,35 @@ class MappingBulkDeleteView(generic.BulkDeleteView):
     table = tables.MappingTable
 
 
-#@register_model_view(models.Mapping, 'HTTP Headers', path='http-headers')
+@register_model_view(models.Mapping, 'httpheader')
 class MappingHttpHeadersView(generic.ObjectChildrenView):
-    queryset = models.Mapping.objects.all()
+    queryset = models.Mapping.objects.all().prefetch_related('http_headers')
     child_model = models.HttpHeader
     table = tables.HttpHeaderTable
+    filterset = filtersets.HttpHeaderFilterSet
+    template_name = "netbox_rps_plugin/httpheader/child.html"
+    hide_if_empty = False
+
     tab = ViewTab(
-        label=_('HTTP Headers')
+        label=_('HTTP Headers'),
+        badge=lambda obj: obj.http_headers.count(),
+        hide_if_empty=False,
     )
 
     def get_children(self, request, parent):
-        return models.HttpHeader.objects.restrict(request.user, 'view').filter(mapping=parent)
+        return parent.http_headers
+
+
+class HttpHeaderEditView(generic.ObjectEditView):
+    queryset = models.HttpHeader.objects.all()
+    form = forms.HttpHeaderForm
+
+
+class HttpHeaderDeleteView(generic.ObjectDeleteView):
+    queryset = models.HttpHeader.objects.all()
+
+
+class HttpHeaderBulkDeleteView(generic.BulkDeleteView):
+    queryset = models.HttpHeader.objects.all()
+    filterset = filtersets.HttpHeaderFilterSet
+    table = tables.HttpHeaderTable
diff --git a/plugins/netbox-rps-plugin/setup.py b/plugins/netbox-rps-plugin/setup.py
index ee5203b..74d03c9 100644
--- a/plugins/netbox-rps-plugin/setup.py
+++ b/plugins/netbox-rps-plugin/setup.py
@@ -2,7 +2,7 @@ from setuptools import find_packages, setup
 
 setup(
     name='netbox-rps-plugin',
-    version='0.7.1',
+    version='0.8.0',
     description='A Netbox plugin to add RPS resources',
     install_requires=[],
     packages=find_packages(),
-- 
GitLab