feat(settings): New model to allow adding templated links to devices and software
!43 #6
This commit is contained in:
@ -1,9 +1,12 @@
|
||||
from django.template import Template, Context
|
||||
from django.utils.html import escape
|
||||
from django.views import generic
|
||||
|
||||
from access.mixin import OrganizationPermission
|
||||
|
||||
from core.exceptions import MissingAttribute
|
||||
|
||||
from settings.models.external_link import ExternalLink
|
||||
from settings.models.user_settings import UserSettings
|
||||
|
||||
|
||||
@ -50,6 +53,61 @@ class ChangeView(View, generic.UpdateView):
|
||||
|
||||
template_name:str = 'form.html.j2'
|
||||
|
||||
# ToDo: on migrating all views to seperate display and change views, external_links will not be required in `ChangView`
|
||||
def get_context_data(self, **kwargs):
|
||||
""" Get template context
|
||||
|
||||
For items that have the ability to have external links, this function
|
||||
adds the external link details to the context.
|
||||
|
||||
!!! Danger "Requirement"
|
||||
This function may be overridden with the caveat that this function is still called.
|
||||
by the overriding function. i.e. `super().get_context_data(skwargs)`
|
||||
|
||||
!!! note
|
||||
The adding of `external_links` within this view is scheduled to be removed.
|
||||
|
||||
Returns:
|
||||
(dict): Context for the template to use inclusive of 'external_links'
|
||||
"""
|
||||
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
external_links_query = None
|
||||
|
||||
|
||||
if self.model._meta.model_name == 'device':
|
||||
|
||||
external_links_query = ExternalLink.objects.filter(devices=True)
|
||||
|
||||
elif self.model._meta.model_name == 'software':
|
||||
|
||||
external_links_query = ExternalLink.objects.filter(software=True)
|
||||
|
||||
|
||||
if external_links_query:
|
||||
|
||||
external_links: list = []
|
||||
|
||||
user_context = Context(context)
|
||||
|
||||
for external_link in external_links_query:
|
||||
|
||||
user_string = Template(external_link)
|
||||
external_link_context: dict = {
|
||||
'name': escape(external_link.name),
|
||||
'link': escape(user_string.render(user_context)),
|
||||
}
|
||||
|
||||
if external_link.colour:
|
||||
|
||||
external_link_context.update({'colour': external_link.colour })
|
||||
external_links += [ external_link_context ]
|
||||
|
||||
context['external_links'] = external_links
|
||||
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class DeleteView(OrganizationPermission, generic.DeleteView):
|
||||
@ -64,6 +122,60 @@ class DisplayView(OrganizationPermission, generic.DetailView):
|
||||
template_name:str = 'form.html.j2'
|
||||
|
||||
|
||||
# ToDo: on migrating all views to seperate display and change views, external_links will not be required in `ChangView`
|
||||
def get_context_data(self, **kwargs):
|
||||
""" Get template context
|
||||
|
||||
For items that have the ability to have external links, this function
|
||||
adds the external link details to the context.
|
||||
|
||||
!!! Danger "Requirement"
|
||||
This function may be overridden with the caveat that this function is still called.
|
||||
by the overriding function. i.e. `super().get_context_data(skwargs)`
|
||||
|
||||
Returns:
|
||||
(dict): Context for the template to use inclusive of 'external_links'
|
||||
"""
|
||||
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
external_links_query = None
|
||||
|
||||
|
||||
if self.model._meta.model_name == 'device':
|
||||
|
||||
external_links_query = ExternalLink.objects.filter(devices=True)
|
||||
|
||||
elif self.model._meta.model_name == 'software':
|
||||
|
||||
external_links_query = ExternalLink.objects.filter(software=True)
|
||||
|
||||
|
||||
if external_links_query:
|
||||
|
||||
external_links: list = []
|
||||
|
||||
user_context = Context(context)
|
||||
|
||||
for external_link in external_links_query:
|
||||
|
||||
user_string = Template(external_link)
|
||||
external_link_context: dict = {
|
||||
'name': escape(external_link.name),
|
||||
'link': escape(user_string.render(user_context)),
|
||||
}
|
||||
|
||||
if external_link.colour:
|
||||
|
||||
external_link_context.update({'colour': external_link.colour })
|
||||
external_links += [ external_link_context ]
|
||||
|
||||
context['external_links'] = external_links
|
||||
|
||||
|
||||
return context
|
||||
|
||||
|
||||
|
||||
class IndexView(View, generic.ListView):
|
||||
|
||||
|
@ -41,6 +41,8 @@ class View(OrganizationPermission, generic.View):
|
||||
|
||||
from config_management.models.groups import ConfigGroups
|
||||
|
||||
from settings.models.external_link import ExternalLink
|
||||
|
||||
if not hasattr(self, 'model'):
|
||||
|
||||
match self.kwargs['model_name']:
|
||||
@ -61,6 +63,10 @@ class View(OrganizationPermission, generic.View):
|
||||
|
||||
self.model = DeviceType
|
||||
|
||||
case 'externallink':
|
||||
|
||||
self.model = ExternalLink
|
||||
|
||||
case 'manufacturer':
|
||||
|
||||
self.model = Manufacturer
|
||||
|
@ -83,7 +83,9 @@
|
||||
<div id="Details" class="tabcontent">
|
||||
<h3>
|
||||
Details
|
||||
<span style="font-weight: normal; float: right;">{% include 'icons/issue_link.html.j2' with issue=6 %}</span>
|
||||
{% 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%">
|
||||
|
||||
|
@ -43,8 +43,12 @@
|
||||
|
||||
<form method="post">
|
||||
<div id="Details" class="tabcontent">
|
||||
<h3>Details</h3>
|
||||
|
||||
<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>
|
||||
|
@ -52,6 +52,7 @@ span.icon-text {
|
||||
padding-right: 10px;
|
||||
height: 30px;
|
||||
display: inline-block;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
span.icon-text a {
|
||||
@ -142,6 +143,16 @@ span.icon-issue {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
span.icon-external-link {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
width: 25px;
|
||||
}
|
||||
|
||||
/* .icon {
|
||||
display: block;
|
||||
content: none;
|
||||
|
21
app/settings/forms/external_links.py
Normal file
21
app/settings/forms/external_links.py
Normal file
@ -0,0 +1,21 @@
|
||||
from django import forms
|
||||
from django.db.models import Q
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from access.models import Organization, TeamUsers
|
||||
|
||||
from core.forms.common import CommonModelForm
|
||||
|
||||
from settings.models.external_link import ExternalLink
|
||||
|
||||
|
||||
class ExternalLinksForm(CommonModelForm):
|
||||
|
||||
prefix = 'external_links'
|
||||
|
||||
class Meta:
|
||||
|
||||
fields = '__all__'
|
||||
|
||||
model = ExternalLink
|
37
app/settings/migrations/0002_externallink.py
Normal file
37
app/settings/migrations/0002_externallink.py
Normal file
@ -0,0 +1,37 @@
|
||||
# Generated by Django 5.0.7 on 2024-07-17 05:02
|
||||
|
||||
import access.fields
|
||||
import access.models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('access', '0001_initial'),
|
||||
('settings', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ExternalLink',
|
||||
fields=[
|
||||
('is_global', models.BooleanField(default=False)),
|
||||
('model_notes', models.TextField(blank=True, default=None, null=True, verbose_name='Notes')),
|
||||
('id', models.AutoField(primary_key=True, serialize=False, unique=True)),
|
||||
('name', models.CharField(help_text='Name to display on link button', max_length=30, unique=True, verbose_name='Button Name')),
|
||||
('template', models.CharField(help_text='External Link template', max_length=180, verbose_name='Link Template')),
|
||||
('colour', models.CharField(blank=True, default=None, help_text='Colour to render the link button. Use HTML colour code', max_length=80, null=True, verbose_name='Button Colour')),
|
||||
('devices', models.BooleanField(default=False, help_text='Render link for devices', verbose_name='Devices')),
|
||||
('software', models.BooleanField(default=False, help_text='Render link for software', verbose_name='Software')),
|
||||
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False)),
|
||||
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False)),
|
||||
('organization', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists])),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
0
app/settings/models/__init__.py
Normal file
0
app/settings/models/__init__.py
Normal file
66
app/settings/models/external_link.py
Normal file
66
app/settings/models/external_link.py
Normal file
@ -0,0 +1,66 @@
|
||||
from django.template import Template
|
||||
|
||||
from access.fields import *
|
||||
from access.models import TenancyObject
|
||||
|
||||
|
||||
|
||||
class ExternalLink(TenancyObject):
|
||||
|
||||
id = models.AutoField(
|
||||
primary_key=True,
|
||||
unique=True,
|
||||
blank=False
|
||||
)
|
||||
|
||||
name = models.CharField(
|
||||
blank = False,
|
||||
max_length = 30,
|
||||
unique = True,
|
||||
help_text = 'Name to display on link button',
|
||||
verbose_name = 'Button Name',
|
||||
)
|
||||
|
||||
slug = None
|
||||
|
||||
template = models.CharField(
|
||||
blank = False,
|
||||
max_length = 180,
|
||||
unique = False,
|
||||
help_text = 'External Link template',
|
||||
verbose_name = 'Link Template',
|
||||
)
|
||||
|
||||
colour = models.CharField(
|
||||
blank = True,
|
||||
null = True,
|
||||
default = None,
|
||||
max_length = 80,
|
||||
unique = False,
|
||||
help_text = 'Colour to render the link button. Use HTML colour code',
|
||||
verbose_name = 'Button Colour',
|
||||
)
|
||||
|
||||
devices = models.BooleanField(
|
||||
default = False,
|
||||
blank = False,
|
||||
help_text = 'Render link for devices',
|
||||
verbose_name = 'Devices',
|
||||
)
|
||||
|
||||
software = models.BooleanField(
|
||||
default = False,
|
||||
blank = False,
|
||||
help_text = 'Render link for software',
|
||||
verbose_name = 'Software',
|
||||
)
|
||||
|
||||
created = AutoCreatedField()
|
||||
|
||||
modified = AutoLastModifiedField()
|
||||
|
||||
|
||||
def __str__(self):
|
||||
""" Return the Template to render """
|
||||
|
||||
return str(self.template)
|
18
app/settings/templates/icons/external_link.html.j2
Normal file
18
app/settings/templates/icons/external_link.html.j2
Normal file
@ -0,0 +1,18 @@
|
||||
<style>
|
||||
.inner-text {
|
||||
background-color: #fff;
|
||||
border-top-right-radius: 15px;
|
||||
border-bottom-right-radius: 15px;
|
||||
border-right: 15px;
|
||||
margin-right: -5px;
|
||||
padding: 1px 5px 1px 5px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<span class="icon-text external-link" style="background-color: {% if external_link.colour %}{{ external_link.colour }}{% else %}#177ee6{% endif %};">
|
||||
<span class="icon-external-link" style="margin-left: 5px;">
|
||||
{% include 'icons/link.svg' %}
|
||||
</span>
|
||||
<a class="inner-text" href="{{ external_link.link }}" target="_blank"> {{ external_link.name }}</a>
|
||||
</span>
|
1
app/settings/templates/icons/link.svg
Normal file
1
app/settings/templates/icons/link.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10.59,13.41C11,13.8 11,14.44 10.59,14.83C10.2,15.22 9.56,15.22 9.17,14.83C7.22,12.88 7.22,9.71 9.17,7.76V7.76L12.71,4.22C14.66,2.27 17.83,2.27 19.78,4.22C21.73,6.17 21.73,9.34 19.78,11.29L18.29,12.78C18.3,11.96 18.17,11.14 17.89,10.36L18.36,9.88C19.54,8.71 19.54,6.81 18.36,5.64C17.19,4.46 15.29,4.46 14.12,5.64L10.59,9.17C9.41,10.34 9.41,12.24 10.59,13.41M13.41,9.17C13.8,8.78 14.44,8.78 14.83,9.17C16.78,11.12 16.78,14.29 14.83,16.24V16.24L11.29,19.78C9.34,21.73 6.17,21.73 4.22,19.78C2.27,17.83 2.27,14.66 4.22,12.71L5.71,11.22C5.7,12.04 5.83,12.86 6.11,13.65L5.64,14.12C4.46,15.29 4.46,17.19 5.64,18.36C6.81,19.54 8.71,19.54 9.88,18.36L13.41,14.83C14.59,13.66 14.59,11.76 13.41,10.59C13,10.2 13,9.56 13.41,9.17Z" /></svg>
|
After Width: | Height: | Size: 795 B |
194
app/settings/templates/settings/external_link.html.j2
Normal file
194
app/settings/templates/settings/external_link.html.j2
Normal file
@ -0,0 +1,194 @@
|
||||
{% extends 'base.html.j2' %}
|
||||
|
||||
{% load markdown %}
|
||||
|
||||
{% block title %}{{ externallink.name }}{% endblock %}
|
||||
|
||||
{% 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 '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="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.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 %}
|
||||
|
||||
{% 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 %}
|
||||
|
||||
{% 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 %}
|
||||
</div>
|
||||
|
||||
{% if tab == 'notes' %}
|
||||
<script>
|
||||
// Get the element with id="defaultOpen" and click on it
|
||||
document.getElementById("NotesOpen").click();
|
||||
</script>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
{% endblock %}
|
42
app/settings/templates/settings/external_links.html.j2
Normal file
42
app/settings/templates/settings/external_links.html.j2
Normal file
@ -0,0 +1,42 @@
|
||||
{% extends 'base.html.j2' %}
|
||||
|
||||
|
||||
{% block content_header_icon %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<input type="button" value="New External Link" onclick="window.location='{% url 'Settings:_external_link_add' %}';">
|
||||
<table class="data">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Organization</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
{% for item in list %}
|
||||
<tr>
|
||||
<td><a href="{% url 'Settings:_external_link_view' pk=item.id %}">{{ item.name }}</a></td>
|
||||
<td>{% if item.is_global %}Global{% else %}{{ item.organization }}{% endif %}</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</table>
|
||||
|
||||
<div class="pagination">
|
||||
<span class="step-links">
|
||||
{% if page_obj.has_previous %}
|
||||
<a href="?page=1">« 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 »</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
{% endblock %}
|
@ -2,7 +2,7 @@ from django.urls import path
|
||||
|
||||
from core.views import celery_log
|
||||
|
||||
from .views import app_settings, home, device_models, device_types, manufacturer, software_categories
|
||||
from settings.views import app_settings, home, device_models, device_types, external_link, manufacturer, software_categories
|
||||
|
||||
from itam.views import device_type, device_model, software_category
|
||||
|
||||
@ -11,6 +11,13 @@ urlpatterns = [
|
||||
|
||||
path("", home.View.as_view(), name="Settings"),
|
||||
|
||||
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"),
|
||||
|
164
app/settings/views/external_link.py
Normal file
164
app/settings/views/external_link.py
Normal file
@ -0,0 +1,164 @@
|
||||
from django.contrib.auth import decorators as auth_decorator
|
||||
from django.db.models import Q
|
||||
from django.urls import reverse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import generic
|
||||
|
||||
|
||||
from access.mixin import OrganizationPermission
|
||||
|
||||
from core.views.common import AddView, ChangeView, DeleteView, DisplayView, IndexView
|
||||
|
||||
from settings.forms.external_links import ExternalLinksForm
|
||||
from settings.models.external_link import ExternalLink
|
||||
|
||||
|
||||
class Index(IndexView):
|
||||
|
||||
context_object_name = "list"
|
||||
|
||||
model = ExternalLink
|
||||
|
||||
paginate_by = 10
|
||||
|
||||
permission_required = [
|
||||
'settings.view_externallink'
|
||||
]
|
||||
|
||||
template_name = 'settings/external_links.html.j2'
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['model_docs_path'] = self.model._meta.app_label + '/external_links/'
|
||||
|
||||
context['content_title'] = 'External Links'
|
||||
|
||||
return context
|
||||
|
||||
|
||||
|
||||
|
||||
class View(ChangeView):
|
||||
|
||||
context_object_name = "externallink"
|
||||
|
||||
form_class = ExternalLinksForm
|
||||
|
||||
model = ExternalLink
|
||||
|
||||
permission_required = [
|
||||
'settings.view_externallink',
|
||||
]
|
||||
|
||||
template_name = 'settings/external_link.html.j2'
|
||||
|
||||
|
||||
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['model_delete_url'] = reverse('Settings:_external_link_delete', args=(self.kwargs['pk'],))
|
||||
|
||||
context['content_title'] = self.object.name
|
||||
|
||||
return context
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
|
||||
return reverse('Settings:_external_link_view', args={self.kwargs['pk']})
|
||||
|
||||
|
||||
@method_decorator(auth_decorator.permission_required("settings.change_externallink", raise_exception=True))
|
||||
def post(self, request, *args, **kwargs):
|
||||
|
||||
return super().post(request, *args, **kwargs)
|
||||
|
||||
|
||||
class Change(ChangeView):
|
||||
|
||||
context_object_name = "externallink"
|
||||
|
||||
form_class = ExternalLinksForm
|
||||
|
||||
model = ExternalLink
|
||||
|
||||
permission_required = [
|
||||
'settings.change_externallink',
|
||||
]
|
||||
|
||||
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:_external_link_view', args={self.kwargs['pk']})
|
||||
|
||||
|
||||
@method_decorator(auth_decorator.permission_required("settings.change_externallink", raise_exception=True))
|
||||
def post(self, request, *args, **kwargs):
|
||||
|
||||
return super().post(request, *args, **kwargs)
|
||||
|
||||
|
||||
|
||||
class Add(AddView):
|
||||
|
||||
|
||||
form_class = ExternalLinksForm
|
||||
|
||||
model = ExternalLink
|
||||
|
||||
permission_required = [
|
||||
'settings.add_externallink',
|
||||
]
|
||||
|
||||
template_name = 'form.html.j2'
|
||||
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
|
||||
return reverse(viewname = 'Settings:External Links')
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['content_title'] = 'Add External Link'
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class Delete(DeleteView):
|
||||
|
||||
model = ExternalLink
|
||||
|
||||
permission_required = [
|
||||
'settings.delete_externallink',
|
||||
]
|
||||
|
||||
template_name = 'form.html.j2'
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
|
||||
return reverse('Settings:External Links')
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['content_title'] = 'Delete ' + self.object.name
|
||||
|
||||
return context
|
||||
|
@ -0,0 +1,17 @@
|
||||
---
|
||||
title: External Links
|
||||
description: No Fuss Computings Centurion ERP External Links model documentation.
|
||||
date: 2024-07-15
|
||||
template: project.html
|
||||
about: https://gitlab.com/nofusscomputing/infrastructure/configuration-management/centurion_erp
|
||||
---
|
||||
|
||||
This model enables the end user to define external links to be rendered alongside other models display pages. The values are added to the page context in the [Change View](../common_views.md#display-view).
|
||||
|
||||
|
||||
## External Links
|
||||
|
||||
::: app.settings.models.external_link.ExternalLink
|
||||
options:
|
||||
inherited_members: true
|
||||
heading_level: 3
|
@ -34,6 +34,11 @@ All models must meet the following requirements:
|
||||
- No `queryset` is to return data that the user has not got access to. _see [queryset()](./api/models/tenancy_object.md#tenancy-object-manager)_
|
||||
|
||||
|
||||
## History
|
||||
|
||||
Currently the adding of history to a model is a manual process. edit the file located at `core.views.history` and within `View.get_object` add the model to the `switch` statement.
|
||||
|
||||
|
||||
## Tests
|
||||
|
||||
The following Unit test cases exists for models:
|
||||
|
18
docs/projects/centurion_erp/user/settings/external_links.md
Normal file
18
docs/projects/centurion_erp/user/settings/external_links.md
Normal file
@ -0,0 +1,18 @@
|
||||
---
|
||||
title: External Links
|
||||
description: External Links user documentation for Centurion ERP by No Fuss Computing
|
||||
date: 2024-07-17
|
||||
template: project.html
|
||||
about: https://gitlab.com/nofusscomputing/infrastructure/configuration-management/centurion_erp
|
||||
---
|
||||
|
||||
External Links allow an end user to specify by means of a jinja template a link that when displayed upon an items display page will add a button with a hyperlink to the url provided. External links can be assigned to: devices and software. This includes both at the same time.
|
||||
|
||||
|
||||
## Create a link
|
||||
|
||||
- Software context is under key `software`
|
||||
|
||||
- Device context is under key `device`
|
||||
|
||||
To add a templated link within the `Link Template` field enter your url, with the variable within jinja braces. for example to add a link that will expand with the devices id, specify `{{ device.id }}`. i.e. `https://domainname.tld/{{ device.id }}`. If the link is for software use key `software`. Available fields under context key all of those that are available at the time the page is rendered.
|
@ -82,6 +82,8 @@ nav:
|
||||
|
||||
- projects/centurion_erp/development/api/models/core_history_save.md
|
||||
|
||||
- projects/centurion_erp/development/api/models/external_links.md
|
||||
|
||||
- projects/centurion_erp/development/api/models/itam_device.md
|
||||
|
||||
- projects/centurion_erp/development/api/models/access_organization_permission_checking.md
|
||||
@ -194,6 +196,8 @@ nav:
|
||||
|
||||
- projects/centurion_erp/user/settings/app_settings.md
|
||||
|
||||
- projects/centurion_erp/user/settings/external_links.md
|
||||
|
||||
|
||||
- Operations:
|
||||
|
||||
|
Reference in New Issue
Block a user