12591 restore view

This commit is contained in:
Arthur 2023-06-16 11:32:54 -07:00
parent cd5f3cc318
commit 68341c55b3
4 changed files with 122 additions and 0 deletions

View File

@ -31,11 +31,20 @@ IMAGEATTACHMENT_IMAGE = '''
{% endif %} {% endif %}
''' '''
REVISION_BUTTONS = """
{% if not record.is_active %}
<a href="{% url 'extras:configrevision_restore' pk=record.pk %}" class="btn btn-sm btn-primary" title="Restore config">
<i class="mdi mdi-file-restore"></i>
</a>
{% endif %}
"""
class ConfigRevisionTable(NetBoxTable): class ConfigRevisionTable(NetBoxTable):
is_active = columns.BooleanColumn() is_active = columns.BooleanColumn()
actions = columns.ActionsColumn( actions = columns.ActionsColumn(
actions=('edit', 'delete'), actions=('edit', 'delete'),
extra_buttons=REVISION_BUTTONS
) )
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):

View File

@ -120,5 +120,6 @@ urlpatterns = [
path('config-revision/', views.ConfigRevisionListView.as_view(), name='configrevision'), path('config-revision/', views.ConfigRevisionListView.as_view(), name='configrevision'),
path('config-revision/add/', views.ConfigRevisionEditView.as_view(), name='configrevision_add'), path('config-revision/add/', views.ConfigRevisionEditView.as_view(), name='configrevision_add'),
path('config-revision/delete/', views.ConfigRevisionBulkDeleteView.as_view(), name='configrevision_bulk_delete'), path('config-revision/delete/', views.ConfigRevisionBulkDeleteView.as_view(), name='configrevision_bulk_delete'),
path('config-revision/<int:pk>/restore', views.ConfigRevisionRestoreView.as_view(), name='configrevision_restore'),
path('config-revision/<int:pk>/', include(get_model_urls('extras', 'configrevision'))), path('config-revision/<int:pk>/', include(get_model_urls('extras', 'configrevision'))),
] ]

View File

@ -14,6 +14,7 @@ from core.models import Job
from core.tables import JobTable from core.tables import JobTable
from extras.dashboard.forms import DashboardWidgetAddForm, DashboardWidgetForm from extras.dashboard.forms import DashboardWidgetAddForm, DashboardWidgetForm
from extras.dashboard.utils import get_widget_class from extras.dashboard.utils import get_widget_class
from netbox.config import get_config, PARAMS
from netbox.views import generic from netbox.views import generic
from utilities.forms import ConfirmationForm, get_field_value from utilities.forms import ConfirmationForm, get_field_value
from utilities.htmx import is_htmx from utilities.htmx import is_htmx
@ -1223,3 +1224,39 @@ class ConfigRevisionBulkDeleteView(generic.BulkDeleteView):
queryset = ConfigRevision.objects.all() queryset = ConfigRevision.objects.all()
filterset = filtersets.ConfigRevisionFilterSet filterset = filtersets.ConfigRevisionFilterSet
table = tables.ConfigRevisionTable table = tables.ConfigRevisionTable
class ConfigRevisionRestoreView(ContentTypePermissionRequiredMixin, View):
def get_required_permission(self):
return 'extras.configrevision_edit'
def get(self, request, pk):
candidate_config = get_object_or_404(ConfigRevision, pk=pk)
# Get the current ConfigRevision
config_version = get_config().version
current_config = ConfigRevision.objects.filter(pk=config_version).first()
params = []
for param in PARAMS:
params.append((
param.name,
current_config.data.get(param.name, None),
candidate_config.data.get(param.name, None)
))
return render(request, 'extras/configrevision_restore.html', {
'object': candidate_config,
'params': params,
})
def post(self, request, pk):
if not request.user.has_perm('extras.configrevision_edit'):
return HttpResponseForbidden()
candidate_config = get_object_or_404(ConfigRevision, pk=pk)
candidate_config.activate()
self.message_user(request, f"Restored configuration revision #{pk}")
return redirect(reverse('admin:extras_configrevision_changelist'))

View File

@ -0,0 +1,75 @@
{% extends 'base/layout.html' %}
{% load helpers %}
{% load buttons %}
{% load perms %}
{% load static %}
{% block title %}{{ script }}{% endblock %}
{% block subtitle %}
{{ script.Meta.description|markdown }}
{% endblock %}
{% block header %}
<div class="row noprint">
<div class="col col-md-12">
<nav class="breadcrumb-container px-3" aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'extras:script_list' %}">Scripts</a></li>
</ol>
</nav>
</div>
</div>
{{ block.super }}
{% endblock header %}
{% block controls %}
<div class="controls">
<div class="control-group">
{% if request.user|can_delete:job %}
{% delete_button job %}
{% endif %}
</div>
</div>
{% endblock controls %}
{% block content %}
<ul class="nav nav-tabs px-3" role="tablist">
<li class="nav-item" role="presentation">
<a href="#log" role="tab" data-bs-toggle="tab" class="nav-link active">Log</a>
</li>
</ul>
<div class="tab-content mb-3">
<div role="tabpanel" class="tab-pane active" id="log">
<div class="row">
<p>Restore configuration #{{ object.pk }} from <strong>{{ object.created }}</strong>?</p>
<table class="table table-striped table-hover">
<thead>
<tr>
<th scope="col">Parameter</th>
<th scope="col">Current Value</th>
<th scope="col">New Value</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{% for param, current, new in params %}
<tr{% if current != new %} class="table-warning"{% endif %}>
<td>{{ param }}</td>
<td>{{ current }}</td>
<td>{{ new }}</td>
<td>{% if current != new %}<img src="{% static 'admin/img/icon-changelink.svg' %}" alt="*" title="Changed">{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock content %}
{% block modals %}
{% endblock modals %}