Compare commits

..

34 Commits

Author SHA1 Message Date
Jon
8d071c68df chore: add Merge/Pull request template
#226
2024-08-14 01:52:22 +09:30
Jon
3b1691ff62 docs(roadmap): update completed features
#226
2024-08-14 01:46:21 +09:30
Jon
a77c43d213 docs(base): detail view template
. #24 #226 closes #22
2024-08-14 01:33:48 +09:30
Jon
086959b431 refactor(itim): services now use details template
. #22 #226
2024-08-14 01:23:17 +09:30
Jon
3f117f9d83 feat(base): create detail view templates
purpose is to aid in the development of a detail form

#22 #24 #226
2024-08-14 00:02:25 +09:30
Jon
6a23845a4f docs: initial adding of template page
#22
2024-08-13 15:03:10 +09:30
Jon
b9c6d04e04 chore: add services navigation icon
!43 #69
2024-08-13 13:23:45 +09:30
Jon
32c0027ecf fix(itim): ensure that the service template config is also rendered as part of device config
!43 #69
2024-08-13 13:23:45 +09:30
Jon
dae52e8646 docs: fluff the port and services
!43 closes #69
2024-08-13 13:23:45 +09:30
Jon
890a5651a0 fix(itim): dont render link if no device
!43 #69
2024-08-13 13:23:45 +09:30
Jon
4cb37f8347 feat(itam): Render Service Config with device config
!43 #69
2024-08-13 13:23:45 +09:30
Jon
a2010b9517 feat(itam): Display deployed services for devices
!43 #69
2024-08-13 13:23:45 +09:30
Jon
c95736ce14 feat(itim): Prevent circular service dependencies
!43 #69
2024-08-13 13:23:45 +09:30
Jon
b46c61954c feat(itim): Port number validation to check for valid port numbers
!43 #69
2024-08-13 13:23:45 +09:30
Jon
afe4266600 feat(itim): Prevent Service template from being assigned as dependent service
!43 #69
2024-08-13 13:23:45 +09:30
Jon
0c8d1c8da1 feat(itim): Add service template support
!43 #69
2024-08-13 13:23:45 +09:30
Jon
eac998b5cc fix(itim): Dont show self within service dependencies
!43 #69
2024-08-13 13:23:45 +09:30
Jon
5914782252 feat(itim): Ports for service management
!43 #69
2024-08-13 13:23:45 +09:30
Jon
73d875c4ac feat(itim): Service Management
!43 #69
2024-08-13 13:23:45 +09:30
Jon
8f439f0675 fix(assistance): Only return distinct values when limiting KB articles
!43 #10
2024-08-13 13:23:45 +09:30
Jon
0f102c6aaf docs(assistance): document kb categories for user
!43 closes #10
2024-08-13 13:23:45 +09:30
Jon
4852c6caeb feat(assistance): Filter KB articles to target user
only intended to filter for users whom dont have change perm.

!43 #10
2024-08-13 13:23:45 +09:30
Jon
3fffba2eba feat(assistance): Add date picker to date fields for KB articles
!43 #10
2024-08-13 13:23:45 +09:30
Jon
a1293984ea feat(assistance): Dont display expired articles for "view" users
!43 #10
2024-08-13 13:23:45 +09:30
Jon
4876db50c1 docs(assistance): document kb for user
!43 #10
2024-08-13 13:23:45 +09:30
Jon
425cc066af feat(base): add code highlighting to markdown
!43 #10
2024-08-13 13:23:45 +09:30
Jon
1086f517fa feat(assistance): Categorised Knowledge base articles
!43 #10
2024-08-13 13:23:45 +09:30
Jon
2fdbf87ddd docs(assistance): added pages for knowledgebase
!43 #10
2024-08-13 13:23:45 +09:30
Jon
86228836c7 chore(base): rename information -> assistance
!43 #10
2024-08-13 13:23:45 +09:30
Jon
a6e6c948a5 feat(itim): Add menu entry
!43 #69 #71
2024-08-13 13:23:45 +09:30
Jon
dcdfa8feb7 feat(itam): Ability to add device configuration
!43 fixes #44
2024-08-13 13:23:45 +09:30
Jon
8388d2e695 test(external_link): add tests
!43 fixes #6
2024-08-13 13:23:45 +09:30
Jon
29f269050f feat(settings): New model to allow adding templated links to devices and software
!43 #6
2024-08-13 13:23:45 +09:30
Jon
93c4fc2009 docs: move settings pages into sub-directory
!43 #6
2024-08-13 13:23:45 +09:30
117 changed files with 1469 additions and 4778 deletions

View File

@ -17,5 +17,5 @@ commitizen:
prerelease_offset: 1
tag_format: $version
update_changelog_on_bump: false
version: 1.1.0
version: 1.0.0-b14
version_scheme: semver

View File

@ -16,17 +16,6 @@ env:
jobs:
mkdocs:
name: 'MKDocs'
permissions:
pull-requests: write
contents: write
statuses: write
checks: write
actions: write
uses: nofusscomputing/action_mkdocs/.github/workflows/reusable_mkdocs.yaml@development
docker:
name: 'Docker'
uses: nofusscomputing/action_docker/.github/workflows/docker.yaml@development

View File

@ -1,85 +1,3 @@
## 1.1.0 (2024-08-23)
### feat
- **itim**: Dont attempt to apply cluster type config if no type specified.
- **itim**: Service config rendered as part of cluster config
- **itim**: dont force config key, validate when it's required
- **itim**: Services assignable to cluster
- **itim**: Ability to add configuration to cluster type
- **itim**: Ability to add external link to cluster
- **itim**: Ability to add and configure Cluster Types
- **itim**: Add cluster to history save
- **itim**: prevent cluster from setting itself as parent
- **itim**: Ability to add and configure cluster
- **itam**: Track if device is virtual
- **api**: Endpoint to fetch user permissions
- **development**: Add function to filter permissions to those used by centurion
- **development**: Add new template tag `choice_ids` for string list casting
- **development**: Render `model_name_plural` as part of back button
- **development**: add to form field `model_name_plural`
- **development**: render heading if section included
- **base**: create detail view templates
- **itam**: Render Service Config with device config
- **itam**: Display deployed services for devices
- **itim**: Prevent circular service dependencies
- **itim**: Port number validation to check for valid port numbers
- **itim**: Prevent Service template from being assigned as dependent service
- **itim**: Add service template support
- **itim**: Ports for service management
- **itim**: Service Management
- **assistance**: Filter KB articles to target user
- **assistance**: Add date picker to date fields for KB articles
- **assistance**: Dont display expired articles for "view" users
- **base**: add code highlighting to markdown
- **assistance**: Categorised Knowledge base articles
- **itim**: Add menu entry
- **itam**: Ability to add device configuration
- **settings**: New model to allow adding templated links to devices and software
### Fixes
- **settings**: return the rendering of external links to models
- **core**: Ensure when saving history json is correctly formatted
- **itim**: Fix name typo in Add Service button
- Ensure tenancy models have `Meta.verbose_name_plural` attribute
- **base**: Use correct url for back button
- **itim**: ensure that the service template config is also rendered as part of device config
- **itim**: dont render link if no device
- **itim**: Dont show self within service dependencies
- **assistance**: Only return distinct values when limiting KB articles
### Refactoring
- **itim**: Add Cluster type to index page
- **itam**: Knowledge Base now uses details template
- **itam**: Device Type now uses details template
- **itam**: Operating System now uses details template
- **itim**: Service Port now uses details template
- **itam**: Device Model now uses details template
- **config_management**: Config Groups now uses details template
- **itam**: Software Categories now uses details template
- **itam**: manufacturer now uses details template
- **itam**: software now uses details template
- **itam**: device now use details template
- **itim**: services now use details template
### Tests
- **itim**: Cluster Types unit tests
- **itim**: Cluster unit tests
- **itam**: Correct Device Type Model permissions test to use "change" view
- **itam**: Correct Operating System Model permissions test to use "change" view
- **config_management**: Correct Device Model permissions test to use "change" view
- **config_management**: Correct Config Group permissions test to use "change" view
- **itam**: Correct Software Category permissions test to use "change" view
- **core**: Correct manufacturer permissions test to use "change" view
- **itam**: Correct software permissions test to use "change" view
- **model**: test for checking if Meta sub-class has variable verbose_name_plural
- **external_link**: add tests
## 1.0.0 (2024-08-23)
## 1.0.0-b14 (2024-08-12)
### Fixes

View File

@ -1,13 +1,13 @@
from django import forms
from django.contrib.auth.models import Permission
from django.db.models import Q
from django.forms import inlineformset_factory
from app import settings
from .team_users import TeamUsersForm, TeamUsers
from access.models import Team
from access.functions import permissions
from app import settings
from core.forms.common import CommonModelForm
@ -66,4 +66,38 @@ class TeamForm(CommonModelForm):
self.fields['permissions'].widget.attrs = {'style': "height: 200px;"}
self.fields['permissions'].queryset = permissions.permission_queryset()
apps = [
'access',
'assistance',
'config_management',
'core',
'django_celery_results',
'itam',
'settings',
]
exclude_models = [
'appsettings',
'chordcounter',
'groupresult',
'organization'
'settings',
'usersettings',
]
exclude_permissions = [
'add_organization',
'add_taskresult',
'change_organization',
'change_taskresult',
'delete_organization',
'delete_taskresult',
]
self.fields['permissions'].queryset = Permission.objects.filter(
content_type__app_label__in=apps,
).exclude(
content_type__model__in=exclude_models
).exclude(
codename__in = exclude_permissions
)

View File

@ -1,45 +0,0 @@
from django.contrib.auth.models import Permission
def permission_queryset():
"""Filter Permissions to those used within the application
Returns:
list: Filtered queryset that only contains the used permissions
"""
apps = [
'access',
'assistance',
'config_management',
'core',
'django_celery_results',
'itam',
'itim',
'settings',
]
exclude_models = [
'appsettings',
'chordcounter',
'groupresult',
'organization'
'settings',
'usersettings',
]
exclude_permissions = [
'add_organization',
'add_taskresult',
'change_organization',
'change_taskresult',
'delete_organization',
'delete_taskresult',
]
return Permission.objects.filter(
content_type__app_label__in=apps,
).exclude(
content_type__model__in=exclude_models
).exclude(
codename__in = exclude_permissions
)

View File

@ -5,9 +5,6 @@ from rest_framework.urlpatterns import format_suffix_patterns
from .views import access, config, index
from api.views.settings import permissions
from api.views.settings import index as settings
from .views.itam import software, config as itam_config
from .views.itam.device import DeviceViewSet
from .views.itam import inventory
@ -39,9 +36,6 @@ urlpatterns = [
path("organization/<int:organization_id>/team/<int:group_ptr_id>/permissions", access.TeamPermissionDetail.as_view(), name='_api_team_permission'),
path("organization/team/", access.TeamList.as_view(), name='_api_teams'),
path("settings", settings.View.as_view(), name='_settings'),
path("settings/permissions", permissions.View.as_view(), name='_settings_permissions'),
]
urlpatterns = format_suffix_patterns(urlpatterns)

View File

@ -1,20 +1,15 @@
# from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin
from django.contrib.auth.models import User
from django.utils.safestring import mark_safe
from rest_framework import generics, permissions, routers, viewsets
from rest_framework.decorators import api_view
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.reverse import reverse
class Index(viewsets.ViewSet):
permission_classes = [
IsAuthenticated,
]
# permission_required = 'access.view_organization'
def get_view_name(self):
return "API Index"
@ -34,7 +29,6 @@ class Index(viewsets.ViewSet):
"devices": reverse("API:device-list", request=request),
"config_groups": reverse("API:_api_config_groups", request=request),
"organizations": reverse("API:_api_orgs", request=request),
"settings": reverse('API:_settings', request=request),
"software": reverse("API:software-list", request=request),
}
)

View File

@ -1,47 +0,0 @@
from django.contrib.auth.models import Permission
from drf_spectacular.utils import extend_schema, OpenApiResponse
from rest_framework import views
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.reverse import reverse
from core.http.common import Http
class View(views.APIView):
permission_classes = [
IsAuthenticated,
]
@extend_schema(
summary = "Settings Index Page",
description = """This endpoint provides the available settings as available via the API.
""",
methods=["GET"],
parameters = None,
tags = ['settings',],
responses = {
200: OpenApiResponse(description='Inventory upload successful'),
401: OpenApiResponse(description='User Not logged in'),
500: OpenApiResponse(description='Exception occured. View server logs for the Stack Trace'),
}
)
def get(self, request, *args, **kwargs):
status = Http.Status.OK
response_data: dict = {
"permissions": reverse('API:_settings_permissions', request=request)
}
return Response(data=response_data,status=status)
def get_view_name(self):
return "Settings"

View File

@ -1,67 +0,0 @@
from drf_spectacular.utils import extend_schema, OpenApiResponse
from rest_framework import views
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from access.functions import permissions
from core.http.common import Http
class View(views.APIView):
permission_classes = [
IsAuthenticated,
]
@extend_schema(
summary = "Fetch available permissions",
description = """This endpoint provides a list of permissions that are available within
Centurion ERP. The format of each permission is `<app name>.<permission>_<model>`.
This endpoint is available to **all** authenticated users.
""",
methods=["GET"],
parameters = None,
tags = ['settings',],
responses = {
200: OpenApiResponse(description='Inventory upload successful'),
401: OpenApiResponse(description='User Not logged in'),
500: OpenApiResponse(description='Exception occured. View server logs for the Stack Trace'),
}
)
def get(self, request, *args, **kwargs):
status = Http.Status.OK
response_data: list = []
try:
for permission in permissions.permission_queryset():
response_data += [ str(f"{permission.content_type.app_label}.{permission.codename}") ]
except PermissionDenied as e:
status = Http.Status.FORBIDDEN
response_data = ''
except Exception as e:
print(f'An error occured{e}')
status = Http.Status.SERVER_ERROR
response_data = 'Unknown Server Error occured'
return Response(data=response_data,status=status)
def get_view_name(self):
return "Permissions"

View File

@ -1,8 +1,6 @@
import importlib
import pytest
import unittest
from access.models import TenancyObject
from access.tests.abstract.tenancy_object import TenancyObject as TenancyObjectTestCases
@ -42,40 +40,6 @@ class TenancyModel(
""" Model to test """
def test_field_exists_verbose_name_plural(self):
"""Test for existance of field in `<model>.Meta`
Field is required for `templates/detail.html.js`
Attribute `verbose_name_plural` must be defined in `Meta` class.
"""
assert 'verbose_name_plural' in self.model._meta.original_attrs
def test_field_not_empty_verbose_name_plural(self):
"""Test field `<model>.Meta` is not empty
Field is required for `templates/detail.html.js`
Attribute `verbose_name_plural` must be defined in `Meta` class.
"""
assert self.model._meta.original_attrs['verbose_name_plural'] is not None
def test_field_type_verbose_name_plural(self):
"""Test field `<model>.Meta` is not empty
Field is required for `templates/detail.html.js`
Attribute `verbose_name_plural` must be of type str.
"""
assert type(self.model._meta.original_attrs['verbose_name_plural']) is str
class ModelAdd(
AddView

View File

@ -1,6 +1,5 @@
from django import forms
from django.urls import reverse
from django.forms import ValidationError
from app import settings
@ -64,84 +63,3 @@ class KnowledgeBaseForm(CommonModelForm):
return cleaned_data
class DetailForm(KnowledgeBaseForm):
tabs: dict = {
"details": {
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'title',
'category',
'responsible_user',
'organization',
'is_global',
'c_created',
'c_modified',
],
"right": [
'release_date',
'expiry_date',
'target_user',
'target_team',
]
},
{
"layout": "single",
"name": "Summary",
"fields": [
'summary',
],
"markdown": [
'summary',
]
},
{
"layout": "single",
"name": "Content",
"fields": [
'content',
],
"markdown": [
'content',
]
}
]
},
"notes": {
"name": "Notes",
"slug": "notes",
"sections": []
}
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['c_created'] = forms.DateTimeField(
label = 'Created',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.created,
)
self.fields['c_modified'] = forms.DateTimeField(
label = 'Modified',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.modified,
)
self.tabs['details'].update({
"edit_url": reverse('Assistance:_knowledge_base_change', args=(self.instance.pk,))
})
self.url_index_view = reverse('Assistance:Knowledge Base')

View File

@ -1,40 +1,232 @@
{% extends 'detail.html.j2' %}
{% extends 'base.html.j2' %}
{% load json %}
{% load markdown %}
{% block content %}
{% block tabs %}
<form action="" method="post">
{% csrf_token %}
<div id="details" class="content-tab">
<script>
{% include 'content/section.html.j2' with tab=form.tabs.details %}
function openCity(evt, cityName) {
var i, tabcontent, tablinks;
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
document.getElementById(cityName).style.display = "block";
evt.currentTarget.className += " active";
}
</script>
<style>
.detail-view-field {
display:unset;
height: 30px;
line-height: 30px;
padding: 0px 20px 40px 20px;
}
.detail-view-field label {
display: inline-block;
font-weight: bold;
width: 200px;
margin: 10px;
height: 30px;
line-height: 30px;
}
.detail-view-field span {
display: inline-block;
width: 340px;
margin: 10px;
border-bottom: 1px solid #ccc;
height: 30px;
line-height: 30px;
}
pre {
word-wrap: break-word;
white-space: pre-wrap;
}
</style>
<div class="tab">
<button
onclick="window.location='{% url 'Assistance:Knowledge Base' %}';"
style="vertical-align: middle; padding: auto; margin: 0px">
<svg xmlns="http://www.w3.org/2000/svg" height="25px" viewBox="0 -960 960 960" width="25px"
style="vertical-align: middle; margin: 0px; padding: 0px border: none; " fill="#6a6e73">
<path
d="m313-480 155 156q11 11 11.5 27.5T468-268q-11 11-28 11t-28-11L228-452q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l184-184q11-11 27.5-11.5T468-692q11 11 11 28t-11 28L313-480Zm264 0 155 156q11 11 11.5 27.5T732-268q-11 11-28 11t-28-11L492-452q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l184-184q11-11 27.5-11.5T732-692q11 11 11 28t-11 28L577-480Z" />
</svg>Back to Articles</button>
<button id="defaultOpen" class="tablinks" onclick="openCity(event, 'Details')">Details</button>
{% if perms.assistance.change_knowledgebase %}
<button class="tablinks" onclick="openCity(event, 'Notes')">Notes</button>
{% endif %}
</div>
<form method="post">
<div id="Details" class="tabcontent">
{% if perms.assistance.change_knowledgebase %}
<h3>Details</h3>
{% csrf_token %}
<div style="align-items:flex-start; align-content: center; display: flexbox; width: 100%">
<div style="display: inline; width: 40%; margin: 30px;">
<div class="detail-view-field">
<label>{{ form.title.label }}</label>
<span>{{ form.title.value }}</span>
</div>
<div class="detail-view-field">
<label>{{ form.category.label }}</label>
<span>
{% if kb.category %}
<a href="{% url 'Settings:_knowledge_base_category_view' kb.category.id %}">{{ kb.category }}</a>
{% else %}
&nbsp;
{% endif %}
</span>
</div>
<div class="detail-view-field">
<label>{{ form.responsible_user.label }}</label>
<span>
{% if form.responsible_user.value %}
{{ kb.responsible_user }}
{% else %}
&nbsp;
{% endif %}
</span>
</div>
<div class="detail-view-field">
<label>{{ form.organization.label }}</label>
<span>
{% if form.organization.value %}
{{ kb.organization }}
{% else %}
&nbsp;
{% endif %}
</span>
</div>
{% if perms.assistance.change_knowledgebase %}
<div id="notes" class="content-tab">
</div>
{% include 'content/section.html.j2' with tab=form.tabs.notes %}
<div style="display: inline; width: 40%; margin: 30px; text-align: left;">
{{ notes_form }}
<div class="detail-view-field">
<label>{{ form.release_date.label }}</label>
<span>
{% if form.release_date.value %}
{{ form.release_date.value }}
{% else %}
&nbsp;
{% endif %}
</span>
</div>
<input type="submit" name="{{notes_form.prefix}}" value="Submit" />
<div class="detail-view-field">
<label>{{ form.expiry_date.label }}</label>
<span>
{% if form.expiry_date.value %}
{{ form.expiry_date.value }}
{% else %}
&nbsp;
{% endif %}
</span>
</div>
<div class="comments">
{% if notes %}
{% for note in notes%}
{% include 'note.html.j2' %}
{% endfor %}
{% endif %}
<div class="detail-view-field">
<label>{{ form.target_user.label }}</label>
<span>
{% if form.target_user.value %}
{{ kb.target_user }}
{% else %}
&nbsp;
{% endif %}
</span>
</div>
<div class="detail-view-field">
<label>{{ form.target_team.label }}</label>
<span>
{% if form.target_team.value %}
{{ form.target_team.value }} {{ kb.target_team }}
{% else %}
&nbsp;
{% endif %}
</span>
</div>
</div>
</div>
</div>
{% endif %}
<input type="button" value="Edit" onclick="window.location='{% url 'Assistance:_knowledge_base_change' kb.id %}';">
{% endif %}
{% if form.summary.value %}
<div style="display: block; width: 100%;">
<h3>Summary</h3>
{{ form.summary.value | safe }}
<br>
<hr />
</div>
{% endif %}
<div style="display: block; width: 100%;">
<h3>Content</h3>
<hr />
<br>
{{ form.content.value | markdown | safe }}
<br>
</div>
<br>
<script>
document.getElementById("defaultOpen").click();
</script>
</div>
{% if perms.assistance.change_knowledgebase %}
<div id="Notes" class="tabcontent">
<h3>
Notes
</h3>
{{ notes_form }}
<input type="submit" name="{{notes_form.prefix}}" value="Submit" />
<div class="comments">
{% if notes %}
{% for note in notes %}
{% include 'note.html.j2' %}
{% endfor %}
{% endif %}
</div>
</div>
{% endif %}
</form>
{% endblock %}
{% endblock %}

View File

@ -7,7 +7,7 @@ from django.utils.decorators import method_decorator
from access.models import TeamUsers
from assistance.forms.knowledge_base import DetailForm, KnowledgeBaseForm
from assistance.forms.knowledge_base import KnowledgeBaseForm
from assistance.models.knowledge_base import KnowledgeBase
from core.forms.comment import AddNoteForm
@ -139,7 +139,7 @@ class View(ChangeView):
context_object_name = "kb"
form_class = DetailForm
form_class = KnowledgeBaseForm
model = KnowledgeBase
@ -168,7 +168,7 @@ class View(ChangeView):
return context
# @method_decorator(auth_decorator.permission_required("assistance.change_knowledgebase", raise_exception=True))
@method_decorator(auth_decorator.permission_required("assistance.change_knowledgebase", raise_exception=True))
def post(self, request, *args, **kwargs):
item = KnowledgeBase.objects.get(pk=self.kwargs['pk'])

View File

@ -1,7 +1,4 @@
from django import forms
from django.urls import reverse
from app import settings
from django.db.models import Q
from config_management.models.groups import ConfigGroups
@ -35,89 +32,3 @@ class ConfigGroupForm(CommonModelForm):
).exclude(
id=int(kwargs['instance'].id)
)
class DetailForm(ConfigGroupForm):
tabs: dict = {
"details": {
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'name',
'parent',
'is_global',
'organization',
'c_created',
'c_modified',
],
"right": [
'model_notes',
]
},
{
"layout": "single",
"fields": [
'config',
]
}
]
},
"child_groups": {
"name": "Child Groups",
"slug": "child_groups",
"sections": []
},
"hosts": {
"name": "Hosts",
"slug": "hosts",
"sections": []
},
"software": {
"name": "Software",
"slug": "software",
"sections": []
},
"configuration": {
"name": "Configuration",
"slug": "configuration",
"sections": []
},
"notes": {
"name": "Notes",
"slug": "notes",
"sections": []
}
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['c_created'] = forms.DateTimeField(
label = 'Created',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.created,
)
self.fields['c_modified'] = forms.DateTimeField(
label = 'Modified',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.modified,
)
self.tabs['details'].update({
"edit_url": reverse('Config Management:_group_change', args=(self.instance.pk,))
})
self.url_index_view = reverse('Config Management:Groups')

View File

@ -1,21 +0,0 @@
# Generated by Django 5.0.7 on 2024-08-17 08:05
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('config_management', '0002_configgrouphosts_configgroupsoftware'),
]
operations = [
migrations.AlterModelOptions(
name='configgroups',
options={'verbose_name_plural': 'Config Groups'},
),
migrations.AlterModelOptions(
name='configgroupsoftware',
options={'ordering': ['-action', 'software'], 'verbose_name_plural': 'Config Group Softwares'},
),
]

View File

@ -35,12 +35,6 @@ class GroupsCommonFields(TenancyObject, models.Model):
class ConfigGroups(GroupsCommonFields, SaveHistory):
class Meta:
verbose_name_plural = 'Config Groups'
reserved_config_keys: list = [
'software'
]
@ -270,8 +264,6 @@ class ConfigGroupSoftware(GroupsCommonFields, SaveHistory):
'software'
]
verbose_name_plural = 'Config Group Softwares'
config_group = models.ForeignKey(
ConfigGroups,

View File

@ -1,180 +1,47 @@
{% extends 'detail.html.j2' %}
{% extends 'base.html.j2' %}
{% load json %}
{% load markdown %}
{% block content %}
<script>
{% block tabs %}
<form action="" method="post">
{% csrf_token %}
function openCity(evt, cityName) {
var i, tabcontent, tablinks;
<div id="details" class="content-tab">
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
{% include 'content/section.html.j2' with tab=form.tabs.details %}
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
document.getElementById(cityName).style.display = "block";
evt.currentTarget.className += " active";
}
</script>
<div class="tab">
<button
onclick="window.location='{% if group.parent %}{% url 'Config Management:_group_view' pk=group.parent.id %}{% else %}{% url 'Config Management:Groups' %}{% endif %}';"
style="vertical-align: middle; padding: auto; margin: 0px">
<svg xmlns="http://www.w3.org/2000/svg" height="25px" viewBox="0 -960 960 960" width="25px"
style="vertical-align: middle; margin: 0px; padding: 0px border: none; " fill="#6a6e73">
<path
d="m313-480 155 156q11 11 11.5 27.5T468-268q-11 11-28 11t-28-11L228-452q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l184-184q11-11 27.5-11.5T468-692q11 11 11 28t-11 28L313-480Zm264 0 155 156q11 11 11.5 27.5T732-268q-11 11-28 11t-28-11L492-452q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l184-184q11-11 27.5-11.5T732-692q11 11 11 28t-11 28L577-480Z" />
</svg>Back to {% if group.parent %}Parent{% else %}Groups{% endif %}</button>
<button id="defaultOpen" class="tablinks" onclick="openCity(event, 'Details')">Details</button>
<button id="defaultOpen" class="tablinks" onclick="openCity(event, 'Children')">Child Groups</button>
<button id="defaultOpen" class="tablinks" onclick="openCity(event, 'Hosts')">Hosts</button>
<button id="defaultOpen" class="tablinks" onclick="openCity(event, 'Software')">Software</button>
<button id="defaultOpen" class="tablinks" onclick="openCity(event, 'Configuration')">Configuration</button>
<button class="tablinks" onclick="openCity(event, 'Notes')">Notes</button>
</div>
<div id="child_groups" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.child_groups %}
<input type="button" value="Add Child Group" onclick="window.location='{% url 'Config Management:_group_add_child' group.id %}';">
<table class="data">
<tr>
<th>Name</th>
<th>Sub-Groups</th>
<th>&nbsp;</th>
</tr>
{% if child_groups %}
{% for group in child_groups %}
<tr>
<td><a href="{% url 'Config Management:_group_view' pk=group.id %}">{{ group.name }}</a></td>
<td>{{ group.count_children }}</td>
<td>&nbsp;</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="4">Nothing Found</td>
</tr>
{% endif %}
</table>
</div>
<div id="hosts" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.hosts %}
<input type="button" value="Add Host" onclick="window.location='{% url 'Config Management:_group_add_host' group.id %}';">
<table class="data">
<tr>
<th>Name</th>
<th>Organization</th>
<th>&nbsp;</th>
</tr>
{% if config_group_hosts %}
{% for host in config_group_hosts %}
<tr>
<td><a href="{% url 'ITAM:_device_view' pk=host.host.id %}">{{ host.host }}</a></td>
<td>{{ host.host.organization }}</td>
<td><a href="{% url 'Config Management:_group_delete_host' group_id=group.id pk=host.id %}">Delete</a></td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="3">Nothing Found</td>
</tr>
{% endif %}
</table>
</div>
<div id="software" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.software %}
<input type="button" value="Add Software Action" onclick="window.location='{% url 'Config Management:_group_software_add' model_pk %}';">
<table>
<thead>
<th>Name</th>
<th>Category</th>
<th>Action</th>
<th>Desired Version</th>
<th>&nbsp;</th>
</thead>
{% if softwares %}
{% for software in softwares %}
<tr>
<td><a href="{% url 'ITAM:_software_view' pk=software.software_id %}">{{ software.software }}</a></td>
<td>{{ software.software.category }}</td>
<td>
{% url 'Config Management:_group_software_change' group_id=group.id pk=software.id as icon_link %}
{% if software.get_action_display == 'Install' %}
{% include 'icons/success_text.html.j2' with icon_text=software.get_action_display icon_link=icon_link %}
{% elif software.get_action_display == 'Remove'%}
{% include 'icons/cross_text.html.j2' with icon_text=software.get_action_display %}
{% else %}
{% include 'icons/add_link.html.j2' with icon_text='Add' %}
{% endif %}
</td>
<td>
{% if software.version %}
{{ software.version }}
{% else %}
-
{% endif %}
</td>
<td>&nbsp;</td>
</tr>
{% endfor %}
{% else %}
<td colspan="5">Nothing Found</td>
{% endif %}
</table>
</div>
<div id="configuration" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.configuration %}
<div>
<textarea cols="90" rows="30" readonly>{{ config }}</textarea>
</div>
</div>
<div id="notes" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.notes %}
{{ notes_form }}
<input type="submit" name="{{notes_form.prefix}}" value="Submit" />
<div class="comments">
{% if notes %}
{% for note in notes%}
{% include 'note.html.j2' %}
{% endfor %}
{% endif %}
</div>
</div>
</form>
{% endblock %}
{% block contents %}
<form method="post">
<div id="Details" class="tabcontent">
<h3>Details</h3>
@ -193,6 +60,28 @@
<div id="Children" class="tabcontent">
<h3>Child Groups</h3>
<input type="button" value="Add Child Group" onclick="window.location='{% url 'Config Management:_group_add_child' group.id %}';">
<table class="data">
<tr>
<th>Name</th>
<th>Sub-Groups</th>
<th>&nbsp;</th>
</tr>
{% if child_groups %}
{% for group in child_groups %}
<tr>
<td><a href="{% url 'Config Management:_group_view' pk=group.id %}">{{ group.name }}</a></td>
<td>{{ group.count_children }}</td>
<td>&nbsp;</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="4">Nothing Found</td>
</tr>
{% endif %}
</table>
</div>
@ -201,6 +90,28 @@
Hosts
</h3>
<input type="button" value="Add Host" onclick="window.location='{% url 'Config Management:_group_add_host' group.id %}';">
<table class="data">
<tr>
<th>Name</th>
<th>Organization</th>
<th>&nbsp;</th>
</tr>
{% if config_group_hosts %}
{% for host in config_group_hosts %}
<tr>
<td><a href="{% url 'ITAM:_device_view' pk=host.host.id %}">{{ host.host }}</a></td>
<td>{{ host.host.organization }}</td>
<td><a href="{% url 'Config Management:_group_delete_host' group_id=group.id pk=host.id %}">Delete</a></td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="3">Nothing Found</td>
</tr>
{% endif %}
</table>
</div>
@ -209,11 +120,52 @@
Software
</h3>
<input type="button" value="Add Software Action" onclick="window.location='{% url 'Config Management:_group_software_add' model_pk %}';">
<table>
<thead>
<th>Name</th>
<th>Category</th>
<th>Action</th>
<th>Desired Version</th>
<th>&nbsp;</th>
</thead>
{% if softwares %}
{% for software in softwares %}
<tr>
<td><a href="{% url 'ITAM:_software_view' pk=software.software_id %}">{{ software.software }}</a></td>
<td>{{ software.software.category }}</td>
<td>
{% url 'Config Management:_group_software_change' group_id=group.id pk=software.id as icon_link %}
{% if software.get_action_display == 'Install' %}
{% include 'icons/success_text.html.j2' with icon_text=software.get_action_display icon_link=icon_link %}
{% elif software.get_action_display == 'Remove'%}
{% include 'icons/cross_text.html.j2' with icon_text=software.get_action_display %}
{% else %}
{% include 'icons/add_link.html.j2' with icon_text='Add' %}
{% endif %}
</td>
<td>
{% if software.version %}
{{ software.version }}
{% else %}
-
{% endif %}
</td>
<td>&nbsp;</td>
</tr>
{% endfor %}
{% else %}
<td colspan="5">Nothing Found</td>
{% endif %}
</table>
</div>
<div id="Configuration" class="tabcontent">
<h3>Configuration</h3>
<div>
<textarea cols="90" rows="30" readonly>{{ config }}</textarea>
</div>
</div>
<div id="Notes" class="tabcontent">

View File

@ -27,7 +27,7 @@ class ConfigGroupPermissions(TestCase, ModelPermissions):
url_name_add = '_group_add'
url_name_change = '_group_change'
url_name_change = '_group_view'
url_name_delete = '_group_delete'

View File

@ -1,6 +1,6 @@
from django.urls import path
from config_management.views.groups.groups import GroupIndexView, GroupAdd, GroupChange, GroupDelete, GroupView, GroupHostAdd, GroupHostDelete
from config_management.views.groups.groups import GroupIndexView, GroupAdd, GroupDelete, GroupView, GroupHostAdd, GroupHostDelete
from config_management.views.groups.software import GroupSoftwareAdd, GroupSoftwareChange, GroupSoftwareDelete
app_name = "Config Management"
@ -9,7 +9,6 @@ urlpatterns = [
path('group', GroupIndexView.as_view(), name='Groups'),
path('group/add', GroupAdd.as_view(), name='_group_add'),
path('group/<int:pk>', GroupView.as_view(), name='_group_view'),
path('group/<int:pk>/edit', GroupChange.as_view(), name='_group_change'),
path('group/<int:pk>/child', GroupAdd.as_view(), name='_group_add_child'),
path('group/<int:pk>/delete', GroupDelete.as_view(), name='_group_delete'),

View File

@ -13,7 +13,7 @@ from itam.models.device import Device
from settings.models.user_settings import UserSettings
from config_management.forms.group_hosts import ConfigGroupHostsForm
from config_management.forms.group.group import ConfigGroupForm, DetailForm
from config_management.forms.group.group import ConfigGroupForm
from config_management.models.groups import ConfigGroups, ConfigGroupHosts, ConfigGroupSoftware
@ -102,7 +102,7 @@ class GroupAdd(AddView):
class GroupChange(ChangeView):
class GroupView(ChangeView):
context_object_name = "group"
@ -110,38 +110,9 @@ class GroupChange(ChangeView):
model = ConfigGroups
permission_required = [
'config_management.change_configgroups',
]
template_name = 'form.html.j2'
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 reverse('Config Management:_group_view', args=(self.kwargs['pk'],))
class GroupView(ChangeView):
context_object_name = "group"
form_class = DetailForm
model = ConfigGroups
permission_required = [
'config_management.view_configgroups',
'config_management.change_configgroups',
]
template_name = 'config_management/group.html.j2'

View File

@ -98,7 +98,3 @@ class CommonModelForm(forms.ModelForm):
|
Q(manager=user)
)
if hasattr(self, 'instance'):
self.model_name_plural = self.instance._meta.verbose_name_plural

View File

@ -1,7 +1,4 @@
from django import forms
from django.urls import reverse
from app import settings
from core.forms.common import CommonModelForm
from core.models.manufacturer import Manufacturer
@ -27,64 +24,3 @@ class ManufacturerForm(
]
model = Manufacturer
class DetailForm(ManufacturerForm):
tabs: dict = {
"details": {
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'name',
'slug',
'organization',
'is_global',
'c_created',
'c_modified',
],
"right": [
'model_notes',
]
}
]
},
# "notes": {
# "name": "Notes",
# "slug": "notes",
# "sections": []
# }
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['c_created'] = forms.DateTimeField(
label = 'Created',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.created,
)
self.fields['c_modified'] = forms.DateTimeField(
label = 'Modified',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.modified,
)
self.tabs['details'].update({
"edit_url": reverse('Settings:_manufacturer_change', args=(self.instance.pk,))
})
self.url_index_view = reverse('Settings:_manufacturers')

View File

@ -1,21 +0,0 @@
# Generated by Django 5.0.7 on 2024-08-17 08:05
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0002_notes'),
]
operations = [
migrations.AlterModelOptions(
name='manufacturer',
options={'ordering': ['name'], 'verbose_name_plural': 'Manufacturers'},
),
migrations.AlterModelOptions(
name='notes',
options={'ordering': ['-created'], 'verbose_name_plural': 'Notes'},
),
]

View File

@ -1,4 +1,4 @@
# Generated by Django 5.0.7 on 2024-08-17 08:05
# Generated by Django 5.0.7 on 2024-07-21 02:35
import django.db.models.deletion
from django.db import migrations, models
@ -7,7 +7,7 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0003_alter_manufacturer_options_alter_notes_options'),
('core', '0002_notes'),
('itim', '0001_initial'),
]

View File

@ -41,18 +41,6 @@ class SaveHistory(models.Model):
value = bool(before[entry])
elif (
"{" in str(after[entry])
and
"}" in str(after[entry])
) or (
"[" in str(after[entry])
and
"]" in str(after[entry])
):
value = str(after[entry]).replace("'", '\"')
else:
value = str(before[entry])
@ -74,18 +62,6 @@ class SaveHistory(models.Model):
value = bool(after[entry])
elif (
"{" in str(after[entry])
and
"}" in str(after[entry])
) or (
"[" in str(after[entry])
and
"]" in str(after[entry])
):
value = str(after[entry]).replace("'", '\"')
else:
value = str(after[entry])

View File

@ -34,9 +34,6 @@ class Manufacturer(TenancyObject, ManufacturerCommonFields, SaveHistory):
'name'
]
verbose_name_plural = 'Manufacturers'
name = models.CharField(
blank = False,
max_length = 50,

View File

@ -46,9 +46,6 @@ class Notes(NotesCommonFields):
'-created'
]
verbose_name_plural = 'Notes'
note = models.TextField(
verbose_name = 'Note',

View File

@ -1,34 +0,0 @@
{% extends 'detail.html.j2' %}
{% load json %}
{% load markdown %}
{% block tabs %}
<form action="" method="post">
{% csrf_token %}
<div id="details" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.details %}
</div>
<div id="notes" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.notes %}
{{ notes_form }}
<input type="submit" name="{{notes_form.prefix}}" value="Submit" />
<div class="comments">
{% if notes %}
{% for note in notes%}
{% include 'note.html.j2' %}
{% endfor %}
{% endif %}
</div>
</div>
</form>
{% endblock %}

View File

@ -1,64 +0,0 @@
from django import template
from django.template.defaultfilters import stringfilter
register = template.Library()
@register.filter()
@stringfilter
def choice_ids(value):
"""Convert choice field value to list
Provide from `{{ field.field.choices }}` the `field.value` and have it converted to a loop
Args:
value (string): for field that has `field.field.choices`, provide `field.value`
Returns:
list: `field.value` casted to a useable list
"""
if value == 'None':
return ''
alist: list = []
if '[' in value:
value = str(value).replace('[', '').replace(']', '')
if ',' in value:
for item in value.split(','):
try:
alist += [ int(item) ]
except:
alist += [ str(item) ]
else:
try:
alist += [ int(item) ]
except:
alist += [ str(item) ]
else:
try:
alist += [ int(value) ]
except:
alist += [ str(value) ]
return alist

View File

@ -26,7 +26,7 @@ class ManufacturerPermissions(TestCase, ModelPermissions):
url_name_add = '_manufacturer_add'
url_name_change = '_manufacturer_change'
url_name_change = '_manufacturer_view'
url_name_delete = '_manufacturer_delete'

View File

@ -83,11 +83,7 @@ class ChangeView(View, generic.UpdateView):
context['open_tab'] = None
if self.model._meta.model_name == 'cluster':
external_links_query = ExternalLink.objects.filter(cluster=True)
elif self.model._meta.model_name == 'device':
if self.model._meta.model_name == 'device':
external_links_query = ExternalLink.objects.filter(devices=True)

View File

@ -47,18 +47,6 @@ class View(OrganizationPermission, generic.View):
match self.kwargs['model_name']:
case 'cluster':
from itim.models.clusters import Cluster
self.model = Cluster
case 'clustertype':
from itim.models.clusters import ClusterType
self.model = ClusterType
case 'configgroups':
self.model = ConfigGroups

View File

@ -1,5 +1,5 @@
from django import forms
from django.urls import reverse
from django.db.models import Q
from app import settings
@ -22,78 +22,16 @@ class DeviceForm(CommonModelForm):
'uuid',
'device_type',
'organization',
'is_virtual',
'model_notes',
'config',
]
class DetailForm(DeviceForm):
tabs: dict = {
"details": {
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'name',
'device_model',
'serial_number',
'uuid',
'device_type',
'organization',
'c_created',
'c_modified',
'lastinventory',
],
"right": [
'model_notes',
'is_virtual',
]
}
]
},
"software": {
"name": "Software",
"slug": "software",
"sections": []
},
"notes": {
"name": "Notes",
"slug": "notes",
"sections": []
},
"config_management": {
"name": "Config Management",
"slug": "config_management",
"sections": []
}
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['c_created'] = forms.DateTimeField(
label = 'Created',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.created,
)
self.fields['c_modified'] = forms.DateTimeField(
label = 'Modified',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.modified,
)
self.fields['lastinventory'] = forms.DateTimeField(
if hasattr(kwargs['instance'], 'inventorydate'):
self.fields['lastinventory'] = forms.DateTimeField(
label="Last Inventory Date",
input_formats=settings.DATETIME_FORMAT,
initial=kwargs['instance'].inventorydate,
@ -101,8 +39,5 @@ class DetailForm(DeviceForm):
required=False,
)
self.tabs['details'].update({
"edit_url": reverse('ITAM:_device_change', args=(self.instance.pk,))
})
self.url_index_view = reverse('ITAM:Devices')
# for key in self.fields.keys():
# self.fields[key].widget.attrs['disabled'] = True

View File

@ -1,7 +1,4 @@
from django import forms
from django.urls import reverse
from app import settings
from django.db.models import Q
from core.forms.common import CommonModelForm
@ -30,62 +27,3 @@ class DeviceModelForm(
]
model = DeviceModel
class DetailForm(DeviceModelForm):
tabs: dict = {
"details": {
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'name',
'slug',
'manufacturer',
'organization',
'is_global',
'c_created',
'c_modified',
],
"right": [
'model_notes',
]
}
]
},
# "notes": {
# "name": "Notes",
# "slug": "notes",
# "sections": []
# },
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['c_created'] = forms.DateTimeField(
label = 'Created',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.created,
)
self.fields['c_modified'] = forms.DateTimeField(
label = 'Modified',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.modified,
)
self.tabs['details'].update({
"edit_url": reverse('Settings:_device_model_change', args=(self.instance.pk,))
})
self.url_index_view = reverse('Settings:_device_models')

View File

@ -1,7 +1,4 @@
from django import forms
from django.urls import reverse
from app import settings
from django.db.models import Q
from core.forms.common import CommonModelForm
@ -29,61 +26,3 @@ class DeviceTypeForm(
]
model = DeviceType
class DetailForm(DeviceTypeForm):
tabs: dict = {
"details": {
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'name',
'slug',
'organization',
'is_global',
'c_created',
'c_modified',
],
"right": [
'model_notes',
]
}
]
},
# "notes": {
# "name": "Notes",
# "slug": "notes",
# "sections": []
# }
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['c_created'] = forms.DateTimeField(
label = 'Created',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.created,
)
self.fields['c_modified'] = forms.DateTimeField(
label = 'Modified',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.modified,
)
self.tabs['details'].update({
"edit_url": reverse('Settings:_device_type_change', args=(self.instance.pk,))
})
self.url_index_view = reverse('Settings:_device_types')

View File

@ -1,5 +1,5 @@
from django import forms
from django.urls import reverse
from django.db.models import Q
from app import settings
@ -9,7 +9,7 @@ from itam.models.operating_system import OperatingSystem
class OperatingSystemForm(CommonModelForm):
class OperatingSystemFormCommon(CommonModelForm):
class Meta:
@ -27,99 +27,27 @@ class OperatingSystemForm(CommonModelForm):
# class Update(OperatingSystemFormCommon):
# def __init__(self, *args, **kwargs):
# super().__init__(*args, **kwargs)
# self.fields['_created'] = forms.DateTimeField(
# label="Created",
# input_formats=settings.DATETIME_FORMAT,
# initial=kwargs['instance'].created,
# disabled=True
# )
# self.fields['_modified'] = forms.DateTimeField(
# label="Modified",
# input_formats=settings.DATETIME_FORMAT,
# initial=kwargs['instance'].modified,
# disabled=True
# )
# if kwargs['instance'].is_global:
# self.fields['is_global'].widget.attrs['disabled'] = True
class DetailForm(OperatingSystemForm):
tabs: dict = {
"details": {
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'name',
'publisher',
'serial_number',
'organization',
'c_created',
'c_modified',
],
"right": [
'model_notes',
]
}
]
},
"versions": {
"name": "Versions",
"slug": "versions",
"sections": []
},
"licences": {
"name": "Licences",
"slug": "licences",
"sections": []
},
"installations": {
"name": "Installations",
"slug": "installations",
"sections": []
},
"notes": {
"name": "Notes",
"slug": "notes",
"sections": []
}
}
class Update(OperatingSystemFormCommon):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['c_created'] = forms.DateTimeField(
label = 'Created',
self.fields['_created'] = forms.DateTimeField(
label="Created",
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.created,
initial=kwargs['instance'].created,
disabled=True
)
self.fields['c_modified'] = forms.DateTimeField(
label = 'Modified',
self.fields['_modified'] = forms.DateTimeField(
label="Modified",
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.modified,
initial=kwargs['instance'].modified,
disabled=True
)
self.tabs['details'].update({
"edit_url": reverse('ITAM:_operating_system_change', args=(self.instance.pk,))
})
self.url_index_view = reverse('ITAM:Operating Systems')
if kwargs['instance'].is_global:
self.fields['is_global'].widget.attrs['disabled'] = True

View File

@ -1,8 +1,4 @@
from django import forms
from django.db.models import Q
from django.urls import reverse
from app import settings
from core.forms.common import CommonModelForm
@ -15,108 +11,22 @@ class SoftwareForm(CommonModelForm):
class Meta:
model = Software
fields = [
'name',
"name",
'publisher',
'slug',
'id',
'organization',
'is_global',
'category',
'model_notes',
]
class SoftwareChange(SoftwareForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not self.instance.is_global:
self.fields['is_global'] = forms.BooleanField(
label = 'Is Global',
initial = self.instance.is_global
)
self.fields['organization'] = forms.CharField(
label = 'Organization',
initial = self.instance.organization
)
class DetailForm(SoftwareForm):
tabs: dict = {
"details": {
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'name',
'publisher',
'slug',
'organization',
'is_global',
'category',
'c_created',
'c_modified',
],
"right": [
'model_notes',
]
}
]
},
"versions": {
"name": "Versions",
"slug": "versions",
"sections": []
},
"licences": {
"name": "Licences",
"slug": "licences",
"sections": []
},
"notes": {
"name": "Notes",
"slug": "notes",
"sections": []
},
"installations": {
"name": "Installations",
"slug": "installations",
"sections": []
}
}
class SoftwareFormUpdate(SoftwareForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields[ 'organization' ] = forms.CharField(
label = 'Organization',
initial = self.instance.organization
)
self.fields['c_created'] = forms.DateTimeField(
label = 'Created',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.created,
)
self.fields['c_modified'] = forms.DateTimeField(
label = 'Modified',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.modified,
)
self.tabs['details'].update({
"edit_url": reverse('ITAM:_software_change', args=(self.instance.pk,))
})
self.url_index_view = reverse('ITAM:Software')
self.fields['is_global'].widget.attrs['disabled'] = True

View File

@ -1,7 +1,4 @@
from django import forms
from django.urls import reverse
from app import settings
from django.db.models import Q
from core.forms.common import CommonModelForm
@ -28,61 +25,3 @@ class SoftwareCategoryForm(
]
model = SoftwareCategory
class DetailForm(SoftwareCategoryForm):
tabs: dict = {
"details": {
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'name',
'slug',
'organization',
'is_global',
'c_created',
'c_modified',
],
"right": [
'model_notes',
]
}
]
},
# "notes": {
# "name": "Notes",
# "slug": "notes",
# "sections": []
# },
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['c_created'] = forms.DateTimeField(
label = 'Created',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.created,
)
self.fields['c_modified'] = forms.DateTimeField(
label = 'Modified',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.modified,
)
self.tabs['details'].update({
"edit_url": reverse('Settings:_software_category_change', args=(self.instance.pk,))
})
self.url_index_view = reverse('Settings:_software_categories')

View File

@ -1,69 +0,0 @@
# Generated by Django 5.0.7 on 2024-08-17 08:05
import itam.models.device
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('itam', '0002_device_config'),
]
operations = [
migrations.AlterModelOptions(
name='device',
options={'verbose_name_plural': 'Devices'},
),
migrations.AlterModelOptions(
name='devicemodel',
options={'ordering': ['manufacturer', 'name'], 'verbose_name_plural': 'Device Models'},
),
migrations.AlterModelOptions(
name='deviceoperatingsystem',
options={'verbose_name_plural': 'Device Operating Systems'},
),
migrations.AlterModelOptions(
name='devicesoftware',
options={'ordering': ['-action', 'software'], 'verbose_name_plural': 'Device Softwares'},
),
migrations.AlterModelOptions(
name='devicetype',
options={'verbose_name_plural': 'Device Types'},
),
migrations.AlterModelOptions(
name='operatingsystem',
options={'verbose_name_plural': 'Operating Systems'},
),
migrations.AlterModelOptions(
name='operatingsystemversion',
options={'verbose_name_plural': 'Operating System Versions'},
),
migrations.AlterModelOptions(
name='software',
options={'verbose_name_plural': 'Softwares'},
),
migrations.AlterModelOptions(
name='softwarecategory',
options={'verbose_name_plural': 'Software Categories'},
),
migrations.AlterModelOptions(
name='softwareversion',
options={'verbose_name_plural': 'Software Versions'},
),
migrations.AddField(
model_name='device',
name='is_virtual',
field=models.BooleanField(blank=True, default=False, help_text='Is this device a virtual machine', verbose_name='Is Virtual'),
),
migrations.AlterField(
model_name='device',
name='name',
field=models.CharField(max_length=50, unique=True, validators=[itam.models.device.Device.validate_hostname_format]),
),
migrations.AlterField(
model_name='device',
name='uuid',
field=models.CharField(blank=True, default=None, help_text='System GUID/UUID.', max_length=50, null=True, unique=True, validators=[itam.models.device.Device.validate_uuid_format], verbose_name='UUID'),
),
]

View File

@ -25,11 +25,6 @@ from settings.models.app_settings import AppSettings
class DeviceType(DeviceCommonFieldsName, SaveHistory):
class Meta:
verbose_name_plural = 'Device Types'
def clean(self):
app_settings = AppSettings.objects.get(owner_organization=None)
@ -49,11 +44,6 @@ class DeviceType(DeviceCommonFieldsName, SaveHistory):
class Device(DeviceCommonFieldsName, SaveHistory):
class Meta:
verbose_name_plural = 'Devices'
reserved_config_keys: list = [
'software'
]
@ -152,15 +142,6 @@ class Device(DeviceCommonFieldsName, SaveHistory):
blank = True,
)
is_virtual = models.BooleanField(
blank = True,
default = False,
help_text = 'Is this device a virtual machine',
null = False,
verbose_name = 'Is Virtual',
)
def save(
self, force_insert=False, force_update=False, using=None, update_fields=None
):
@ -327,9 +308,6 @@ class DeviceSoftware(DeviceCommonFields, SaveHistory):
'software'
]
verbose_name_plural = 'Device Softwares'
class Actions(models.TextChoices):
INSTALL = '1', 'Install'
@ -405,12 +383,6 @@ class DeviceSoftware(DeviceCommonFields, SaveHistory):
class DeviceOperatingSystem(DeviceCommonFields, SaveHistory):
class Meta:
verbose_name_plural = 'Device Operating Systems'
device = models.ForeignKey(
Device,
on_delete = models.CASCADE,

View File

@ -20,9 +20,6 @@ class DeviceModel(DeviceCommonFieldsName, SaveHistory):
'name',
]
verbose_name_plural = 'Device Models'
manufacturer = models.ForeignKey(
Manufacturer,
on_delete=models.CASCADE,

View File

@ -42,12 +42,6 @@ class OperatingSystemFieldsName(OperatingSystemCommonFields):
class OperatingSystem(OperatingSystemFieldsName, SaveHistory):
class Meta:
verbose_name_plural = 'Operating Systems'
publisher = models.ForeignKey(
Manufacturer,
on_delete=models.CASCADE,
@ -63,12 +57,6 @@ class OperatingSystem(OperatingSystemFieldsName, SaveHistory):
class OperatingSystemVersion(OperatingSystemCommonFields, SaveHistory):
class Meta:
verbose_name_plural = 'Operating System Versions'
operating_system = models.ForeignKey(
OperatingSystem,
on_delete=models.CASCADE,

View File

@ -37,12 +37,6 @@ class SoftwareCommonFields(TenancyObject, models.Model):
class SoftwareCategory(SoftwareCommonFields, SaveHistory):
class Meta:
verbose_name_plural = 'Software Categories'
def clean(self):
app_settings = AppSettings.objects.get(owner_organization=None)
@ -61,12 +55,6 @@ class SoftwareCategory(SoftwareCommonFields, SaveHistory):
class Software(SoftwareCommonFields, SaveHistory):
class Meta:
verbose_name_plural = 'Softwares'
publisher = models.ForeignKey(
Manufacturer,
on_delete=models.CASCADE,
@ -103,12 +91,6 @@ class Software(SoftwareCommonFields, SaveHistory):
class SoftwareVersion(SoftwareCommonFields, SaveHistory):
class Meta:
verbose_name_plural = 'Software Versions'
software = models.ForeignKey(
Software,
on_delete=models.CASCADE,

View File

@ -1,26 +1,189 @@
{% extends 'detail.html.j2' %}
{% extends 'base.html.j2' %}
{% load json %}
{% load markdown %}
{% block title %}{{ device.name }}{% endblock %}
{% block tabs %}
{% block content %}
<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' %}';"
style="vertical-align: middle; padding: auto; margin: 0px">
<svg xmlns="http://www.w3.org/2000/svg" height="25px" viewBox="0 -960 960 960" width="25px"
style="vertical-align: middle; margin: 0px; padding: 0px border: none; " fill="#6a6e73">
<path d="m313-480 155 156q11 11 11.5 27.5T468-268q-11 11-28 11t-28-11L228-452q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l184-184q11-11 27.5-11.5T468-692q11 11 11 28t-11 28L313-480Zm264 0 155 156q11 11 11.5 27.5T732-268q-11 11-28 11t-28-11L492-452q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l184-184q11-11 27.5-11.5T732-692q11 11 11 28t-11 28L577-480Z" />
</svg> Back to Devices</button>
<button id="defaultOpen" class="tablinks" onclick="openCity(event, 'Details')">Details</button>
<button id="SoftwareOpen" class="tablinks" onclick="openCity(event, 'Software')">Software</button>
<button id="NotesOpen" class="tablinks" onclick="openCity(event, 'Notes')">Notes</button>
<button id="ConfigManagementOpen" class="tablinks" onclick="openCity(event, 'ConfigManagement')">Config Management</button>
<!-- <button class="tablinks" onclick="openCity(event, 'Installations')">Installations</button> -->
</div>
<style>
.detail-view-field {
display:unset;
height: 30px;
line-height: 30px;
padding: 0px 20px 40px 20px;
}
.detail-view-field label {
display: inline-block;
font-weight: bold;
width: 200px;
margin: 10px;
/*padding: 10px;*/
height: 30px;
line-height: 30px;
}
.detail-view-field span {
display: inline-block;
width: 340px;
margin: 10px;
/*padding: 10px;*/
border-bottom: 1px solid #ccc;
height: 30px;
line-height: 30px;
}
</style>
<form action="" method="post">
{% csrf_token %}
<div id="details" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.details %}
<hr />
<div id="Details" class="tabcontent">
<h3>
Details
{% for external_link in external_links %}
<span style="font-weight: normal; float: right;">{% include 'icons/external_link.html.j2' with external_link=external_link %}</span>
{% endfor %}
</h3>
<div style="align-items:flex-start; align-content: center; display: flexbox; width: 100%">
<div style="display: inline; width: 40%; margin: 30px;">
<div class="detail-view-field">
<label>{{ form.name.label }}</label>
<span>{{ form.name.value }}</span>
</div>
<div class="detail-view-field">
<label>{{ form.device_model.label }}</label>
<span>
{% if device.device_model %}
{{ device.device_model }}
{% else %}
&nbsp;
{% endif %}
</span>
</div>
<div class="detail-view-field">
<label>{{ form.serial_number.label }}</label>
<span>
{% if form.serial_number.value %}
{{ form.serial_number.value }}
{% else %}
&nbsp;
{% endif %}
</span>
</div>
<div class="detail-view-field">
<label>{{ form.uuid.label }}</label>
<span>
{% if form.uuid.value %}
{{ form.uuid.value }}
{% else %}
&nbsp;
{% endif %}
</span>
</div>
<div class="detail-view-field">
<label>{{ form.device_type.label }}</label>
<span>
{% if device.device_type %}
{{ device.device_type }}
{% else %}
&nbsp;
{% endif %}
</span>
</div>
<div class="detail-view-field">
<label>{{ form.organization.label }}</label>
<span>{{ device.organization }}</span>
</div>
<div class="detail-view-field">
<label>{{ form.lastinventory.label }}</label>
<span>
{% if form.lastinventory.value %}
{{ form.lastinventory.value }}
{% else %}
&nbsp;
{% endif %}
</span>
</div>
</div>
<div style="display: inline; width: 40%; margin: 30px; text-align: left;">
<div>
<label style="font-weight: bold; width: 100%; border-bottom: 1px solid #ccc; display: block; text-align: inherit;">{{ form.model_notes.label }}</label>
<div style="display: inline-block; text-align: left;">
{% if form.model_notes.value %}
{{ form.model_notes.value | markdown | safe }}
{% else %}
&nbsp;
{% endif %}
</div>
</div>
</div>
</div>
<input type="button" value="Edit" onclick="window.location='{% url 'ITAM:_device_change' device.id %}';">
<div style="display: block; width: 100%;">
<h3>Operating System</h3>
<br>
{{ operating_system.as_p }}
<input type="submit" name="{{ operating_system.prefix }}" value="Submit" />
<input type="submit" name="{{operating_system.prefix}}" value="Submit" />
</div>
<div style="display: block; width: 100%;">
<h3>Dependent Services</h3>
<table>
@ -41,8 +204,7 @@
</tr>
{% endif %}
</table>
</div>
</div>
<div style="display: block; width: 100%;">
<h3>Device Config</h3>
@ -50,15 +212,19 @@
<textarea cols="90" rows="30" readonly>{{ device.config }}</textarea>
</div>
<input type="button" value="Edit" onclick="window.location='{% url 'ITAM:_device_change' device.id %}';">
{% if not tab %}
<script>
// Get the element with id="defaultOpen" and click on it
document.getElementById("defaultOpen").click();
</script>
{% endif %}
</div>
</div>
<div id="software" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.software %}
<div id="Software" class="tabcontent">
<h3>Software</h3>
<hr>
Installed Software: {{ installed_software }}
<input type="button" value="Add Software Action" onclick="window.location='{% url 'ITAM:_device_software_add' device.id %}';">
@ -134,14 +300,19 @@
</span>
</div>
</div>
{% if tab == 'software' %}
<script>
// Get the element with id="defaultOpen" and click on it
document.getElementById("SoftwareOpen").click();
</script>
{% endif %}
</div>
<div id="notes" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.notes %}
<div id="Notes" class="tabcontent">
<h3>
Notes
</h3>
{{ notes_form }}
<input type="submit" name="{{notes_form.prefix}}" value="Submit" />
<div class="comments">
@ -152,14 +323,17 @@
{% endif %}
</div>
</div>
{% if tab == 'notes' %}
<script>
// Get the element with id="defaultOpen" and click on it
document.getElementById("NotesOpen").click();
</script>
{% endif %}
</div>
<div id="config_management" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.config_management %}
<div id="ConfigManagement" class="tabcontent">
<h3>Configuration Management</h3>
<div>
<textarea cols="90" rows="30" readonly>{{ config }}</textarea>
</div>
@ -186,6 +360,13 @@
{% endif %}
</table>
</div>
{% if tab == 'configmanagement' %}
<script>
// Get the element with id="defaultOpen" and click on it
document.getElementById("ConfigManagementOpen").click();
</script>
{% endif %}
</div>
</form>
{% endblock %}
{% endblock %}

View File

@ -1,34 +0,0 @@
{% extends 'detail.html.j2' %}
{% load json %}
{% load markdown %}
{% block tabs %}
<form action="" method="post">
{% csrf_token %}
<div id="details" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.details %}
</div>
<div id="notes" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.notes %}
{{ notes_form }}
<input type="submit" name="{{notes_form.prefix}}" value="Submit" />
<div class="comments">
{% if notes %}
{% for note in notes%}
{% include 'note.html.j2' %}
{% endfor %}
{% endif %}
</div>
</div>
</form>
{% endblock %}

View File

@ -1,36 +0,0 @@
{% extends 'detail.html.j2' %}
{% load json %}
{% load markdown %}
{% block tabs %}
<form action="" method="post">
{% csrf_token %}
<div id="details" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.details %}
</div>
<div id="notes" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.notes %}
{{ notes_form }}
<input type="submit" name="{{notes_form.prefix}}" value="Submit" />
<div class="comments">
{% if notes %}
{% for note in notes%}
{% include 'note.html.j2' %}
{% endfor %}
{% endif %}
</div>
</div>
</form>
{% endblock %}

View File

@ -1,116 +1,118 @@
{% extends 'detail.html.j2' %}
{% extends 'base.html.j2' %}
{% load json %}
{% load markdown %}
{% block title %}{{ operating_system.name }}{% endblock %}
{% block content %}
{% block tabs %}
<form action="" method="post">
<script>
function openCity(evt, cityName) {
var i, tabcontent, tablinks;
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
document.getElementById(cityName).style.display = "block";
evt.currentTarget.className += " active";
}
</script>
<div class="tab">
<button onclick="window.location='{% url 'ITAM:Operating Systems' %}';" style="vertical-align: middle; padding: auto; margin: 0px">
<svg xmlns="http://www.w3.org/2000/svg" height="25px" viewBox="0 -960 960 960" width="25px" style="vertical-align: middle; margin: 0px; padding: 0px border: none; " fill="#6a6e73">
<path d="m313-480 155 156q11 11 11.5 27.5T468-268q-11 11-28 11t-28-11L228-452q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l184-184q11-11 27.5-11.5T468-692q11 11 11 28t-11 28L313-480Zm264 0 155 156q11 11 11.5 27.5T732-268q-11 11-28 11t-28-11L492-452q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l184-184q11-11 27.5-11.5T732-692q11 11 11 28t-11 28L577-480Z"/>
</svg> Back to Operating Systems</button>
<button id="defaultOpen" class="tablinks" onclick="openCity(event, 'Details')">Details</button>
<button class="tablinks" onclick="openCity(event, 'Versions')">Versions</button>
<button class="tablinks" onclick="openCity(event, 'Licences')">Licences</button>
<button class="tablinks" onclick="openCity(event, 'Notes')">Notes</button>
<button class="tablinks" onclick="openCity(event, 'Installations')">Installations</button>
</div>
<form method="post">
<div id="Details" class="tabcontent">
<h3>Details</h3>
{% csrf_token %}
{{ form }}
<br>
<input type="submit" value="Submit">
<div id="details" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.details %}
<script>
document.getElementById("defaultOpen").click();
</script>
</div>
<div id="versions" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.versions %}
<table>
<thead>
<th>Version</th>
<th>Installations</th>
<th>Vulnerable</th>
<th>&nbsp;</th>
</thead>
{% for version in operating_system_versions %}
<tr>
<td><a href="{% url 'ITAM:_operating_system_version_view' operating_system_id=operating_system.id pk=version.id %}">{{ version.name }}</a></td>
<td>{% if version.installs == 0%}-{% else %}{{ version.installs }}{% endif %}</td>
<td>&nbsp;</td>
<td><a href="{% url 'ITAM:_operating_system_version_delete' operating_system_id=operating_system.id pk=version.id %}">DELETE</a></td>
</tr>
{% endfor %}
</table>
<div id="Versions" class="tabcontent">
<h3>Versions</h3>
<input type="button" value="New Operating System Version" onclick="window.location='{% url 'ITAM:_operating_system_version_add' pk=operating_system.id %}';">
<table>
<thead>
<th>Version</th>
<th>Installations</th>
<th>Vulnerable</th>
<th>&nbsp;</th>
</thead>
{% for version in operating_system_versions %}
<tr>
<td><a href="{% url 'ITAM:_operating_system_version_view' operating_system_id=operating_system.id pk=version.id %}">{{ version.name }}</a></td>
<td>{% if version.installs == 0%}-{% else %}{{ version.installs }}{% endif %}</td>
<td>&nbsp;</td>
<td><a href="{% url 'ITAM:_operating_system_version_delete' operating_system_id=operating_system.id pk=version.id %}">DELETE</a></td>
</tr>
{% endfor %}
</table>
</div>
<div id="licences" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.licences %}
{% include 'icons/issue_link.html.j2' with issue=4 %}
<table>
<thead>
<th>Name</th>
<th>Type</th>
<th>Available</th>
<th>&nbsp;</th>
</thead>
<tr>
<td>GPL-3</td>
<td>Open Source</td>
<td>1 / 5</td>
<th>&nbsp;</th>
</tr>
<tr>
<td>MIT</td>
<td>Open Source</td>
<td>Unlimited</td>
<th>&nbsp;</th>
</tr>
<tr>
<td>Windows Device</td>
<td>CAL</td>
<td>11 / 15</td>
<th>&nbsp;</th>
</tr>
</table>
<div id="Licences" class="tabcontent">
<h3>Licences</h3>
{% include 'icons/issue_link.html.j2' with issue=4 %}
<table>
<thead>
<th>Name</th>
<th>Type</th>
<th>Available</th>
<th>&nbsp;</th>
</thead>
<tr>
<td>GPL-3</td>
<td>Open Source</td>
<td>1 / 5</td>
<th>&nbsp;</th>
</tr>
<tr>
<td>MIT</td>
<td>Open Source</td>
<td>Unlimited</td>
<th>&nbsp;</th>
</tr>
<tr>
<td>Windows Device</td>
<td>CAL</td>
<td>11 / 15</td>
<th>&nbsp;</th>
</tr>
</table>
</div>
<div id="installations" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.installations %}
<table>
<thead>
<th>Device</th>
<th>Organization</th>
<th>Version</th>
<th title="Date Software Installed">Installed</th>
<th>&nbsp;</th>
</thead>
{% for install in installs %}
<tr>
<td><a href="{% url 'ITAM:_device_view' pk=install.device_id %}">{{ install.device }}</a></td>
<td>{{ install.organization }}</td>
<td>{{ install.version }}</td>
<td>
{% if install.installdate %}
{{ install.installdate }}
{% else %}
-
{% endif %}
</td>
<td>&nbsp;</td>
</tr>
{% endfor %}
</table>
</div>
<div id="notes" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.notes %}
<div id="Notes" class="tabcontent">
<h3>
Notes
</h3>
{{ notes_form }}
<input type="submit" name="{{notes_form.prefix}}" value="Submit" />
<div class="comments">
@ -121,8 +123,36 @@
{% endif %}
</div>
</div>
<div id="Installations" class="tabcontent">
<h3>Installations</h3>
<table>
<thead>
<th>Device</th>
<th>Organization</th>
<th>Version</th>
<th title="Date Software Installed">Installed</th>
<th>&nbsp;</th>
</thead>
{% for install in installs %}
<tr>
<td><a href="{% url 'ITAM:_device_view' pk=install.device_id %}">{{ install.device }}</a></td>
<td>{{ install.organization }}</td>
<td>{{ install.version }}</td>
<td>
{% if install.installdate %}
{{ install.installdate }}
{% else %}
-
{% endif %}
</td>
<td>&nbsp;</td>
</tr>
{% endfor %}
</table>
</div>
</form>
{% endblock %}
{% endblock %}

View File

@ -1,85 +1,123 @@
{% extends 'detail.html.j2' %}
{% extends 'base.html.j2' %}
{% load json %}
{% load markdown %}
{% block title %}{{ software.name }}{% endblock %}
{% block content %}
{% block tabs %}
<form action="" method="post">
{% csrf_token %}
<div id="details" class="content-tab">
<script>
{% include 'content/section.html.j2' with tab=form.tabs.details %}
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' %}';" style="vertical-align: middle; padding: auto; margin: 0px">
<svg xmlns="http://www.w3.org/2000/svg" height="25px" viewBox="0 -960 960 960" width="25px" style="vertical-align: middle; margin: 0px; padding: 0px border: none; " fill="#6a6e73">
<path d="m313-480 155 156q11 11 11.5 27.5T468-268q-11 11-28 11t-28-11L228-452q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l184-184q11-11 27.5-11.5T468-692q11 11 11 28t-11 28L313-480Zm264 0 155 156q11 11 11.5 27.5T732-268q-11 11-28 11t-28-11L492-452q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l184-184q11-11 27.5-11.5T732-692q11 11 11 28t-11 28L577-480Z"/>
</svg> 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, 'Notes')">Notes</button>
<button class="tablinks" onclick="openCity(event, 'Installations')">Installations</button>
</div>
<form method="post">
<div id="Details" class="tabcontent">
<h3>
Details
{% for external_link in external_links %}
<span style="font-weight: normal; float: right;">{% include 'icons/external_link.html.j2' with external_link=external_link %}</span>
{% endfor %}
</h3>
{% csrf_token %}
{{ form }}
<br>
<input type="submit" value="Submit">
<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>
<input type="button" value="New Software Version" onclick="window.location='{% url 'ITAM:_software_version_add' pk=software.id %}';">
<table>
<thead>
<th>Version</th>
<th>Installations</th>
<th>Vulnerable</th>
<th>&nbsp;</th>
</thead>
{% for version in software_versions %}
<tr>
<td><a href="{% url 'ITAM:_software_version_view' software_id=software.id pk=version.id %}">{{ version.name }}</a></td>
<td>{{ version.installs }}</td>
<td>{% include 'icons/issue_link.html.j2' with issue=3 %}</td>
<td>&nbsp;</td>
</tr>
{% endfor %}
</table>
</div>
<div id="versions" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.versions %}
<input type="button" value="New Software Version" onclick="window.location='{% url 'ITAM:_software_version_add' pk=software.id %}';">
<table>
<thead>
<th>Version</th>
<th>Installations</th>
<th>Vulnerable</th>
<th>&nbsp;</th>
</thead>
{% for version in software_versions %}
<tr>
<td><a href="{% url 'ITAM:_software_version_view' software_id=software.id pk=version.id %}">{{ version.name }}</a></td>
<td>{{ version.installs }}</td>
<td>{% include 'icons/issue_link.html.j2' with issue=3 %}</td>
<td>&nbsp;</td>
</tr>
{% endfor %}
</table>
<div id="Licences" class="tabcontent">
<h3>Licences</h3>
{% include 'icons/issue_link.html.j2' with issue=4 %}
<table>
<thead>
<th>Name</th>
<th>Type</th>
<th>Available</th>
<th>&nbsp;</th>
</thead>
<tr>
<td>GPL-3</td>
<td>Open Source</td>
<td>1 / 5</td>
<th>&nbsp;</th>
</tr>
<tr>
<td>MIT</td>
<td>Open Source</td>
<td>Unlimited</td>
<th>&nbsp;</th>
</tr>
<tr>
<td>Windows Device</td>
<td>CAL</td>
<td>11 / 15</td>
<th>&nbsp;</th>
</tr>
</table>
</div>
<div id="licences" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.licences %}
{% include 'icons/issue_link.html.j2' with issue=4 %}
<table>
<thead>
<th>Name</th>
<th>Type</th>
<th>Available</th>
<th>&nbsp;</th>
</thead>
<tr>
<td>GPL-3</td>
<td>Open Source</td>
<td>1 / 5</td>
<th>&nbsp;</th>
</tr>
<tr>
<td>MIT</td>
<td>Open Source</td>
<td>Unlimited</td>
<th>&nbsp;</th>
</tr>
<tr>
<td>Windows Device</td>
<td>CAL</td>
<td>11 / 15</td>
<th>&nbsp;</th>
</tr>
</table>
</div>
<div id="notes" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.notes %}
<div id="Notes" class="tabcontent">
<h3>
Notes
</h3>
{{ notes_form }}
<input type="submit" name="{{notes_form.prefix}}" value="Submit" />
<div class="comments">
@ -90,61 +128,58 @@
{% endif %}
</div>
</div>
</div>
<div id="installations" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.installations %}
<table>
<thead>
<th>Device</th>
<th>Organization</th>
<th title="Not Set/Install/Remove">Action</th>
<th>Installed Version</th>
<th title="Date Software Installed">Install Date</th>
<th>&nbsp;</th>
</thead>
{% if device_software %}
{% for device in device_software %}
<tr>
<td><a href="{% url 'ITAM:_device_view' pk=device.device.id %}">{{ device.device }}</a></td>
<td>{{ device.organization }}</td>
<td>
{% if device.get_action_display == 'Install' %}
{% include 'icons/success_text.html.j2' with icon_text=device.get_action_display %}
{% elif device.get_action_display == 'Remove'%}
{% include 'icons/cross_text.html.j2' with icon_text=device.get_action_display %}
{% else %}
-
{% endif %}
</td>
<td>
{% if device.installedversion %}
{{ device.installedversion }}
{% else %}
-
{% endif %}
</td>
<td>
{% if device.installed %}
{{ device.installed }}
{% else %}
-
{% endif %}
</td>
<td>&nbsp;</td>
</tr>
{% endfor %}
<div id="Installations" class="tabcontent">
<h3>Installations</h3>
<table>
<thead>
<th>Device</th>
<th>Organization</th>
<th title="Not Set/Install/Remove">Action</th>
<th>Installed Version</th>
<th title="Date Software Installed">Install Date</th>
<th>&nbsp;</th>
</thead>
{% if device_software %}
{% for device in device_software %}
<tr>
<td><a href="{% url 'ITAM:_device_view' pk=device.device.id %}">{{ device.device }}</a></td>
<td>{{ device.organization }}</td>
<td>
{% if device.get_action_display == 'Install' %}
{% include 'icons/success_text.html.j2' with icon_text=device.get_action_display %}
{% elif device.get_action_display == 'Remove'%}
{% include 'icons/cross_text.html.j2' with icon_text=device.get_action_display %}
{% else %}
-
{% endif %}
</td>
<td>
{% if device.installedversion %}
{{ device.installedversion }}
{% else %}
-
{% endif %}
</td>
<td>
{% if device.installed %}
{{ device.installed }}
{% else %}
<tr>
<td colspan="6">Nothing Found</td>
</tr>
-
{% endif %}
</table>
</td>
<td>&nbsp;</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="6">Nothing Found</td>
</tr>
{% endif %}
</table>
</div>
</form>
{% endblock %}
{% endblock %}

View File

@ -1,150 +0,0 @@
{% extends 'detail.html.j2' %}
{% load json %}
{% load markdown %}
{% block tabs %}
<form action="" method="post">
{% csrf_token %}
<div id="details" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.details %}
</div>
<div id="versions" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.versions %}
<input type="button" value="New Software Version" onclick="window.location='{% url 'ITAM:_software_version_add' pk=software.id %}';">
<table>
<thead>
<th>Version</th>
<th>Installations</th>
<th>Vulnerable</th>
<th>&nbsp;</th>
</thead>
{% for version in software_versions %}
<tr>
<td><a href="{% url 'ITAM:_software_version_view' software_id=software.id pk=version.id %}">{{ version.name }}</a></td>
<td>{{ version.installs }}</td>
<td>{% include 'icons/issue_link.html.j2' with issue=3 %}</td>
<td>&nbsp;</td>
</tr>
{% endfor %}
</table>
</div>
<div id="licences" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.licences %}
{% include 'icons/issue_link.html.j2' with issue=4 %}
<table>
<thead>
<th>Name</th>
<th>Type</th>
<th>Available</th>
<th>&nbsp;</th>
</thead>
<tr>
<td>GPL-3</td>
<td>Open Source</td>
<td>1 / 5</td>
<th>&nbsp;</th>
</tr>
<tr>
<td>MIT</td>
<td>Open Source</td>
<td>Unlimited</td>
<th>&nbsp;</th>
</tr>
<tr>
<td>Windows Device</td>
<td>CAL</td>
<td>11 / 15</td>
<th>&nbsp;</th>
</tr>
</table>
</div>
<div id="notes" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.notes %}
{{ notes_form }}
<input type="submit" name="{{notes_form.prefix}}" value="Submit" />
<div class="comments">
{% if notes %}
{% for note in notes%}
{% include 'note.html.j2' %}
{% endfor %}
{% endif %}
</div>
</div>
<div id="installations" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.installations %}
<table>
<thead>
<th>Device</th>
<th>Organization</th>
<th title="Not Set/Install/Remove">Action</th>
<th>Installed Version</th>
<th title="Date Software Installed">Install Date</th>
<th>&nbsp;</th>
</thead>
{% if device_software %}
{% for device in device_software %}
<tr>
<td><a href="{% url 'ITAM:_device_view' pk=device.device.id %}">{{ device.device }}</a></td>
<td>{{ device.organization }}</td>
<td>
{% if device.get_action_display == 'Install' %}
{% include 'icons/success_text.html.j2' with icon_text=device.get_action_display %}
{% elif device.get_action_display == 'Remove'%}
{% include 'icons/cross_text.html.j2' with icon_text=device.get_action_display %}
{% else %}
-
{% endif %}
</td>
<td>
{% if device.installedversion %}
{{ device.installedversion }}
{% else %}
-
{% endif %}
</td>
<td>
{% if device.installed %}
{{ device.installed }}
{% else %}
-
{% endif %}
</td>
<td>&nbsp;</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="6">Nothing Found</td>
</tr>
{% endif %}
</table>
</div>
</form>
{% endblock %}

View File

@ -30,7 +30,7 @@ class DeviceModelPermissions(TestCase, ModelPermissions):
url_name_add = '_device_model_add'
url_name_change = '_device_model_change'
url_name_change = '_device_model_view'
url_name_delete = '_device_model_delete'

View File

@ -26,7 +26,7 @@ class DeviceTypePermissions(TestCase, ModelPermissions):
url_name_add = '_device_type_add'
url_name_change = '_device_type_change'
url_name_change = '_device_type_view'
url_name_delete = '_device_type_delete'

View File

@ -27,7 +27,7 @@ class OperatingSystemPermissions(TestCase, ModelPermissions):
url_name_add = '_operating_system_add'
url_name_change = '_operating_system_change'
url_name_change = '_operating_system_view'
url_name_delete = '_operating_system_delete'

View File

@ -26,7 +26,7 @@ class SoftwarePermissions(TestCase, ModelPermissions):
url_name_add = '_software_add'
url_name_change = '_software_change'
url_name_change = '_software_view'
url_name_delete = '_software_delete'

View File

@ -29,7 +29,7 @@ class SoftwareCategoryPermissions(TestCase, ModelPermissions):
url_name_add = '_software_category_add'
url_name_change = '_software_category_change'
url_name_change = '_software_category_view'
url_name_delete = '_software_category_delete'

View File

@ -18,7 +18,6 @@ urlpatterns = [
path("operating_system", operating_system.IndexView.as_view(), name="Operating Systems"),
path("operating_system/<int:pk>", operating_system.View.as_view(), name="_operating_system_view"),
path("operating_system/<int:pk>/edit", operating_system.Change.as_view(), name="_operating_system_change"),
path("operating_system/add", operating_system.Add.as_view(), name="_operating_system_add"),
path("operating_system/delete/<int:pk>", operating_system.Delete.as_view(), name="_operating_system_delete"),
@ -31,7 +30,6 @@ urlpatterns = [
path("software/", software.IndexView.as_view(), name="Software"),
path("software/<int:pk>/", software.View.as_view(), name="_software_view"),
path("software/<int:pk>/change", software.Change.as_view(), name="_software_change"),
path("software/<int:pk>/delete", software.Delete.as_view(), name="_software_delete"),
path("software/<int:pk>/version/add", software_version.Add.as_view(), name="_software_version_add"),
path("software/<int:software_id>/version/<int:pk>", software_version.View.as_view(), name="_software_version_view"),

View File

@ -21,7 +21,7 @@ from core.views.common import AddView, ChangeView, DeleteView, IndexView
from itam.forms.device_softwareadd import SoftwareAdd
from itam.forms.device_softwareupdate import SoftwareUpdate
from itam.forms.device.device import DetailForm, DeviceForm
from itam.forms.device.device import DeviceForm
from itam.forms.device.operating_system import Update as OperatingSystemForm
from itim.models.services import Service
@ -79,7 +79,7 @@ class View(ChangeView):
template_name = 'itam/device.html.j2'
form_class = DetailForm
form_class = DeviceForm
context_object_name = "device"

View File

@ -2,7 +2,7 @@ from django.contrib.auth import decorators as auth_decorator
from django.urls import reverse
from django.utils.decorators import method_decorator
from itam.forms.device_model import DetailForm, DeviceModelForm
from itam.forms.device_model import DeviceModelForm
from itam.models.device_models import DeviceModel
from core.views.common import AddView, ChangeView, DeleteView
@ -11,7 +11,7 @@ from settings.models.user_settings import UserSettings
class Change(ChangeView):
class View(ChangeView):
form_class = DeviceModelForm
@ -20,40 +20,13 @@ class Change(ChangeView):
model = DeviceModel
permission_required = [
'itam.view_devicemodel',
'itam.change_devicemodel',
]
template_name = 'form.html.j2'
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 reverse('Settings:_device_model_view', args=(self.kwargs['pk'],))
class View(ChangeView):
form_class = DetailForm
context_object_name = "device_model"
model = DeviceModel
permission_required = [
'itam.view_devicemodel',
]
template_name = 'itam/device_model.html.j2'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)

View File

@ -5,11 +5,48 @@ from django.utils.decorators import method_decorator
from core.views.common import AddView, ChangeView, DeleteView, IndexView
from itam.models.device import DeviceType
from itam.forms.device_type import DetailForm, DeviceTypeForm
from itam.forms.device_type import DeviceTypeForm
class View(ChangeView):
form_class = DeviceTypeForm
model = DeviceType
permission_required = [
'itam.view_devicetype',
'itam.change_devicetype'
]
template_name = 'form.html.j2'
context_object_name = "device_category"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['model_delete_url'] = reverse('Settings:_device_type_delete', args=(self.kwargs['pk'],))
context['content_title'] = self.object.name
return context
def get_success_url(self, **kwargs):
return reverse('Settings:_device_type_view', args=(self.kwargs['pk'],))
@method_decorator(auth_decorator.permission_required("itam.change_devicetype", raise_exception=True))
def post(self, request, *args, **kwargs):
return super().post(request, *args, **kwargs)
class Add(AddView):
form_class = DeviceTypeForm
@ -36,36 +73,6 @@ class Add(AddView):
return context
class Change(ChangeView):
form_class = DeviceTypeForm
model = DeviceType
permission_required = [
'itam.change_devicetype'
]
template_name = 'form.html.j2'
context_object_name = "device_category"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['model_delete_url'] = reverse('Settings:_device_type_delete', args=(self.kwargs['pk'],))
context['content_title'] = self.object.name
return context
def get_success_url(self, **kwargs):
return reverse('Settings:_device_type_view', args=(self.kwargs['pk'],))
class Delete(DeleteView):
model = DeviceType
@ -91,39 +98,3 @@ class Delete(DeleteView):
context['content_title'] = 'Delete ' + self.object.name
return context
class View(ChangeView):
form_class = DetailForm
model = DeviceType
permission_required = [
'itam.view_devicetype',
]
template_name = 'itam/device_type.html.j2'
context_object_name = "device_category"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['model_delete_url'] = reverse('Settings:_device_type_delete', args=(self.kwargs['pk'],))
context['content_title'] = self.object.name
return context
def get_success_url(self, **kwargs):
return reverse('Settings:_device_type_view', args=(self.kwargs['pk'],))
@method_decorator(auth_decorator.permission_required("itam.change_devicetype", raise_exception=True))
def post(self, request, *args, **kwargs):
return super().post(request, *args, **kwargs)

View File

@ -9,120 +9,11 @@ from core.views.common import AddView, ChangeView, DeleteView, IndexView
from itam.models.device import DeviceOperatingSystem
from itam.models.operating_system import OperatingSystem, OperatingSystemVersion
from itam.forms.operating_system.update import DetailForm, OperatingSystemForm
from itam.forms.operating_system.update import OperatingSystemFormCommon, Update
from settings.models.user_settings import UserSettings
class Add(AddView):
form_class = OperatingSystemForm
model = OperatingSystem
permission_required = [
'itam.add_operatingsystem',
]
template_name = 'form.html.j2'
def get_initial(self):
return {
'organization': UserSettings.objects.get(user = self.request.user).default_organization
}
def get_success_url(self, **kwargs):
return reverse('ITAM:Operating Systems')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = 'Add Operating System'
return context
class Change(ChangeView):
context_object_name = "operating_system"
form_class = OperatingSystemForm
model = OperatingSystem
permission_required = [
'itam.change_operatingsystem',
]
template_name = 'form.html.j2'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = self.object.name
return context
@method_decorator(auth_decorator.permission_required("itam.change_operatingsystem", raise_exception=True))
def post(self, request, *args, **kwargs):
operatingsystem = OperatingSystem.objects.get(pk=self.kwargs['pk'])
notes = AddNoteForm(request.POST, prefix='note')
if notes.is_bound and notes.is_valid() and notes.instance.note != '':
notes.instance.organization = operatingsystem.organization
notes.instance.operatingsystem = operatingsystem
notes.instance.usercreated = request.user
notes.save()
return super().post(request, *args, **kwargs)
def get_success_url(self, **kwargs):
return reverse('ITAM:_operating_system_view', args=(self.kwargs['pk'],))
class Delete(DeleteView):
model = OperatingSystem
permission_required = [
'itam.delete_operatingsystem',
]
template_name = 'form.html.j2'
def get_success_url(self, **kwargs):
return reverse('ITAM:Operating Systems')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['model_pk'] = self.kwargs['pk']
context['model_name'] = self.model._meta.verbose_name.replace(' ', '')
context['content_title'] = 'Delete ' + self.object.name
return context
class IndexView(IndexView):
model = OperatingSystem
permission_required = [
@ -157,12 +48,13 @@ class View(ChangeView):
context_object_name = "operating_system"
form_class = DetailForm
form_class = Update
model = OperatingSystem
permission_required = [
'itam.view_operatingsystem',
'itam.change_operatingsystem',
]
template_name = 'itam/operating_system.html.j2'
@ -204,7 +96,7 @@ class View(ChangeView):
return context
# @method_decorator(auth_decorator.permission_required("itam.change_operatingsystem", raise_exception=True))
@method_decorator(auth_decorator.permission_required("itam.change_operatingsystem", raise_exception=True))
def post(self, request, *args, **kwargs):
operatingsystem = OperatingSystem.objects.get(pk=self.kwargs['pk'])
@ -225,3 +117,65 @@ class View(ChangeView):
def get_success_url(self, **kwargs):
return reverse('ITAM:_operating_system_view', args=(self.kwargs['pk'],))
class Add(AddView):
form_class = OperatingSystemFormCommon
model = OperatingSystem
permission_required = [
'itam.add_operatingsystem',
]
template_name = 'form.html.j2'
def get_initial(self):
return {
'organization': UserSettings.objects.get(user = self.request.user).default_organization
}
def get_success_url(self, **kwargs):
return reverse('ITAM:Operating Systems')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = 'Add Operating System'
return context
class Delete(DeleteView):
model = OperatingSystem
permission_required = [
'itam.delete_operatingsystem',
]
template_name = 'form.html.j2'
def get_success_url(self, **kwargs):
return reverse('ITAM:Operating Systems')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['model_pk'] = self.kwargs['pk']
context['model_name'] = self.model._meta.verbose_name.replace(' ', '')
context['content_title'] = 'Delete ' + self.object.name
return context

View File

@ -9,7 +9,7 @@ from core.views.common import AddView, ChangeView, DeleteView, IndexView
from itam.models.device import DeviceSoftware
from itam.models.software import Software, SoftwareVersion
from itam.forms.software.update import DetailForm, SoftwareForm, SoftwareChange
from itam.forms.software.update import SoftwareForm, SoftwareFormUpdate
from settings.models.user_settings import UserSettings
@ -51,39 +51,11 @@ class IndexView(IndexView):
class Change(ChangeView):
model = Software
permission_required = [
'itam.change_software',
]
template_name = 'form.html.j2'
form_class = SoftwareChange
def get_success_url(self, **kwargs):
return reverse('ITAM:_software_view', args=(self.kwargs['pk'],))
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = 'Edit ' + self.object.name
return context
class View(ChangeView):
context_object_name = "software"
form_class = DetailForm
form_class = SoftwareFormUpdate
model = Software

View File

@ -4,13 +4,13 @@ from django.utils.decorators import method_decorator
from core.views.common import AddView, ChangeView, DeleteView
from itam.forms.software_category import DetailForm, SoftwareCategoryForm
from itam.forms.software_category import SoftwareCategoryForm
from itam.models.software import Software, SoftwareCategory
from settings.models.user_settings import UserSettings
class Change(ChangeView):
class View(ChangeView):
context_object_name = "software"
@ -19,6 +19,7 @@ class Change(ChangeView):
model = SoftwareCategory
permission_required = [
'itam.view_softwarecategory',
'itam.change_softwarecategory',
]
@ -47,38 +48,6 @@ class Change(ChangeView):
class View(ChangeView):
context_object_name = "software"
form_class = DetailForm
model = SoftwareCategory
permission_required = [
'itam.view_softwarecategory',
]
template_name = 'itam/software_categories.html.j2'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['model_delete_url'] = reverse('Settings:_software_category_delete', args=(self.kwargs['pk'],))
context['content_title'] = self.object.name
return context
def get_success_url(self, **kwargs):
return reverse('Settings:_software_category_view', args=(self.kwargs['pk'],))
class Add(AddView):
form_class = SoftwareCategoryForm

View File

@ -1,94 +0,0 @@
from django import forms
from django.forms import ValidationError
from django.urls import reverse
from itim.models.clusters import ClusterType
from app import settings
from core.forms.common import CommonModelForm
class ClusterTypeForm(CommonModelForm):
class Meta:
fields = '__all__'
model = ClusterType
prefix = 'cluster_type'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class DetailForm(ClusterTypeForm):
tabs: dict = {
"details": {
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'name',
'organization',
'c_created',
'c_modified'
],
"right": [
'model_notes',
]
},
{
"layout": "single",
"name": "Configuration",
"fields": [
'config'
],
"json": [
'config',
]
}
]
},
"notes": {
"name": "Notes",
"slug": "notes",
"sections": []
}
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['c_created'] = forms.DateTimeField(
label = 'Created',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.created,
)
self.fields['c_modified'] = forms.DateTimeField(
label = 'Modified',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.modified,
)
self.tabs['details'].update({
"edit_url": reverse('Settings:_cluster_type_change', args=(self.instance.pk,))
})
self.url_index_view = reverse('Settings:_cluster_types')

View File

@ -1,152 +0,0 @@
from django import forms
from django.forms import ValidationError
from django.urls import reverse
from itim.models.clusters import Cluster
from app import settings
from core.forms.common import CommonModelForm
class ClusterForm(CommonModelForm):
class Meta:
fields = '__all__'
model = Cluster
prefix = 'cluster'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['parent_cluster'].queryset = self.fields['parent_cluster'].queryset.exclude(
id=self.instance.pk
)
self.fields['devices'].queryset = self.fields['devices'].queryset.exclude(
is_virtual=False
)
def clean(self):
cleaned_data = super().clean()
pk = self.instance.id
parent_cluster = cleaned_data.get("parent_cluster")
if pk:
if parent_cluster == pk:
raise ValidationError("Cluster can't have itself as its parent cluster")
return cleaned_data
class DetailForm(ClusterForm):
tabs: dict = {
"details": {
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'parent_cluster',
'cluster_type',
'name',
'organization',
'c_created',
'c_modified'
],
"right": [
'model_notes',
'resources',
]
},
]
},
"rendered_config": {
"name": "Rendered Config",
"slug": "rendered_config",
"sections": [
{
"layout": "single",
"fields": [
'rendered_config',
],
"json": [
'rendered_config'
]
}
]
},
"notes": {
"name": "Notes",
"slug": "notes",
"sections": []
}
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# self.fields['config_variables'] = forms.fields.JSONField(
# widget = forms.Textarea(
# attrs = {
# "cols": "80",
# "rows": "100"
# }
# ),
# label = 'Rendered Configuration',
# initial = self.instance.config_variables,
# )
self.fields['c_created'] = forms.DateTimeField(
label = 'Created',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.created,
)
self.fields['c_modified'] = forms.DateTimeField(
label = 'Modified',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.modified,
)
self.fields['resources'] = forms.CharField(
label = 'Available Resources',
disabled = True,
initial = 'xx/yy CPU, xx/yy RAM, xx/yy Storage',
)
self.fields['rendered_config'] = forms.fields.JSONField(
label = 'Available Resources',
disabled = True,
initial = self.instance.rendered_config,
)
self.tabs['details'].update({
"edit_url": reverse('ITIM:_cluster_change', args=(self.instance.pk,))
})
self.url_index_view = reverse('ITIM:Clusters')

View File

@ -1,8 +1,8 @@
from django import forms
from django.urls import reverse
# from django import forms
# from django.forms import ValidationError
from app import settings
# from app import settings
from itim.models.services import Port
@ -22,67 +22,3 @@ class PortForm(CommonModelForm):
model = Port
prefix = 'port'
class DetailForm(PortForm):
tabs: dict = {
"details": {
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'number',
'description',
'protocol',
'organization',
'c_created',
'c_modified',
'lastinventory',
],
"right": [
'model_notes',
]
}
]
},
"services": {
"name": "Services",
"slug": "services",
"sections": []
},
"notes": {
"name": "Notes",
"slug": "notes",
"sections": []
},
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['c_created'] = forms.DateTimeField(
label = 'Created',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.created,
)
self.fields['c_modified'] = forms.DateTimeField(
label = 'Modified',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.modified,
)
self.tabs['details'].update({
"edit_url": reverse('Settings:_port_change', args=(self.instance.pk,))
})
self.url_index_view = reverse('Settings:_ports')

View File

@ -44,7 +44,6 @@ class ServiceForm(CommonModelForm):
dependent_service = cleaned_data.get("dependent_service")
device = cleaned_data.get("device")
cluster = cleaned_data.get("cluster")
config_key_variable = cleaned_data.get("config_key_variable")
is_template = cleaned_data.get("is_template")
template = cleaned_data.get("template")
port = cleaned_data.get("port")
@ -66,10 +65,6 @@ class ServiceForm(CommonModelForm):
raise ValidationError('Port(s) must be assigned to a service.')
if not is_template and not config_key_variable:
raise ValidationError('Configuration Key must be specified')
if dependent_service:
for dependency in dependent_service:
@ -165,6 +160,3 @@ class DetailForm(ServiceForm):
self.tabs['details'].update({
"edit_url": reverse('ITIM:_service_change', args=(self.instance.pk,))
})
self.url_index_view = reverse('ITIM:Services')

View File

@ -1,4 +1,4 @@
# Generated by Django 5.0.7 on 2024-08-17 08:05
# Generated by Django 5.0.7 on 2024-07-21 02:35
import access.fields
import access.models
@ -14,7 +14,7 @@ class Migration(migrations.Migration):
dependencies = [
('access', '0001_initial'),
('itam', '0003_alter_device_options_alter_devicemodel_options_and_more'),
('itam', '0002_device_config'),
]
operations = [
@ -29,8 +29,8 @@ class Migration(migrations.Migration):
('organization', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists])),
],
options={
'verbose_name': 'Cluster Type',
'verbose_name_plural': 'Cluster Types',
'verbose_name': 'ClusterType',
'verbose_name_plural': 'ClusterTypes',
'ordering': ['name'],
},
),
@ -43,13 +43,11 @@ class Migration(migrations.Migration):
('name', models.CharField(help_text='Name of the Cluster', max_length=50, verbose_name='Name')),
('slug', access.fields.AutoSlugField()),
('config', models.JSONField(blank=True, default=None, help_text='Cluster Configuration', null=True, verbose_name='Configuration')),
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False)),
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False)),
('devices', models.ManyToManyField(blank=True, default=None, help_text='Devices that are deployed upon the cluster.', related_name='cluster_device', to='itam.device', verbose_name='Devices')),
('nodes', models.ManyToManyField(blank=True, default=None, help_text='Hosts for resource consumption that the cluster is deployed upon', related_name='cluster_node', to='itam.device', verbose_name='Nodes')),
('node', models.ManyToManyField(blank=True, default=None, help_text='Hosts for resource consumption that the cluster is deployed upon', related_name='cluster_node', to='itam.device', verbose_name='Nodes')),
('organization', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists])),
('parent_cluster', models.ForeignKey(blank=True, default=None, help_text='Parent Cluster for this cluster', null=True, on_delete=django.db.models.deletion.CASCADE, to='itim.cluster', verbose_name='Parent Cluster')),
('cluster_type', models.ForeignKey(blank=True, default=None, help_text='Type of Cluster', null=True, on_delete=django.db.models.deletion.CASCADE, to='itim.clustertype', verbose_name='Cluster Type')),
('cluster_type', models.ForeignKey(blank=True, default=None, help_text='Parent Cluster for this cluster', null=True, on_delete=django.db.models.deletion.CASCADE, to='itim.clustertype', verbose_name='Parent Cluster')),
],
options={
'verbose_name': 'Cluster',

View File

@ -1,25 +0,0 @@
# Generated by Django 5.0.7 on 2024-08-18 03:57
import access.fields
import django.utils.timezone
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('itim', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='clustertype',
name='created',
field=access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False),
),
migrations.AddField(
model_name='clustertype',
name='modified',
field=access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False),
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 5.0.7 on 2024-08-19 04:57
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('itim', '0002_clustertype_created_clustertype_modified'),
]
operations = [
migrations.AddField(
model_name='clustertype',
name='config',
field=models.JSONField(blank=True, default=None, help_text='Cluster Type Configuration that is applied to all clusters of this type', null=True, verbose_name='Configuration'),
),
]

View File

@ -18,9 +18,9 @@ class ClusterType(TenancyObject):
'name',
]
verbose_name = "Cluster Type"
verbose_name = "ClusterType"
verbose_name_plural = "Cluster Types"
verbose_name_plural = "ClusterTypes"
id = models.AutoField(
@ -40,25 +40,6 @@ class ClusterType(TenancyObject):
slug = AutoSlugField()
config = models.JSONField(
blank = True,
default = None,
help_text = 'Cluster Type Configuration that is applied to all clusters of this type',
null = True,
verbose_name = 'Configuration',
)
created = AutoCreatedField()
modified = AutoLastModifiedField()
def __str__(self):
return self.name
class Cluster(TenancyObject):
@ -94,10 +75,10 @@ class Cluster(TenancyObject):
ClusterType,
blank = True,
default = None,
help_text = 'Type of Cluster',
help_text = 'Parent Cluster for this cluster',
null = True,
on_delete = models.CASCADE,
verbose_name = 'Cluster Type',
verbose_name = 'Parent Cluster',
)
name = models.CharField(
@ -118,7 +99,7 @@ class Cluster(TenancyObject):
verbose_name = 'Configuration',
)
nodes = models.ManyToManyField(
node = models.ManyToManyField(
Device,
blank = True,
default = None,
@ -136,44 +117,6 @@ class Cluster(TenancyObject):
verbose_name = 'Devices',
)
created = AutoCreatedField()
modified = AutoLastModifiedField()
@property
def rendered_config(self):
from itim.models.services import Service
rendered_config: dict = {}
if self.cluster_type:
if self.cluster_type.config:
rendered_config.update(
self.cluster_type.config
)
for service in Service.objects.filter(cluster = self.pk):
if service.config_variables:
rendered_config.update( service.config_variables )
if self.config:
rendered_config.update(
self.config
)
return rendered_config
def __str__(self):

View File

@ -169,7 +169,7 @@ class Service(TenancyObject):
)
config_key_variable = models.CharField(
blank = True,
blank = False,
help_text = 'Key name to use when merging with cluster/device config.',
max_length = 50,
null = True,

View File

@ -1,108 +0,0 @@
{% extends 'detail.html.j2' %}
{% load json %}
{% load markdown %}
{% block tabs %}
<div id="details" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.details %}
<hr />
<div style="display: block; width: 100%;">
<h3>Nodes</h3>
<table>
<tr>
<th>Name</th>
<th>Organization</th>
</tr>
{% if cluster.nodes.all %}
{% for node in cluster.nodes.all %}
<tr>
<td><a href="{% url 'ITAM:_device_view' node.pk %}">{{ node }}</a></td>
<td>{{ node.organization }}</td>
</tr>
{% endfor%}
{% else %}
<tr>
<td colspan="2"> Nothing Found</td>
</tr>
{% endif %}
</table>
</div>
<div style="display: block; width: 100%;">
<h3>Devices</h3>
<table>
<tr>
<th>Name</th>
<th>Organization</th>
</tr>
{% if cluster.devices.all %}
{% for device in cluster.devices.all %}
<tr>
<td><a href="{% url 'ITAM:_device_view' device.pk %}">{{ device }}</a></td>
<td>{{ device.organization }}</td>
</tr>
{% endfor%}
{% else %}
<tr>
<td colspan="2"> Nothing Found</td>
</tr>
{% endif %}
</table>
</div>
<div style="display: block; width: 100%;">
<h3>Services</h3>
<table>
<tr>
<th>Name</th>
<th>Ports</th>
</tr>
{% if services %}
{% for service in services.all %}
<tr>
<td><a href="{% url 'ITIM:_service_view' service.id %}">{{ service.name }}</a></td>
<td>
{% for port in service.port.all %}
{{ port.protocol }}/{{ port.number }} - {{ port.description }},
{% endfor %}
</td>
</tr>
{% endfor%}
{% else %}
<tr>
<td colspan="2"> Nothing Found</td>
</tr>
{% endif %}
</table>
</div>
<div style="display: block; width: 100%;">
<h3>Config</h3>
<pre>{{ cluster.config | json_pretty }}</pre>
</div>
</div>
<div id="rendered_config" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.rendered_config %}
</div>
{% endblock %}

View File

@ -1,53 +0,0 @@
{% extends 'base.html.j2' %}
{% block content %}
<input type="button" value="New Cluster" onclick="window.location='{% url 'ITIM:_cluster_add' %}';">
<table class="data">
<tr>
<th>Title</th>
<th>Type</th>
<th>Organization</th>
<th>&nbsp;</th>
</tr>
{% if items %}
{% for item in items %}
<tr>
<td><a href="{% url 'ITIM:_cluster_view' pk=item.id %}">{{ item.name }}</a></td>
<td>
{% if item.cluster_type %}
{{ item.cluster_type }}
{% else %}
&nbsp;
{% endif %}
</td>
<td>{{ item.organization }}</td>
<td>&nbsp;</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="4">Nothing Found</td>
</tr>
{% endif %}
</table>
<br>
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
<a href="?page=1">&laquo; first</a>
<a href="?page={{ page_obj.previous_page_number }}">previous</a>
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">next</a>
<a href="?page={{ page_obj.paginator.num_pages }}">last &raquo;</a>
{% endif %}
</span>
</div>
{% endblock %}

View File

@ -1,22 +0,0 @@
{% extends 'detail.html.j2' %}
{% load json %}
{% load markdown %}
{% block tabs %}
<div id="details" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.details %}
</div>
<div id="notes" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.notes %}
</div>
{% endblock %}

View File

@ -1,47 +0,0 @@
{% extends 'base.html.j2' %}
{% block content %}
<input type="button" value="New Cluster Type" onclick="window.location='{% url 'Settings:_cluster_type_add' %}';">
<table class="data">
<tr>
<th>Title</th>
<th>&nbsp;</th>
<th>Organization</th>
<th>&nbsp;</th>
</tr>
{% if items %}
{% for item in items %}
<tr>
<td><a href="{% url 'Settings:_cluster_type_view' pk=item.id %}">{{ item.name }}</a></td>
<td>&nbsp;</td>
<td>{{ item.organization }}</td>
<td>&nbsp;</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="4">Nothing Found</td>
</tr>
{% endif %}
</table>
<br>
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
<a href="?page=1">&laquo; first</a>
<a href="?page={{ page_obj.previous_page_number }}">previous</a>
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">next</a>
<a href="?page={{ page_obj.paginator.num_pages }}">last &raquo;</a>
{% endif %}
</span>
</div>
{% endblock %}

View File

@ -1,55 +1,196 @@
{% extends 'detail.html.j2' %}
{% extends 'base.html.j2' %}
{% load json %}
{% load markdown %}
{% block content %}
{% block tabs %}
<form action="" method="post">
{% csrf_token %}
<div id="details" class="content-tab">
<script>
{% include 'content/section.html.j2' with tab=form.tabs.details %}
function openCity(evt, cityName) {
var i, tabcontent, tablinks;
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
document.getElementById(cityName).style.display = "block";
evt.currentTarget.className += " active";
}
</script>
<style>
.detail-view-field {
display: unset;
height: 30px;
line-height: 30px;
padding: 0px 20px 40px 20px;
}
.detail-view-field label {
display: inline-block;
font-weight: bold;
width: 200px;
margin: 10px;
height: 30px;
line-height: 30px;
}
.detail-view-field span {
display: inline-block;
width: 340px;
margin: 10px;
border-bottom: 1px solid #ccc;
height: 30px;
line-height: 30px;
}
pre {
word-wrap: break-word;
white-space: pre-wrap;
}
</style>
<div class="tab">
<button onclick="window.location='{% url 'Settings:_ports' %}';"
style="vertical-align: middle; padding: auto; margin: 0px">
<svg xmlns="http://www.w3.org/2000/svg" height="25px" viewBox="0 -960 960 960" width="25px"
style="vertical-align: middle; margin: 0px; padding: 0px border: none; " fill="#6a6e73">
<path d="m313-480 155 156q11 11 11.5 27.5T468-268q-11 11-28 11t-28-11L228-452q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l184-184q11-11 27.5-11.5T468-692q11 11 11 28t-11 28L313-480Zm264 0 155 156q11 11 11.5 27.5T732-268q-11 11-28 11t-28-11L492-452q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l184-184q11-11 27.5-11.5T732-692q11 11 11 28t-11 28L577-480Z" />
</svg>Back to Ports</button>
<button id="defaultOpen" class="tablinks" onclick="openCity(event, 'Details')">Details</button>
<button class="tablinks" onclick="openCity(event, 'Services')">Services</button>
{% if perms.assistance.change_service %}
<button class="tablinks" onclick="openCity(event, 'Notes')">Notes</button>
{% endif %}
</div>
<form method="post">
<div id="Details" class="tabcontent">
<h3>Details</h3>
<div id="services" class="content-tab">
{% include 'content/section.html.j2' with tab=form.tabs.services %}
<table>
<tr>
<th>Name</th>
<th>Organization</th>
</tr>
{% for service in services %}
<tr>
<td><a href="{% url 'ITIM:_service_view' service.pk %}">{{ service.name }}</a></td>
<td>{{ service.organization }}</td>
</tr>
{% endfor%}
</table>
</div>
{% csrf_token %}
<div style="align-items:flex-start; align-content: center; display: flexbox; width: 100%">
<div id="notes" class="content-tab">
<div style="display: inline; width: 40%; margin: 30px;">
{% include 'content/section.html.j2' with tab=form.tabs.notes %}
<div class="detail-view-field">
<label>{{ form.number.label }}</label>
<span>{{ form.number.value }}</span>
</div>
<div class="detail-view-field">
<label>{{ form.description.label }}</label>
<span>
{% if form.description.value %}
{{ form.description.value }}
{% else %}
&nbsp;
{% endif %}
</span>
</div>
<div class="detail-view-field">
<label>{{ form.protocol.label }}</label>
<span>{{ form.protocol.value }}</span>
</div>
<div class="detail-view-field">
<label>{{ form.organization.label }}</label>
<span>{{ item.organization }}</span>
</div>
<div class="detail-view-field">
<label>Created</label>
<span>{{ item.created }}</span>
</div>
<div class="detail-view-field">
<label>Modified</label>
<span>{{ item.modified }}</span>
</div>
</div>
<div style="display: inline; width: 40%; margin: 30px; text-align: left;">
<div>
<label
style="font-weight: bold; width: 100%; border-bottom: 1px solid #ccc; display: block; text-align: inherit;">{{ form.model_notes.label }}</label>
<div style="display: inline-block; text-align: left;">
{% if form.model_notes.value %}
{{ form.model_notes.value | markdown | safe }}
{% else %}
&nbsp;
{% endif %}
</div>
</div>
</div>
</div>
<input type="button" value="Edit" onclick="window.location='{% url 'Settings:_port_change' item.pk %}';">
<br>
<script>
document.getElementById("defaultOpen").click();
</script>
{{ notes_form }}
<input type="submit" name="{{notes_form.prefix}}" value="Submit" />
<div class="comments">
{% if notes %}
{% for note in notes%}
{% include 'note.html.j2' %}
{% endfor %}
{% endif %}
</div>
</div>
<div id="Services" class="tabcontent">
<h3>
Services
</h3>
<table>
<tr>
<th>Name</th>
<th>Organization</th>
</tr>
{% for service in services %}
<tr>
<td><a href="{% url 'ITIM:_service_view' service.pk %}">{{ service.name }}</a></td>
<td>{{ service.organization }}</td>
</tr>
{% endfor%}
</table>
</div>
</div>
{% if perms.assistance.change_knowledgebase %}
<div id="Notes" class="tabcontent">
<h3>
Notes
</h3>
{{ notes_form }}
<input type="submit" name="{{ notes_form.prefix }}" value="Submit" />
<div class="comments">
{% if notes %}
{% for note in notes %}
{% include 'note.html.j2' %}
{% endfor %}
{% endif %}
</div>
</div>
{% endif %}
</form>
{% endblock %}
{% endblock %}

View File

@ -2,7 +2,7 @@
{% block content %}
<input type="button" value="New Service" onclick="window.location='{% url 'ITIM:_service_add' %}';">
<input type="button" value="New Article" onclick="window.location='{% url 'ITIM:_service_add' %}';">
<table class="data">
<tr>
<th>Title</th>

View File

@ -1,42 +0,0 @@
import pytest
import unittest
from django.test import TestCase
from access.models import Organization
from app.tests.abstract.models import TenancyModel
from itim.models.clusters import Cluster
@pytest.mark.django_db
class ClusterModel(
TestCase,
TenancyModel
):
model = Cluster
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an organization for user and item
2. Create an item
"""
self.organization = Organization.objects.create(name='test_org')
self.item = self.model.objects.create(
organization = self.organization,
name = 'one',
)
self.second_item = self.model.objects.create(
organization = self.organization,
name = 'one_two',
)

View File

@ -1,78 +0,0 @@
import pytest
import unittest
import requests
from django.test import TestCase, Client
from access.models import Organization
from core.models.history import History
from core.tests.abstract.history_entry import HistoryEntry
from core.tests.abstract.history_entry_parent_model import HistoryEntryParentItem
from itim.models.clusters import Cluster
class ClusterHistory(TestCase, HistoryEntry, HistoryEntryParentItem):
model = Cluster
@classmethod
def setUpTestData(self):
""" Setup Test """
organization = Organization.objects.create(name='test_org')
self.organization = organization
self.item_parent = self.model.objects.create(
name = 'test_item_parent_' + self.model._meta.model_name,
organization = self.organization
)
self.item_create = self.model.objects.create(
name = 'test_item_' + self.model._meta.model_name,
organization = self.organization,
)
self.history_create = History.objects.get(
action = History.Actions.ADD[0],
item_pk = self.item_create.pk,
item_class = self.model._meta.model_name,
)
self.item_change = self.item_create
self.item_change.name = 'test_item_' + self.model._meta.model_name + '_changed'
self.item_change.save()
self.field_after_expected_value = '{"name": "' + self.item_change.name + '"}'
self.history_change = History.objects.get(
action = History.Actions.UPDATE[0],
item_pk = self.item_change.pk,
item_class = self.model._meta.model_name,
)
self.item_delete = self.model.objects.create(
name = 'test_item_delete_' + self.model._meta.model_name,
organization = self.organization,
)
self.deleted_pk = self.item_delete.pk
self.item_delete.delete()
self.history_delete = History.objects.filter(
item_pk = self.deleted_pk,
item_class = self.model._meta.model_name,
)
self.history_delete_children = History.objects.filter(
item_parent_pk = self.deleted_pk,
item_parent_class = self.item_parent._meta.model_name,
)

View File

@ -1,95 +0,0 @@
# from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, User
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import reverse
from django.test import TestCase, Client
import pytest
import unittest
import requests
from access.models import Organization, Team, TeamUsers, Permission
from itim.models.clusters import Cluster
from core.tests.abstract.history_permissions import HistoryPermissions
class ClusterHistoryPermissions(TestCase, HistoryPermissions):
item_model = Cluster
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an organization for user and item
2. create an organization that is different to item
3. Create a device
4. Add history device history entry as item
5. create a user
6. create user in different organization (with the required permission)
"""
organization = Organization.objects.create(name='test_org')
self.organization = organization
different_organization = Organization.objects.create(name='test_different_organization')
self.item = self.item_model.objects.create(
organization=organization,
name = 'deviceone'
)
self.history = self.model.objects.get(
item_pk = self.item.id,
item_class = self.item._meta.model_name,
action = self.model.Actions.ADD,
)
view_permissions = Permission.objects.get(
codename = 'view_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
view_team = Team.objects.create(
team_name = 'view_team',
organization = organization,
)
view_team.permissions.set([view_permissions])
self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password")
self.view_user = User.objects.create_user(username="test_user_view", password="password")
teamuser = TeamUsers.objects.create(
team = view_team,
user = self.view_user
)
self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password")
different_organization_team = Team.objects.create(
team_name = 'different_organization_team',
organization = different_organization,
)
different_organization_team.permissions.set([
view_permissions,
])
TeamUsers.objects.create(
team = different_organization_team,
user = self.different_organization_user
)

View File

@ -1,189 +0,0 @@
# from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, User
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import reverse
from django.test import TestCase, Client
import pytest
import unittest
import requests
from access.models import Organization, Team, TeamUsers, Permission
from app.tests.abstract.model_permissions import ModelPermissions
from itim.models.clusters import Cluster
class ClusterPermissions(TestCase, ModelPermissions):
model = Cluster
app_namespace = 'ITIM'
url_name_view = '_cluster_view'
url_name_add = '_cluster_add'
url_name_change = '_cluster_change'
url_name_delete = '_cluster_delete'
url_delete_response = reverse('ITIM:Clusters')
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an organization for user and item
. create an organization that is different to item
2. Create a device
3. create teams with each permission: view, add, change, delete
4. create a user per team
"""
organization = Organization.objects.create(name='test_org')
self.organization = organization
different_organization = Organization.objects.create(name='test_different_organization')
self.item = self.model.objects.create(
organization=organization,
name = 'deviceone'
)
self.url_view_kwargs = {'pk': self.item.id}
# self.url_add_kwargs = {'pk': self.item.id}
self.add_data = {'device': 'device', 'organization': self.organization.id}
self.url_change_kwargs = {'pk': self.item.id}
self.change_data = {'device': 'device', 'organization': self.organization.id}
self.url_delete_kwargs = {'pk': self.item.id}
self.delete_data = {'device': 'device', 'organization': self.organization.id}
view_permissions = Permission.objects.get(
codename = 'view_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
view_team = Team.objects.create(
team_name = 'view_team',
organization = organization,
)
view_team.permissions.set([view_permissions])
add_permissions = Permission.objects.get(
codename = 'add_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
add_team = Team.objects.create(
team_name = 'add_team',
organization = organization,
)
add_team.permissions.set([add_permissions])
change_permissions = Permission.objects.get(
codename = 'change_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
change_team = Team.objects.create(
team_name = 'change_team',
organization = organization,
)
change_team.permissions.set([change_permissions])
delete_permissions = Permission.objects.get(
codename = 'delete_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
delete_team = Team.objects.create(
team_name = 'delete_team',
organization = organization,
)
delete_team.permissions.set([delete_permissions])
self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password")
self.view_user = User.objects.create_user(username="test_user_view", password="password")
teamuser = TeamUsers.objects.create(
team = view_team,
user = self.view_user
)
self.add_user = User.objects.create_user(username="test_user_add", password="password")
teamuser = TeamUsers.objects.create(
team = add_team,
user = self.add_user
)
self.change_user = User.objects.create_user(username="test_user_change", password="password")
teamuser = TeamUsers.objects.create(
team = change_team,
user = self.change_user
)
self.delete_user = User.objects.create_user(username="test_user_delete", password="password")
teamuser = TeamUsers.objects.create(
team = delete_team,
user = self.delete_user
)
self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password")
different_organization_team = Team.objects.create(
team_name = 'different_organization_team',
organization = different_organization,
)
different_organization_team.permissions.set([
view_permissions,
add_permissions,
change_permissions,
delete_permissions,
])
TeamUsers.objects.create(
team = different_organization_team,
user = self.different_organization_user
)

View File

@ -1,29 +0,0 @@
import pytest
import unittest
import requests
from django.test import TestCase
from app.tests.abstract.models import PrimaryModel
class ClusterViews(
TestCase,
PrimaryModel
):
add_module = 'itim.views.clusters'
add_view = 'Add'
change_module = add_module
change_view = 'Change'
delete_module = add_module
delete_view = 'Delete'
display_module = add_module
display_view = 'View'
index_module = add_module
index_view = 'Index'

View File

@ -1,42 +0,0 @@
import pytest
import unittest
from django.test import TestCase
from access.models import Organization
from app.tests.abstract.models import TenancyModel
from itim.models.clusters import ClusterType
@pytest.mark.django_db
class ClusterTypeModel(
TestCase,
TenancyModel
):
model = ClusterType
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an organization for user and item
2. Create an item
"""
self.organization = Organization.objects.create(name='test_org')
self.item = self.model.objects.create(
organization = self.organization,
name = 'one',
)
self.second_item = self.model.objects.create(
organization = self.organization,
name = 'one_two',
)

View File

@ -1,78 +0,0 @@
import pytest
import unittest
import requests
from django.test import TestCase, Client
from access.models import Organization
from core.models.history import History
from core.tests.abstract.history_entry import HistoryEntry
from core.tests.abstract.history_entry_parent_model import HistoryEntryParentItem
from itim.models.clusters import ClusterType
class ClusterTypeHistory(TestCase, HistoryEntry, HistoryEntryParentItem):
model = ClusterType
@classmethod
def setUpTestData(self):
""" Setup Test """
organization = Organization.objects.create(name='test_org')
self.organization = organization
self.item_parent = self.model.objects.create(
name = 'test_item_parent_' + self.model._meta.model_name,
organization = self.organization
)
self.item_create = self.model.objects.create(
name = 'test_item_' + self.model._meta.model_name,
organization = self.organization,
)
self.history_create = History.objects.get(
action = History.Actions.ADD[0],
item_pk = self.item_create.pk,
item_class = self.model._meta.model_name,
)
self.item_change = self.item_create
self.item_change.name = 'test_item_' + self.model._meta.model_name + '_changed'
self.item_change.save()
self.field_after_expected_value = '{"name": "' + self.item_change.name + '"}'
self.history_change = History.objects.get(
action = History.Actions.UPDATE[0],
item_pk = self.item_change.pk,
item_class = self.model._meta.model_name,
)
self.item_delete = self.model.objects.create(
name = 'test_item_delete_' + self.model._meta.model_name,
organization = self.organization,
)
self.deleted_pk = self.item_delete.pk
self.item_delete.delete()
self.history_delete = History.objects.filter(
item_pk = self.deleted_pk,
item_class = self.model._meta.model_name,
)
self.history_delete_children = History.objects.filter(
item_parent_pk = self.deleted_pk,
item_parent_class = self.item_parent._meta.model_name,
)

View File

@ -1,95 +0,0 @@
# from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, User
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import reverse
from django.test import TestCase, Client
import pytest
import unittest
import requests
from access.models import Organization, Team, TeamUsers, Permission
from itim.models.clusters import ClusterType
from core.tests.abstract.history_permissions import HistoryPermissions
class ClusterTypeHistoryPermissions(TestCase, HistoryPermissions):
item_model = ClusterType
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an organization for user and item
2. create an organization that is different to item
3. Create a device
4. Add history device history entry as item
5. create a user
6. create user in different organization (with the required permission)
"""
organization = Organization.objects.create(name='test_org')
self.organization = organization
different_organization = Organization.objects.create(name='test_different_organization')
self.item = self.item_model.objects.create(
organization=organization,
name = 'deviceone'
)
self.history = self.model.objects.get(
item_pk = self.item.id,
item_class = self.item._meta.model_name,
action = self.model.Actions.ADD,
)
view_permissions = Permission.objects.get(
codename = 'view_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
view_team = Team.objects.create(
team_name = 'view_team',
organization = organization,
)
view_team.permissions.set([view_permissions])
self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password")
self.view_user = User.objects.create_user(username="test_user_view", password="password")
teamuser = TeamUsers.objects.create(
team = view_team,
user = self.view_user
)
self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password")
different_organization_team = Team.objects.create(
team_name = 'different_organization_team',
organization = different_organization,
)
different_organization_team.permissions.set([
view_permissions,
])
TeamUsers.objects.create(
team = different_organization_team,
user = self.different_organization_user
)

View File

@ -1,189 +0,0 @@
# from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, User
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import reverse
from django.test import TestCase, Client
import pytest
import unittest
import requests
from access.models import Organization, Team, TeamUsers, Permission
from app.tests.abstract.model_permissions import ModelPermissions
from itim.models.clusters import ClusterType
class ClusterTypePermissions(TestCase, ModelPermissions):
model = ClusterType
app_namespace = 'Settings'
url_name_view = '_cluster_type_view'
url_name_add = '_cluster_type_add'
url_name_change = '_cluster_type_change'
url_name_delete = '_cluster_type_delete'
url_delete_response = reverse('Settings:_cluster_types')
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an organization for user and item
. create an organization that is different to item
2. Create a device
3. create teams with each permission: view, add, change, delete
4. create a user per team
"""
organization = Organization.objects.create(name='test_org')
self.organization = organization
different_organization = Organization.objects.create(name='test_different_organization')
self.item = self.model.objects.create(
organization=organization,
name = 'deviceone'
)
self.url_view_kwargs = {'pk': self.item.id}
# self.url_add_kwargs = {'pk': self.item.id}
self.add_data = {'device': 'device', 'organization': self.organization.id}
self.url_change_kwargs = {'pk': self.item.id}
self.change_data = {'device': 'device', 'organization': self.organization.id}
self.url_delete_kwargs = {'pk': self.item.id}
self.delete_data = {'device': 'device', 'organization': self.organization.id}
view_permissions = Permission.objects.get(
codename = 'view_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
view_team = Team.objects.create(
team_name = 'view_team',
organization = organization,
)
view_team.permissions.set([view_permissions])
add_permissions = Permission.objects.get(
codename = 'add_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
add_team = Team.objects.create(
team_name = 'add_team',
organization = organization,
)
add_team.permissions.set([add_permissions])
change_permissions = Permission.objects.get(
codename = 'change_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
change_team = Team.objects.create(
team_name = 'change_team',
organization = organization,
)
change_team.permissions.set([change_permissions])
delete_permissions = Permission.objects.get(
codename = 'delete_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
delete_team = Team.objects.create(
team_name = 'delete_team',
organization = organization,
)
delete_team.permissions.set([delete_permissions])
self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password")
self.view_user = User.objects.create_user(username="test_user_view", password="password")
teamuser = TeamUsers.objects.create(
team = view_team,
user = self.view_user
)
self.add_user = User.objects.create_user(username="test_user_add", password="password")
teamuser = TeamUsers.objects.create(
team = add_team,
user = self.add_user
)
self.change_user = User.objects.create_user(username="test_user_change", password="password")
teamuser = TeamUsers.objects.create(
team = change_team,
user = self.change_user
)
self.delete_user = User.objects.create_user(username="test_user_delete", password="password")
teamuser = TeamUsers.objects.create(
team = delete_team,
user = self.delete_user
)
self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password")
different_organization_team = Team.objects.create(
team_name = 'different_organization_team',
organization = different_organization,
)
different_organization_team.permissions.set([
view_permissions,
add_permissions,
change_permissions,
delete_permissions,
])
TeamUsers.objects.create(
team = different_organization_team,
user = self.different_organization_user
)

View File

@ -1,29 +0,0 @@
import pytest
import unittest
import requests
from django.test import TestCase
from app.tests.abstract.models import PrimaryModel
class ClusterTypeViews(
TestCase,
PrimaryModel
):
add_module = 'itim.views.cluster_types'
add_view = 'Add'
change_module = add_module
change_view = 'Change'
delete_module = add_module
delete_view = 'Delete'
display_module = add_module
display_view = 'View'
index_module = add_module
index_view = 'Index'

View File

@ -1,7 +1,7 @@
from django.urls import path
from itim.views import clusters, services
from itim.views import services
app_name = "ITIM"
urlpatterns = [
@ -12,10 +12,4 @@ urlpatterns = [
path("service/<int:pk>/delete", services.Delete.as_view(), name="_service_delete"),
path("service/<int:pk>", services.View.as_view(), name="_service_view"),
path("clusters", clusters.Index.as_view(), name="Clusters"),
path("clusters/add", clusters.Add.as_view(), name="_cluster_add"),
path("clusters/<int:pk>/edit", clusters.Change.as_view(), name="_cluster_change"),
path("clusters/<int:pk>/delete", clusters.Delete.as_view(), name="_cluster_delete"),
path("clusters/<int:pk>", clusters.View.as_view(), name="_cluster_view"),
]

View File

@ -1,185 +0,0 @@
from django.contrib.auth import decorators as auth_decorator
from django.urls import reverse
from django.utils.decorators import method_decorator
from core.forms.comment import AddNoteForm
from core.models.notes import Notes
from core.views.common import AddView, ChangeView, DeleteView, IndexView
from itim.forms.cluster_type import ClusterTypeForm, DetailForm
from itim.models.clusters import ClusterType
from settings.models.user_settings import UserSettings
class Add(AddView):
form_class = ClusterTypeForm
model = ClusterType
permission_required = [
'itim.add_clustertype',
]
def get_initial(self):
initial: dict = {
'organization': UserSettings.objects.get(user = self.request.user).default_organization
}
if 'pk' in self.kwargs:
if self.kwargs['pk']:
initial.update({'parent': self.kwargs['pk']})
self.model.parent.field.hidden = True
return initial
def get_success_url(self, **kwargs):
return reverse('Settings:_cluster_type_types')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = 'New Cluster Type'
return context
class Change(ChangeView):
form_class = ClusterTypeForm
model = ClusterType
permission_required = [
'itim.change_clustertype',
]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = str(self.object)
return context
def get_success_url(self, **kwargs):
return reverse('Settings:_cluster_type_view', args=(self.kwargs['pk'],))
class Delete(DeleteView):
model = ClusterType
permission_required = [
'itim.delete_clustertype',
]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = 'Delete ' + str(self.object)
return context
def get_success_url(self, **kwargs):
return reverse('Settings:_cluster_types')
class Index(IndexView):
context_object_name = "items"
model = ClusterType
paginate_by = 10
permission_required = [
'itim.view_clustertype'
]
template_name = 'itim/cluster_type_index.html.j2'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['model_docs_path'] = self.model._meta.app_label + '/' + self.model._meta.model_name
context['content_title'] = 'Cluster Types'
return context
class View(ChangeView):
context_object_name = "item"
form_class = DetailForm
model = ClusterType
permission_required = [
'itim.view_clustertype',
]
template_name = 'itim/cluster_type.html.j2'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['notes_form'] = AddNoteForm(prefix='note')
context['notes'] = Notes.objects.filter(service=self.kwargs['pk'])
context['model_pk'] = self.kwargs['pk']
context['model_name'] = self.model._meta.model_name
context['model_delete_url'] = reverse('Settings:_cluster_type_delete', args=(self.kwargs['pk'],))
context['content_title'] = self.object.name
return context
def post(self, request, *args, **kwargs):
item = ClusterType.objects.get(pk=self.kwargs['pk'])
notes = AddNoteForm(request.POST, prefix='note')
if notes.is_bound and notes.is_valid() and notes.instance.note != '':
notes.instance.service = item
notes.instance.organization = item.organization
notes.save()
def get_success_url(self, **kwargs):
return reverse('Settings:_cluster_type_view', args=(self.kwargs['pk'],))

View File

@ -1,190 +0,0 @@
from django.contrib.auth import decorators as auth_decorator
from django.urls import reverse
from django.utils.decorators import method_decorator
from core.forms.comment import AddNoteForm
from core.models.notes import Notes
from core.views.common import AddView, ChangeView, DeleteView, IndexView
from itim.forms.clusters import ClusterForm, DetailForm
from itim.models.clusters import Cluster
from itim.models.services import Service
from settings.models.user_settings import UserSettings
class Add(AddView):
form_class = ClusterForm
model = Cluster
permission_required = [
'itim.add_cluster',
]
def get_initial(self):
initial: dict = {
'organization': UserSettings.objects.get(user = self.request.user).default_organization
}
if 'pk' in self.kwargs:
if self.kwargs['pk']:
initial.update({'parent': self.kwargs['pk']})
self.model.parent.field.hidden = True
return initial
def get_success_url(self, **kwargs):
return reverse('ITIM:Clusters')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = 'New Cluster'
return context
class Change(ChangeView):
form_class = ClusterForm
model = Cluster
permission_required = [
'itim.change_cluster',
]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = str(self.object)
return context
def get_success_url(self, **kwargs):
return reverse('ITIM:_cluster_view', args=(self.kwargs['pk'],))
class Delete(DeleteView):
model = Cluster
permission_required = [
'itim.delete_cluster',
]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = 'Delete ' + str(self.object)
return context
def get_success_url(self, **kwargs):
return reverse('ITIM:Clusters')
class Index(IndexView):
context_object_name = "items"
model = Cluster
paginate_by = 10
permission_required = [
'itim.view_cluster'
]
template_name = 'itim/cluster_index.html.j2'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['model_docs_path'] = self.model._meta.app_label + '/' + self.model._meta.model_name
context['content_title'] = 'Clusters'
return context
class View(ChangeView):
context_object_name = "cluster"
form_class = DetailForm
model = Cluster
permission_required = [
'itim.view_cluster',
]
template_name = 'itim/cluster.html.j2'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['notes_form'] = AddNoteForm(prefix='note')
context['notes'] = Notes.objects.filter(service=self.kwargs['pk'])
context['model_pk'] = self.kwargs['pk']
context['model_name'] = self.model._meta.model_name
context['model_delete_url'] = reverse('ITIM:_cluster_delete', args=(self.kwargs['pk'],))
context['services'] = Service.objects.filter(
cluster = self.kwargs['pk']
)
context['content_title'] = self.object.name
return context
def post(self, request, *args, **kwargs):
item = Cluster.objects.get(pk=self.kwargs['pk'])
notes = AddNoteForm(request.POST, prefix='note')
if notes.is_bound and notes.is_valid() and notes.instance.note != '':
notes.instance.service = item
notes.instance.organization = item.organization
notes.save()
def get_success_url(self, **kwargs):
return reverse('ITIM:_cluster_view', args=(self.kwargs['pk'],))

View File

@ -6,7 +6,7 @@ from core.forms.comment import AddNoteForm
from core.models.notes import Notes
from core.views.common import AddView, ChangeView, DeleteView, IndexView
from itim.forms.ports import DetailForm, PortForm
from itim.forms.ports import PortForm
from itim.models.services import Port, Service
from settings.models.user_settings import UserSettings
@ -138,7 +138,7 @@ class View(ChangeView):
context_object_name = "item"
form_class = DetailForm
form_class = PortForm
model = Port

View File

@ -1,12 +1,10 @@
from django import forms
# from django.contrib.auth.models import User
from django.urls import reverse
from django.db.models import Q
from django.contrib.auth.models import User
from access.models import Organization, TeamUsers
from app import settings
from core.forms.common import CommonModelForm
from settings.models.external_link import ExternalLink
@ -21,64 +19,3 @@ class ExternalLinksForm(CommonModelForm):
fields = '__all__'
model = ExternalLink
class DetailForm(ExternalLinksForm):
tabs: dict = {
"details": {
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'organization',
'name',
'template',
'colour',
'cluster',
'devices'
'software',
'c_created',
'c_modified',
],
"right": [
'model_notes',
]
}
]
},
"notes": {
"name": "Notes",
"slug": "notes",
"sections": []
},
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['c_created'] = forms.DateTimeField(
label = 'Created',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.created,
)
self.fields['c_modified'] = forms.DateTimeField(
label = 'Modified',
input_formats=settings.DATETIME_FORMAT,
disabled = True,
initial = self.instance.modified,
)
self.tabs['details'].update({
"edit_url": reverse('Settings:_external_link_change', args=(self.instance.pk,))
})
self.url_index_view = reverse('Settings:External Links')

View File

@ -1,17 +0,0 @@
# Generated by Django 5.0.7 on 2024-08-17 08:05
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('settings', '0002_externallink'),
]
operations = [
migrations.AlterModelOptions(
name='externallink',
options={'verbose_name_plural': 'External Links'},
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 5.0.7 on 2024-08-18 05:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('settings', '0003_alter_externallink_options'),
]
operations = [
migrations.AddField(
model_name='externallink',
name='cluster',
field=models.BooleanField(default=False, help_text='Render link for clusters', verbose_name='Clusters'),
),
]

View File

@ -7,12 +7,6 @@ from access.models import TenancyObject
class ExternalLink(TenancyObject):
class Meta:
verbose_name_plural = 'External Links'
id = models.AutoField(
primary_key=True,
unique=True,
@ -47,13 +41,6 @@ class ExternalLink(TenancyObject):
verbose_name = 'Button Colour',
)
cluster = models.BooleanField(
default = False,
blank = False,
help_text = 'Render link for clusters',
verbose_name = 'Clusters',
)
devices = models.BooleanField(
default = False,
blank = False,

View File

@ -1,31 +1,194 @@
{% extends 'detail.html.j2' %}
{% extends 'base.html.j2' %}
{% load json %}
{% load markdown %}
{% block title %}{{ externallink.name }}{% endblock %}
{% block tabs %}
{% block content %}
<div id="details" class="content-tab">
<script>
{% include 'content/section.html.j2' with tab=form.tabs.details %}
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 'Settings:External Links' %}';"
style="vertical-align: middle; padding: auto; margin: 0px">
<svg xmlns="http://www.w3.org/2000/svg" height="25px" viewBox="0 -960 960 960" width="25px"
style="vertical-align: middle; margin: 0px; padding: 0px border: none; " fill="#6a6e73">
<path d="m313-480 155 156q11 11 11.5 27.5T468-268q-11 11-28 11t-28-11L228-452q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l184-184q11-11 27.5-11.5T468-692q11 11 11 28t-11 28L313-480Zm264 0 155 156q11 11 11.5 27.5T732-268q-11 11-28 11t-28-11L492-452q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l184-184q11-11 27.5-11.5T732-692q11 11 11 28t-11 28L577-480Z" />
</svg> Back to External Links</button>
<button id="defaultOpen" class="tablinks" onclick="openCity(event, 'Details')">Details</button>
<button id="NotesOpen" class="tablinks" onclick="openCity(event, 'Notes')">Notes</button>
</div>
<style>
.detail-view-field {
display:unset;
height: 30px;
line-height: 30px;
padding: 0px 20px 40px 20px;
}
.detail-view-field label {
display: inline-block;
font-weight: bold;
width: 200px;
margin: 10px;
/*padding: 10px;*/
height: 30px;
line-height: 30px;
}
.detail-view-field span {
display: inline-block;
width: 340px;
margin: 10px;
border-bottom: 1px solid #ccc;
height: 30px;
line-height: 30px;
}
</style>
<form action="" method="post">
{% csrf_token %}
<div id="notes" class="content-tab">
<div id="Details" class="tabcontent">
<h3>
Details
{% for external_link in external_links %}
<span style="font-weight: normal; float: right;">{% include 'icons/external_link.html.j2' with external_link=external_link %}</span>
{% endfor %}
</h3>
<div style="align-items:flex-start; align-content: center; display: flexbox; width: 100%">
{% include 'content/section.html.j2' with tab=form.tabs.notes %}
<div style="display: inline; width: 40%; margin: 30px;">
<div class="detail-view-field">
<label>{{ form.organization.label }}</label>
<span>{{ externallink.organization }}</span>
</div>
<div class="detail-view-field">
<label>{{ form.name.label }}</label>
<span>{{ form.name.value }}</span>
</div>
<div class="detail-view-field">
<label>{{ form.template.label }}</label>
<span>{{ externallink.template }}</span>
</div>
<div class="detail-view-field">
<label>{{ form.colour.label }}</label>
<span>
{% if form.colour.value %}
{{ form.colour.value }}
{% else %}
&nbsp;
{% endif %}
</span>
</div>
<div class="detail-view-field">
<label>{{ form.devices.label }}</label>
<span> {{ form.devices.value }}</span>
</div>
<div class="detail-view-field">
<label>{{ form.software.label }}</label>
<span>{{ externallink.software }}</span>
</div>
<div class="detail-view-field">
<label>Created</label>
<span>{{ externallink.created }}</span>
</div>
<div class="detail-view-field">
<label>Modified</label>
<span>{{ externallink.modified }}</span>
</div>
</div>
<div style="display: inline; width: 40%; margin: 30px; text-align: left;">
<div>
<label style="font-weight: bold; width: 100%; border-bottom: 1px solid #ccc; display: block; text-align: inherit;">{{ form.model_notes.label }}</label>
<div style="display: inline-block; text-align: left;">
{% if form.model_notes.value %}
{{ form.model_notes.value | markdown | safe }}
{% else %}
&nbsp;
{% endif %}
</div>
</div>
</div>
</div>
<input type="button" value="Edit" onclick="window.location='{% url 'Settings:_external_link_change' externallink.id %}';">
{% if not tab %}
<script>
// Get the element with id="defaultOpen" and click on it
document.getElementById("defaultOpen").click();
</script>
{% endif %}
</div>
<div id="Notes" class="tabcontent">
<h3>
Notes
</h3>
{{ notes_form }}
<input type="submit" name="{{notes_form.prefix}}" value="Submit" />
<div class="comments">
{% if notes %}
{% for note in notes%}
{% include 'note.html.j2' %}
{% endfor %}
{% endif %}
{% if notes %}
{% for note in notes%}
{% include 'note.html.j2' %}
{% endfor %}
{% endif %}
</div>
</div>
{% if tab == 'notes' %}
<script>
// Get the element with id="defaultOpen" and click on it
document.getElementById("NotesOpen").click();
</script>
{% endif %}
</div>
{% endblock %}
</form>
{% endblock %}

View File

@ -62,7 +62,6 @@ div#content article h3 {
<article style="">
<h3>ITIM</h3>
<ul>
<li><a href="{% url 'Settings:_cluster_types' %}">Cluster Types</a></li>
<li><a href="{% url 'Settings:_ports' %}">Service Ports</a></li>
</ul>
</article>

View File

@ -8,49 +8,49 @@ from settings.views import app_settings, home, device_models, device_types, exte
from itam.views import device_type, device_model, software_category
from itim.views import cluster_types, ports
from itim.views import ports
app_name = "Settings"
urlpatterns = [
path("", home.View.as_view(), name="Settings"),
path('application', app_settings.View.as_view(), name="_settings_application"),
path("cluster_types", cluster_types.Index.as_view(), name="_cluster_types"),
path("cluster_types/add", cluster_types.Add.as_view(), name="_cluster_type_add"),
path("cluster_types/<int:pk>/edit", cluster_types.Change.as_view(), name="_cluster_type_change"),
path("cluster_types/<int:pk>/delete", cluster_types.Delete.as_view(), name="_cluster_type_delete"),
path("cluster_types/<int:pk>", cluster_types.View.as_view(), name="_cluster_type_view"),
path("device_models", device_models.Index.as_view(), name="_device_models"),
path("device_model/<int:pk>", device_model.View.as_view(), name="_device_model_view"),
path("device_model/<int:pk>/edit", device_model.Change.as_view(), name="_device_model_change"),
path("device_model/add/", device_model.Add.as_view(), name="_device_model_add"),
path("device_model/<int:pk>/delete", device_model.Delete.as_view(), name="_device_model_delete"),
path("device_type/", device_types.Index.as_view(), name="_device_types"),
path("device_type/<int:pk>", device_type.View.as_view(), name="_device_type_view"),
path("device_type/add/", device_type.Add.as_view(), name="_device_type_add"),
path("device_type/<int:pk>/delete", device_type.Delete.as_view(), name="_device_type_delete"),
path("device_type/<int:pk>/edit", device_type.Change.as_view(), name="_device_type_change"),
path("external_links", external_link.Index.as_view(), name="External Links"),
path("external_links/add", external_link.Add.as_view(), name="_external_link_add"),
path("external_links/<int:pk>", external_link.View.as_view(), name="_external_link_view"),
path("external_links/<int:pk>/edit", external_link.Change.as_view(), name="_external_link_change"),
path("external_links/<int:pk>/delete", external_link.Delete.as_view(), name="_external_link_delete"),
path('application', app_settings.View.as_view(), name="_settings_application"),
path("task_results", celery_log.Index.as_view(), name="_task_results"),
path("task_result/<int:pk>", celery_log.View.as_view(), name="_task_result_view"),
path("device_models", device_models.Index.as_view(), name="_device_models"),
path("device_model/<int:pk>", device_model.View.as_view(), name="_device_model_view"),
path("device_model/add/", device_model.Add.as_view(), name="_device_model_add"),
path("device_model/<int:pk>/delete", device_model.Delete.as_view(), name="_device_model_delete"),
path("device_type/", device_types.Index.as_view(), name="_device_types"),
path("device_type/<int:pk>", device_type.View.as_view(), name="_device_type_view"),
path("device_type/add/", device_type.Add.as_view(), name="_device_type_add"),
path("device_type/<int:pk>/delete", device_type.Delete.as_view(), name="_device_type_delete"),
path("kb/category", knowledge_base_category.Index.as_view(), name="KB Categories"),
path("kb/category/add", knowledge_base_category.Add.as_view(), name="_knowledge_base_category_add"),
path("kb/category/<int:pk>/edit", knowledge_base_category.Change.as_view(), name="_knowledge_base_category_change"),
path("kb/category/<int:pk>/delete", knowledge_base_category.Delete.as_view(), name="_knowledge_base_category_delete"),
path("kb/category/<int:pk>", knowledge_base_category.View.as_view(), name="_knowledge_base_category_view"),
path("software_category", software_categories.Index.as_view(), name="_software_categories"),
path("software_category/<int:pk>", software_category.View.as_view(), name="_software_category_view"),
path("software_category/add/", software_category.Add.as_view(), name="_software_category_add"),
path("software_category/<int:pk>/delete", software_category.Delete.as_view(), name="_software_category_delete"),
path("manufacturers", manufacturer.Index.as_view(), name="_manufacturers"),
path("manufacturer/<int:pk>", manufacturer.View.as_view(), name="_manufacturer_view"),
path("manufacturer/add/", manufacturer.Add.as_view(), name="_manufacturer_add"),
path("manufacturer/<int:pk>/edit", manufacturer.Change.as_view(), name="_manufacturer_change"),
path("manufacturer/<int:pk>/delete", manufacturer.Delete.as_view(), name="_manufacturer_delete"),
path("ports", ports.Index.as_view(), name="_ports"),
@ -59,13 +59,4 @@ urlpatterns = [
path("port/<int:pk>/delete", ports.Delete.as_view(), name="_port_delete"),
path("port/<int:pk>", ports.View.as_view(), name="_port_view"),
path("software_category", software_categories.Index.as_view(), name="_software_categories"),
path("software_category/<int:pk>", software_category.View.as_view(), name="_software_category_view"),
path("software_category/add/", software_category.Add.as_view(), name="_software_category_add"),
path("software_category/<int:pk>/edit", software_category.Change.as_view(), name="_software_category_change"),
path("software_category/<int:pk>/delete", software_category.Delete.as_view(), name="_software_category_delete"),
path("task_results", celery_log.Index.as_view(), name="_task_results"),
path("task_result/<int:pk>", celery_log.View.as_view(), name="_task_result_view"),
]

View File

@ -9,7 +9,7 @@ from access.mixin import OrganizationPermission
from core.views.common import AddView, ChangeView, DeleteView, DisplayView, IndexView
from settings.forms.external_links import DetailForm, ExternalLinksForm
from settings.forms.external_links import ExternalLinksForm
from settings.models.external_link import ExternalLink
@ -44,7 +44,7 @@ class View(ChangeView):
context_object_name = "externallink"
form_class = DetailForm
form_class = ExternalLinksForm
model = ExternalLink

View File

@ -6,7 +6,7 @@ from django.views import generic
from access.mixin import OrganizationPermission
from core.forms.manufacturer import DetailForm, ManufacturerForm
from core.forms.manufacturer import ManufacturerForm
from core.models.manufacturer import Manufacturer
from core.views.common import AddView, ChangeView, DeleteView, IndexView
@ -37,7 +37,7 @@ class Index(IndexView):
class Change(ChangeView):
class View(ChangeView):
context_object_name = "manufacturer"
@ -46,46 +46,13 @@ class Change(ChangeView):
model = Manufacturer
permission_required = [
'core.view_manufacturer',
'core.change_manufacturer',
]
template_name = 'form.html.j2'
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"/settings/manufacturer/{self.kwargs['pk']}"
@method_decorator(auth_decorator.permission_required("core.change_manufacturer", raise_exception=True))
def post(self, request, *args, **kwargs):
return super().post(request, *args, **kwargs)
class View(ChangeView):
context_object_name = "manufacturer"
form_class = DetailForm
model = Manufacturer
permission_required = [
'core.view_manufacturer',
]
template_name = 'core/manufacturer.html.j2'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
@ -103,6 +70,12 @@ class View(ChangeView):
return f"/settings/manufacturer/{self.kwargs['pk']}"
@method_decorator(auth_decorator.permission_required("core.change_manufacturer", raise_exception=True))
def post(self, request, *args, **kwargs):
return super().post(request, *args, **kwargs)
class Add(AddView):

View File

@ -1,6 +1,5 @@
{% load json %}
{% load markdown %}
{% load choice_ids %}
{% if field.widget_type == 'textarea' or field.label == 'Notes' %}
@ -30,7 +29,7 @@
{% if field.value %}
<div style="display:block; width: 95%; text-align: left;">{{ field.value | markdown | safe }}</div>
{{ field.value | markdown | safe }}
{% else %}
@ -54,41 +53,18 @@
<label>{{ field.label }}</label>
<span>
{% if field.field.choices %} {# Display the selected choice text value #}
{% for id, value in field.field.choices %}
{% if field.value %}
{% for field_value in field.value|choice_ids %}
{% if field.value == id %}
{% for id, value in field.field.choices %}
{{ value }}
{% if field_value == id %}
{% endif %}
{{ value }},
{% endif %}
{% endfor %}
{% endfor %}
{% else %}
&nbsp;
{% endif %}
{%endfor%}
{% else %}
{% if field.value is not None %}
{{ field.value }}
{% else %}
&nbsp;
{% endif %}
{{ field.value }}
{% endif %}
</span>
</div>

Some files were not shown because too many files have changed in this diff Show More