simpler state change and action input
This commit is contained in:
parent
63b8815d1b
commit
070004f9ea
|
@ -2,21 +2,15 @@ from django import forms
|
||||||
from .models import State
|
from .models import State
|
||||||
|
|
||||||
|
|
||||||
class AddStateForm(forms.Form):
|
class ChangeStateForm(forms.Form):
|
||||||
add_note = forms.BooleanField(required=False)
|
previous_state = forms.CharField(widget=forms.HiddenInput())
|
||||||
|
snapshot_uuid = forms.UUIDField(widget=forms.HiddenInput())
|
||||||
|
new_state = forms.CharField(widget=forms.HiddenInput())
|
||||||
|
|
||||||
|
|
||||||
|
class AddNoteForm(forms.Form):
|
||||||
|
snapshot_uuid = forms.UUIDField(widget=forms.HiddenInput())
|
||||||
note = forms.CharField(
|
note = forms.CharField(
|
||||||
required=False,
|
required=True,
|
||||||
widget=forms.Textarea(attrs={'rows': 4, 'maxlength': 200, 'placeholder': 'Max 200 characters'}),
|
widget=forms.Textarea(attrs={'rows': 4, 'maxlength': 200, 'placeholder': 'Max 200 characters'}),
|
||||||
)
|
)
|
||||||
state_id = forms.IntegerField(required=True, widget=forms.HiddenInput())
|
|
||||||
snapshot_uuid = forms.UUIDField(required=True, widget=forms.HiddenInput())
|
|
||||||
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
cleaned_data = super().clean()
|
|
||||||
add_note = cleaned_data.get('add_note')
|
|
||||||
note = cleaned_data.get('note')
|
|
||||||
|
|
||||||
if add_note == True and not note:
|
|
||||||
self.add_error('note', 'Please enter a note if you checked "Add a note".')
|
|
||||||
return cleaned_data
|
|
|
@ -58,7 +58,7 @@ class Note(models.Model):
|
||||||
institution = models.ForeignKey(Institution, on_delete=models.CASCADE)
|
institution = models.ForeignKey(Institution, on_delete=models.CASCADE)
|
||||||
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
|
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
|
||||||
date = models.DateTimeField(auto_now_add=True)
|
date = models.DateTimeField(auto_now_add=True)
|
||||||
description = models.TextField(max_lenght=250)
|
description = models.TextField()
|
||||||
snapshot_uuid = models.UUIDField()
|
snapshot_uuid = models.UUIDField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -5,7 +5,8 @@ app_name = 'action'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|
||||||
path("new/", views.NewActionView.as_view(), name="new_action"),
|
path("new/", views.ChangeStateView.as_view(), name="change_state"),
|
||||||
path('state/<int:pk>/undo/', views.ActionUndoView.as_view(), name='undo_action'),
|
path('state/<int:pk>/undo/', views.UndoStateView.as_view(), name='undo_state'),
|
||||||
|
path('note/add/', views.AddNoteView.as_view(), name='add_note'),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from django.shortcuts import redirect, get_object_or_404
|
from django.shortcuts import redirect, get_object_or_404
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from action.forms import AddStateForm
|
from action.forms import ChangeStateForm, AddNoteForm
|
||||||
from django.views.generic.edit import DeleteView
|
from django.views.generic.edit import DeleteView, CreateView, FormView
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from action.models import State, StateDefinition
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from action.models import State, StateDefinition, Note
|
||||||
from device.models import Device
|
from device.models import Device
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -12,35 +13,34 @@ import logging
|
||||||
device_logger = logging.getLogger('device_log')
|
device_logger = logging.getLogger('device_log')
|
||||||
|
|
||||||
|
|
||||||
class NewActionView(View):
|
class ChangeStateView(View):
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
form = AddStateForm(request.POST)
|
form = ChangeStateForm(request.POST)
|
||||||
|
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
state_definition_id = form.cleaned_data['state_id']
|
previous_state = form.cleaned_data['previous_state']
|
||||||
state_definition = get_object_or_404(StateDefinition, pk=state_definition_id)
|
new_state = form.cleaned_data['new_state']
|
||||||
snapshot_uuid = form.cleaned_data['snapshot_uuid']
|
snapshot_uuid = form.cleaned_data['snapshot_uuid']
|
||||||
#TODO: implement notes
|
|
||||||
note = form.cleaned_data.get('note', '')
|
|
||||||
|
|
||||||
state = State.objects.create(
|
State.objects.create(
|
||||||
snapshot_uuid=snapshot_uuid,
|
snapshot_uuid=snapshot_uuid,
|
||||||
state=state_definition.state,
|
state=new_state,
|
||||||
user=request.user,
|
user=self.request.user,
|
||||||
institution=request.user.institution,
|
institution=self.request.user.institution,
|
||||||
)
|
)
|
||||||
#TODO: also change logger for full fledged table
|
|
||||||
device_logger.info(f"<Updated> State to '{state_definition.state}', for device '{snapshot_uuid}') by user {self.request.user}.")
|
|
||||||
|
|
||||||
messages.success(request, f"Action to '{state_definition.state}' has been added.")
|
device_logger.info(f"<Updated> State to '{new_state}', from '{previous_state}' ) by user {self.request.user}.")
|
||||||
return redirect(request.META.get('HTTP_REFERER'))
|
|
||||||
|
message = _("State changed from '{}' to '{}'.".format(previous_state, new_state) )
|
||||||
|
messages.success(request,message)
|
||||||
else:
|
else:
|
||||||
messages.error(request, "There was an error with your submission.")
|
messages.error(request, "There was an error with your submission.")
|
||||||
return redirect(request.META.get('HTTP_REFERER'))
|
|
||||||
|
return redirect(request.META.get('HTTP_REFERER') )
|
||||||
|
|
||||||
|
|
||||||
class ActionUndoView(DeleteView):
|
class UndoStateView(DeleteView):
|
||||||
model = State
|
model = State
|
||||||
|
|
||||||
def delete(self, request, *args, **kwargs):
|
def delete(self, request, *args, **kwargs):
|
||||||
|
@ -52,3 +52,24 @@ class ActionUndoView(DeleteView):
|
||||||
messages.info(self.request, f"Action to state: {self.object.state} has been deleted.")
|
messages.info(self.request, f"Action to state: {self.object.state} has been deleted.")
|
||||||
device_logger.info(f"<Deleted> State '{self.object.state}', for device '{self.object.snapshot_uuid}') by user {self.request.user}.")
|
device_logger.info(f"<Deleted> State '{self.object.state}', for device '{self.object.snapshot_uuid}') by user {self.request.user}.")
|
||||||
return self.request.META.get('HTTP_REFERER', reverse_lazy('device:details', args=[self.object.snapshot_uuid]))
|
return self.request.META.get('HTTP_REFERER', reverse_lazy('device:details', args=[self.object.snapshot_uuid]))
|
||||||
|
|
||||||
|
|
||||||
|
class AddNoteView(View):
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
form = AddNoteForm(request.POST)
|
||||||
|
|
||||||
|
if form.is_valid():
|
||||||
|
note = form.cleaned_data['note']
|
||||||
|
snapshot_uuid = form.cleaned_data['snapshot_uuid']
|
||||||
|
Note.objects.create(
|
||||||
|
snapshot_uuid=snapshot_uuid,
|
||||||
|
description=note,
|
||||||
|
user=self.request.user,
|
||||||
|
institution=self.request.user.institution,
|
||||||
|
)
|
||||||
|
messages.success(request, _("Note has been added"))
|
||||||
|
else:
|
||||||
|
messages.error(request, "There was an error with your submission.")
|
||||||
|
|
||||||
|
return redirect(request.META.get('HTTP_REFERER') )
|
||||||
|
|
|
@ -5,36 +5,56 @@
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<h3>{{ object.shortid }}
|
<h3>{{ object.shortid }}</h3>
|
||||||
</h3>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col text-end">
|
<div class="col text-end">
|
||||||
{% if state_definitions %}
|
<div class="btn-group" role="group" aria-label="Actions">
|
||||||
<div class="dropdown">
|
|
||||||
<button class="btn btn-green-admin dropdown-toggle" type="button" id="addStateDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
<!-- change state button -->
|
||||||
{% trans "Action" %}
|
{% if state_definitions %}
|
||||||
</button>
|
<div class="dropdown ms-2">
|
||||||
<ul class="dropdown-menu" aria-labelledby="addStateDropdown">
|
<a class="btn btn-green-admin dropdown-toggle" id="addStateDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
{% for state in state_definitions %}
|
{% trans "Change state" %}
|
||||||
<li>
|
{% if device_states %}
|
||||||
<a class="dropdown-item d-flex justify-content-between align-items-center" href="#" data-bs-toggle="modal" data-bs-target="#addStateModal{{ state.id }}">
|
({{ device_states.0.state }})
|
||||||
<span class="badge bg-secondary rounded-pill-sm">{{ forloop.counter }}
|
{% endif %}
|
||||||
</span>
|
</a>
|
||||||
<span>{{ state.state }}
|
<ul class="dropdown-menu" aria-labelledby="addStateDropdown" style="width: 100%;">
|
||||||
</span>
|
{% for state in state_definitions %}
|
||||||
|
<li style="width: 100%;">
|
||||||
|
<form id="changeStateForm{{ state.id }}" method="post" action="{% url 'action:change_state' %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="previous_state" value="{{ device_states.0.state }}">
|
||||||
|
<input type="hidden" name="snapshot_uuid" value="{{ object.last_uuid }}">
|
||||||
|
<input type="hidden" name="new_state" value="{{ state.state }}">
|
||||||
|
|
||||||
|
<a class="dropdown-item d-flex justify-content-between align-items-center" href="#" onclick="document.getElementById('changeStateForm{{ state.id }}').submit(); return false;">
|
||||||
|
<span>{{ state.state }}</span>
|
||||||
|
<span class="badge bg-secondary rounded-pill-sm">{{ forloop.counter }}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</form>
|
||||||
{% endfor %}
|
</li>
|
||||||
</ul>
|
{% endfor %}
|
||||||
</div>
|
</ul>
|
||||||
{% else %}
|
</div>
|
||||||
<button class="btn btn-green-admin" type="button" disabled>
|
{% else %}
|
||||||
{% trans "Action" %}
|
<button class="btn btn-green-admin" type="button" disabled>
|
||||||
|
<i class="bi bi-plus"></i> {% trans "Change state" %}
|
||||||
|
{% if device_states %}
|
||||||
|
({{ device_states.0.state }})
|
||||||
|
{% endif %}
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
<!-- Add note button -->
|
||||||
|
<button class="btn btn-warning ms-2" type="button" data-bs-toggle="modal" data-bs-target="#addNoteModal">
|
||||||
|
<i class="bi bi-sticky"></i> {% trans "Add a note" %}
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<ul class="nav nav-tabs nav-tabs-bordered">
|
<ul class="nav nav-tabs nav-tabs-bordered">
|
||||||
|
@ -45,7 +65,7 @@
|
||||||
<a href="#log" class="nav-link" data-bs-toggle="tab" data-bs-target="#log">{% trans 'Log' %}</a>
|
<a href="#log" class="nav-link" data-bs-toggle="tab" data-bs-target="#log">{% trans 'Log' %}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a href="#user_properties" class="nav-link" data-bs-toggle="tab" data-bs-target="#user_properties">{% trans 'User properties' %}</a>
|
<a href="#user_prope rties" class="nav-link" data-bs-toggle="tab" data-bs-target="#user_properties">{% trans 'User properties' %}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a href="#documents" class="nav-link" data-bs-toggle="tab" data-bs-target="#documents">{% trans 'Documents' %}</a>
|
<a href="#documents" class="nav-link" data-bs-toggle="tab" data-bs-target="#documents">{% trans 'Documents' %}</a>
|
||||||
|
@ -84,75 +104,30 @@
|
||||||
|
|
||||||
{% include 'tabs/evidences.html' %}
|
{% include 'tabs/evidences.html' %}
|
||||||
|
|
||||||
|
<div class="modal fade" id="addNoteModal" tabindex="-1" aria-labelledby="addNoteModalLabel" aria-hidden="true">
|
||||||
<!-- add state to device - popup modal-->
|
<div class="modal-dialog">
|
||||||
{% if state_definitions %}
|
|
||||||
{% for state in state_definitions %}
|
|
||||||
<div class="modal fade" id="addStateModal{{ state.id }}" tabindex="-1" aria-labelledby="addStateModalLabel{{ state.id }}" aria-hidden="true">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
|
|
||||||
<form method="post" action="{% url 'action:new_action'%}">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="addStateModalLabel{{ state.id }}">{% trans "Summary of changes" %}</h5>
|
<h5 class="modal-title" id="addNoteModalLabel">{% trans "Add a Note" %}</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% trans 'Close' %}"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body m-1">
|
<div class="modal-body">
|
||||||
{% if device_states and device_states.0.state == state.state %}
|
<form method="post" action="{% url 'action:add_note' %}">
|
||||||
<div class="alert alert-warning d-flex align-items-center">
|
{% csrf_token %}
|
||||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
<div class="mb-3">
|
||||||
<span>{% trans "The device is already in the selected state." %}</span>
|
<input type="hidden" name="snapshot_uuid" value="{{ object.last_uuid }}">
|
||||||
</div>
|
<label for="noteDescription" class="form-label">{% trans "Note" %}</label>
|
||||||
{% endif %}
|
<textarea class="form-control" id="noteDescription" name="note" placeholder="Max 250 characters" name="note" rows="3" required></textarea>
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<i class="bi bi-arrow-right-circle text-danger me-2"></i>
|
|
||||||
|
|
||||||
<span class="text-danger fw-bold me-2">{% trans "From:" %}</span>
|
|
||||||
<span class="text-danger fw-italic">
|
|
||||||
{% if device_states %}
|
|
||||||
{{device_states.0.state}}
|
|
||||||
{% else %}
|
|
||||||
{% trans 'None' %}
|
|
||||||
{% endif %}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex align-items-center mt-2">
|
<div class="modal-footer">
|
||||||
<i class="bi bi-arrow-right-circle text-success me-2"></i>
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
|
||||||
<span class="text-success fw-bold me-2">{% trans "To:" %}</span>
|
<button type="submit" class="btn btn-green-admin">{% trans "Save Note" %}</button>
|
||||||
<span class="text-success fw-italic">{{ state.state }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
|
|
||||||
<div class="form-check form-switch mt-3 d-flex justify-content-end">
|
|
||||||
<label class="form-check-label font-monospace" for="addNoteCheckbox{{ state.id }}">
|
|
||||||
{% trans "Add a note" %}
|
|
||||||
</label>
|
|
||||||
<input class="form-check-input ms-2" type="checkbox" id="addNoteCheckbox{{ state.id }}" data-bs-toggle="collapse" data-bs-target="#collapseInput{{ state.id }}" name="add_note">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3 mt-2 collapse" id="collapseInput{{ state.id }}">
|
|
||||||
<textarea type="text" class="form-control" id="stateNote{{ state.id }}" name="note" rows="4" maxlength="200" placeholder="{% trans "Max 200 characters" %}"></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input type="hidden" name="state_id" value="{{ state.id }}">
|
|
||||||
<input type="hidden" name="snapshot_uuid" value="{{ object.last_uuid }}">
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
|
|
||||||
<button type="submit" class="btn btn-primary">{% trans "Add State" %}</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ from utils.device import create_property, create_doc, create_index
|
||||||
from utils.forms import MultipleFileField
|
from utils.forms import MultipleFileField
|
||||||
from device.models import Device
|
from device.models import Device
|
||||||
from evidence.parse import Build
|
from evidence.parse import Build
|
||||||
from evidence.models import SystemProperty
|
from evidence.models import SystemProperty, UserProperty
|
||||||
from utils.save_snapshots import move_json, save_in_disk
|
from utils.save_snapshots import move_json, save_in_disk
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue