Localization and several UI changes #51
|
@ -11,17 +11,22 @@ class Command(BaseCommand):
|
|||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('institution_name', type=str, help='The name of the institution')
|
||||
parser.add_argument(
|
||||
'language_code',
|
||||
type=str,
|
||||
help='The language code to set (e.g., "es", "en", "ca")',
|
||||
)
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
default_states = [
|
||||
_("INBOX"),
|
||||
_("VISUAL INSPECTION"),
|
||||
_("REPAIR"),
|
||||
_("INSTALL"),
|
||||
_("TEST"),
|
||||
_("PACKAGING"),
|
||||
_("DONATION"),
|
||||
_("DISMANTLE")
|
||||
"INBOX",
|
||||
"VISUAL INSPECTION",
|
||||
"REPAIR",
|
||||
"INSTALL",
|
||||
"TEST",
|
||||
"PACKAGING",
|
||||
"DONATION",
|
||||
"DISMANTLE"
|
||||
]
|
||||
|
||||
institution_name = kwargs['institution_name']
|
||||
|
@ -32,6 +37,37 @@ class Command(BaseCommand):
|
|||
logger.error(txt, institution.name)
|
||||
return
|
||||
|
||||
# If using djangos localization framework for initial states, then we would need institution-wide languange preferences
|
||||
lang_code = kwargs['language_code']
|
||||
match lang_code:
|
||||
case "en":
|
||||
pass
|
||||
case "es":
|
||||
default_states = [
|
||||
"ENTRADA",
|
||||
"INSPECCION VISUAL",
|
||||
"REPARACIÓN",
|
||||
"INSTALADO",
|
||||
"PRUEBAS",
|
||||
"EMPAQUETADO",
|
||||
"DONACION",
|
||||
"DESMANTELADO"
|
||||
]
|
||||
case "ca":
|
||||
default_states = [
|
||||
"ENTRADA",
|
||||
"INSPECCIÓ VISUAL",
|
||||
"REPARACIÓ",
|
||||
"INSTAL·LAT",
|
||||
"PROVES",
|
||||
"EMPAQUETAT",
|
||||
"DONACIÓ",
|
||||
"DESMANTELLAT"
|
||||
]
|
||||
case _:
|
||||
logger.error("Language not supported %s", lang_code)
|
||||
return
|
||||
|
||||
for state in default_states:
|
||||
state_def, created = StateDefinition.objects.get_or_create(
|
||||
institution=institution,
|
||||
|
|
|
@ -1,19 +1,30 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>{{ subtitle }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-fluid">
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<a href="{% url 'admin:institution' user.institution.pk %}" class="btn btn-green-admin">
|
||||
{% translate "Institution" %}
|
||||
</a>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title fw-bold mb-3">
|
||||
<i class="bi bi-building me-2"></i> {% translate "Institution" %}
|
||||
</h5>
|
||||
<p class="card-text text-muted flex-grow-1">
|
||||
{% translate "Edit and manage the institution's details and settings." %}
|
||||
</p>
|
||||
<div class="text-end">
|
||||
<a href="{% url 'admin:institution' user.institution.pk %}" class="btn btn-green-admin d-inline-flex align-items-center">
|
||||
<span class="me-2">{% translate "Edit Institution" %}</span>
|
||||
<i class="bi bi-arrow-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,36 +1,40 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block actions %}
|
||||
|
||||
<a href="{% url 'admin:new_user' %}" class="btn btn-green-admin">
|
||||
<i class="bi bi-person-add"></i>
|
||||
{% translate "New user" %}
|
||||
</a>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>{{ subtitle }}</h3>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<a href="{% url 'admin:new_user' %}" class="btn btn-green-admin">{% translate "Add new user" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
<h3>{{ subtitle }}</h3>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Email</th>
|
||||
<th>is Admin</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<table class="table table-hover table-bordered">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th scope="col">{% trans "Email" %}</th>
|
||||
<th scope="col">{% trans "Admin" %}</th>
|
||||
<th scope="col" class="text-center">{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
{% for u in users %}
|
||||
<tr>
|
||||
<td>{{ u.email }}</td>
|
||||
<td>{{ u.is_admin }}</td>
|
||||
<td><a href="{% url 'admin:edit_user' u.pk %}"><i class="bi bi-eye"></i></td>
|
||||
<td><a href="{% url 'admin:delete_user' u.pk %}" class="text-danger" title="Remove"><i class="bi bi-trash"></i></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<td>{% if u.is_admin %}{% trans "Yes" %}{% else %}{% trans "No" %}{% endif %}</td>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'admin:edit_user' u.pk %}"><i class="bi bi-pencil me-4"></i></a>
|
||||
|
||||
<a href="{% url 'admin:delete_user' u.pk %}" class="text-danger" title="Remove"><i class="bi bi-trash"></i> </a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -1,19 +1,24 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% load django_bootstrap5 %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>{{ subtitle }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% load django_bootstrap5 %}
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
Are you sure than want remove the lot {{ object.name }} with {{ object.devices.count }} devices.
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
{% blocktranslate %}
|
||||
Are you sure you want to remove the lot <strong>{{ object.name }}</strong> with <strong>{{ object.devices.count }}</strong> devices?
|
||||
{% endblocktranslate %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
|
@ -31,7 +36,10 @@
|
|||
{% bootstrap_form form %}
|
||||
<div class="form-actions-no-box">
|
||||
<a class="btn btn-grey" href="{% url 'admin:users' %}">{% translate "Cancel" %}</a>
|
||||
<input class="btn btn-green-admin" type="submit" name="submit" value="{% translate 'Delete' %}" />
|
||||
<button type="submit" class="btn btn-danger">
|
||||
<i class="bi bi-trash me-1"></i>
|
||||
{% translate "Delete" %}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
|
|
@ -1,32 +1,49 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% load i18n django_bootstrap5 %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>{{ subtitle }}</h3>
|
||||
<div class="ms-3 mt-4">
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
<h3>{{ subtitle }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% load django_bootstrap5 %}
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% if form.errors %}
|
||||
<div class="alert alert-danger alert-icon alert-icon-border alert-dismissible" role="alert">
|
||||
<div class="icon"><span class="mdi mdi-close-circle-o"></span></div>
|
||||
<div class="message">
|
||||
{% for field, error in form.errors.items %}
|
||||
{{ error }}<br />
|
||||
{% endfor %}
|
||||
<button class="btn-close" type="button" data-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% bootstrap_form form %}
|
||||
<div class="form-actions-no-box">
|
||||
<a class="btn btn-grey" href="{% url 'admin:panel' %}">{% translate "Cancel" %}</a>
|
||||
<input class="btn btn-green-admin" type="submit" name="submit" value="{% translate 'Save' %}" />
|
||||
</div>
|
||||
<form role="form" method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
|
||||
</form>
|
||||
{% if form.errors %}
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<strong>{% translate "Please fix the following errors:" %}</strong>
|
||||
<ul>
|
||||
{% for field, errors in form.errors.items %}
|
||||
<li>{{ field|title }}: {{ errors|join:", " }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{% bootstrap_field form.name addon_before='<i class="bi bi-building"></i>' %}
|
||||
{% bootstrap_field form.location addon_before='<i class="bi bi-geo-alt-fill"></i>' %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% bootstrap_field form.responsable_person addon_before='<i class="bi bi-person-fill"></i>' %}
|
||||
{% bootstrap_field form.supervisor_person addon_before='<i class="bi bi-person-check-fill"></i>' %}
|
||||
{% bootstrap_field form.logo addon_before='<i class="bi bi-image-fill"></i>' %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions mt-5 ms-2">
|
||||
<a class="btn btn-secondary" href="{% url 'admin:panel' %}">
|
||||
{% translate "Cancel" %}
|
||||
</a>
|
||||
<button type="submit" class="btn btn-green-admin ms-3">
|
||||
<i class="bi bi-floppy me-2"></i>{% translate "Save" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n django_bootstrap5 %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>{{ subtitle }}</h3>
|
||||
</div>
|
||||
<div class="col text-end">
|
||||
{% block actions %}
|
||||
<button type="button" class="btn btn-green-admin" data-bs-toggle="modal" data-bs-target="#addStateModal">
|
||||
{% trans "Add" %}
|
||||
<i class="bi bi-plus"></i>
|
||||
{% trans "New State" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-4">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h3>{{ subtitle }}</h3>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{% if state_definitions %}
|
||||
<table class="table table-hover table-bordered align-middle">
|
||||
|
|
|
@ -31,7 +31,7 @@ class AdminView(DashboardView):
|
|||
class PanelView(AdminView, TemplateView):
|
||||
template_name = "admin_panel.html"
|
||||
title = _("Admin")
|
||||
breadcrumb = _("admin") + " /"
|
||||
breadcrumb = _("Admin") + " / " + _("Panel") + " /"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
@ -41,7 +41,7 @@ class PanelView(AdminView, TemplateView):
|
|||
class UsersView(AdminView, TemplateView):
|
||||
template_name = "admin_users.html"
|
||||
title = _("Users")
|
||||
breadcrumb = _("admin / Users") + " /"
|
||||
breadcrumb = _("Admin") + " / " + _("Panel") + " / " + _("Users")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
@ -54,7 +54,7 @@ class UsersView(AdminView, TemplateView):
|
|||
class CreateUserView(AdminView, NotifyActivateUserByEmail, CreateView):
|
||||
template_name = "user.html"
|
||||
title = _("User")
|
||||
breadcrumb = _("admin / User") + " /"
|
||||
breadcrumb = _("Admin") + " / " + _("Users") + " / " +_("New")
|
||||
success_url = reverse_lazy('admin:users')
|
||||
model = User
|
||||
fields = (
|
||||
|
@ -79,7 +79,7 @@ class CreateUserView(AdminView, NotifyActivateUserByEmail, CreateView):
|
|||
class DeleteUserView(AdminView, DeleteView):
|
||||
template_name = "delete_user.html"
|
||||
title = _("Delete user")
|
||||
breadcrumb = "admin / Delete user"
|
||||
breadcrumb = _("Admin") + " / " + _("Users") + " / " +_("Delete")
|
||||
success_url = reverse_lazy('admin:users')
|
||||
model = User
|
||||
fields = (
|
||||
|
@ -96,7 +96,7 @@ class DeleteUserView(AdminView, DeleteView):
|
|||
class EditUserView(AdminView, UpdateView):
|
||||
template_name = "user.html"
|
||||
title = _("Edit user")
|
||||
breadcrumb = "admin / Edit user"
|
||||
breadcrumb = _("Admin") + " / " + _("Users") + " / " +_("Edit")
|
||||
success_url = reverse_lazy('admin:users')
|
||||
model = User
|
||||
fields = (
|
||||
|
@ -116,6 +116,7 @@ class InstitutionView(AdminView, UpdateView):
|
|||
template_name = "institution.html"
|
||||
title = _("Edit institution")
|
||||
section = "admin"
|
||||
breadcrumb = _("Admin") + " / " + _("Institution") + " / "
|
||||
subtitle = _('Edit institution')
|
||||
model = Institution
|
||||
success_url = reverse_lazy('admin:panel')
|
||||
|
@ -127,6 +128,16 @@ class InstitutionView(AdminView, UpdateView):
|
|||
"supervisor_person"
|
||||
)
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form = super().get_form(form_class)
|
||||
form.fields["name"].help_text = _("Full name of the institution.")
|
||||
form.fields["logo"].help_text = _("URL to the institution's logo.")
|
||||
form.fields["location"].help_text = _("The address or city of the institution.")
|
||||
form.fields["responsable_person"].help_text = _("Name of the institution's responsable person.")
|
||||
form.fields["supervisor_person"].help_text = _("The supervisor's full name.")
|
||||
|
||||
return form
|
||||
|
||||
def get_form_kwargs(self):
|
||||
self.object = self.request.user.institution
|
||||
kwargs = super().get_form_kwargs()
|
||||
|
@ -146,7 +157,7 @@ class StateDefinitionContextMixin(ContextMixin):
|
|||
class StatesPanelView(AdminView, StateDefinitionContextMixin, TemplateView):
|
||||
template_name = "states_panel.html"
|
||||
title = _("States Panel")
|
||||
breadcrumb = _("admin / States Panel") + " /"
|
||||
breadcrumb = _("Admin") + " / " + _("States") + " / "
|
||||
|
||||
|
||||
class AddStateDefinitionView(AdminView, StateDefinitionContextMixin, CreateView):
|
||||
|
|
|
@ -122,7 +122,6 @@ class NewSnapshotView(ApiMixing):
|
|||
owner=self.tk.owner.institution
|
||||
).first()
|
||||
|
||||
|
||||
if not prop:
|
||||
logger.error("Error: No property for uuid: %s", ev_uuid)
|
||||
return JsonResponse({'status': 'fail'}, status=500)
|
||||
|
|
|
@ -174,3 +174,15 @@ h3 {
|
|||
.btn-orange {
|
||||
background-color: #f5b587;
|
||||
}
|
||||
|
||||
/* Clase para botones con funcionalidad no implementados */
|
||||
.btn-todo {
|
||||
background-color: #6c757d;
|
||||
color: #fff; /* texto en blanco*/
|
||||
opacity: 0.65;
|
||||
cursor: not-allowed;
|
||||
|
||||
.btn-todo:disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{% load i18n static %}
|
||||
{% load i18n static language_code %}
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
@ -7,7 +7,7 @@
|
|||
{% block meta %}
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="robots" content="NONE,NOARCHIVE" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="viewp ort" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="Pangea">
|
||||
{% endblock %}
|
||||
|
@ -64,17 +64,24 @@
|
|||
<div class="navbar-nav navbar-sub-brand">
|
||||
{{ user.institution.name|upper }}
|
||||
</div>
|
||||
<div class="navbar-nav">
|
||||
<div class="nav-item text-nowrap">
|
||||
<i id="user-avatar" class="bi bi-person-circle"></i>
|
||||
<a class="navbar-sub-brand px-3" href="{% url 'user:panel' %}">{{ user.email }}</a>
|
||||
<a class="logout" href="{% url 'login:logout' %}">
|
||||
<i class="fa-solid fa-arrow-right-from-bracket"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<nav class="navbar navbar-expand-sm">
|
||||
<div class="container-fluid d-flex justify-content-end">
|
||||
<div class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle navbar-sub-brand px-3" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
{{ user.email }}
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li><a class="dropdown-item" href="{% url 'user:panel' %}"><i class="bi bi-person"></i> {% trans 'Profile' %}</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'api:tokens' %}"><i class="bi bi-key"></i> {% trans 'Token' %}</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'user:settings' %}"><i class="bi bi-gear"></i> {% trans 'Settings File' %}</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item text-danger" href="{% url 'login:logout' %}"><i class="fa-solid fa-arrow-right-from-bracket"></i> {% trans 'Logout' %} </a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
|
||||
|
@ -113,7 +120,7 @@
|
|||
<ul class="flex-column mb-2 ul_sidebar accordion-collapse {% if path == 'unassigned_devices' %}expanded{% else %}collapse{% endif %}" id="ul_devices" data-bs-parent="#sidebarMenu">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link{% if path == 'unassigned_devices' %} active2{% endif %}" href="{% url 'dashboard:unassigned_devices' %}">
|
||||
{% trans 'Unassigned devices' %}
|
||||
{% trans 'Unassigned' %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -141,7 +148,7 @@
|
|||
<ul class="flex-column mb-2 ul_sidebar accordion-collapse {% if path in 'upload list' %}expanded{% else %}collapse{% endif %}" id="ul_evidences" data-bs-parent="#sidebarMenu">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link{% if path == 'upload' %} active2{% endif %}" href="{% url 'evidence:upload' %}">
|
||||
{% trans 'Upload one' %}
|
||||
{% trans 'Upload' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
|
@ -159,12 +166,12 @@
|
|||
<ul class="flex-column mb-2 ul_sidebar accordion-collapse {% if path in 'import add' %}expanded{% else %}collapse{% endif %}" id="ul_placeholders" data-bs-parent="#sidebarMenu">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link{% if path == 'import' %} active2{% endif %}" href="{% url 'evidence:import' %}">
|
||||
{% trans 'Upload Spreadsheet' %}
|
||||
{% trans 'Import from spreadsheet' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link{% if path == 'add' %} active2{% endif %}" href="{% url 'device:add' %}">
|
||||
{% trans 'Create one' %}
|
||||
{% trans 'Add device' %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -195,7 +202,7 @@
|
|||
<form method="post" action="{% url 'dashboard:search' %}">
|
||||
{% csrf_token %}
|
||||
<div class="input-group rounded">
|
||||
<input type="search" name="search" class="form-control rounded" placeholder="Search your device..." aria-label="Search" aria-describedby="search-addon" />
|
||||
<input type="search" name="search" class="form-control rounded" placeholder="{% trans 'Search your device' %}" aria-label="Search" aria-describedby="search-addon" />
|
||||
<span class="input-group-text border-0" id="search-addon">
|
||||
<i class="fas fa-search"></i>
|
||||
</span>
|
||||
|
@ -212,24 +219,41 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-wrap gap-2 justify-content-end m-2 mb-4">
|
||||
{% block actions %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
<div class= "mx-2">
|
||||
{% block content %}
|
||||
{% endblock content %}
|
||||
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="footer text-center mt-auto py-3">
|
||||
<div class="container">
|
||||
<span class="text-muted">{{ commit_id }}</span>
|
||||
<footer class="footer mt-auto py-3" style="width: 100%;">
|
||||
<div class="container-fluid">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="text-muted mx-auto">{{ commit_id }}</span>
|
||||
{% include "language_picker.html" %}
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
{% block script %}
|
||||
{% block script %}
|
||||
<script src="{% static "js/jquery-3.3.1.slim.min.js" %}"></script>
|
||||
<script src="{% static "js/popper.min.js" %}"></script>
|
||||
<script src="{% static "js/bootstrap.min.js" %}"></script>
|
||||
|
||||
<script>
|
||||
// initialize bootstrap tooltips for those objects with data-bs-toggle="tooltip"
|
||||
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
||||
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
|
||||
return new bootstrap.Tooltip(tooltipTriggerEl);
|
||||
});
|
||||
</script>
|
||||
|
||||
{% block extrascript %}{% endblock %}
|
||||
{% endblock %}
|
||||
</body>
|
||||
|
|
20
dashboard/templates/language_picker.html
Normal file
20
dashboard/templates/language_picker.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
|
||||
{% load i18n language_code %}
|
||||
|
||||
<div class="dropdown">
|
||||
<form action="{% url 'set_language' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<button class="btn btn-tertiary dropdown-toggle" type="button" id="languageDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% get_language_info_list for LANGUAGES as languages %}
|
||||
{{ LANGUAGE_CODE|get_language_code:languages }}
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="languageDropdown">
|
||||
{% for lang in languages %}
|
||||
<li>
|
||||
<button class="dropdown-item" type="submit" name="language" value="{{ lang.code }}">{{ lang.name }}</button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</form>
|
||||
</div>
|
|
@ -2,83 +2,151 @@
|
|||
{% load i18n %}
|
||||
{% load paginacion %}
|
||||
|
||||
{% block actions %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>{{ subtitle }}</h3>
|
||||
</div>
|
||||
<div class="col text-center">
|
||||
<a href="{# url 'dashboard:exports' object.id #}" type="button" class="btn btn-green-admin">
|
||||
{% if lot %}
|
||||
<a href="{#% url 'lot:documents' object.id %#}" type="button" class="btn btn-green-admin btn-todo" data-bs-toggle="tooltip" title="{% trans " NOT IMPLEMENTED. Menu for adding documents for the lot" %} ">
|
||||
<i class="bi bi-folder2"></i>
|
||||
{% trans 'Documents' %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if lot %}
|
||||
<a href="{% url 'lot:properties' object.id %}" type="button" class="btn btn-green-admin" >
|
||||
<i class="bi bi-tag"></i>
|
||||
{% trans 'Properties' %}
|
||||
</a>
|
||||
{% endif %}
|
||||
<a href="{# url 'dashboard:exports' object.id #}" type="button" class="btn btn-todo" data-bs-toggle="tooltip" title=" {% trans "NOT IMPLEMENTED. This action tries to emulate what devicehub-teal did, which was related to opening a dialog where you can select different options for export the devices as csv for all selected devices" %}" >
|
||||
<i class="bi bi-reply"></i>
|
||||
{% trans 'Exports' %}
|
||||
</a>
|
||||
{% if lot %}
|
||||
<a href="{% url 'lot:properties' object.id %}" type="button" class="btn btn-green-admin">
|
||||
<i class="bi bi-tag"></i>
|
||||
{% trans 'properties' %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dataTable-container">
|
||||
|
||||
{% endblock%}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% if lot.name %}
|
||||
<h3 class="text-muted"> <i class="bi bi-folder2-open me-2"></i>{% trans "Lot" %} {{ lot.name }}</h3>
|
||||
{% endif %}
|
||||
|
||||
<div class="dataTable-container mt-4">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" data-sortable="">
|
||||
select
|
||||
</th>
|
||||
<th scope="col" data-sortable="">
|
||||
shortid
|
||||
</th>
|
||||
<th scope="col" data-sortable="">
|
||||
type
|
||||
</th>
|
||||
<th scope="col" data-sortable="">
|
||||
manufacturer
|
||||
</th>
|
||||
<th scope="col" data-sortable="">
|
||||
model
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for dev in devices %}
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<input type="checkbox" name="devices" value="{{ dev.id }}" />
|
||||
</td>
|
||||
<td>
|
||||
<a href="{% url 'device:details' dev.id %}">
|
||||
{{ dev.shortid }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ dev.type }}
|
||||
</td>
|
||||
<td>
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="d-flex justify-content-end m-2 mb-4">
|
||||
<button id="remove-button" class="btn btn btn-danger me-2" type="submit" value="{% url 'lot:del_devices' %}" name="url" disabled>
|
||||
<i class="bi bi-folder-minus pe-2"></i>
|
||||
{% trans 'Unassign' %}
|
||||
</button>
|
||||
<button class="btn btn-green-user" type="submit" name="url" value="{% url 'lot:add_devices' %}">
|
||||
<i class="bi bi-folder-symlink"></i>
|
||||
{% trans 'Assign to lot' %}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<table class="table table-hover table-bordered">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th scope="col" class="text-center">
|
||||
<input type="checkbox" id="select-all" />
|
||||
</th>
|
||||
<th scope="col" class="text-center">
|
||||
{% trans "Short ID" %}
|
||||
</th>
|
||||
<th scope="col" class="text-center">
|
||||
{% trans "Type" %}
|
||||
</th>
|
||||
<th scope="col" class="text-center">
|
||||
{% trans "Manufacturer" %}
|
||||
</th>
|
||||
<th scope="col" class="text-center">
|
||||
{% trans "Model" %}
|
||||
</th>
|
||||
<th scope="col" class="text-center">
|
||||
{% trans "Current State" %}
|
||||
</th>
|
||||
<th scope="col" data-type="date" class="text-center" data-format="YYYY-MM-DD HH:mm">
|
||||
{% trans "Evidence last updated" %}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for dev in devices %}
|
||||
<tr>
|
||||
<td class="text-center">
|
||||
<input type="checkbox" name="devices" value="{{ dev.id }}" />
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'device:details' dev.id %}">
|
||||
{{ dev.shortid }}
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
{% if dev.type == "Laptop" or dev.type == "Netbook" %}
|
||||
<i class="bi bi-laptop"></i>
|
||||
{% elif dev.type == "Desktop" or dev.type == "Server" %}
|
||||
<i class="bi bi-pc-display"></i>
|
||||
{% elif dev.type == "Motherboard" %}
|
||||
<i class="bi bi-motherboard"></i>
|
||||
{% elif dev.type == "GraphicCard" %}
|
||||
<i class="bi bi-gpu-card"></i>
|
||||
{% elif dev.type == "HardDrive" %}
|
||||
<i class="bi bi-hdd"></i>
|
||||
{% elif dev.type == "SolidStateDrive" %}
|
||||
<i class="bi bi-device-ssd"></i>
|
||||
{% elif dev.type == "NetworkAdapter" %}
|
||||
<i class="bi bi-pci-card-network"></i>
|
||||
{% elif dev.type == "Processor" %}
|
||||
<i class="bi bi-cpu"></i>
|
||||
{% elif dev.type == "RamModule" %}
|
||||
<i class="bi bi-memory"></i>
|
||||
{% elif dev.type == "SoundCard" %}
|
||||
<i class="bi bi-speaker"></i>
|
||||
{% elif dev.type == "Display" %}
|
||||
<i class="bi bi-display"></i>
|
||||
{% elif dev.type == "Battery" %}
|
||||
<i class="bi bi-battery"></i>
|
||||
{% elif dev.type == "Camera" %}
|
||||
<i class="bi bi-camera"></i>
|
||||
{% endif %}
|
||||
{{dev.type}}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
{{ dev.manufacturer }}
|
||||
</td>
|
||||
<td>
|
||||
{% if dev.version %}
|
||||
{{dev.version}} {{ dev.model }}
|
||||
{% else %}
|
||||
{{ dev.model }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
{% if dev.version %}
|
||||
{{dev.version}} {{ dev.model }}
|
||||
{% else %}
|
||||
{{ dev.model }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
{{ dev.get_current_state.state|default:"N/A" }}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
{{ dev.last_evidence.created }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<button class="btn btn-green-admin" type="submit" value="{% url 'lot:del_devices' %}" name="url">Remove</button> <button class="btn btn-green-admin" type="submit" name="url" value="{% url 'lot:add_devices' %}">add</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col">
|
||||
|
||||
<div class="mt-5 d-flex align-items-center justify-content-center">
|
||||
{% render_pagination page total_pages limit %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Placeholder check-all js
|
||||
document.getElementById('select-all').onclick = function() {
|
||||
var checkboxes = document.querySelectorAll('input[type="checkbox"]');
|
||||
for (var checkbox of checkboxes) {
|
||||
checkbox.checked = this.checked;
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
11
dashboard/templatetags/language_code.py
Normal file
11
dashboard/templatetags/language_code.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
from django import template
|
||||
from django.utils.translation import get_language_info
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@register.filter
|
||||
def get_language_code(language_code, languages):
|
||||
for lang in languages:
|
||||
if lang['code'] == language_code:
|
||||
return lang['name_local'].lower()
|
||||
return language_code.lower()
|
|
@ -16,7 +16,7 @@ class UnassignedDevicesView(InventaryMixin):
|
|||
template_name = "unassigned_devices.html"
|
||||
section = "Unassigned"
|
||||
title = _("Unassigned Devices")
|
||||
breadcrumb = "Devices / Unassigned Devices"
|
||||
breadcrumb = _("Devices") + " / " + _("Unassigned") + " / "
|
||||
|
||||
def get_devices(self, user, offset, limit):
|
||||
return Device.get_unassigned(self.request.user.institution, offset, limit)
|
||||
|
@ -26,7 +26,7 @@ class LotDashboardView(InventaryMixin, DetailsMixin):
|
|||
template_name = "unassigned_devices.html"
|
||||
section = "dashboard_lot"
|
||||
title = _("Lot Devices")
|
||||
breadcrumb = "Lot / Devices"
|
||||
breadcrumb = _("Lot") + " / " + _("Devices") + " / "
|
||||
model = Lot
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
@ -50,7 +50,7 @@ class SearchView(InventaryMixin):
|
|||
template_name = "unassigned_devices.html"
|
||||
section = "Search"
|
||||
title = _("Search Devices")
|
||||
breadcrumb = "Devices / Search Devices"
|
||||
breadcrumb = _("Devices") + " / " + _("Search") + " / "
|
||||
|
||||
def get_devices(self, user, offset, limit):
|
||||
post = dict(self.request.POST)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from django import forms
|
||||
from utils.device import create_property, create_doc, create_index
|
||||
from utils.save_snapshots import move_json, save_in_disk
|
||||
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
#TODO: translate device types
|
||||
DEVICE_TYPES = [
|
||||
("Desktop", "Desktop"),
|
||||
("Laptop", "Laptop"),
|
||||
|
@ -22,11 +22,11 @@ DEVICE_TYPES = [
|
|||
|
||||
|
||||
class DeviceForm(forms.Form):
|
||||
type = forms.ChoiceField(choices = DEVICE_TYPES, required=False)
|
||||
amount = forms.IntegerField(required=False, initial=1)
|
||||
custom_id = forms.CharField(required=False)
|
||||
name = forms.CharField(required=False)
|
||||
value = forms.CharField(required=False)
|
||||
type = forms.ChoiceField(choices = DEVICE_TYPES, required=False, label= _(u"Type"))
|
||||
amount = forms.IntegerField(required=False, initial=1, label= _(u"Amount"))
|
||||
custom_id = forms.CharField(required=False, label=_(u"Custom id"))
|
||||
name = forms.CharField(required=False, label= _(u"Name"))
|
||||
value = forms.CharField(required=False, label=_(u"Value"))
|
||||
|
||||
|
||||
class BaseDeviceFormSet(forms.BaseFormSet):
|
||||
|
|
|
@ -125,10 +125,14 @@ class Device:
|
|||
def last_uuid(self):
|
||||
if self.uuid:
|
||||
return self.uuid
|
||||
|
||||
if not self.uuids:
|
||||
self.get_uuids()
|
||||
|
||||
return self.uuids[0]
|
||||
|
||||
def get_current_state(self):
|
||||
uuid = self.last_uuid
|
||||
uuid = self.last_uuid()
|
||||
|
||||
return State.objects.filter(snapshot_uuid=uuid).order_by('-date').first()
|
||||
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Top bar buttons -->
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>{{ object.shortid }}</h3>
|
||||
</div>
|
||||
<div class="col text-end">
|
||||
<div class="btn-group" role="group" aria-label="Actions">
|
||||
{% block actions %}
|
||||
<!-- Top bar buttons -->
|
||||
|
||||
<!-- change state button -->
|
||||
{% if state_definitions %}
|
||||
|
@ -52,9 +45,10 @@
|
|||
<i class="bi bi-sticky"></i> {% trans "Add a note" %}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h3>{{ object.shortid }}</h3>
|
||||
|
||||
|
||||
<div class="row">
|
||||
|
@ -127,6 +121,25 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if dpps %}
|
||||
<div class="tab-pane fade" id="dpps">
|
||||
<h5 class="card-title">{% trans 'List of dpps' %}</h5>
|
||||
<div class="list-group col">
|
||||
{% for d in dpps %}
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<small class="text-muted">{{ d.timestamp }}</small>
|
||||
<span>{{ d.type }}</span>
|
||||
</div>
|
||||
<p class="mb-1">
|
||||
<a href="{% url 'did:device_web' d.signature %}">{{ d.signature }}</a>
|
||||
</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -28,33 +28,30 @@
|
|||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% if form.errors %}
|
||||
<div class="alert alert-danger alert-icon alert-icon-border alert-dismissible" role="alert">
|
||||
<div class="icon"><span class="mdi mdi-close-circle-o"></span></div>
|
||||
<div class="message">
|
||||
{% for field, error in form.errors.items %}
|
||||
{{ error }}<br />
|
||||
{% endfor %}
|
||||
<button class="btn-close" type="button" data-dismiss="alert" aria-label="Close"></button>
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bi bi-exclamation-circle me-2"></i>
|
||||
<div>
|
||||
{% for field, error in form.errors.items %}
|
||||
{{ error }}<br />
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{{ form.management_form }}
|
||||
<div class="container" id="formset-container">
|
||||
<div class="row mb-2">
|
||||
<div class="col">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4">
|
||||
{% bootstrap_field form.0.type %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-2">
|
||||
<div class="col">
|
||||
{% bootstrap_field form.0.amount %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-2">
|
||||
<div class="col">
|
||||
<div class="col-md-4">
|
||||
{% bootstrap_field form.0.custom_id %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
{% bootstrap_field form.0.amount %}
|
||||
</div> </div>
|
||||
<div class="row mb-2">
|
||||
<div class="col-10">
|
||||
<span class="fw-bold">{% trans 'Component details' %}</span>
|
||||
|
|
49
device/templates/tabs/documents.html
Normal file
49
device/templates/tabs/documents.html
Normal file
|
@ -0,0 +1,49 @@
|
|||
|
||||
{% load i18n %}
|
||||
<div class="tab-pane fade" id="documents">
|
||||
<div class="btn-group mt-1 mb-3">
|
||||
<a href="{% url 'device:add_document' object.pk %}" class="btn btn-primary">
|
||||
<i class="bi bi-plus">
|
||||
</i>
|
||||
{% trans 'Add new document' %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h5 class="card-title">{% trans 'Documents' %}
|
||||
</h5>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">
|
||||
{% trans 'Key' %}
|
||||
</th>
|
||||
<th scope="col">
|
||||
{% trans 'Value' %}
|
||||
</th>
|
||||
<th scope="col" data-type="date" data-format="YYYY-MM-DD HH:mm">
|
||||
{% trans 'Created on' %}
|
||||
</th>
|
||||
<th>
|
||||
</th>
|
||||
<th>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for a in object.get_user_documents %}
|
||||
<tr>
|
||||
<td>{{ a.key }}
|
||||
</td>
|
||||
<td>{{ a.value }}
|
||||
</td>
|
||||
<td>{{ a.created }}
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
|
@ -16,7 +16,7 @@
|
|||
{% for snap in object.evidences %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{% url 'evidence:details' snap.uuid %}">{{ snap.uuid }}</a>
|
||||
<a href="{% url 'evidence:details' snap.uuid %}">{{ snap.uuid|truncatechars:7|upper }}</a>
|
||||
</td>
|
||||
<td>
|
||||
{% if snap.did_document %}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{% load i18n %}
|
||||
|
||||
{% load paginacion %}
|
||||
<div class="tab-pane fade" id="log">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover table-bordered bg-gradient">
|
||||
|
@ -12,17 +13,19 @@
|
|||
</thead>
|
||||
<tbody>
|
||||
{% for log in device_logs %}
|
||||
<tr>
|
||||
<td width="13%">{{ log.date|date:"M j, Y, H:i" }}</td>
|
||||
<td class="fst-italic">{{ log.event }}</td>
|
||||
<td>{{ log.user.get_full_name|default:log.user.username }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="13%">{{ log.date|date:"M j, Y, H:i" }}</td>
|
||||
<td class="fst-italic">{{ log.event }}</td>
|
||||
<td>{{ log.user.get_full_name|default:log.user.username }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="3" class="text-center">{% trans 'No logs recorded.' %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3" class="text-center">{% trans 'No logs recorded.' %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% render_pagination device_logs.number device_logs.paginator.num_pages 10 %}
|
||||
</div>
|
||||
|
|
|
@ -2,130 +2,109 @@
|
|||
{% load i18n %}
|
||||
|
||||
<div class="tab-pane fade" id="user_properties">
|
||||
<div class="d-flex justify-content-end mt-1 mb-3">
|
||||
<a href="{% url 'device:add_user_property' object.pk %}"
|
||||
class="btn btn-green-admin d-flex align-items-center">
|
||||
<i class="bi bi-plus me-1"></i>
|
||||
{% trans 'New user property' %}
|
||||
</a>
|
||||
<div class="d-flex justify-content-end mt-3 mb-4">
|
||||
<a href="{% url 'device:add_user_property' object.pk %}"
|
||||
class="btn btn-green-user d-flex align-items-center">
|
||||
<i class="bi bi-plus me-2"></i>
|
||||
{% trans 'New user property' %}
|
||||
</a>
|
||||
</div>
|
||||
<h5 class="card-title mb-4">{% trans 'User properties' %}</h5>
|
||||
|
||||
<h5 class="card-title">{% trans 'User properties' %}</h5>
|
||||
|
||||
<table class="table table-hover table-bordered table-responsive align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th scope="col">{% trans 'Key' %}</th>
|
||||
<th scope="col">{% trans 'Value' %}</th>
|
||||
<th scope="col" data-type="date" class="text-end" data-format="YYYY-MM-DD HH:mm">{% trans 'Created on' %}</th>
|
||||
<th scope="col" width="5%" class="text-end" title="{% trans 'Actions' %}"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for a in object.get_user_properties %}
|
||||
<tr>
|
||||
<td>{{ a.key }}
|
||||
</td>
|
||||
<td>{{ a.value }}
|
||||
</td>
|
||||
<td class="text-end">{{ a.created }}
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group ">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-outline-info d-flex align-items-center" data-bs-toggle="modal"
|
||||
data-bs-target="#editModal{{ a.id }}" >
|
||||
<i class="bi bi-pencil me-1"></i>
|
||||
{% trans 'Edit' %}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-outline-danger d-flex align-items-center"
|
||||
data-bs-toggle="modal" data-bs-target="#deleteModal{{ a.id }}">
|
||||
<i class="bi bi-trash me-1"></i>
|
||||
{% trans 'Delete' %}
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-bordered align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th scope="col">{% trans 'Key' %}</th>
|
||||
<th scope="col">{% trans 'Value' %}</th>
|
||||
<th scope="col" data-type="date" class="text-end" data-format="YYYY-MM-DD HH:mm">{% trans 'Created on' %}</th>
|
||||
<th scope="col" width="10%" class="text-end">{% trans 'Actions' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for a in object.get_user_properties %}
|
||||
<tr>
|
||||
<td>{{ a.key }}</td>
|
||||
<td>{{ a.value }}</td>
|
||||
<td class="text-end">{{ a.created }}</td>
|
||||
<td class="text-end">
|
||||
<div class="btn-group">
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-outline-primary d-flex align-items-center me-2"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#editModal{{ a.id }}">
|
||||
<i class="bi bi-pencil me-1"></i>
|
||||
{% trans 'Edit' %}
|
||||
</button>
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-outline-danger d-flex align-items-center"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#deleteModal{{ a.id }}">
|
||||
<i class="bi bi-trash me-1"></i>
|
||||
{% trans 'Delete' %}
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- pop up modal for delete confirmation -->
|
||||
{% for a in object.get_user_properties %}
|
||||
<div class="modal fade" id="deleteModal{{ a.id }}" tabindex="-1" aria-labelledby="deleteModalLabel{{ a.id }}" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="deleteModalLabel{{ a.id }}">{% trans "Confirm Deletion" %}
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
<strong>{% trans "Key:" %}
|
||||
</strong> {{ a.key }}
|
||||
</p>
|
||||
<p>
|
||||
<strong>{% trans "Value:" %}
|
||||
</strong> {{ a.value }}
|
||||
</p>
|
||||
<p>
|
||||
<strong>{% trans "Created on:" %}
|
||||
</strong> {{ a.created }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer justify-content-center">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}
|
||||
</button>
|
||||
<form method="post" action="{% url 'device:delete_user_property' object.id a.id %}">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-danger">{% trans "Delete" %}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-light">
|
||||
<h5 class="modal-title" id="deleteModalLabel{{ a.id }}">{% trans "Confirm Deletion" %}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p><strong>{% trans "Key:" %}</strong> {{ a.key }}</p>
|
||||
<p><strong>{% trans "Value:" %}</strong> {{ a.value }}</p>
|
||||
<p><strong>{% trans "Created on:" %}</strong> {{ a.created }}</p>
|
||||
</div>
|
||||
<div class="modal-footer justify-content-center">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
|
||||
<form method="post" action="{% url 'device:delete_user_property' object.id a.id %}">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-danger">{% trans "Delete" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<!-- popup modals for edit button -->
|
||||
{% for a in object.get_user_properties %}
|
||||
<div class="modal fade" id="editModal{{ a.id }}" tabindex="-1" aria-labelledby="editModalLabel{{ a.id }}" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="editModalLabel{{ a.id }}">{% trans "Edit User Property" %}
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="editForm{{ a.id }}" method="post" action="{% url 'device:update_user_property' object.id a.id %}">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label for="key" class="form-label">{% trans "Key" %}
|
||||
</label>
|
||||
<input type="text" class="form-control" id="key" name="key" value="{{ a.key }}">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="value" class="form-label">{% trans "Value" %}
|
||||
</label>
|
||||
<input type="text" class="form-control" id="value" name="value" value="{{ a.value }}">
|
||||
</div>
|
||||
<div class="modal-footer justify-content-center">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary">{% trans "Save changes" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-light">
|
||||
<h5 class="modal-title" id="editModalLabel{{ a.id }}">{% trans "Edit User Property" %}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="editForm{{ a.id }}" method="post" action="{% url 'device:update_user_property' object.id a.id %}">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label for="key" class="form-label">{% trans "Key" %}</label>
|
||||
<input type="text" class="form-control" id="key" name="key" value="{{ a.key }}">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="value" class="form-label">{% trans "Value" %}</label>
|
||||
<input type="text" class="form-control" id="value" name="value" value="{{ a.value }}">
|
||||
</div>
|
||||
<div class="modal-footer justify-content-center">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
|
||||
<button type="submit" class="btn btn-primary">{% trans "Save changes" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
|
|
@ -5,6 +5,7 @@ from django.http import JsonResponse
|
|||
from django.conf import settings
|
||||
from django.db import IntegrityError
|
||||
from django.urls import reverse_lazy
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from django.contrib import messages
|
||||
from django.shortcuts import get_object_or_404, redirect, Http404
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
@ -39,7 +40,7 @@ class DeviceLogMixin(DashboardView):
|
|||
class NewDeviceView(DashboardView, FormView):
|
||||
template_name = "new_device.html"
|
||||
title = _("New Device")
|
||||
breadcrumb = "Device / New Device"
|
||||
breadcrumb = _("Device") + " / " + _("New") + " / "
|
||||
success_url = reverse_lazy('dashboard:unassigned_devices')
|
||||
form_class = DeviceFormSet
|
||||
|
||||
|
@ -56,7 +57,7 @@ class NewDeviceView(DashboardView, FormView):
|
|||
class EditDeviceView(DashboardView, UpdateView):
|
||||
template_name = "new_device.html"
|
||||
title = _("Update Device")
|
||||
breadcrumb = "Device / Update Device"
|
||||
breadcrumb = _("Device") + " / " + _("Update") + " / "
|
||||
success_url = reverse_lazy('dashboard:unassigned_devices')
|
||||
model = SystemProperty
|
||||
|
||||
|
@ -75,7 +76,7 @@ class EditDeviceView(DashboardView, UpdateView):
|
|||
class DetailsView(DashboardView, TemplateView):
|
||||
template_name = "details.html"
|
||||
title = _("Device")
|
||||
breadcrumb = "Device / Details"
|
||||
breadcrumb = _("Device") + " / " + _("Details") + " / "
|
||||
model = SystemProperty
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
@ -104,9 +105,21 @@ class DetailsView(DashboardView, TemplateView):
|
|||
institution=self.request.user.institution
|
||||
).order_by('order')
|
||||
device_states = State.objects.filter(snapshot_uuid__in=uuids).order_by('-date')
|
||||
device_logs = DeviceLog.objects.filter(
|
||||
snapshot_uuid__in=uuids).order_by('-date')
|
||||
device_notes = Note.objects.filter(snapshot_uuid__in=uuids).order_by('-date')
|
||||
|
||||
# Paginate device_logs
|
||||
# TODO: when clicking pagination button, it reloads the page on the details tab, instead of the log one
|
||||
device_logs_list = DeviceLog.objects.filter(snapshot_uuid__in=uuids).order_by('-date')
|
||||
paginator = Paginator(device_logs_list, 10) # Show last 10 logs
|
||||
|
||||
page = self.request.GET.get('page')
|
||||
try:
|
||||
device_logs = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
device_logs = paginator.page(1)
|
||||
except EmptyPage:
|
||||
device_logs = paginator.page(paginator.num_pages)
|
||||
|
||||
context.update({
|
||||
'object': self.object,
|
||||
'snapshot': last_evidence,
|
||||
|
@ -179,7 +192,7 @@ class PublicDeviceWebView(TemplateView):
|
|||
class AddUserPropertyView(DeviceLogMixin, CreateView):
|
||||
template_name = "new_user_property.html"
|
||||
title = _("New User Property")
|
||||
breadcrumb = "Device / New Property"
|
||||
breadcrumb = _("Device") + " / " + _("Property") + " / " + _("New")
|
||||
model = UserProperty
|
||||
fields = ("key", "value")
|
||||
|
||||
|
@ -226,7 +239,7 @@ class AddUserPropertyView(DeviceLogMixin, CreateView):
|
|||
class UpdateUserPropertyView(DeviceLogMixin, UpdateView):
|
||||
template_name = "new_user_property.html"
|
||||
title = _("Update User Property")
|
||||
breadcrumb = "Device / Update Property"
|
||||
breadcrumb = _("Device") + " / " + _("Property") + " / " + _("Update")
|
||||
model = UserProperty
|
||||
fields = ("key", "value")
|
||||
|
||||
|
|
|
@ -65,7 +65,9 @@ ENABLE_EMAIL = config("ENABLE_EMAIL", default=True, cast=bool)
|
|||
|
||||
EVIDENCES_DIR = config("EVIDENCES_DIR", default=os.path.join(BASE_DIR, "db"))
|
||||
|
||||
|
||||
LOCALE_PATHS = [
|
||||
os.path.join(BASE_DIR, 'locale'),
|
||||
]
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
|
@ -119,7 +121,13 @@ TEMPLATES = [
|
|||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
"django.template.context_processors.i18n",
|
||||
],
|
||||
|
||||
'libraries':{
|
||||
'get_language_code': 'dashboard.templatetags.language_code',
|
||||
}
|
||||
|
||||
},
|
||||
},
|
||||
]
|
||||
|
@ -172,8 +180,9 @@ if TIME_ZONE == "UTC":
|
|||
|
||||
USE_L10N = True
|
||||
LANGUAGES = [
|
||||
('es', 'Spanish'),
|
||||
('en', 'English'),
|
||||
('es', 'español'),
|
||||
('en', 'english'),
|
||||
('ca', 'català'),
|
||||
]
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
|
|
12
dhub/urls.py
12
dhub/urls.py
|
@ -16,6 +16,10 @@ Including another URLconf
|
|||
"""
|
||||
from django.conf import settings
|
||||
from django.urls import path, include
|
||||
from django.conf.urls.i18n import i18n_patterns
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from django.views.i18n import set_language
|
||||
|
||||
urlpatterns = [
|
||||
# path('api/', include('snapshot.urls')),
|
||||
|
@ -35,3 +39,11 @@ if settings.DPP:
|
|||
path('dpp/', include('dpp.urls')),
|
||||
path('did/', include('did.urls')),
|
||||
])
|
||||
|
||||
urlpatterns += i18n_patterns(
|
||||
path("language/", set_language, name='set_language'),
|
||||
)
|
||||
|
||||
if settings.DEBUG:
|
||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
|
|
@ -5,6 +5,7 @@ RUN apt update && \
|
|||
apt-get install -y \
|
||||
python3-xapian \
|
||||
git \
|
||||
gettext \
|
||||
sqlite3 \
|
||||
curl \
|
||||
jq \
|
||||
|
|
|
@ -71,6 +71,7 @@ class UserTagForm(forms.Form):
|
|||
self.pk = None
|
||||
self.uuid = kwargs.pop('uuid', None)
|
||||
self.user = kwargs.pop('user')
|
||||
|
||||
instance = SystemProperty.objects.filter(
|
||||
uuid=self.uuid,
|
||||
key='CUSTOM_ID',
|
||||
|
@ -88,6 +89,7 @@ class UserTagForm(forms.Form):
|
|||
if not data:
|
||||
return False
|
||||
self.tag = data
|
||||
|
||||
self.instance = SystemProperty.objects.filter(
|
||||
uuid=self.uuid,
|
||||
key='CUSTOM_ID',
|
||||
|
@ -103,15 +105,18 @@ class UserTagForm(forms.Form):
|
|||
if self.instance:
|
||||
old_value = self.instance.value
|
||||
if not self.tag:
|
||||
message =_("<Deleted> Evidence Tag. Old Value: '{}'").format(old_value)
|
||||
message = _("<Deleted> Evidence Tag. Old Value: '{}'").format(
|
||||
old_value
|
||||
)
|
||||
self.instance.delete()
|
||||
else:
|
||||
self.instance.value = self.tag
|
||||
self.instance.save()
|
||||
if old_value != self.tag:
|
||||
message=_("<Updated> Evidence Tag. Old Value: '{}'. New Value: '{}'").format(old_value, self.tag)
|
||||
msg = "<Updated> Evidence Tag. Old Value: '{}'. New Value: '{}'"
|
||||
message=_(msg).format(old_value, self.tag)
|
||||
else:
|
||||
message =_("<Created> Evidence Tag. Value: '{}'").format(self.tag)
|
||||
message = _("<Created> Evidence Tag. Value: '{}'").format(self.tag)
|
||||
SystemProperty.objects.create(
|
||||
uuid=self.uuid,
|
||||
key='CUSTOM_ID',
|
||||
|
@ -119,7 +124,7 @@ class UserTagForm(forms.Form):
|
|||
owner=self.user.institution,
|
||||
user=self.user
|
||||
)
|
||||
|
||||
|
||||
DeviceLog.objects.create(
|
||||
snapshot_uuid=self.uuid,
|
||||
event= message,
|
||||
|
@ -127,7 +132,6 @@ class UserTagForm(forms.Form):
|
|||
institution=self.user.institution
|
||||
)
|
||||
|
||||
|
||||
class ImportForm(forms.Form):
|
||||
file_import = forms.FileField(label=_("File to import"))
|
||||
|
||||
|
@ -176,8 +180,8 @@ class ImportForm(forms.Form):
|
|||
table = []
|
||||
for row in self.rows:
|
||||
doc = create_doc(row)
|
||||
property = create_property(doc, self.user)
|
||||
table.append((doc, property))
|
||||
prop = create_property(doc, self.user)
|
||||
table.append((doc, prop))
|
||||
|
||||
if commit:
|
||||
for doc, cred in table:
|
||||
|
|
|
@ -1,27 +1,88 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% block actions %}
|
||||
|
||||
<a href="{% url 'evidence:download' object.uuid %}" class="btn btn-green-admin">
|
||||
<i class="bi bi-file-earmark-arrow-down"></i>
|
||||
{% trans "Download File" %}
|
||||
</a>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>{{ object.id }}</h3>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12 mb-4">
|
||||
<div class="card d-flex flex-wrap">
|
||||
<div class="card-header">
|
||||
<strong class="card-title">Evidence
|
||||
<span class="text-muted" id="uuid">{{ object.uuid }}</span>
|
||||
<button class="btn btn-sm btn-outline-secondary ms-1" onclick="copyToClipboard()">
|
||||
<i class="bi bi-clipboard"></i>
|
||||
</button>
|
||||
</strong>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<!-- Erase server checkbox -->
|
||||
{% load django_bootstrap5 %}
|
||||
<form role="form" method="post" class="p-2 rounded d-flex d-inline-flex align-items-center gap-3" style="max-width: 400px; {% if form2.erase_server.value %}background-color: #f5faf7!important;{% endif %}">
|
||||
{% csrf_token %}
|
||||
|
||||
{% if form2.errors %}
|
||||
<div class="alert alert-danger d-flex align-items-center alert-dismissible fade show w-100" role="alert">
|
||||
<i class="mdi mdi-alert-circle me-2"></i>
|
||||
<div>
|
||||
{% for field, error in form2.errors.items %}
|
||||
<strong>{{ field|title }}:</strong> {{ error }}<br />
|
||||
{% endfor %}
|
||||
</div>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- submit button -->
|
||||
<button class="btn btn-sm btn btn-outline-success" d-flex align-items-end gap-1" type="submit" name="submit_form2">
|
||||
<i class="bi bi-floppy2-fill"></i> {% translate 'Save' %}
|
||||
</button>
|
||||
<!-- erase server switch -->
|
||||
<div class="form-check form-switch m-0">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="{{ form2.erase_server.id_for_label }}"
|
||||
name="{{ form2.erase_server.name }}"
|
||||
onchange="toggleSaveButton(this)"
|
||||
{% if form2.erase_server.value %}
|
||||
checked
|
||||
{% endif %}>
|
||||
<label class="form-check-label ps-1" for="{{ form2.erase_server.id_for_label }}">
|
||||
{% if form2.erase_server.value %}
|
||||
<i class="pe-1 bi bi-eraser"></i>
|
||||
{% translate "It is an erase server" %}
|
||||
{% else %}
|
||||
<i class="pe-1 bi bi-x-circle"></i>
|
||||
{% translate "It is not an erase server" %}
|
||||
{% endif %}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<ul class="nav nav-tabs nav-tabs-bordered">
|
||||
<li class="nav-items">
|
||||
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#device">{% trans "Devices" %}</button>
|
||||
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#device">{% trans "Device" %}</button>
|
||||
</li>
|
||||
<li class="nav-items">
|
||||
<a href="#tag" class="nav-link" data-bs-toggle="tab" data-bs-target="#tag">{% trans "Tag" %}</a>
|
||||
</li>
|
||||
<li class="nav-items">
|
||||
<a href="{% url 'evidence:erase_server' object.uuid %}" class="nav-link">{% trans "Erase Server" %}</a>
|
||||
</li>
|
||||
<li class="nav-items">
|
||||
<a href="{% url 'evidence:download' object.uuid %}" class="nav-link">{% trans "Download File" %}</a>
|
||||
<a href="#tag" class="nav-link" data-bs-toggle="tab" data-bs-target="#tag">{% trans "Tag" %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -35,13 +96,13 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th scope="col" data-sortable="">
|
||||
{% trans "Type" %}
|
||||
{% trans "Algorithm" %}
|
||||
</th>
|
||||
<th scope="col" data-sortable="">
|
||||
{% trans "Identificator" %}
|
||||
{% trans "Device ID" %}
|
||||
</th>
|
||||
<th scope="col" data-sortable="">
|
||||
{% trans "Data" %}
|
||||
{% trans "Date" %}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -53,7 +114,8 @@
|
|||
</td>
|
||||
<td>
|
||||
<small class="text-muted">
|
||||
<a href="{% url 'device:details' snap.value %}">{{ snap.value }}</a>
|
||||
<a href="{% url 'device:details' snap.value %}">{{ snap.value|truncatechars:7|upper }}</a>
|
||||
{{ dev.shortid }}
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
|
@ -67,6 +129,7 @@
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="tag">
|
||||
{% load django_bootstrap5 %}
|
||||
<div class="list-group col-6">
|
||||
|
@ -88,7 +151,7 @@
|
|||
<div class="row">
|
||||
<div class="col">
|
||||
<a class="btn btn-grey" href="">{% translate "Cancel" %}</a>
|
||||
<input class="btn btn-green-admin" type="submit" name="submit" value="{% translate 'Save' %}" />
|
||||
<input class="btn btn-green-admin" type="submit" name="submit_form1" value="{% translate 'Save' %}" />
|
||||
</div>
|
||||
{% if form.tag.value %}
|
||||
<div class="col-1">
|
||||
|
@ -101,10 +164,24 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block extrascript %}
|
||||
<script>
|
||||
function toggleSaveButton(checkbox) {
|
||||
const saveButton = document.getElementById('saveButton');
|
||||
// Enable the button if the checkbox state changes
|
||||
saveButton.disabled = false;
|
||||
}
|
||||
function copyToClipboard() {
|
||||
const uuid = document.getElementById('uuid').innerText;
|
||||
navigator.clipboard.writeText(uuid).then(() => {
|
||||
alert('UUID copied to clipboard!');
|
||||
}).catch(() => {
|
||||
alert('Failed to copy UUID.');
|
||||
});
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
// Obtener el hash de la URL (ejemplo: #components)
|
||||
const hash = window.location.hash;
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>{{ object.id }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<ul class="nav nav-tabs nav-tabs-bordered">
|
||||
<li class="nav-items">
|
||||
<a href="{% url 'evidence:details' object.uuid %}" class="nav-link">{% trans "Devices" %}</a>
|
||||
</li>
|
||||
<li class="nav-items">
|
||||
<a href="{% url 'evidence:details' object.uuid %}#tag" class="nav-link">{% trans "Tag" %}</a>
|
||||
</li>
|
||||
<li class="nav-items">
|
||||
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#erase_server">{% trans "Erase Server" %}</button>
|
||||
</li>
|
||||
<li class="nav-items">
|
||||
<a href="{% url 'evidence:download' object.uuid %}" class="nav-link">{% trans "Download File" %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-content pt-2">
|
||||
|
||||
<div class="tab-pane fade show active" id="erase_server">
|
||||
|
||||
{% load django_bootstrap5 %}
|
||||
<div class="list-group col-6">
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% if form.errors %}
|
||||
<div class="alert alert-danger alert-icon alert-icon-border alert-dismissible" role="alert">
|
||||
<div class="icon"><span class="mdi mdi-close-circle-o"></span></div>
|
||||
<div class="message">
|
||||
{% for field, error in form.errors.items %}
|
||||
{{ error }}<br />
|
||||
{% endfor %}
|
||||
<button class="btn-close" type="button" data-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% bootstrap_form form %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<a class="btn btn-grey" href="">{% translate "Cancel" %}</a>
|
||||
<input class="btn btn-green-admin" type="submit" name="submit" value="{% translate 'Save' %}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -8,23 +8,119 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- override invalid-feedback class -->
|
||||
<style>
|
||||
.invalid-feedback {
|
||||
color: #670000;
|
||||
font-size: 1rem;
|
||||
.drop-area {
|
||||
width: 320px;
|
||||
padding: 1.5rem;
|
||||
background: white;
|
||||
border: 2px dashed #ccc;
|
||||
border-radius: 12px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.drop-area:hover, .drop-area.dragover {
|
||||
border-color: #007bff;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.drop-area.dragover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
{% load django_bootstrap5 %}
|
||||
<form role="form" method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<form role="form" method="post" enctype="multipart/form-data" class="row d-flex justify-content-center ">
|
||||
{% csrf_token %}
|
||||
|
||||
{% bootstrap_form form alert_error_type="none" error_css_class="alert alert-danger alert-icon alert-icon-border" %}
|
||||
<div class="form-actions-no-box">
|
||||
<a class="btn btn-grey" href="{% url 'dashboard:unassigned_devices' %}">{% translate "Cancel" %}</a>
|
||||
<input class="btn btn-green-admin" type="submit" name="submit" value="{% translate 'Save' %}" />
|
||||
</div>
|
||||
<!-- Drag n drop -->
|
||||
<div id="drop-zone" class="drop-area rounded p-5 text-center">
|
||||
<i class="bi bi-upload fs-1 mb-3"></i>
|
||||
<p class="text-muted mb-0">{% translate "Drag and drop here, or click to select manually" %}</p>
|
||||
|
||||
{# support for both evidence file and spreadsheet forms #}
|
||||
{% if form.evidence_file %}
|
||||
{% bootstrap_form form exclude="evidence_file" alert_error_type="none" error_css_class="alert alert-danger" %}
|
||||
<input type="file" id="file-input" name="{{ form.evidence_file.html_name }}" class="visually-hidden" {% if form.evidence_file.field.widget.attrs.multiple %}multiple required{% endif %}>
|
||||
{% elif form.file_import %}
|
||||
{% bootstrap_form form exclude="file_import" alert_error_type="none" error_css_class="alert alert-danger" %}
|
||||
<input type="file" id="file-input" name="{{ form.file_import.html_name }}" class="visually-hidden">
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
<!-- File Preview -->
|
||||
<div id="file-preview" class="mt-3 text-center ">
|
||||
<ul id="file-list" class="list-unstyled"></ul>
|
||||
|
||||
{% if form.evidence_file.errors or form.file_import.errors %}
|
||||
<div class="text-center mt-2">
|
||||
<ul class="list-unstyled text-danger mb-0">
|
||||
{% for error in form.evidence_file.errors %}
|
||||
<li class="font-weight-bold" >{{ error }}</li>
|
||||
{% endfor %}
|
||||
{% for error in form.file_import.errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-center gap-3 mt-4">
|
||||
<a class="btn btn-outline-secondary" href="{% url 'dashboard:unassigned_devices' %}">{% translate "Cancel" %}</a>
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="bi bi-upload me-2"></i>{% translate "Upload" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const dropZone = document.getElementById("drop-zone");
|
||||
const fileInput = document.getElementById("file-input");
|
||||
const filePreview = document.getElementById("file-preview");
|
||||
const fileList = document.getElementById("file-list");
|
||||
|
||||
dropZone.addEventListener("click", () => fileInput.click());
|
||||
fileInput.addEventListener("change", (event) => {
|
||||
updateFilePreview(event.target.files);
|
||||
});
|
||||
|
||||
// Handle drag and drop events
|
||||
dropZone.addEventListener("dragover", (event) => {
|
||||
event.preventDefault();
|
||||
dropZone.classList.add("border-primary", "bg-light");
|
||||
});
|
||||
dropZone.addEventListener("dragleave", () => {
|
||||
dropZone.classList.remove("border-primary", "bg-light");
|
||||
});
|
||||
|
||||
dropZone.addEventListener("drop", (event) => {
|
||||
event.preventDefault();
|
||||
dropZone.classList.remove("border-primary", "bg-light");
|
||||
fileInput.files = event.dataTransfer.files;
|
||||
updateFilePreview(event.dataTransfer.files);
|
||||
});
|
||||
|
||||
// Function to update the file preview
|
||||
function updateFilePreview(files) {
|
||||
fileList.innerHTML = "";
|
||||
if (files.length > 0) {
|
||||
filePreview.classList.remove("d-none");
|
||||
Array.from(files).forEach((file) => {
|
||||
const listItem = document.createElement("li");
|
||||
listItem.textContent = file.name;
|
||||
fileList.appendChild(listItem);
|
||||
});
|
||||
} else {
|
||||
filePreview.classList.add("d-none");
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -18,7 +18,6 @@ urlpatterns = [
|
|||
path("upload", views.UploadView.as_view(), name="upload"),
|
||||
path("import", views.ImportView.as_view(), name="import"),
|
||||
path("<uuid:pk>", views.EvidenceView.as_view(), name="details"),
|
||||
path("<uuid:pk>/eraseserver", views.EraseServerView.as_view(), name="erase_server"),
|
||||
path("<uuid:pk>/download", views.DownloadEvidenceView.as_view(), name="download"),
|
||||
path("tag/<str:pk>/delete", views.DeleteEvidenceTagView.as_view(), name="delete_tag"),
|
||||
]
|
||||
|
|
|
@ -27,7 +27,7 @@ class ListEvidencesView(DashboardView, TemplateView):
|
|||
template_name = "evidences.html"
|
||||
section = "evidences"
|
||||
title = _("Evidences")
|
||||
breadcrumb = "Evidences"
|
||||
breadcrumb = _("Evidence") + " / " + _("All") + " / "
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
@ -43,10 +43,18 @@ class UploadView(DashboardView, FormView):
|
|||
template_name = "upload.html"
|
||||
section = "evidences"
|
||||
title = _("Upload Evidence")
|
||||
breadcrumb = "Evidences / Upload"
|
||||
breadcrumb = _("Evidence") + " / " + _("Upload") + " / "
|
||||
success_url = reverse_lazy('evidence:list')
|
||||
form_class = UploadForm
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context.update({
|
||||
"help_text": _('Upload the snapshots generated by Workbench.'),
|
||||
})
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
form.save(self.request.user)
|
||||
messages.success(self.request, _("Evidence uploaded successfully."))
|
||||
|
@ -62,7 +70,7 @@ class ImportView(DashboardView, FormView):
|
|||
template_name = "upload.html"
|
||||
section = "evidences"
|
||||
title = _("Import Evidence")
|
||||
breadcrumb = "Evidences / Import"
|
||||
breadcrumb = _("Evidence") + " / " + _("Import") + " / "
|
||||
success_url = reverse_lazy('evidence:list')
|
||||
form_class = ImportForm
|
||||
|
||||
|
@ -86,7 +94,7 @@ class EvidenceView(DashboardView, FormView):
|
|||
template_name = "ev_details.html"
|
||||
section = "evidences"
|
||||
title = _("Evidences")
|
||||
breadcrumb = "Evidences / Details"
|
||||
breadcrumb = _("Evidence") + " / " + _("Details") + " / "
|
||||
success_url = reverse_lazy('evidence:list')
|
||||
form_class = UserTagForm
|
||||
|
||||
|
@ -103,6 +111,7 @@ class EvidenceView(DashboardView, FormView):
|
|||
context = super().get_context_data(**kwargs)
|
||||
context.update({
|
||||
'object': self.object,
|
||||
'form2': EraseServerForm(**self.get_form_kwargs(), data=self.request.POST or None),
|
||||
})
|
||||
return context
|
||||
|
||||
|
@ -113,6 +122,23 @@ class EvidenceView(DashboardView, FormView):
|
|||
kwargs['user'] = self.request.user
|
||||
return kwargs
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
form1 = self.get_form()
|
||||
#Empty param @initial makes it work, but i doubt it is the correct logic
|
||||
form2 = EraseServerForm(request.POST, user=self.request.user, initial={}, uuid=self.kwargs.get('pk'))
|
||||
|
||||
if "submit_form1" in request.POST and form1.is_valid():
|
||||
return self.form_valid(form1)
|
||||
elif "submit_form2" in request.POST and form2.is_valid():
|
||||
return self.form2_valid(form2)
|
||||
|
||||
return self.form_invalid(form1, form2)
|
||||
|
||||
def form2_valid(self, form):
|
||||
form.save(self.request.user)
|
||||
response = super().form_valid(form)
|
||||
return response
|
||||
|
||||
def form_valid(self, form):
|
||||
form.save(self.request.user)
|
||||
response = super().form_valid(form)
|
||||
|
@ -142,51 +168,6 @@ class DownloadEvidenceView(DashboardView, TemplateView):
|
|||
return response
|
||||
|
||||
|
||||
class EraseServerView(DashboardView, FormView):
|
||||
template_name = "ev_eraseserver.html"
|
||||
section = "evidences"
|
||||
title = _("Evidences")
|
||||
breadcrumb = "Evidences / Details"
|
||||
success_url = reverse_lazy('evidence:list')
|
||||
form_class = EraseServerForm
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.pk = kwargs['pk']
|
||||
self.object = Evidence(self.pk)
|
||||
if self.object.owner != self.request.user.institution:
|
||||
raise Http403
|
||||
|
||||
self.object.get_properties()
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context.update({
|
||||
'object': self.object,
|
||||
})
|
||||
return context
|
||||
|
||||
def get_form_kwargs(self):
|
||||
self.pk = self.kwargs.get('pk')
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs['uuid'] = self.pk
|
||||
kwargs['user'] = self.request.user
|
||||
return kwargs
|
||||
|
||||
def form_valid(self, form):
|
||||
form.save(self.request.user)
|
||||
response = super().form_valid(form)
|
||||
return response
|
||||
|
||||
def form_invalid(self, form):
|
||||
response = super().form_invalid(form)
|
||||
return response
|
||||
|
||||
def get_success_url(self):
|
||||
success_url = reverse_lazy('evidence:details', args=[self.pk])
|
||||
return success_url
|
||||
|
||||
|
||||
class DeleteEvidenceTagView(DashboardView, DeleteView):
|
||||
model = SystemProperty
|
||||
|
||||
|
@ -197,14 +178,14 @@ class DeleteEvidenceTagView(DashboardView, DeleteView):
|
|||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
|
||||
message = _("<Deleted> Evidence Tag: {}").format(self.object.value)
|
||||
message = _("<Deleted> Evidence Tag: {}").format(self.object.value)
|
||||
DeviceLog.objects.create(
|
||||
snapshot_uuid=self.object.uuid,
|
||||
event=message,
|
||||
user=self.request.user,
|
||||
institution=self.request.user.institution
|
||||
)
|
||||
self.object.delete()
|
||||
self.object.delete()
|
||||
|
||||
messages.info(self.request, _("Evicende Tag deleted successfully."))
|
||||
return self.handle_success()
|
||||
|
@ -214,6 +195,6 @@ class DeleteEvidenceTagView(DashboardView, DeleteView):
|
|||
|
||||
def get_success_url(self):
|
||||
return self.request.META.get(
|
||||
'HTTP_REFERER',
|
||||
'HTTP_REFERER',
|
||||
reverse_lazy('evidence:details', args=[self.object.uuid])
|
||||
)
|
||||
|
|
BIN
locale/ca/LC_MESSAGES/django.mo
Normal file
BIN
locale/ca/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
1296
locale/ca/LC_MESSAGES/django.po
Normal file
1296
locale/ca/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load diff
BIN
locale/es/LC_MESSAGES/django.mo
Normal file
BIN
locale/es/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
1291
locale/es/LC_MESSAGES/django.po
Normal file
1291
locale/es/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,46 +1,52 @@
|
|||
{% extends "login_base.html" %}
|
||||
{% load i18n static %}
|
||||
{% load i18n static language_code %}
|
||||
|
||||
{% block login_content %}
|
||||
|
||||
<div class="pt-2 pb-3">
|
||||
<h5 class="card-title text-center pb-0 fs-4 help"> {% trans "Sign in" %}</h5>
|
||||
</div>
|
||||
|
||||
<form action="{% url 'login:login' %}" method="post" class="row g-3 needs-validation" novalidate>
|
||||
{% csrf_token %}
|
||||
<div class="col-12">
|
||||
<div class="col-12 mb-">
|
||||
<input type="email" name="username" maxlength="100" autocapitalize="off"
|
||||
autocorrect="off" class="form-control textinput textInput" id="yourEmail" required
|
||||
autocorrect="off" class="form-control textinput textInput {% if form.username.errors %}is-invalid{% endif %}" id="yourEmail" required
|
||||
autofocus placeholder="{{ form.username.label }}"
|
||||
{% if form.username.value %}value="{{ form.username.value }}" {% endif %}>
|
||||
<div class="invalid-feedback">Please enter your email.</div>
|
||||
{% if form.username.errors %}
|
||||
<p class="text-error">
|
||||
{{ form.username.errors|striptags }}
|
||||
</p>
|
||||
<div class="invalid-feedback d-block">
|
||||
{{ form.username.errors|striptags }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<div class="col-12 mb-3">
|
||||
<div class="input-group">
|
||||
<input type="password" name="password" maxlength="100" autocapitalize="off"
|
||||
autocorrect="off" class="form-control textinput textInput" id="id_password"
|
||||
placeholder="{{ form.password.label }}" required>
|
||||
<input type="password" name="password" maxlength="100" autocapitalize="off"
|
||||
autocorrect="off" class="form-control textinput textInput {% if form.password.errors %}is-invalid{% endif %}" id="id_password"
|
||||
placeholder="{{ form.password.label }}" required>
|
||||
<i class="input-group-text bi bi-eye" id="togglePassword" style="cursor: pointer"></i>
|
||||
</div>
|
||||
{% if form.password.errors %}
|
||||
<p class="text-error">
|
||||
{{ form.password.errors|striptags }}
|
||||
</p>
|
||||
{% endif %}
|
||||
<i class="input-group-text bi bi-eye" id="togglePassword" style="cursor: pointer">
|
||||
</i>
|
||||
<div class="invalid-feedback d-block">
|
||||
{{ form.password.errors|striptags }}
|
||||
</div>
|
||||
<div class="invalid-feedback">Please enter your password!</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<input name="next" type="hidden" value="{{ success_url }}">
|
||||
|
||||
<div class="col-12">
|
||||
<button class="btn btn-primary w-100" type="submit">Next</button>
|
||||
</div>
|
||||
<div class="col-12 mb-3">
|
||||
<button class="btn btn-green-user w-100" type="submit">{% trans "Login" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
<div id="login-footer" class="mt-3">
|
||||
<a href="{% url 'login:password_reset' %}" data-toggle="modal" data-target="#forgotPasswordModal">{% trans "Forgot your password? Click here to recover" %}</a>
|
||||
|
||||
|
||||
|
||||
<div id="login-footer" class="d-flex justify-content-between align-items-center mt-4">
|
||||
<a href="{% url 'login:password_reset' %}" data-toggle="modal" data-target="#forgotPasswordModal">{% trans "Forgot your password?" %}</a>
|
||||
{% include "language_picker.html" %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
|
||||
<div class="container">
|
||||
|
||||
<section class="section register min-vh-100 d-flex flex-column align-items-center justify-content-center py-4">
|
||||
<section class="section register min-vh-100 d-flex flex-column align-items-center justify-content-center">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-4 col-md-6 d-flex flex-column align-items-center justify-content-center">
|
||||
|
@ -57,51 +57,37 @@
|
|||
</a>
|
||||
</div><!-- End Logo -->
|
||||
|
||||
<div class="card mb-3 shadow p-3 mb-5 bg-body rounded">
|
||||
<div class="card shadow bg-body rounded">
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<div class="pt-2 pb-3">
|
||||
<h5 class="card-title text-center pb-0 fs-4 help">Sign in</h5>
|
||||
|
||||
</div>
|
||||
|
||||
{% block login_content %}
|
||||
|
||||
{% endblock login_content %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if messages %}
|
||||
<div class="col-12 mt-3">
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-danger show text-center" role="alert">
|
||||
{{message}}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="credits">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<main class="col-md-12 bt-5">
|
||||
{% block messages %}
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-{{ message.tags|default:'info' }} alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endblock messages %}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="footer text-center">
|
||||
<footer class="footer text-center fixed-bottom bg-light py-3">
|
||||
<div class="container">
|
||||
<span class="text-muted">{{ commit_id }}</span>
|
||||
</div>
|
||||
|
|
|
@ -2,26 +2,23 @@
|
|||
{% load i18n django_bootstrap5 %}
|
||||
|
||||
{% block login_content %}
|
||||
|
||||
<div class="well">
|
||||
<div class="row-fluid">
|
||||
<h2>{% trans 'Password reset' %}</h2>
|
||||
<span>{% trans "Forgotten your password? Enter your email address below, and we'll email instructions for setting a new one." %}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="pt-2 pb-3">
|
||||
<h4 class="card-title text-center pb-2 help"> {% trans "Password Reset" %}</h5>
|
||||
</div>
|
||||
<p class="text-center text-muted fs-6">{% trans "Enter your email address below, and we'll email instructions for setting a new one." %}</p>
|
||||
<form action="{% url 'login:password_reset' %}" method="post" class="mt-4">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form layout='floating' %}
|
||||
{% bootstrap_form_errors form type='non_fields' %}
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-primary">{% trans 'Reset' %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="well">
|
||||
<div class="row-fluid">
|
||||
<div>
|
||||
<form action="{% url 'login:password_reset' %}" role="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
{% bootstrap_form_errors form type='non_fields' %}
|
||||
<div class="form-actions-no-box">
|
||||
<input type="submit" name="submit" value="{% trans 'Reset my password' %}" class="btn btn-primary form-control" id="submit-id-submit">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div><!-- /.row-fluid -->
|
||||
</div><!--/.well-->
|
||||
{% endblock %}
|
||||
<div class="text-center mt-3">
|
||||
<a href="{% url 'login:login' %}" class="btn btn-link text-secondary">{% trans 'Back to login' %}</a>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -6,11 +6,19 @@
|
|||
<div class="row-fluid">
|
||||
<div class="well" style="width: 800px; margin: auto auto 50px auto">
|
||||
<div class="row-fluid">
|
||||
<h2>{% trans 'Password reset complete' %}</h2>
|
||||
<p>{% trans 'Your password has been set. You may go ahead and log in now.' %}</p>
|
||||
<a href="{% url 'login:login' %}">{% trans 'Login' %}</a>
|
||||
|
||||
<h2 class="card-title ">{% trans 'Password reset complete' %}</h2>
|
||||
|
||||
<p class="text-muted fs-6 mt-4">{% trans 'Your new password has been set. You may go ahead and log in now.' %}</p>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div><!--/.well-->
|
||||
</div><!-- /.row-fluid -->
|
||||
|
||||
<div class="text-end mt-3">
|
||||
<a href="{% url 'login:login' %}" class="btn btn-link text-secondary">{% trans 'Back to login' %}</a>
|
||||
</div>
|
||||
</div><!-- /.container-fluid -->
|
||||
{% endblock %}
|
||||
|
|
|
@ -4,12 +4,14 @@
|
|||
{% block login_content %}
|
||||
<div class="well">
|
||||
<div class="row-fluid">
|
||||
<h2>{% trans 'Password reset sent' %}</h2>
|
||||
|
||||
<p>{% trans "We've emailed you instructions for setting your password, if an account exists with the email you entered. You should receive them shortly." %}</p>
|
||||
|
||||
<p>{% trans "If you don't receive an email, please make sure you've entered the address you registered with, and check your spam folder." %}</p>
|
||||
<h4 class="card-title text-center text-bold">{% trans 'Password reset sent' %}</h4>
|
||||
<p class="text-center text-muted mt-4 fs-7">{% trans "We've sent you an email with instructions to reset your password. If an account with the provided email exists, you should receive it shortly." %}</p>
|
||||
<p class="text-center text-muted fs-7">{% trans "If you don't receive an email, please check the email address you entered and look in your spam folder." %}</p>
|
||||
|
||||
</div><!-- /.row-fluid -->
|
||||
|
||||
<div class="text-center mt-3">
|
||||
<a href="{% url 'login:login' %}" class="btn btn-link text-primary">{% trans 'Back to login' %}</a>
|
||||
</div>
|
||||
</div><!--/.well-->
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import logging
|
||||
|
||||
from django.contrib import messages
|
||||
from django.conf import settings
|
||||
from django.urls import reverse_lazy
|
||||
from django.contrib.auth import views as auth_views
|
||||
|
@ -39,6 +40,10 @@ class LoginView(auth_views.LoginView):
|
|||
return redirect(reverse_lazy("login:login"))
|
||||
|
||||
return redirect(self.extra_context['success_url'])
|
||||
|
||||
def form_invalid(self, form):
|
||||
messages.error(self.request, _("Login error. Check credentials."))
|
||||
return self.render_to_response(self.get_context_data(form=form), status=401)
|
||||
|
||||
|
||||
def LogoutView(request):
|
||||
|
|
|
@ -1,28 +1,27 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
<h3>{{ subtitle }}</h3>
|
||||
</div>
|
||||
<div class="col text-center">
|
||||
{% block actions %}
|
||||
{% if show_closed %}
|
||||
<a href="?show_closed=false" class="btn btn-green-admin">
|
||||
{% trans 'Hide closed lots' %}
|
||||
<i class="bi bi-archive-fill"></i>
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="?show_closed=true" class="btn btn-green-admin">
|
||||
{% trans 'Show closed lots' %}
|
||||
<i class="bi bi-archive"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
<a href="{% url 'lot:add' %}" type="button" class="btn btn-green-admin">
|
||||
<i class="bi bi-plus"></i>
|
||||
{% trans 'Add new lot' %}
|
||||
<i class="bi bi-folder-plus"></i>
|
||||
{% trans 'New lot' %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
|
||||
<div class="row">
|
||||
<table class= "table table-striped table-sm">
|
||||
|
|
|
@ -1,25 +1,22 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block actions %}
|
||||
<a href="{% url 'lot:add_property' lot.pk %}" class="btn btn-green-admin d-flex align-items-center">
|
||||
<i class="bi bi-plus pe-2"></i>
|
||||
Add Property
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>Lot {{ lot.name }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<h3>Lot {{ lot.name }}</h3>
|
||||
|
||||
<div class="row">
|
||||
<div class="tab-pane fade show active" id="details">
|
||||
<div class="d-flex justify-content-end mt-1 mb-3">
|
||||
<a href="{% url 'lot:add_property' lot.pk %}" class="btn btn-green-admin d-flex align-items-center">
|
||||
|
||||
<i class="bi bi-plus"></i>
|
||||
Add new lot Property
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h5 class="card-title mt-2">Properties</h5>
|
||||
<table class="table table-hover table-bordered table-responsive align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
|
|
18
lot/views.py
18
lot/views.py
|
@ -17,7 +17,7 @@ from lot.forms import LotsForm
|
|||
class NewLotView(DashboardView, CreateView):
|
||||
template_name = "new_lot.html"
|
||||
title = _("New lot")
|
||||
breadcrumb = "lot / New lot"
|
||||
breadcrumb = _("Lot") + " / " + _("New") + " / "
|
||||
success_url = reverse_lazy('dashboard:unassigned_devices')
|
||||
model = Lot
|
||||
fields = (
|
||||
|
@ -38,7 +38,7 @@ class NewLotView(DashboardView, CreateView):
|
|||
class DeleteLotView(DashboardView, DeleteView):
|
||||
template_name = "delete_lot.html"
|
||||
title = _("Delete lot")
|
||||
breadcrumb = "lot / Delete lot"
|
||||
breadcrumb = _("Lot") + " / " + _("Delete") + " / "
|
||||
success_url = reverse_lazy('dashboard:unassigned_devices')
|
||||
model = Lot
|
||||
fields = (
|
||||
|
@ -57,7 +57,7 @@ class DeleteLotView(DashboardView, DeleteView):
|
|||
class EditLotView(DashboardView, UpdateView):
|
||||
template_name = "new_lot.html"
|
||||
title = _("Edit lot")
|
||||
breadcrumb = "Lot / Edit lot"
|
||||
breadcrumb = _("Lot") + " / " + _("Edit") + " / "
|
||||
success_url = reverse_lazy('dashboard:unassigned_devices')
|
||||
model = Lot
|
||||
fields = (
|
||||
|
@ -83,7 +83,7 @@ class EditLotView(DashboardView, UpdateView):
|
|||
class AddToLotView(DashboardView, FormView):
|
||||
template_name = "list_lots.html"
|
||||
title = _("Add to lots")
|
||||
breadcrumb = "lot / add to lots"
|
||||
breadcrumb = _("Lot") + " / " + _("Assign Device") + " / "
|
||||
success_url = reverse_lazy('dashboard:unassigned_devices')
|
||||
form_class = LotsForm
|
||||
|
||||
|
@ -112,7 +112,7 @@ class AddToLotView(DashboardView, FormView):
|
|||
|
||||
class DelToLotView(AddToLotView):
|
||||
title = _("Remove from lots")
|
||||
breadcrumb = "lot / remove from lots"
|
||||
breadcrumb = _("Lot") + " / " + _("Unassign Device") + " / "
|
||||
|
||||
def form_valid(self, form):
|
||||
form.devices = self.get_session_devices()
|
||||
|
@ -124,7 +124,7 @@ class DelToLotView(AddToLotView):
|
|||
class LotsTagsView(DashboardView, TemplateView):
|
||||
template_name = "lots.html"
|
||||
title = _("lots")
|
||||
breadcrumb = _("lots") + " /"
|
||||
breadcrumb = _("Lot") + " / "
|
||||
success_url = reverse_lazy('dashboard:unassigned_devices')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
@ -149,7 +149,7 @@ class LotsTagsView(DashboardView, TemplateView):
|
|||
class LotPropertiesView(DashboardView, TemplateView):
|
||||
template_name = "properties.html"
|
||||
title = _("New Lot Property")
|
||||
breadcrumb = "Lot / New property"
|
||||
breadcrumb = _("Lot") + " / " + _("Property") + " / "
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
self.pk = kwargs.get('pk')
|
||||
|
@ -172,7 +172,7 @@ class LotPropertiesView(DashboardView, TemplateView):
|
|||
class AddLotPropertyView(DashboardView, CreateView):
|
||||
template_name = "new_property.html"
|
||||
title = _("New Lot Property")
|
||||
breadcrumb = "Device / New property"
|
||||
breadcrumb = _("Lot") + " / " + _("Property") + " / " +_("New")
|
||||
success_url = reverse_lazy('dashboard:unassigned_devices')
|
||||
model = LotProperty
|
||||
fields = ("key", "value")
|
||||
|
@ -206,7 +206,7 @@ class AddLotPropertyView(DashboardView, CreateView):
|
|||
class UpdateLotPropertyView(DashboardView, UpdateView):
|
||||
template_name = "properties.html"
|
||||
title = _("Update lot Property")
|
||||
breadcrumb = "Lot / Update Property"
|
||||
breadcrumb = _("Lot") + " / " + _("Property") + " / " +_("Update")
|
||||
model = LotProperty
|
||||
fields = ("key", "value")
|
||||
|
||||
|
|
|
@ -1,27 +1,53 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% load i18n get_language_code %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
<h3>{{ subtitle }}</h3>
|
||||
</div>
|
||||
<div class="col text-center">
|
||||
{{ user.email }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<a class="nav-link fw-bold" href="{% url 'api:tokens' %}">
|
||||
{% translate 'Admin your Tokens' %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col">
|
||||
<a class="nav-link fw-bold" href="{% url 'user:settings' %}">
|
||||
{% translate 'Download a settings file' %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-5">
|
||||
<div class="col d-flex align-items-center">
|
||||
|
||||
{% endblock %}
|
||||
<i class="bi bi-person-circle fs-2 me-3"></i>
|
||||
<h3 class="mb-0">{{ user.email }}</h3>
|
||||
</div>
|
||||
|
||||
{# The language picker is mostly here to fill up some space on user settings #}
|
||||
<div class="col text-end">
|
||||
|
||||
{% include "language_picker.html" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100 shadow-sm border-0">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title mb-3">
|
||||
<i class="bi bi-key me-2"></i> {% translate 'Token Management' %}
|
||||
</h5>
|
||||
<p class="card-text flex-grow-1">{% translate 'Manage your personal tokens for using Devicehub.' %}</p>
|
||||
<div class="text-end">
|
||||
<a href="{% url 'api:tokens' %}" class="btn btn-outline-dark btn-sm d-inline-flex align-items-center">
|
||||
<span class="me-2">{% translate 'Go' %}</span>
|
||||
<i class="bi bi-arrow-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100 shadow-sm border-0">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title mb-3">
|
||||
<i class="bi bi-gear me-2"></i> {% translate 'Settings File' %}
|
||||
</h5>
|
||||
<p class="card-text flex-grow-1">{% translate 'Download a settings file for your Workbench.' %}</p>
|
||||
<div class="text-end">
|
||||
<a href="{% url 'user:settings' %}" class="btn btn-outline-dark btn-sm d-inline-flex align-items-center">
|
||||
<span class="me-2">{% translate 'Go' %}</span>
|
||||
<i class="bi bi-arrow-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>{% endblock %}
|
||||
|
|
Loading…
Reference in a new issue