1
.gitignore
vendored
1
.gitignore
vendored
@ -4,4 +4,3 @@ __pycache__
|
||||
**db.sqlite3
|
||||
**.coverage
|
||||
artifacts/
|
||||
**itam/
|
23
app/api/serializers/itam/device.py
Normal file
23
app/api/serializers/itam/device.py
Normal file
@ -0,0 +1,23 @@
|
||||
from rest_framework import serializers
|
||||
from itam.models.device import Device
|
||||
|
||||
|
||||
|
||||
|
||||
class DeviceSerializer(serializers.ModelSerializer):
|
||||
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name="_api_device_view", format="html"
|
||||
)
|
||||
|
||||
config = serializers.SerializerMethodField('get_device_config')
|
||||
|
||||
def get_device_config(self, device):
|
||||
|
||||
return device.get_configuration(device.id)
|
||||
|
||||
|
||||
|
||||
class Meta:
|
||||
model = Device
|
||||
fields = '__all__'
|
15
app/api/serializers/itam/software.py
Normal file
15
app/api/serializers/itam/software.py
Normal file
@ -0,0 +1,15 @@
|
||||
from rest_framework import serializers
|
||||
from itam.models.device import Software
|
||||
|
||||
|
||||
|
||||
|
||||
class SoftwareSerializer(serializers.ModelSerializer):
|
||||
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name="_api_software_view", format="html"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Software
|
||||
fields = '__all__'
|
@ -3,6 +3,7 @@ from rest_framework.urlpatterns import format_suffix_patterns
|
||||
|
||||
from .views import access, index
|
||||
|
||||
from .views.itam import device as itam_device, software as itam_software, config as itam_config
|
||||
|
||||
urlpatterns = [
|
||||
path("", index.IndexView.as_view(), name='_api_home'),
|
||||
@ -11,6 +12,15 @@ urlpatterns = [
|
||||
path("organization/<int:organization_id>/team/<int:group_ptr_id>/", access.TeamDetail.as_view(), name='_api_team'),
|
||||
path("organization/team/", access.TeamList.as_view(), name='_api_teams'),
|
||||
|
||||
|
||||
path("config/<str:device_name>/", itam_config.View.as_view(), name="_api_device_config"),
|
||||
|
||||
path("device/", itam_device.List.as_view(), name="_api_devices"),
|
||||
path("device/<int:pk>/", itam_device.Detail.as_view(), name="_api_device_view"),
|
||||
|
||||
path("software/", itam_software.List.as_view(), name="_api_softwares"),
|
||||
path("software/<int:pk>/", itam_software.Detail.as_view(), name="_api_software_view"),
|
||||
|
||||
]
|
||||
|
||||
urlpatterns = format_suffix_patterns(urlpatterns)
|
||||
|
@ -29,5 +29,7 @@ class IndexView(PermissionRequiredMixin, LoginRequiredMixin, routers.APIRootView
|
||||
{
|
||||
"organizations": reverse("_api_orgs", request=request),
|
||||
"teams": reverse("_api_teams", request=request),
|
||||
"devices": reverse("_api_devices", request=request),
|
||||
"software": reverse("_api_softwares", request=request),
|
||||
}
|
||||
)
|
||||
|
0
app/api/views/itam/__init__.py
Normal file
0
app/api/views/itam/__init__.py
Normal file
19
app/api/views/itam/config.py
Normal file
19
app/api/views/itam/config.py
Normal file
@ -0,0 +1,19 @@
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin
|
||||
|
||||
from itam.models.device import Device
|
||||
|
||||
from rest_framework import views
|
||||
from rest_framework.response import Response
|
||||
|
||||
|
||||
class View(views.APIView):
|
||||
|
||||
def get(self, request, device_name):
|
||||
|
||||
device = Device.objects.get(slug=device_name)
|
||||
|
||||
return Response(device.get_configuration(device.id))
|
||||
|
||||
|
||||
def get_view_name(self):
|
||||
return "Device Config"
|
24
app/api/views/itam/device.py
Normal file
24
app/api/views/itam/device.py
Normal file
@ -0,0 +1,24 @@
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin
|
||||
|
||||
from rest_framework import generics
|
||||
|
||||
from itam.models.device import Device
|
||||
from api.serializers.itam.device import DeviceSerializer
|
||||
|
||||
|
||||
class List(PermissionRequiredMixin, LoginRequiredMixin, generics.ListCreateAPIView):
|
||||
permission_required = 'itam.view_device'
|
||||
queryset = Device.objects.all()
|
||||
serializer_class = DeviceSerializer
|
||||
|
||||
def get_view_name(self):
|
||||
return "Devices"
|
||||
|
||||
|
||||
class Detail(PermissionRequiredMixin, LoginRequiredMixin, generics.RetrieveUpdateDestroyAPIView):
|
||||
permission_required = 'itam.view_device'
|
||||
queryset = Device.objects.all()
|
||||
serializer_class = DeviceSerializer
|
||||
|
||||
def get_view_name(self):
|
||||
return "Device"
|
24
app/api/views/itam/software.py
Normal file
24
app/api/views/itam/software.py
Normal file
@ -0,0 +1,24 @@
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin
|
||||
|
||||
from rest_framework import generics
|
||||
|
||||
from itam.models.software import Software
|
||||
from api.serializers.itam.software import SoftwareSerializer
|
||||
|
||||
|
||||
class List(PermissionRequiredMixin, LoginRequiredMixin, generics.ListCreateAPIView):
|
||||
permission_required = 'itam.view_software'
|
||||
queryset = Software.objects.all()
|
||||
serializer_class = SoftwareSerializer
|
||||
|
||||
def get_view_name(self):
|
||||
return "Softwares"
|
||||
|
||||
|
||||
class Detail(PermissionRequiredMixin, LoginRequiredMixin, generics.RetrieveUpdateDestroyAPIView):
|
||||
permission_required = 'itam.view_software'
|
||||
queryset = Software.objects.all()
|
||||
serializer_class = SoftwareSerializer
|
||||
|
||||
def get_view_name(self):
|
||||
return "Software"
|
@ -43,6 +43,7 @@ INSTALLED_APPS = [
|
||||
'social_django',
|
||||
'core.apps.CoreConfig',
|
||||
'access.apps.AccessConfig',
|
||||
'itam.apps.ItamConfig',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
@ -28,6 +28,7 @@ urlpatterns = [
|
||||
name="change_password"),
|
||||
path("account/", include("django.contrib.auth.urls")),
|
||||
path("organization/", include("access.urls")),
|
||||
path("itam/", include("itam.urls")),
|
||||
|
||||
]
|
||||
|
||||
|
0
app/itam/__init__.py
Normal file
0
app/itam/__init__.py
Normal file
6
app/itam/apps.py
Normal file
6
app/itam/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ItamConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'itam'
|
0
app/itam/forms/__init__.py
Normal file
0
app/itam/forms/__init__.py
Normal file
21
app/itam/forms/device_softwareadd.py
Normal file
21
app/itam/forms/device_softwareadd.py
Normal file
@ -0,0 +1,21 @@
|
||||
from django import forms
|
||||
from django.db.models import Q
|
||||
|
||||
from itam.models.device import DeviceSoftware
|
||||
from itam.models.software import Software
|
||||
|
||||
|
||||
class SoftwareAdd(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = DeviceSoftware
|
||||
fields = [
|
||||
'software',
|
||||
'action'
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
organizations = kwargs.pop('organizations')
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.fields['software'].queryset = Software.objects.filter(Q(organization_id__in=organizations) | Q(is_global = True))
|
0
app/itam/forms/software/__init__.py
Normal file
0
app/itam/forms/software/__init__.py
Normal file
23
app/itam/forms/software/update.py
Normal file
23
app/itam/forms/software/update.py
Normal file
@ -0,0 +1,23 @@
|
||||
from django import forms
|
||||
from django.db.models import Q
|
||||
|
||||
from itam.models.software import Software
|
||||
|
||||
|
||||
class Update(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Software
|
||||
fields = [
|
||||
"name",
|
||||
'slug',
|
||||
'id',
|
||||
'organization',
|
||||
'is_global',
|
||||
'category',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.fields['is_global'].widget.attrs['disabled'] = True
|
118
app/itam/migrations/0001_initial.py
Normal file
118
app/itam/migrations/0001_initial.py
Normal file
@ -0,0 +1,118 @@
|
||||
# Generated by Django 5.0.6 on 2024-05-15 06:10
|
||||
|
||||
import access.fields
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('access', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='DeviceType',
|
||||
fields=[
|
||||
('is_global', models.BooleanField(default=False)),
|
||||
('id', models.AutoField(primary_key=True, serialize=False, unique=True)),
|
||||
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False)),
|
||||
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False)),
|
||||
('name', models.CharField(max_length=50, unique=True)),
|
||||
('slug', access.fields.AutoSlugField()),
|
||||
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='access.organization')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Device',
|
||||
fields=[
|
||||
('is_global', models.BooleanField(default=False)),
|
||||
('id', models.AutoField(primary_key=True, serialize=False, unique=True)),
|
||||
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False)),
|
||||
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False)),
|
||||
('name', models.CharField(max_length=50, unique=True)),
|
||||
('slug', access.fields.AutoSlugField()),
|
||||
('serial_number', models.CharField(blank=True, default=None, max_length=50, null=True, verbose_name='Serial Number')),
|
||||
('uuid', models.CharField(blank=True, default=None, max_length=50, null=True, verbose_name='UUID')),
|
||||
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='access.organization')),
|
||||
('device_type', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='itam.devicetype')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Software',
|
||||
fields=[
|
||||
('is_global', models.BooleanField(default=False)),
|
||||
('id', models.AutoField(primary_key=True, serialize=False, unique=True)),
|
||||
('name', models.CharField(max_length=50, unique=True)),
|
||||
('slug', access.fields.AutoSlugField()),
|
||||
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False)),
|
||||
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False)),
|
||||
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='access.organization')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DeviceSoftware',
|
||||
fields=[
|
||||
('is_global', models.BooleanField(default=False)),
|
||||
('id', models.AutoField(primary_key=True, serialize=False, unique=True)),
|
||||
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False)),
|
||||
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False)),
|
||||
('action', models.CharField(choices=[('1', 'Install'), ('0', 'Remove')], default=None, max_length=1)),
|
||||
('device', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='itam.device')),
|
||||
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='access.organization')),
|
||||
('software', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='itam.software')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SoftwareCategory',
|
||||
fields=[
|
||||
('is_global', models.BooleanField(default=False)),
|
||||
('id', models.AutoField(primary_key=True, serialize=False, unique=True)),
|
||||
('name', models.CharField(max_length=50, unique=True)),
|
||||
('slug', access.fields.AutoSlugField()),
|
||||
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False)),
|
||||
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False)),
|
||||
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='access.organization')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='software',
|
||||
name='category',
|
||||
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='itam.softwarecategory'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SoftwareVersion',
|
||||
fields=[
|
||||
('is_global', models.BooleanField(default=False)),
|
||||
('id', models.AutoField(primary_key=True, serialize=False, unique=True)),
|
||||
('name', models.CharField(max_length=50, unique=True)),
|
||||
('slug', access.fields.AutoSlugField()),
|
||||
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False)),
|
||||
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False)),
|
||||
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='access.organization')),
|
||||
('software', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='itam.software')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
0
app/itam/migrations/__init__.py
Normal file
0
app/itam/migrations/__init__.py
Normal file
142
app/itam/models/device.py
Normal file
142
app/itam/models/device.py
Normal file
@ -0,0 +1,142 @@
|
||||
from django.db import models
|
||||
|
||||
from access.fields import *
|
||||
from access.models import TenancyObject
|
||||
from itam.models.software import Software
|
||||
|
||||
|
||||
|
||||
class DeviceCommonFields(TenancyObject, models.Model):
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
id = models.AutoField(
|
||||
primary_key=True,
|
||||
unique=True,
|
||||
blank=False
|
||||
)
|
||||
|
||||
created = AutoCreatedField()
|
||||
|
||||
modified = AutoLastModifiedField()
|
||||
|
||||
|
||||
|
||||
class DeviceCommonFieldsName(DeviceCommonFields):
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
name = models.CharField(
|
||||
blank = False,
|
||||
max_length = 50,
|
||||
unique = True,
|
||||
)
|
||||
|
||||
slug = AutoSlugField()
|
||||
|
||||
|
||||
|
||||
class DeviceType(DeviceCommonFieldsName):
|
||||
|
||||
def __str__(self):
|
||||
|
||||
return self.name
|
||||
|
||||
|
||||
|
||||
class Device(DeviceCommonFieldsName):
|
||||
|
||||
serial_number = models.CharField(
|
||||
verbose_name = 'Serial Number',
|
||||
max_length = 50,
|
||||
default = None,
|
||||
null = True,
|
||||
blank = True
|
||||
|
||||
)
|
||||
|
||||
uuid = models.CharField(
|
||||
verbose_name = 'UUID',
|
||||
max_length = 50,
|
||||
default = None,
|
||||
null = True,
|
||||
blank = True
|
||||
|
||||
)
|
||||
|
||||
|
||||
device_type = models.ForeignKey(
|
||||
DeviceType,
|
||||
on_delete=models.CASCADE,
|
||||
default = None,
|
||||
null = True,
|
||||
blank= True
|
||||
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
return self.name
|
||||
|
||||
def get_configuration(self, id):
|
||||
|
||||
softwares = DeviceSoftware.objects.filter(device=id)
|
||||
|
||||
config = {
|
||||
"software": []
|
||||
}
|
||||
|
||||
for software in softwares:
|
||||
|
||||
if int(software.action) == 1:
|
||||
|
||||
state = 'present'
|
||||
|
||||
elif int(software.action) == 0:
|
||||
|
||||
state = 'absent'
|
||||
|
||||
software = {
|
||||
"name": software.software.slug,
|
||||
"state": state
|
||||
}
|
||||
config['software'] = config['software'] + [ software ]
|
||||
|
||||
return config
|
||||
|
||||
|
||||
|
||||
class DeviceSoftware(DeviceCommonFields):
|
||||
""" A way for the device owner to configure software to install/remove """
|
||||
|
||||
|
||||
class Actions(models.TextChoices):
|
||||
INSTALL = '1', 'Install'
|
||||
REMOVE = '0', 'Remove'
|
||||
|
||||
|
||||
device = models.ForeignKey(
|
||||
Device,
|
||||
on_delete=models.CASCADE,
|
||||
default = None,
|
||||
null = False,
|
||||
blank= False
|
||||
|
||||
)
|
||||
|
||||
software = models.ForeignKey(
|
||||
Software,
|
||||
on_delete=models.CASCADE,
|
||||
default = None,
|
||||
null = False,
|
||||
blank= False
|
||||
|
||||
)
|
||||
|
||||
action = models.CharField(
|
||||
max_length=1,
|
||||
choices=Actions,
|
||||
default=None,
|
||||
)
|
66
app/itam/models/software.py
Normal file
66
app/itam/models/software.py
Normal file
@ -0,0 +1,66 @@
|
||||
from django.db import models
|
||||
|
||||
from access.fields import *
|
||||
from access.models import TenancyObject
|
||||
|
||||
|
||||
|
||||
class SoftwareCommonFields(TenancyObject, models.Model):
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
id = models.AutoField(
|
||||
primary_key=True,
|
||||
unique=True,
|
||||
blank=False
|
||||
)
|
||||
|
||||
name = models.CharField(
|
||||
blank = False,
|
||||
max_length = 50,
|
||||
unique = True,
|
||||
)
|
||||
|
||||
slug = AutoSlugField()
|
||||
|
||||
created = AutoCreatedField()
|
||||
|
||||
modified = AutoLastModifiedField()
|
||||
|
||||
|
||||
|
||||
class SoftwareCategory(SoftwareCommonFields):
|
||||
|
||||
def __str__(self):
|
||||
|
||||
return self.name
|
||||
|
||||
|
||||
|
||||
class Software(SoftwareCommonFields):
|
||||
|
||||
category = models.ForeignKey(
|
||||
SoftwareCategory,
|
||||
on_delete=models.CASCADE,
|
||||
default = None,
|
||||
null = True,
|
||||
blank= True
|
||||
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
return self.name
|
||||
|
||||
|
||||
|
||||
class SoftwareVersion(SoftwareCommonFields):
|
||||
|
||||
software = models.ForeignKey(
|
||||
Software,
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
|
||||
|
91
app/itam/templates/itam/device.html.j2
Normal file
91
app/itam/templates/itam/device.html.j2
Normal file
@ -0,0 +1,91 @@
|
||||
{% extends 'base.html.j2' %}
|
||||
|
||||
{% block title %}{{ device.name }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
|
||||
<script>
|
||||
|
||||
function openCity(evt, cityName) {
|
||||
// Declare all variables
|
||||
var i, tabcontent, tablinks;
|
||||
|
||||
// Get all elements with class="tabcontent" and hide them
|
||||
tabcontent = document.getElementsByClassName("tabcontent");
|
||||
for (i = 0; i < tabcontent.length; i++) {
|
||||
tabcontent[i].style.display = "none";
|
||||
}
|
||||
|
||||
// Get all elements with class="tablinks" and remove the class "active"
|
||||
tablinks = document.getElementsByClassName("tablinks");
|
||||
for (i = 0; i < tablinks.length; i++) {
|
||||
tablinks[i].className = tablinks[i].className.replace(" active", "");
|
||||
}
|
||||
|
||||
// Show the current tab, and add an "active" class to the button that opened the tab
|
||||
document.getElementById(cityName).style.display = "block";
|
||||
evt.currentTarget.className += " active";
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="tab">
|
||||
<button onclick="window.location='{% url 'ITAM:Devices' %}';">
|
||||
<< Back to Devices</button>
|
||||
<button id="defaultOpen" class="tablinks" onclick="openCity(event, 'Details')">Details</button>
|
||||
<button id="defaultOpen" class="tablinks" onclick="openCity(event, 'OperatingSystem')">Operating System</button>
|
||||
<button class="tablinks" onclick="openCity(event, 'Software')">Software</button>
|
||||
<button class="tablinks" onclick="openCity(event, 'ConfigManagement')">Config Management</button>
|
||||
<!-- <button class="tablinks" onclick="openCity(event, 'Installations')">Installations</button> -->
|
||||
</div>
|
||||
|
||||
<!-- Tab content -->
|
||||
<div id="Details" class="tabcontent">
|
||||
<h3>Details</h3>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form }}
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
|
||||
<script>
|
||||
// Get the element with id="defaultOpen" and click on it
|
||||
document.getElementById("defaultOpen").click();
|
||||
</script>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="OperatingSystem" class="tabcontent">
|
||||
<h3>Operating System</h3>
|
||||
</div>
|
||||
|
||||
<div id="Software" class="tabcontent">
|
||||
<h3>Software</h3>
|
||||
|
||||
<input type="button" value="Add Software Action" onclick="window.location='{% url 'ITAM:_device_software_add' device.id %}';">
|
||||
<table>
|
||||
<thead>
|
||||
<th>Name</th>
|
||||
<th>Action</th>
|
||||
<th>Installed</th>
|
||||
<th> </th>
|
||||
</thead>
|
||||
{% for software in softwares %}
|
||||
<tr>
|
||||
<td><a href="{% url 'ITAM:_software_view' pk=software.id %}">{{ software.software }}</a></td>
|
||||
<td><a href="{% url 'ITAM:_device_software_view' device_id=device.id pk=software.id %}">{{ software.get_action_display }}</a></td>
|
||||
<td> </td>
|
||||
<th> </th>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="ConfigManagement" class="tabcontent">
|
||||
<h3>Configuration Management</h3>
|
||||
<div>
|
||||
<textarea cols="90" rows="30" readonly>{{ config }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
31
app/itam/templates/itam/device_index.html.j2
Normal file
31
app/itam/templates/itam/device_index.html.j2
Normal file
@ -0,0 +1,31 @@
|
||||
{% extends 'base.html.j2' %}
|
||||
|
||||
{% block title %}Devices{% endblock %}
|
||||
{% block content_header_icon %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
|
||||
<input type="button" value="New Device" onclick="window.location='{% url 'ITAM:_device_add' %}';">
|
||||
<input type="button" value="New Device Type" onclick="window.location='{% url 'ITAM:_device_type_add' %}';">
|
||||
<table class="data">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Device Type</th>
|
||||
<th>Manufacturer</th>
|
||||
<th>Model</th>
|
||||
<th>Organization</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
{% for device in devices %}
|
||||
<tr>
|
||||
<td><a href="{% url 'ITAM:_device_view' pk=device.id %}">{{ device.name }}</a></td>
|
||||
<td>{{ device.device_type }}</td>
|
||||
<td>manufacturer</td>
|
||||
<td>model</td>
|
||||
<td>{% if software.is_global %}Global{% else %}{{ device.organization }}{% endif %}</td>
|
||||
<td><a href="{% url 'ITAM:_device_delete' pk=device.id %}">Delete</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</table>
|
||||
{% endblock %}
|
128
app/itam/templates/itam/software.html.j2
Normal file
128
app/itam/templates/itam/software.html.j2
Normal file
@ -0,0 +1,128 @@
|
||||
{% extends 'base.html.j2' %}
|
||||
|
||||
{% block title %}{{ software.name }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
|
||||
<script>
|
||||
|
||||
function openCity(evt, cityName) {
|
||||
// Declare all variables
|
||||
var i, tabcontent, tablinks;
|
||||
|
||||
// Get all elements with class="tabcontent" and hide them
|
||||
tabcontent = document.getElementsByClassName("tabcontent");
|
||||
for (i = 0; i < tabcontent.length; i++) {
|
||||
tabcontent[i].style.display = "none";
|
||||
}
|
||||
|
||||
// Get all elements with class="tablinks" and remove the class "active"
|
||||
tablinks = document.getElementsByClassName("tablinks");
|
||||
for (i = 0; i < tablinks.length; i++) {
|
||||
tablinks[i].className = tablinks[i].className.replace(" active", "");
|
||||
}
|
||||
|
||||
// Show the current tab, and add an "active" class to the button that opened the tab
|
||||
document.getElementById(cityName).style.display = "block";
|
||||
evt.currentTarget.className += " active";
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="tab">
|
||||
<button onclick="window.location='{% url 'ITAM:Software' %}';">
|
||||
<< Back to Software</button>
|
||||
<button id="defaultOpen" class="tablinks" onclick="openCity(event, 'Details')">Details</button>
|
||||
<button id="defaultOpen" class="tablinks" onclick="openCity(event, 'Versions')">Versions</button>
|
||||
<button class="tablinks" onclick="openCity(event, 'Licences')">Licences</button>
|
||||
<button class="tablinks" onclick="openCity(event, 'Installations')">Installations</button>
|
||||
</div>
|
||||
|
||||
<!-- Tab content -->
|
||||
<div id="Details" class="tabcontent">
|
||||
<h3>Details</h3>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form }}
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
|
||||
<script>
|
||||
// Get the element with id="defaultOpen" and click on it
|
||||
document.getElementById("defaultOpen").click();
|
||||
</script>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="Versions" class="tabcontent">
|
||||
<h3>Versions</h3>
|
||||
Not Yet Implemented
|
||||
<table>
|
||||
<thead>
|
||||
<th>Version</th>
|
||||
<th>Installations</th>
|
||||
<th>Vulnerable</th>
|
||||
<th> </th>
|
||||
</thead>
|
||||
<tr>
|
||||
<td>1.0.0</td>
|
||||
<td>5</td>
|
||||
<td><input type="checkbox" checked disabled></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="Licences" class="tabcontent">
|
||||
<h3>Licences</h3>
|
||||
Not Yet Implemented
|
||||
<table>
|
||||
<thead>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Available</th>
|
||||
<th> </th>
|
||||
</thead>
|
||||
<tr>
|
||||
<td>GPL-3</td>
|
||||
<td>Open Source</td>
|
||||
<td>1 / 5</td>
|
||||
<th> </th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MIT</td>
|
||||
<td>Open Source</td>
|
||||
<td>Unlimited</td>
|
||||
<th> </th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Windows Device</td>
|
||||
<td>CAL</td>
|
||||
<td>11 / 15</td>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="Installations" class="tabcontent">
|
||||
<h3>Installations</h3>
|
||||
Dev Notes: This table will show joined tables installed software and device software action as a single row.
|
||||
<table>
|
||||
<thead>
|
||||
<th>Device</th>
|
||||
<th title="Not Set/Install/Remove">Action</th>
|
||||
<th title="Date Software Installed">Installed</th>
|
||||
<th> </th>
|
||||
</thead>
|
||||
{% for device in device_software %}
|
||||
<tr>
|
||||
<td><a href="{% url 'ITAM:_device_view' pk=device.device.id %}">{{ device.device }}</a></td>
|
||||
<td>{{ device.get_action_display }}</td>
|
||||
<td>Not Implemented</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
29
app/itam/templates/itam/software_index.html.j2
Normal file
29
app/itam/templates/itam/software_index.html.j2
Normal file
@ -0,0 +1,29 @@
|
||||
{% extends 'base.html.j2' %}
|
||||
|
||||
{% block title %}Software{% endblock %}
|
||||
{% block content_header_icon %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
|
||||
<input type="button" value="New Software" onclick="window.location='{% url 'ITAM:_software_add' %}';">
|
||||
<input type="button" value="New Category" onclick="window.location='{% url 'ITAM:_software_category_add' %}';">
|
||||
<table class="data">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Created</th>
|
||||
<th>Modified</th>
|
||||
<th>Organization</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
{% for software in softwares %}
|
||||
<tr>
|
||||
<td><a href="{% url 'ITAM:_software_view' pk=software.id %}">{{ software.name }}</a></td>
|
||||
<td>{{ software.created }}</td>
|
||||
<td>{{ software.modified }}</td>
|
||||
<td>{% if software.is_global %}Global{% else %}{{ software.organization }}{% endif %}</td>
|
||||
<td><a href="{% url 'ITAM:_software_delete' pk=software.id %}">Delete</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</table>
|
||||
{% endblock %}
|
22
app/itam/tests/test_devices.py
Normal file
22
app/itam/tests/test_devices.py
Normal file
@ -0,0 +1,22 @@
|
||||
# from django.conf import settings
|
||||
# from django.shortcuts import reverse
|
||||
from django.test import TestCase, Client
|
||||
|
||||
import pytest
|
||||
import unittest
|
||||
import requests
|
||||
|
||||
# from django.contrib.auth import get_user_model
|
||||
# from django.core.exceptions import ValidationError
|
||||
# from access.models import Organization
|
||||
|
||||
# class Test_app_structure_auth(unittest.TestCase):
|
||||
# User = get_user_model()
|
||||
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="to be written")
|
||||
def test_device_software_action(user):
|
||||
"""Ensure only software that is from the same organization or is global can be added to the device
|
||||
"""
|
||||
pass
|
22
app/itam/tests/test_software.py
Normal file
22
app/itam/tests/test_software.py
Normal file
@ -0,0 +1,22 @@
|
||||
# from django.conf import settings
|
||||
# from django.shortcuts import reverse
|
||||
from django.test import TestCase, Client
|
||||
|
||||
import pytest
|
||||
import unittest
|
||||
import requests
|
||||
|
||||
# from django.contrib.auth import get_user_model
|
||||
# from django.core.exceptions import ValidationError
|
||||
# from access.models import Organization
|
||||
|
||||
# class Test_app_structure_auth(unittest.TestCase):
|
||||
|
||||
@pytest.mark.skip(reason="to be written")
|
||||
def test_software_update_is_global_no_change(user):
|
||||
"""Once software is set to global it can't be changed.
|
||||
|
||||
global status can't be changed as non-global items may reference the item.
|
||||
"""
|
||||
|
||||
pass
|
27
app/itam/urls.py
Normal file
27
app/itam/urls.py
Normal file
@ -0,0 +1,27 @@
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
from .views import device, device_type, software, software_category
|
||||
|
||||
app_name = "ITAM"
|
||||
urlpatterns = [
|
||||
|
||||
path("device/", device.IndexView.as_view(), name="Devices"),
|
||||
|
||||
path("device/<int:pk>/", device.View.as_view(), name="_device_view"),
|
||||
path("device/<int:pk>/software/add", device.SoftwareAdd.as_view(), name="_device_software_add"),
|
||||
path("device/<int:device_id>/software/<int:pk>", device.SoftwareView.as_view(), name="_device_software_view"),
|
||||
|
||||
path("device/<int:pk>/delete", device.Delete.as_view(), name="_device_delete"),
|
||||
path("device/add/", device.Add.as_view(), name="_device_add"),
|
||||
|
||||
path("device_type/add/", device_type.Add.as_view(), name="_device_type_add"),
|
||||
|
||||
path("software/", software.IndexView.as_view(), name="Software"),
|
||||
path("software/<int:pk>/", software.View.as_view(), name="_software_view"),
|
||||
path("software/<int:pk>/delete", software.Delete.as_view(), name="_software_delete"),
|
||||
path("software/add/", software.Add.as_view(), name="_software_add"),
|
||||
|
||||
path("software_category/add/", software_category.Add.as_view(), name="_software_category_add"),
|
||||
|
||||
]
|
0
app/itam/views/__init__.py
Normal file
0
app/itam/views/__init__.py
Normal file
189
app/itam/views/device.py
Normal file
189
app/itam/views/device.py
Normal file
@ -0,0 +1,189 @@
|
||||
import json
|
||||
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||
from django.db.models import Q
|
||||
from django.views import generic
|
||||
|
||||
from access.mixin import OrganizationPermission
|
||||
from access.models import Organization
|
||||
|
||||
from ..models.device import Device, DeviceSoftware
|
||||
from itam.forms.device_softwareadd import SoftwareAdd
|
||||
|
||||
|
||||
class IndexView(PermissionRequiredMixin, OrganizationPermission, generic.ListView):
|
||||
model = Device
|
||||
permission_required = 'itam.view_device'
|
||||
template_name = 'itam/device_index.html.j2'
|
||||
context_object_name = "devices"
|
||||
|
||||
def get_queryset(self):
|
||||
|
||||
if self.request.user.is_superuser:
|
||||
|
||||
return Device.objects.filter().order_by('name')
|
||||
|
||||
else:
|
||||
|
||||
return Device.objects.filter(Q(organization__in=self.user_organizations()) | Q(is_global = True)).order_by('name')
|
||||
|
||||
|
||||
|
||||
class View(OrganizationPermission, generic.UpdateView):
|
||||
model = Device
|
||||
permission_required = [
|
||||
'itam.view_device'
|
||||
]
|
||||
template_name = 'itam/device.html.j2'
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'name',
|
||||
'serial_number',
|
||||
'uuid',
|
||||
'device_type',
|
||||
'is_global'
|
||||
]
|
||||
|
||||
context_object_name = "device"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
softwares = DeviceSoftware.objects.filter(device=self.kwargs['pk'])
|
||||
|
||||
context['content_title'] = self.object.name
|
||||
context['softwares'] = softwares
|
||||
|
||||
config = self.object.get_configuration(self.kwargs['pk'])
|
||||
context['config'] = json.dumps(config, indent=4, sort_keys=True)
|
||||
|
||||
return context
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
|
||||
return f"/itam/device/{self.kwargs['pk']}/"
|
||||
|
||||
|
||||
|
||||
class SoftwareView(OrganizationPermission, generic.UpdateView):
|
||||
model = DeviceSoftware
|
||||
permission_required = [
|
||||
'itam.view_devicesoftware'
|
||||
]
|
||||
template_name = 'form.html.j2'
|
||||
|
||||
fields = [
|
||||
'action',
|
||||
]
|
||||
|
||||
|
||||
context_object_name = "devicesoftware"
|
||||
|
||||
|
||||
def form_valid(self, form):
|
||||
device = Device.objects.get(pk=self.kwargs['device_id'])
|
||||
|
||||
form.instance.organization_id = device.organization.id
|
||||
form.instance.device_id = self.kwargs['device_id']
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
|
||||
return f"/itam/device/{self.kwargs['device_id']}/"
|
||||
|
||||
|
||||
|
||||
class Add(PermissionRequiredMixin, OrganizationPermission, generic.CreateView):
|
||||
model = Device
|
||||
permission_required = [
|
||||
'itam.add_device',
|
||||
]
|
||||
template_name = 'form.html.j2'
|
||||
fields = [
|
||||
'name',
|
||||
'serial_number',
|
||||
'uuid',
|
||||
'device_type',
|
||||
'organization',
|
||||
'is_global'
|
||||
]
|
||||
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
|
||||
return f"/itam/device/"
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['content_title'] = 'Add Device'
|
||||
|
||||
return context
|
||||
|
||||
|
||||
|
||||
class SoftwareAdd(PermissionRequiredMixin, OrganizationPermission, generic.CreateView):
|
||||
model = DeviceSoftware
|
||||
permission_required = [
|
||||
'itam.add_devicesoftware',
|
||||
]
|
||||
template_name = 'form.html.j2'
|
||||
# fields = [
|
||||
# 'software',
|
||||
# 'action'
|
||||
# ]
|
||||
|
||||
form_class = SoftwareAdd
|
||||
|
||||
|
||||
def form_valid(self, form):
|
||||
device = Device.objects.get(pk=self.kwargs['pk'])
|
||||
print(f'HERE={device.organization.id}')
|
||||
form.instance.organization_id = device.organization.id
|
||||
form.instance.device_id = self.kwargs['pk']
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs['organizations'] = self.user_organizations()
|
||||
return kwargs
|
||||
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
|
||||
return f"/itam/device/{self.kwargs['pk']}/"
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['content_title'] = 'Add Device'
|
||||
|
||||
return context
|
||||
|
||||
|
||||
|
||||
class Delete(PermissionRequiredMixin, OrganizationPermission, generic.DeleteView):
|
||||
model = Device
|
||||
permission_required = [
|
||||
'itam.delete_device',
|
||||
]
|
||||
template_name = 'form.html.j2'
|
||||
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
|
||||
return f"/itam/device/"
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['content_title'] = 'Delete ' + self.object.name
|
||||
|
||||
return context
|
63
app/itam/views/device_type.py
Normal file
63
app/itam/views/device_type.py
Normal file
@ -0,0 +1,63 @@
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||
from django.views import generic
|
||||
|
||||
from access.mixin import OrganizationPermission
|
||||
|
||||
from ..models.device import DeviceType
|
||||
|
||||
|
||||
|
||||
class View(OrganizationPermission, generic.UpdateView):
|
||||
model = DeviceType
|
||||
permission_required = [
|
||||
'itam.view_device_type'
|
||||
]
|
||||
template_name = 'form.html.j2'
|
||||
|
||||
fields = [
|
||||
"name",
|
||||
'slug',
|
||||
'id',
|
||||
'organization',
|
||||
'is_global',
|
||||
]
|
||||
|
||||
context_object_name = "device_category"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['content_title'] = self.object.name
|
||||
|
||||
return context
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
|
||||
return f"/itam/device/{self.kwargs['pk']}/"
|
||||
|
||||
|
||||
|
||||
class Add(PermissionRequiredMixin, OrganizationPermission, generic.CreateView):
|
||||
model = DeviceType
|
||||
permission_required = [
|
||||
'access.add_device_type',
|
||||
]
|
||||
template_name = 'form.html.j2'
|
||||
fields = [
|
||||
'name',
|
||||
'organization',
|
||||
'is_global'
|
||||
]
|
||||
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
|
||||
return f"/itam/device/"
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['content_title'] = 'Add Device Type'
|
||||
|
||||
return context
|
100
app/itam/views/software.py
Normal file
100
app/itam/views/software.py
Normal file
@ -0,0 +1,100 @@
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||
from django.db.models import Q
|
||||
from django.views import generic
|
||||
|
||||
from access.mixin import OrganizationPermission
|
||||
|
||||
from itam.models.device import DeviceSoftware
|
||||
from itam.models.software import Software
|
||||
from itam.forms.software.update import Update as SoftwareUpdate_Form
|
||||
|
||||
class IndexView(PermissionRequiredMixin, OrganizationPermission, generic.ListView):
|
||||
model = Software
|
||||
permission_required = 'itam.view_software'
|
||||
template_name = 'itam/software_index.html.j2'
|
||||
context_object_name = "softwares"
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
|
||||
if self.request.user.is_superuser:
|
||||
|
||||
return Software.objects.filter().order_by('name')
|
||||
|
||||
else:
|
||||
|
||||
return Software.objects.filter(Q(organization__in=self.user_organizations()) | Q(is_global = True)).order_by('name')
|
||||
|
||||
|
||||
|
||||
|
||||
class View(OrganizationPermission, generic.UpdateView):
|
||||
model = Software
|
||||
permission_required = [
|
||||
'itam.view_software'
|
||||
]
|
||||
template_name = 'itam/software.html.j2'
|
||||
|
||||
form_class = SoftwareUpdate_Form
|
||||
|
||||
context_object_name = "software"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['content_title'] = self.object.name
|
||||
|
||||
context['device_software'] = DeviceSoftware.objects.filter(Q(device__in=self.user_organizations(), software=self.kwargs['pk']))
|
||||
|
||||
return context
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
|
||||
return f"/itam/software/{self.kwargs['pk']}/"
|
||||
|
||||
|
||||
|
||||
class Add(PermissionRequiredMixin, OrganizationPermission, generic.CreateView):
|
||||
model = Software
|
||||
permission_required = [
|
||||
'access.add_software',
|
||||
]
|
||||
template_name = 'form.html.j2'
|
||||
fields = [
|
||||
'name',
|
||||
'category',
|
||||
'organization',
|
||||
'is_global'
|
||||
]
|
||||
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
|
||||
return f"/itam/software/"
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['content_title'] = 'Add Software'
|
||||
|
||||
return context
|
||||
|
||||
class Delete(PermissionRequiredMixin, OrganizationPermission, generic.DeleteView):
|
||||
model = Software
|
||||
permission_required = [
|
||||
'access.delete_software',
|
||||
]
|
||||
template_name = 'form.html.j2'
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
|
||||
return f"/itam/software/"
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['content_title'] = 'Delete ' + self.object.name
|
||||
|
||||
return context
|
106
app/itam/views/software_category.py
Normal file
106
app/itam/views/software_category.py
Normal file
@ -0,0 +1,106 @@
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||
from django.views import generic
|
||||
|
||||
from access.mixin import OrganizationPermission
|
||||
|
||||
from ..models.software import Software, SoftwareCategory
|
||||
|
||||
|
||||
class IndexView(PermissionRequiredMixin, OrganizationPermission, generic.ListView):
|
||||
model = Software
|
||||
permission_required = 'itam.view_software'
|
||||
template_name = 'itam/software_index.html.j2'
|
||||
context_object_name = "softwares"
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
|
||||
if self.request.user.is_superuser:
|
||||
|
||||
return Software.objects.filter().order_by('name')
|
||||
|
||||
else:
|
||||
|
||||
return Software.objects.filter(organization=self.user_organizations()).order_by('name')
|
||||
|
||||
|
||||
|
||||
class View(OrganizationPermission, generic.UpdateView):
|
||||
model = Software
|
||||
permission_required = [
|
||||
'itam.view_software'
|
||||
]
|
||||
template_name = 'form.html.j2'
|
||||
|
||||
fields = [
|
||||
"name",
|
||||
'slug',
|
||||
'id',
|
||||
'organization',
|
||||
'is_global',
|
||||
]
|
||||
|
||||
context_object_name = "software"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['content_title'] = self.object.name
|
||||
|
||||
return context
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
|
||||
return f"/itam/software/{self.kwargs['pk']}/"
|
||||
|
||||
|
||||
|
||||
class Add(PermissionRequiredMixin, OrganizationPermission, generic.CreateView):
|
||||
model = SoftwareCategory
|
||||
permission_required = [
|
||||
'access.add_software',
|
||||
]
|
||||
template_name = 'form.html.j2'
|
||||
fields = [
|
||||
'name',
|
||||
'organization',
|
||||
'is_global'
|
||||
]
|
||||
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
|
||||
return f"/itam/software/"
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['content_title'] = 'Add Software Category'
|
||||
|
||||
return context
|
||||
|
||||
class Delete(PermissionRequiredMixin, OrganizationPermission, generic.DeleteView):
|
||||
model = Software
|
||||
permission_required = [
|
||||
'access.delete_software',
|
||||
]
|
||||
template_name = 'form.html.j2'
|
||||
# fields = [
|
||||
# 'name',
|
||||
# 'organization',
|
||||
# 'is_global'
|
||||
# ]
|
||||
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
|
||||
return f"/itam/software/"
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['content_title'] = 'Delete ' + self.object.name
|
||||
|
||||
return context
|
Reference in New Issue
Block a user