diff --git a/Lagerregal/urls.py b/Lagerregal/urls.py index ef3291a7..d7c14ec9 100644 --- a/Lagerregal/urls.py +++ b/Lagerregal/urls.py @@ -175,6 +175,7 @@ path('devices_ajax/get_attributes/', login_required(devicetypes_ajax.GetTypeAttributes.as_view()), name="get-attributes"), path('devices_ajax/user_lendings/', login_required(devices_ajax.UserLendings.as_view()), name="get-user-lendings"), path('devices_ajax/devicedetails//', login_required(devicedata_ajax.DeviceDetails.as_view()), name="device-details"), + path('devices_ajax/devicedetails//json', login_required(devicedata_ajax.DeviceDetailsJson.as_view()), name="device-details-json"), path('devices_ajax/devicesoftware//', login_required(devicedata_ajax.DeviceSoftware.as_view()), name="device-software"), ] diff --git a/devicedata/ajax.py b/devicedata/ajax.py index 20733460..e02d0502 100644 --- a/devicedata/ajax.py +++ b/devicedata/ajax.py @@ -1,4 +1,4 @@ -from django.http import HttpResponse +from django.http import HttpResponse, JsonResponse from django.shortcuts import get_object_or_404 from django.shortcuts import render from django.views.generic.base import View @@ -7,12 +7,15 @@ from devicedata.providers.opsi import OpsiProvider from devicedata.providers.puppet import PuppetProvider from devices.models import Device +import logging data_providers = { "opsi": OpsiProvider, "puppet": PuppetProvider } +logger = logging.getLogger(__name__) + def _get_provider(device): if device.data_provider is not None and device.data_provider in data_providers.keys(): @@ -31,14 +34,32 @@ def get(request, device): device = get_object_or_404(Device, pk=device) try: device_info = _get_provider(device).get_device_info(device) - except: + except Exception as e: + logger.error(e) return HttpResponse(_("Could not load data.")) context = { - 'device_info': device_info + 'device_info': device_info.raw_entries } return render(request, 'devicedata/device_info.html', context) +class DeviceDetailsJson(View): + + @staticmethod + def get(request, device): + device = get_object_or_404(Device, pk=device) + provider = _get_provider(device) + if provider is None: + return JsonResponse({}) + device_info = provider.get_device_info(device) + raw_entries = [{"name": entry.name, + "type": entry.type, + "raw_value": entry.raw_value} for entry in device_info.raw_entries] + formatted_entries = [{"name": entry.name, + "value": entry.value} for entry in device_info.formatted_entries] + return JsonResponse({"raw_entries": raw_entries, "formatted_entries": formatted_entries}) + + class DeviceSoftware(View): @staticmethod @@ -46,9 +67,10 @@ def get(request, device): device = get_object_or_404(Device, pk=device) try: software_info = _get_provider(device).get_software_info(device) - except: + except Exception as e: + logger.error(e) return HttpResponse(_("Could not load data.")) context = { 'software_info': software_info } - return render(request, 'devicedata/software_info.html', context) \ No newline at end of file + return render(request, 'devicedata/software_info.html', context) diff --git a/devicedata/providers/base_provider.py b/devicedata/providers/base_provider.py index d16aea96..4b1b96c2 100644 --- a/devicedata/providers/base_provider.py +++ b/devicedata/providers/base_provider.py @@ -2,8 +2,30 @@ from abc import ABC, abstractmethod -class DeviceInfo: - pass +class BaseDeviceInfo(ABC): + formatted_entries = [] + raw_entries = [] + + def find_entries(self, entry_type): + return [entry for entry in self.raw_entries if entry.type == entry_type] + + def __init__(self, raw_entries): + self.formatted_entries = [] + self.raw_entries = raw_entries + self.format_entries() + + @abstractmethod + def format_entries(self): + pass + + +class FormattedDeviceInfoEntry: + name = "" + value = "" + + def __init__(self, name, value): + self.name = name + self.value = value class DeviceInfoEntry: diff --git a/devicedata/providers/helpers.py b/devicedata/providers/helpers.py new file mode 100644 index 00000000..11b81f1f --- /dev/null +++ b/devicedata/providers/helpers.py @@ -0,0 +1,8 @@ +def format_bytes(size, power=2**10): + # 2**10 = 1024 + n = 0 + power_labels = {0: '', 1: 'K', 2: 'M', 3: 'G', 4: 'T'} + while size > power: + size /= power + n += 1 + return "{0:g}{1}".format(size, power_labels[n]+"B") \ No newline at end of file diff --git a/devicedata/providers/opsi.py b/devicedata/providers/opsi.py index 603a297a..6b0d8d40 100644 --- a/devicedata/providers/opsi.py +++ b/devicedata/providers/opsi.py @@ -1,8 +1,77 @@ from django.core.exceptions import ObjectDoesNotExist from Lagerregal import settings -from devicedata.providers.base_provider import BaseProvider, SoftwareEntry, DeviceInfoEntry +from devicedata.providers.base_provider import BaseProvider, SoftwareEntry, DeviceInfoEntry, BaseDeviceInfo, \ + FormattedDeviceInfoEntry +from devicedata.providers.helpers import format_bytes from devicedata.providers.opsirpc import OpsiConnection +from django.utils.translation import ugettext_lazy as _ + + +class OpsiDeviceInfo(BaseDeviceInfo): + + def format_chassis(self): + entries = self.find_entries("CHASSIS") + if len(entries) > 0: + self.formatted_entries.append( + FormattedDeviceInfoEntry(_("Serial Number"), entries[0].raw_value["serialNumber"])) + self.formatted_entries.append(FormattedDeviceInfoEntry(_("Type"), entries[0].raw_value["chassisType"])) + + def format_system(self): + entries = self.find_entries("COMPUTER_SYSTEM") + if len(entries) > 0: + self.formatted_entries.append(FormattedDeviceInfoEntry(_("Manufacturer"), entries[0].raw_value["vendor"])) + self.formatted_entries.append(FormattedDeviceInfoEntry(_("Hostname"), entries[0].raw_value["hostId"])) + self.formatted_entries.append(FormattedDeviceInfoEntry(_("Last Seen"), entries[0].raw_value["lastseen"])) + + def format_processor(self): + entries = self.find_entries("PROCESSOR") + if len(entries) > 0: + self.formatted_entries.append(FormattedDeviceInfoEntry(_("Processor"), entries[0].name)) + + def format_memory(self): + entries = self.find_entries("MEMORY_MODULE") + capacities = [] + for entry in entries: + capacities.append(entry.raw_value["capacity"]) + total_capacity = format_bytes(sum(capacities)) + formatted_capacities = ", ".join([format_bytes(capacity) for capacity in capacities]) + self.formatted_entries.append( + FormattedDeviceInfoEntry(_("Memory"), "{0} ({1})".format(total_capacity, formatted_capacities))) + + def format_storage(self): + entries = self.find_entries("HARDDISK_DRIVE") + drives = [] + for entry in entries: + if "USB" not in entry.raw_value["name"]: + drives.append(entry.raw_value) + formatted_capacities = "
".join( + ["{0} {1}".format(drive["model"], format_bytes(drive["size"], power=1000)) for drive in drives]) + self.formatted_entries.append(FormattedDeviceInfoEntry(_("Storage"), formatted_capacities)) + + def format_network(self): + entries = self.find_entries("NETWORK_CONTROLLER") + controllers = [] + for entry in entries: + if entry.raw_value["ipAddress"] is not None: + controllers.append(entry.raw_value) + formatted_controllers = "
".join( + ["{0} {1}".format(controller["description"], controller["ipAddress"]) for controller in controllers]) + self.formatted_entries.append(FormattedDeviceInfoEntry(_("Network"), formatted_controllers)) + + def format_graphics(self): + entries = self.find_entries("VIDEO_CONTROLLER") + if len(entries) > 0: + self.formatted_entries.append(FormattedDeviceInfoEntry(_("Graphics"), entries[0].name)) + + def format_entries(self): + self.format_chassis() + self.format_system() + self.format_processor() + self.format_memory() + self.format_storage() + self.format_network() + self.format_graphics() class OpsiProvider(BaseProvider): @@ -15,6 +84,8 @@ def __get_host(self, device): host = None for ip in device.ipaddress_set.all(): response = self.__connection.host_getObjects(ipAddress=ip.address) + if len(response) == 0: + raise ObjectDoesNotExist() host = response[0] for h in response: if str(device.id) in h['id']: @@ -31,7 +102,7 @@ def get_device_info(self, device): device_entries = [] for entry in hardware: device_entries.append(DeviceInfoEntry(entry["hardwareClass"], entry["name"], entry)) - return device_entries + return OpsiDeviceInfo(device_entries) def get_software_info(self, device): host = self.__get_host(device) diff --git a/devicedata/providers/puppet.py b/devicedata/providers/puppet.py index c15b6d8a..5af59d63 100644 --- a/devicedata/providers/puppet.py +++ b/devicedata/providers/puppet.py @@ -5,11 +5,95 @@ import requests from django.core.exceptions import ValidationError, ObjectDoesNotExist -from django.http import HttpResponse -from urllib3.exceptions import SubjectAltNameWarning - from Lagerregal import settings -from devicedata.providers.base_provider import BaseProvider, DeviceInfoEntry, SoftwareEntry +from devicedata.providers.base_provider import BaseProvider, BaseDeviceInfo, DeviceInfoEntry, SoftwareEntry, \ + FormattedDeviceInfoEntry +from django.utils.translation import ugettext_lazy as _ + +from devicedata.providers.helpers import format_bytes + + +class PuppetDeviceInfo(BaseDeviceInfo): + + def format_serialnumber(self): + entries = self.find_entries("sp_serial_number") + entries.extend(self.find_entries("serialnumber")) + if len(entries) > 0: + self.formatted_entries.append(FormattedDeviceInfoEntry(_("Serial Number"), entries[0].raw_value)) + + def format_type(self): + entries = self.find_entries("sp_machine_name") + if len(entries) > 0: + self.formatted_entries.append(FormattedDeviceInfoEntry(_("Type"), entries[0].raw_value)) + + def format_hostname(self): + entries = self.find_entries("fqdn") + if len(entries) > 0: + self.formatted_entries.append(FormattedDeviceInfoEntry(_("Hostname"), entries[0].raw_value)) + + def format_lastseen(self): + entries = self.find_entries("timestamp") + if len(entries) > 0: + self.formatted_entries.append(FormattedDeviceInfoEntry(_("Last Seen"), entries[0].raw_value)) + + def format_processor(self): + entries = self.find_entries("processors") + if len(entries) > 0: + processors = {processor for processor in entries[0].raw_value["models"]} + self.formatted_entries.append( + FormattedDeviceInfoEntry(_("Processor"), "
".join(processors))) + + def format_memory(self): + entries = self.find_entries("memory") + if len(entries) > 0: + system = entries[0].raw_value["system"] + self.formatted_entries.append( + FormattedDeviceInfoEntry(_("Memory"), format_bytes(system["total_bytes"]))) + + def format_storage(self): + entries = self.find_entries("disks") + drives = [] + for entry in entries: + for disk_identifier in entry.raw_value: + disk = entry.raw_value[disk_identifier] + if "size_bytes" in disk and disk["size_bytes"] < 1000000000: + # Smaller than 10gb. This most likely is not a hard drive. + continue + if "model" in disk and "USB" not in disk["model"]: + disk["identifier"] = disk_identifier + drives.append(disk) + formatted_capacities = "
".join( + ["{0} ({1}): {2}".format(drive["model"], drive["identifier"], format_bytes(drive["size_bytes"])) for drive in drives]) + self.formatted_entries.append(FormattedDeviceInfoEntry(_("Storage"), formatted_capacities)) + + def format_network(self): + entries = self.find_entries("networking") + controllers = [] + for entry in entries: + interfaces = entry.raw_value["interfaces"] + for interface in interfaces: + if "mac" in interfaces[interface] and interfaces[interface]["mac"] is not None: + interfaces[interface]["identifier"] = interface + controllers.append(interfaces[interface]) + formatted_controllers = "
".join( + ["{0}: {1}".format(controller["identifier"], controller["ip"]) for controller in controllers]) + self.formatted_entries.append(FormattedDeviceInfoEntry(_("Network"), formatted_controllers)) + + def format_graphics(self): + entries = self.find_entries("VIDEO_CONTROLLER") + if len(entries) > 0: + self.formatted_entries.append(FormattedDeviceInfoEntry(_("Graphics"), entries[0].name)) + + def format_entries(self): + self.format_serialnumber() + self.format_type() + self.format_hostname() + self.format_lastseen() + self.format_processor() + self.format_memory() + self.format_storage() + self.format_network() + self.format_graphics() class PuppetProvider(BaseProvider): @@ -56,7 +140,7 @@ def get_device_info(self, device): device_entries = [] for entry in res: device_entries.append(DeviceInfoEntry(entry["name"], None, entry["value"])) - return device_entries + return PuppetDeviceInfo(device_entries) def get_software_info(self, device): software_fact = settings.PUPPETDB_SETTINGS['software_fact'] diff --git a/devices/forms.py b/devices/forms.py index 167bb84d..9b617de2 100644 --- a/devices/forms.py +++ b/devices/forms.py @@ -258,7 +258,7 @@ def clean(self): if key.startswith("attribute_") and attribute != "": attributenumber = key.split("_")[1] typeattribute = get_object_or_404(TypeAttribute, pk=attributenumber) - if re.match(typeattribute.regex, attribute) is None: + if typeattribute.regex is not None and re.match(typeattribute.regex, attribute) is None: self._errors[key] = self.error_class( [_("Doesn't match the given regex \"{0}\".".format(typeattribute.regex))]) unclean_data.append(key) diff --git a/devices/views.py b/devices/views.py index 6f10088c..6a8ea216 100644 --- a/devices/views.py +++ b/devices/views.py @@ -198,6 +198,7 @@ class DeviceDetail(PermissionRequiredMixin, DetailView): context_object_name = 'device' object = None permission_required = 'devices.view_device' + template_name = "devices/detail/device_detail.html" def get_object(self, queryset=None): if self.object is not None: @@ -1729,7 +1730,7 @@ def get_context_data(self, **kwargs): class PublicDeviceDetailView(DetailView): - template_name = "devices/device_detail.html" + template_name = "devices/detail/device_detail.html" context_object_name = "device" def get_queryset(self): diff --git a/templates/devices/detail/device_detail.html b/templates/devices/detail/device_detail.html new file mode 100644 index 00000000..12ebce00 --- /dev/null +++ b/templates/devices/detail/device_detail.html @@ -0,0 +1,518 @@ +{% extends "base.html" %} +{% load devicetags %} +{% load i18n %} +{% load staticfiles %} +{% load usertags %} + +{% block title %}{{ device.name }}{% endblock %} + +{% block header %} + {% trans "Device" %}: {{ device.name }} +{% endblock %} + +{% block pullright %} + {% has_perm 'devices.change_device' user device as can_change_device %} + {% has_perm 'devices.add_device' user device as can_add_device %} + {% has_perm 'devices.delete_device' user device as can_delete_device %} + {% has_perm 'devices.lend_device' user device as can_lend_device %} + + {% if device.archived == None and device.trashed == None %} + {% if can_lend_device %} + {% if device.currentlending != None %} + + {% trans "Edit lending information" %} + + + {% trans "returned" %} + + {% else %} + + {% trans "Lend" %} + + {% endif %} + {% endif %} + + {% if can_lend_device %} + + {% trans "Send Mail" %} + + {% endif %} + + {% if can_change_device %} + + {% trans "Edit" %} + +
+ {% csrf_token %} + {% with is_bookmarked=device|check_bookmark:user %} + {% if is_bookmarked %} + + {% else %} + + {% endif %} + {% endwith %} +
+ {% endif %} + +
+ + +
+ {% else %} + {% if can_change_device and device.archived != None %} +
+ {% csrf_token %} + +
+ {% endif %} + + {% if can_change_device and device.trashed != None %} +
+ {% csrf_token %} + +
+ {% endif %} + {% endif %} +{% endblock %} + +{% block content %} + {% has_perm 'devices.change_device' user device as can_change_device %} + {% has_perm 'devices.add_device' user device as can_add_device %} + {% has_perm 'devices.delete_device' user device as can_delete_device %} + {% has_perm 'devices.lend_device' user device as can_lend_device %} + {% has_perm 'devices.view_devicedetails' user device as can_view_devicedetails %} + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% if device.used_in %} + + + + + {% endif %} + {% if usedset %} + + + + + {% endif %} + + + + + + + + + + + + + {% if device.manual %} + + + + + {% endif %} + {% if device.contact %} + + + + + {% endif %} + {% if device.creator %} + + + + + {% endif %} + + + + + {% if device.trashed %} + + + + + {% endif %} + {% if device.inventoried %} + + + + + {% endif %} + +
{% trans "Lagerregal ID" %}{{ device.pk }}
{% trans "Inventorynumber" %}{{ device.inventorynumber }}
{% trans "Serialnumber" %}{{ device.serialnumber }}
{% trans "Hostname" %}{{ device.hostname }}
{% trans "Devicetype" %} + {% if device.devicetype %} + {{ device.devicetype }} + {% else %} + — + {% endif %} +
{% trans "Manufacturer" %} + {% if device.manufacturer %} + {{ device.manufacturer }} + {% else %} + — + {% endif %} +
{% trans "Room" %} + {% if device.room %} + {{ device.room }} + {% else %} + — + {% endif %} +
{% trans "Used in" %}{{ device.used_in }}
{% trans "Uses" %} + {% for element in usedset %} + {{ element }}
+ {% endfor %} +
{% trans "Devicegroup" %} + {% if device.group %} + {{ device.group }} + {% else %} + — + {% endif %} +
{% trans "Department" %} + {% if device.department %} + {{ device.department }} + {% else %} + — + {% endif %} +
{% trans "Short term device" %} + {% if device.templending %} + + {% else %} + + {% endif %} +
{% trans "Manual" %} + + {% with filename=device.manual|filename|splitstr:'.'|last %} + {{ device.name|add:'.'|add:filename }} + {% endwith %} + +
{% trans "Contact" %}{{ device.contact }}
{% trans "Created by" %}{{ device.creator }}
{% trans "Last edited" %} + {% if lastedit != None %} + {{ lastedit.revision.date_created }} by + {{ lastedit.revision.user }} + {% else %} + {% trans "Not edited yet" %} + {% endif %} +
{% trans "Trashed on" %}{{ device.trashed }}
{% trans "Last inventoried on" %}{{ device.inventoried }}
+ +
{% trans "Details" %}
+ + + {% for attributevalue in attributevalue_list %} + + + + + {% endfor %} + +
{{ attributevalue.typeattribute.name }}{{ attributevalue }}
+ + {% if device.webinterface != "" %} + {% trans "Go to webinterface" %} + {% endif %} + + {% if device.description %} +

{% trans "Description" %}

+ {{ device.description|linebreaks }} + {% endif %} +
+
+ {% if device.pictures.count > 0 %} + {% include "devices/detail/device_pictures.html" %} + {% endif %} + + {% include "devices/detail/device_lending_info.html" %} +
+
+ {% include "devices/detail/device_ipaddress_card.html" %} + + {% include "devices/detail/device_tags_card.html" %} +
+
+ + + +
+ {% if can_change_device %} +
+ {% include "devices/detail/device_notes_tab.html" %} +
+ {% endif %} + {% if can_change_device %} +
+ {% include "devices/detail/device_lending_tab.html" %} +
+ {% endif %} + + {% if can_change_device %} +
+ {% include "devices/detail/device_history_tab.html" %} +
+ {% endif %} + + {% if can_lend_device %} +
+ {% include "devices/detail/device_mail_tab.html" %} +
+ {% endif %} + + {% if can_view_devicedetails %} +
+

{% trans "Device data from backend providers" %}

+
+ {% trans "Loading Device Details..." %} +
+
+
+

{% trans "Installed software from backend providers" %}

+
+ {% trans "Loading Software List..." %} +
+
+ {% endif %} +
+{% endblock %} + +{% block htmlend %} + {% has_perm 'devices.lend_device' user device as can_lend_device %} + + {% if can_lend_device %} + {% include "snippets/modals/lending.html" with modalname="lendModal" %} + {% if device.currentlending %} + {% include "snippets/modals/lendingReturn.html" with modalname="returnModal" %} + {% endif %} + + {% include "snippets/modals/devicemailModal.html" with modalname="mailModal" %} + {% include "snippets/modals/deviceImageModal.html" with modalname="pictureModal" %} + {% endif %} + + + {% if display_printbutton %} + {% include "snippets/modals/deviceprintDymoModal.html" with modalname="printDymoModal" label_path=label_path %} + {% endif %} +{% endblock %} + +{% block scriptend %} + $(function() { + $("#id_ipaddresses").on("change", function(e) { + if ($(this).val()) { + $("#submitipaddress").removeClass("disabled"); + } else { + $("#submitipaddress").addClass("disabled"); + } + }); + + $("#id_tags").on("change", function(e) { + if ($(this).val()) { + $("#submittags").removeClass("disabled"); + } else { + $("#submittags").addClass("disabled"); + } + }); + + $("#id_mailtemplate").change(function() { + if ($(this).val() !== "") { + $.ajax({ + url: "{% url "load-mailtemplate" %}", + data: {"template":$(this).val(), "recipients":$("#id_emailrecipients").val()}, + }).done(function(data) { + $("#id_emailsubject").val(data.subject); + $("#id_emailbody").val(data.body); + $('#id_emailrecipients').val(data.recipients); + }); + } + }); + + $(document).on('click', '.dropdown-cancel', function(e) { + e.stopPropagation(); + }); + + $(document).on('click', '.delete-picture', function(e) { + var picture_id = e.target.id.replace("picture", ""); + var url = "{% url "device-api-picture" device.pk 0 %}".replace("pictures/0", "pictures/" + picture_id); + $.ajax({ + url: url, + type: 'DELETE', + success: function() { + var row = e.target.parentNode.parentNode; + var modalBody = row.parentNode; + modalBody.removeChild(row); + }, + }); + }); + + $(document).on('click', '.rotate-picture-right', function(e) { + rotate_and_reload(this, "right"); + }); + $(document).on('click', '.rotate-picture-left', function(e) { + rotate_and_reload(this, "left"); + }); + + function rotate_and_reload(target, direction){ + var picture_id = target.id.replace("picture", "") + var url = "{% url "device-api-picture-rotate" device.pk 0 "placeholder" %}".replace("pictures/0", "pictures/"+picture_id).replace("placeholder", direction); + $.ajax({ + url: url, + type: 'PATCH', + success: function(data) { + var d = new Date(); + img = target.parentElement.parentElement.getElementsByTagName('img')[0]; + if (data.new_source.length) { + // image was converted to png + img.src = img.src.replace(data.old_source, data.new_source); + } else { + // force cache invalidation and reload + img.src = img.src.split("?")[0] + '?' + d.getTime(); + } + }, + }); + } + + $.ajax({ + url: "{% url "device-details-json" device.pk %}", + type: 'GET', + success: function(data) { + for (index in data["formatted_entries"]) { + var entry = data["formatted_entries"][index]; + var row = '' + entry["name"] + '' + entry["value"] + ''; + $('#details_table tbody').append(row); + } + + var rowCount = $('#details_table tr').length; + console.log(rowCount); + if (rowCount == 0) { + $('#details_header').remove(); + $('#details_table').remove(); + } + } + }); + + $("#devicedetailsentry").load("{% url "device-details" device.pk %}"); + $("#softwaredetails").load("{% url "device-software" device.pk %}"); + }); +{% endblock %} diff --git a/templates/devices/detail/device_history_tab.html b/templates/devices/detail/device_history_tab.html new file mode 100644 index 00000000..7827a883 --- /dev/null +++ b/templates/devices/detail/device_history_tab.html @@ -0,0 +1,34 @@ +{% load i18n %} +

{% trans "10 last edits" %}

+ + + + + + + + + + {% for action in version_list %} + + + + + + {% endfor %} + +
{% trans 'Date/time' %}{% trans 'User' %}{% trans 'Comment' %}
+ + {{ action.revision.date_created }} + + + {% if action.revision.user %} + {{ action.revision.user }} + {% else %} + {% trans 'Unknown' %} + {% endif %} + {{ action.revision.comment|linebreaksbr|default:"" }}
+ + {% trans "View edit history" %} + diff --git a/templates/devices/detail/device_ipaddress_card.html b/templates/devices/detail/device_ipaddress_card.html new file mode 100644 index 00000000..9107a757 --- /dev/null +++ b/templates/devices/detail/device_ipaddress_card.html @@ -0,0 +1,36 @@ +{% load i18n %} +
+
{% trans "IP-Addresses" %}
+ + + {% if can_change_device and device.archived == None %} + + {% endif %} +
\ No newline at end of file diff --git a/templates/devices/detail/device_lending_info.html b/templates/devices/detail/device_lending_info.html new file mode 100644 index 00000000..1126f386 --- /dev/null +++ b/templates/devices/detail/device_lending_info.html @@ -0,0 +1,33 @@ +{% load i18n %} +

{% trans "Current Lending information" %}

+{% if device.currentlending != None %} + + + + + + + + + + + + + + + + + +
{% trans "Lent to" %} + {{ device.currentlending.owner }} +
{% trans "Since" %}{{ device.currentlending.lenddate }}
{% trans "Due to" %} + {{ device.currentlending.duedate }} + {% if device.currentlending.duedate < weekago %} + + {% elif device.currentlending.duedate < today %} + + {% endif %} +
{% trans "Overdue notification" %}{{ device.currentlending.duedate_email|default_if_none:_("Not sent") }}
+{% else %} +

{% trans "Currently not lent" %}

+{% endif %} \ No newline at end of file diff --git a/templates/devices/detail/device_lending_tab.html b/templates/devices/detail/device_lending_tab.html new file mode 100644 index 00000000..357c5dcc --- /dev/null +++ b/templates/devices/detail/device_lending_tab.html @@ -0,0 +1,25 @@ +{% load i18n %} +

{% trans "10 last lendings" %}

+ + + + + + + + + + + {% for lending in lending_list %} + + + + + + + {% endfor %} + +
{% trans "User" %}{% trans "Since" %}{% trans "Duedate" %}{% trans "Returned" %}
{{ lending.owner }}{{ lending.lenddate }}{{ lending.duedate|default_if_none:_("never") }}{{ lending.returndate|default_if_none:_("not returned") }}
+ + {% trans "View lending history" %} + diff --git a/templates/devices/detail/device_mail_tab.html b/templates/devices/detail/device_mail_tab.html new file mode 100644 index 00000000..0ed80fe3 --- /dev/null +++ b/templates/devices/detail/device_mail_tab.html @@ -0,0 +1,23 @@ +{% load i18n %} +

{% trans "10 last sent mails" %}

+ + + + + + + + + + + {% for mail in mail_list %} + + + + + + + {% endfor %} + +
{% trans "Mailtemplate" %}{% trans "Subject" %}{% trans "Sent by" %}{% trans "Sent at" %}
{{ mail.mailtemplate.name }}{{ mail.subject }}{{ mail.sent_by }}{{ mail.sent_at }}
\ No newline at end of file diff --git a/templates/devices/detail/device_notes_tab.html b/templates/devices/detail/device_notes_tab.html new file mode 100644 index 00000000..b3b84234 --- /dev/null +++ b/templates/devices/detail/device_notes_tab.html @@ -0,0 +1,51 @@ +{% load i18n %} +
    + {% for note in device.notes.all %} +
  • +
    +
    + {% if note.creator.avatar %} + + {% endif %} +

    {{ note.creator }}

    + {{ note.created_at|date:"SHORT_DATETIME_FORMAT" }} +
    +
    +
    + {{ note.note|linebreaks }} +
    + {% if note.creator == user %} +
    + +
    + {% csrf_token %} + +
    +
    + {% endif %} +
    +
    +
  • + {% endfor %} +
+ +
+
+ {% csrf_token %} +
+ +
+ + +
+ +
+
+
diff --git a/templates/devices/detail/device_pictures.html b/templates/devices/detail/device_pictures.html new file mode 100644 index 00000000..1229ce19 --- /dev/null +++ b/templates/devices/detail/device_pictures.html @@ -0,0 +1,30 @@ +{% load i18n %} + \ No newline at end of file diff --git a/templates/devices/detail/device_tags_card.html b/templates/devices/detail/device_tags_card.html new file mode 100644 index 00000000..073c6e62 --- /dev/null +++ b/templates/devices/detail/device_tags_card.html @@ -0,0 +1,26 @@ +{% load i18n %} +
+
{% trans "Tags" %}
+
    + {% for tag in device.tags.all %} +
  • + {{ tag.name }} + + + +
  • + {% endfor %} +
+ + +
\ No newline at end of file diff --git a/templates/devices/device_detail.html b/templates/devices/device_detail.html deleted file mode 100644 index 76312c1e..00000000 --- a/templates/devices/device_detail.html +++ /dev/null @@ -1,719 +0,0 @@ -{% extends "base.html" %} -{% load devicetags %} -{% load i18n %} -{% load staticfiles %} -{% load usertags %} - -{% block title %}{{ device.name }}{% endblock %} - -{% block header %} - {% trans "Device" %}: {{ device.name }} -{% endblock %} - -{% block pullright %} - {% has_perm 'devices.change_device' user device as can_change_device %} - {% has_perm 'devices.add_device' user device as can_add_device %} - {% has_perm 'devices.delete_device' user device as can_delete_device %} - {% has_perm 'devices.lend_device' user device as can_lend_device %} - - {% if device.archived == None and device.trashed == None %} - {% if can_lend_device %} - {% if device.currentlending != None %} - - {% trans "Edit lending information" %} - - - {% trans "returned" %} - - {% else %} - - {% trans "Lend" %} - - {% endif %} - {% endif %} - - {% if can_lend_device %} - - {% trans "Send Mail" %} - - {% endif %} - - {% if can_change_device %} - - {% trans "Edit" %} - -
- {% csrf_token %} - {% with is_bookmarked=device|check_bookmark:user %} - {% if is_bookmarked %} - - {% else %} - - {% endif %} - {% endwith %} -
- {% endif %} - -
- - -
- {% else %} - {% if can_change_device and device.archived != None %} -
- {% csrf_token %} - -
- {% endif %} - - {% if can_change_device and device.trashed != None %} -
- {% csrf_token %} - -
- {% endif %} - {% endif %} -{% endblock %} - -{% block content %} - {% has_perm 'devices.change_device' user device as can_change_device %} - {% has_perm 'devices.add_device' user device as can_add_device %} - {% has_perm 'devices.delete_device' user device as can_delete_device %} - {% has_perm 'devices.lend_device' user device as can_lend_device %} - {% has_perm 'devices.view_devicedetails' user device as can_view_devicedetails %} - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {% if device.used_in %} - - - - - {% endif %} - {% if usedset %} - - - - - {% endif %} - {% for attributevalue in attributevalue_list %} - - - - - {% endfor %} - - - - - - - - - - - - - {% if device.manual %} - - - - - {% endif %} - {% if device.contact %} - - - - - {% endif %} - {% if device.creator %} - - - - - {% endif %} - - - - - {% if device.trashed %} - - - - - {% endif %} - {% if device.inventoried %} - - - - - {% endif %} - -
{% trans "Lagerregal ID" %}{{ device.pk }}
{% trans "Inventorynumber" %}{{ device.inventorynumber }}
{% trans "Serialnumber" %}{{ device.serialnumber }}
{% trans "Hostname" %}{{ device.hostname }}
{% trans "Devicetype" %} - {% if device.devicetype %} - {{ device.devicetype }} - {% else %} - — - {% endif %} -
{% trans "Manufacturer" %} - {% if device.manufacturer %} - {{ device.manufacturer }} - {% else %} - — - {% endif %} -
{% trans "Room" %} - {% if device.room %} - {{ device.room }} - {% else %} - — - {% endif %} -
{% trans "Used in" %}{{device.used_in}}
{% trans "Uses" %} - {% for element in usedset%} - {{element}}
- {% endfor %} -
{{ attributevalue.typeattribute.name }}{{ attributevalue }}
{% trans "Devicegroup" %} - {% if device.group %} - {{ device.group }} - {% else %} - — - {% endif %} -
{% trans "Department" %} - {% if device.department %} - {{ device.department }} - {% else %} - — - {% endif %} -
{% trans "Short term device" %} - {% if device.templending %} - - {% else %} - - {% endif %} -
{% trans "Manual" %} - - {% with filename=device.manual|filename|splitstr:'.'|last %} - {{ device.name|add:'.'|add:filename }} - {% endwith %} - -
{% trans "Contact" %}{{ device.contact }}
{% trans "Created by" %}{{ device.creator }}
{% trans "Last edited" %} - {% if lastedit != None %} - {{ lastedit.revision.date_created }} by {{ lastedit.revision.user }} - {% else %} - {% trans "Not edited yet" %} - {% endif %} -
{% trans "Trashed on" %}{{ device.trashed }}
{% trans "Last inventoried on" %}{{ device.inventoried }}
- - {% if device.webinterface != "" %} - {% trans "Go to webinterface" %} - {% endif %} - - {% if device.description %} -

{% trans "Description" %}

- {{ device.description|linebreaks }} - {% endif %} -
-
- {% if device.pictures.count > 0 %} - - {% endif %} - -

{% trans "Current Lending information" %}

- {% if device.currentlending != None %} - - - - - - - - - - - - - - - - - -
{% trans "Lent to" %} - {{ device.currentlending.owner }} -
{% trans "Since" %}{{ device.currentlending.lenddate }}
{% trans "Due to" %} - {{ device.currentlending.duedate }} - {% if device.currentlending.duedate < weekago %} - - {% elif device.currentlending.duedate < today %} - - {% endif %} -
{% trans "Overdue notification" %}{{ device.currentlending.duedate_email|default_if_none:_("Not sent") }}
- {% else %} -

{% trans "Currently not lent" %}

- {% endif %} -
-
-
-
{% trans "IP-Addresses" %}
- - - {% if can_change_device and device.archived == None%} - - {% endif %} -
- -
-
{% trans "Tags" %}
-
    - {% for tag in device.tags.all %} -
  • - {{ tag.name }} - - - -
  • - {% endfor %} -
- - -
-
-
- - - -
- {% if can_change_device %} -
-
    - {% for note in device.notes.all %} -
  • -
    -
    - {% if note.creator.avatar %} - - {% endif %} -

    {{ note.creator }}

    - {{ note.created_at|date:"SHORT_DATETIME_FORMAT" }} -
    -
    -
    - {{ note.note|linebreaks }} -
    - {% if note.creator == user %} -
    - -
    - {% csrf_token %} - -
    -
    - {% endif %} -
    -
    -
  • - {% endfor %} -
- -
-
- {% csrf_token %} -
- -
- - -
- -
-
-
-
- {% endif %} - {% if can_change_device %} -
-

{% trans "10 last lendings" %}

- - - - - - - - - - - {% for lending in lending_list %} - - - - - - - {% endfor %} - -
{% trans "User" %}{% trans "Since" %}{% trans "Duedate" %}{% trans "Returned" %}
{{ lending.owner }}{{ lending.lenddate }}{{ lending.duedate|default_if_none:_("never") }}{{ lending.returndate|default_if_none:_("not returned") }}
- - {% trans "View lending history" %} - -
- {% endif %} - - {% if can_change_device %} -
-

{% trans "10 last edits" %}

- - - - - - - - - - {% for action in version_list %} - - - - - - {% endfor %} - -
{% trans 'Date/time' %}{% trans 'User' %}{% trans 'Comment' %}
- - {{ action.revision.date_created }} - - - {% if action.revision.user %} - {{ action.revision.user }} - {% else %} - {% trans 'Unknown' %} - {% endif %} - {{ action.revision.comment|linebreaksbr|default:"" }}
- - {% trans "View edit history" %} - -
- {% endif %} - - {% if can_lend_device %} -
-

{% trans "10 last sent mails" %}

- - - - - - - - - - - {% for mail in mail_list %} - - - - - - - {% endfor %} - -
{% trans "Mailtemplate" %}{% trans "Subject" %}{% trans "Sent by" %}{% trans "Sent at" %}
{{ mail.mailtemplate.name }}{{ mail.subject }}{{ mail.sent_by }}{{ mail.sent_at }}
-
- {% endif %} - - {% if can_view_devicedetails %} -
-

{% trans "Device data from backend providers" %}

-
- {% trans "Loading Device Details..." %} -
-
-
-

{% trans "Installed software from backend providers" %}

-
- {% trans "Loading Software List..." %} -
-
- {% endif %} -
-{% endblock %} - -{% block htmlend %} - {% has_perm 'devices.lend_device' user device as can_lend_device %} - - {% if can_lend_device %} - {% include "snippets/modals/lending.html" with modalname="lendModal" %} - {% if device.currentlending %} - {% include "snippets/modals/lendingReturn.html" with modalname="returnModal" %} - {% endif %} - - {% include "snippets/modals/devicemailModal.html" with modalname="mailModal" %} - {% include "snippets/modals/deviceImageModal.html" with modalname="pictureModal" %} - {% endif %} - - - {% if display_printbutton %} - {% include "snippets/modals/deviceprintDymoModal.html" with modalname="printDymoModal" label_path=label_path %} - {% endif %} -{% endblock %} - -{% block scriptend %} - $(function() { - $("#id_ipaddresses").on("change", function(e) { - if ($(this).val()) { - $("#submitipaddress").removeClass("disabled"); - } else { - $("#submitipaddress").addClass("disabled"); - } - }); - - $("#id_tags").on("change", function(e) { - if ($(this).val()) { - $("#submittags").removeClass("disabled"); - } else { - $("#submittags").addClass("disabled"); - } - }); - - $("#id_mailtemplate").change(function() { - if ($(this).val() !== "") { - $.ajax({ - url: "{% url "load-mailtemplate" %}", - data: {"template":$(this).val(), "recipients":$("#id_emailrecipients").val()}, - }).done(function(data) { - $("#id_emailsubject").val(data.subject); - $("#id_emailbody").val(data.body); - $('#id_emailrecipients').val(data.recipients); - }); - } - }); - - $(document).on('click', '.dropdown-cancel', function(e) { - e.stopPropagation(); - }); - - $(document).on('click', '.delete-picture', function(e) { - var picture_id = e.target.id.replace("picture", ""); - var url = "{% url "device-api-picture" device.pk 0 %}".replace("pictures/0", "pictures/" + picture_id); - $.ajax({ - url: url, - type: 'DELETE', - success: function() { - var row = e.target.parentNode.parentNode; - var modalBody = row.parentNode; - modalBody.removeChild(row); - }, - }); - }); - - $(document).on('click', '.rotate-picture-right', function(e) { - rotate_and_reload(this, "right"); - }); - $(document).on('click', '.rotate-picture-left', function(e) { - rotate_and_reload(this, "left"); - }); - - function rotate_and_reload(target, direction){ - var picture_id = target.id.replace("picture", "") - var url = "{% url "device-api-picture-rotate" device.pk 0 "placeholder" %}".replace("pictures/0", "pictures/"+picture_id).replace("placeholder", direction); - $.ajax({ - url: url, - type: 'PATCH', - success: function(data) { - var d = new Date(); - img = target.parentElement.parentElement.getElementsByTagName('img')[0]; - if (data.new_source.length) { - // image was converted to png - img.src = img.src.replace(data.old_source, data.new_source); - } else { - // force cache invalidation and reload - img.src = img.src.split("?")[0] + '?' + d.getTime(); - } - }, - }); - } - - $("#devicedetailsentry").load("{% url "device-details" device.pk %}"); - $("#softwaredetails").load("{% url "device-software" device.pk %}"); - }); -{% endblock %}