1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

bootstrap 5 class updates

This commit is contained in:
checktheroads
2021-04-13 21:51:12 -07:00
parent 21d31b5747
commit d1d2ad6a5c
33 changed files with 611 additions and 530 deletions

View File

@ -33,7 +33,7 @@ IPADDRESS_LINK = """
{% if record.pk %}
<a href="{{ record.get_absolute_url }}">{{ record.address }}</a>
{% elif perms.ipam.add_ipaddress %}
<a href="{% url 'ipam:ipaddress_add' %}?address={{ record.1 }}{% if object.vrf %}&vrf={{ object.vrf.pk }}{% endif %}{% if object.tenant %}&tenant={{ object.tenant.pk }}{% endif %}" class="btn btn-xs btn-success">{% if record.0 <= 65536 %}{{ record.0 }}{% else %}Many{% endif %} IP{{ record.0|pluralize }} available</a>
<a href="{% url 'ipam:ipaddress_add' %}?address={{ record.1 }}{% if object.vrf %}&vrf={{ object.vrf.pk }}{% endif %}{% if object.tenant %}&tenant={{ object.tenant.pk }}{% endif %}" class="btn btn-sm btn-success">{% if record.0 <= 65536 %}{{ record.0 }}{% else %}Many{% endif %} IP{{ record.0|pluralize }} available</a>
{% else %}
{% if record.0 <= 65536 %}{{ record.0 }}{% else %}Many{% endif %} IP{{ record.0|pluralize }} available
{% endif %}
@ -65,7 +65,7 @@ VLAN_LINK = """
{% if record.pk %}
<a href="{{ record.get_absolute_url }}">{{ record.vid }}</a>
{% elif perms.ipam.add_vlan %}
<a href="{% url 'ipam:vlan_add' %}?vid={{ record.vid }}&group={{ vlan_group.pk }}{% if vlan_group.site %}&site={{ vlan_group.site.pk }}{% endif %}" class="btn btn-xs btn-success">{{ record.available }} VLAN{{ record.available|pluralize }} available</a>
<a href="{% url 'ipam:vlan_add' %}?vid={{ record.vid }}&group={{ vlan_group.pk }}{% if vlan_group.site %}&site={{ vlan_group.site.pk }}{% endif %}" class="btn btn-sm btn-success">{{ record.available }} VLAN{{ record.available|pluralize }} available</a>
{% else %}
{{ record.available }} VLAN{{ record.available|pluralize }} available
{% endif %}
@ -90,7 +90,7 @@ VLAN_ROLE_LINK = """
VLANGROUP_ADD_VLAN = """
{% with next_vid=record.get_next_available_vid %}
{% if next_vid and perms.ipam.add_vlan %}
<a href="{% url 'ipam:vlan_add' %}?group={{ record.pk }}&vid={{ next_vid }}" title="Add VLAN" class="btn btn-xs btn-success">
<a href="{% url 'ipam:vlan_add' %}?group={{ record.pk }}&vid={{ next_vid }}" title="Add VLAN" class="btn btn-sm btn-success">
<i class="mdi mdi-plus-thick" aria-hidden="true"></i>
</a>
{% endif %}

View File

@ -245,7 +245,8 @@ span.color-label {
}
textarea#id_local_context_data,
textarea.markdown {
textarea.markdown,
textarea#id_public_key {
font-family: $font-family-monospace;
}
@ -355,3 +356,7 @@ span.bi-plus:before {
table tbody tr.success {
background-color: rgba($success, 0.15);
}
table td,
table th {
font-size: $font-size-sm;
}

View File

@ -0,0 +1,8 @@
import Clipboard from 'clipboard';
import { getElements } from './util';
export function initClipboard() {
for (const element of getElements('a.copy-token', 'button.copy-secret')) {
new Clipboard(element);
}
}

View File

@ -26,6 +26,11 @@ type APIObjectBase = {
[k: string]: JSONAble;
};
type APIKeyPair = {
public_key: string;
private_key: string;
};
type APIReference = {
id: number;
name: string;

View File

@ -1,5 +1,4 @@
import 'babel-polyfill';
import '@popperjs/core';
import 'bootstrap';
import 'clipboard';
import './netbox';

View File

@ -1,11 +1,13 @@
import { Tooltip } from 'bootstrap';
import { Modal, Tooltip } from 'bootstrap';
import Masonry from 'masonry-layout';
import { initApiSelect, initStaticSelect, initColorSelect } from './select';
import { initDateSelector } from './dateSelector';
import { initMessageToasts } from './toast';
import { initSpeedSelector, initForms } from './forms';
import { initRackElevation } from './buttons';
import { initClipboard } from './clipboard';
import { initSearchBar } from './search';
// import { initGenerateKeyPair } from './secrets';
import { getElements } from './util';
const INITIALIZERS = [
@ -18,6 +20,8 @@ const INITIALIZERS = [
initSpeedSelector,
initColorSelect,
initRackElevation,
initClipboard,
// initGenerateKeyPair,
] as (() => void)[];
/**
@ -30,6 +34,10 @@ function initBootstrap(): void {
for (const tooltip of getElements('[data-bs-toggle="tooltip"]')) {
new Tooltip(tooltip, { container: 'body', boundary: 'window' });
}
for (const modal of getElements('[data-bs-toggle="modal"]')) {
// for (const modal of getElements('div.modal')) {
new Modal(modal);
}
initMessageToasts();
initForms();
}

View File

@ -0,0 +1,62 @@
import { apiGetBase, getElements, isApiError } from './util';
/**
*
* $('#generate_keypair').click(function() {
$('#new_keypair_modal').modal('show');
$.ajax({
url: netbox_api_path + 'secrets/generate-rsa-key-pair/',
type: 'GET',
dataType: 'json',
success: function (response, status) {
var public_key = response.public_key;
var private_key = response.private_key;
$('#new_pubkey').val(public_key);
$('#new_privkey').val(private_key);
},
error: function (xhr, ajaxOptions, thrownError) {
alert("There was an error generating a new key pair.");
}
});
});
*/
export function initGenerateKeyPair() {
const element = document.getElementById('new_keypair_modal') as HTMLDivElement;
const accept = document.getElementById('use_new_pubkey') as HTMLButtonElement;
const publicElem = element.querySelector<HTMLTextAreaElement>('textarea#new_pubkey');
const privateElem = element.querySelector<HTMLTextAreaElement>('textarea#new_privkey');
function handleOpen() {
for (const elem of [publicElem, privateElem]) {
if (elem !== null) {
elem.setAttribute('readonly', '');
}
}
apiGetBase<APIKeyPair>('/api/secrets/generate-rsa-key-pair').then(data => {
if (!isApiError(data)) {
const { private_key: priv, public_key: pub } = data;
if (publicElem !== null && privateElem !== null) {
publicElem.value = pub;
privateElem.value = priv;
}
}
});
}
function handleAccept() {
const publicKeyField = document.getElementById('id_public_key') as HTMLTextAreaElement;
if (publicElem !== null) {
publicKeyField.value = publicElem.value;
publicKeyField.innerText = publicElem.value;
}
}
element.addEventListener('shown.bs.modal', handleOpen);
accept.addEventListener('click', handleAccept);
}
export function initLockUnlock() {
for (const element of getElements<HTMLButtonElement>('button.unlock-secret')) {
function handleClick() {
const { secretId } = element.dataset;
}
}
}

View File

@ -32,6 +32,18 @@ export function getCsrfToken(): string {
return csrfToken;
}
export async function apiGetBase<T extends Record<string, unknown>>(
url: string,
): Promise<T | APIError> {
const token = getCsrfToken();
const res = await fetch(url, {
method: 'GET',
headers: { 'X-CSRFToken': token },
});
const json = (await res.json()) as T | APIError;
return json;
}
/**
* Fetch data from the NetBox API (authenticated).
* @param url API endpoint
@ -39,13 +51,7 @@ export function getCsrfToken(): string {
export async function getApiData<T extends APIObjectBase>(
url: string,
): Promise<APIAnswer<T> | APIError> {
const token = getCsrfToken();
const res = await fetch(url, {
method: 'GET',
headers: { 'X-CSRFToken': token },
});
const json = (await res.json()) as APIAnswer<T> | APIError;
return json;
return await apiGetBase<APIAnswer<T>>(url);
}
export function getElements<K extends keyof SVGElementTagNameMap>(

View File

@ -73,7 +73,7 @@ class SecretListView(generic.ObjectListView):
filterset = filters.SecretFilterSet
filterset_form = forms.SecretFilterForm
table = tables.SecretTable
action_buttons = ('import', 'export')
action_buttons = ('add', 'import', 'export')
class SecretView(generic.ObjectView):

View File

@ -215,16 +215,4 @@
</a>
</li>
{% endif %}
{% if perms.extras.view_objectchange %}
<li
role="presentation"
class="nav-item">
<a
href="{% url 'dcim:device_changelog' pk=object.pk %}"
class="nav-link{% if active_tab == 'changelog' %} active{% endif %}"
>
Change Log
</a>
</li>
{% endif %}
{% endblock %}

View File

@ -1,10 +1,10 @@
{% if perms.dcim.change_cable %}
{% if cable.status == 'connected' %}
<a href="#" class="btn btn-warning btn-xs cable-toggle connected" title="Mark planned" data="{{ cable.pk }}">
<a href="#" class="btn btn-warning btn-sm cable-toggle connected" title="Mark Planned" data="{{ cable.pk }}">
<i class="mdi mdi-lan-disconnect" aria-hidden="true"></i>
</a>
{% else %}
<a href="#" class="btn btn-success btn-xs cable-toggle" title="Mark installed" data="{{ cable.pk }}">
<a href="#" class="btn btn-success btn-sm cable-toggle" title="Mark Installed" data="{{ cable.pk }}">
<i class="mdi mdi-lan-connect" aria-hidden="true"></i>
</a>
{% endif %}

View File

@ -6,13 +6,13 @@
{% load plugins %}
{% block breadcrumbs %}
<li><a href="{% url 'dcim:powerfeed_list' %}">Power Feeds</a></li>
<li><a href="{{ object.power_panel.site.get_absolute_url }}">{{ object.power_panel.site }}</a></li>
<li><a href="{{ object.power_panel.get_absolute_url }}">{{ object.power_panel }}</a></li>
<li class="breadcrumb-item"><a href="{% url 'dcim:powerfeed_list' %}">Power Feeds</a></li>
<li class="breadcrumb-item"><a href="{{ object.power_panel.site.get_absolute_url }}">{{ object.power_panel.site }}</a></li>
<li class="breadcrumb-item"><a href="{{ object.power_panel.get_absolute_url }}">{{ object.power_panel }}</a></li>
{% if object.rack %}
<li><a href="{{ object.rack.get_absolute_url }}">{{ object.rack }}</a></li>
<li class="breadcrumb-item"><a href="{{ object.rack.get_absolute_url }}">{{ object.rack }}</a></li>
{% endif %}
<li>{{ object }}</li>
<li class="breadcrumb-item">{{ object }}</li>
{% endblock %}
{% block content %}
@ -25,13 +25,13 @@
<div class="card-body">
<table class="table table-hover attr-table">
<tr>
<td>Power Panel</td>
<th scope="row">Power Panel</th>
<td>
<a href="{{ object.power_panel.get_absolute_url }}">{{ object.power_panel }}</a>
</td>
</tr>
<tr>
<td>Rack</td>
<th scope="row">Rack</th>
<td>
{% if object.rack %}
<a href="{{ object.rack.get_absolute_url }}">{{ object.rack }}</a>
@ -41,19 +41,19 @@
</td>
</tr>
<tr>
<td>Type</td>
<th scope="row">Type</th>
<td>
<span class="badge bg-{{ object.get_type_class }}">{{ object.get_type_display }}</span>
</td>
</tr>
<tr>
<td>Status</td>
<th scope="row">Status</th>
<td>
<span class="badge bg-{{ object.get_status_class }}">{{ object.get_status_display }}</span>
</td>
</tr>
<tr>
<td>Connected Device</td>
<th scope="row">Connected Device</th>
<td>
{% if object.connected_endpoint %}
<a href="{{ object.connected_endpoint.device.get_absolute_url }}">{{ object.connected_endpoint.device }}</a> ({{ object.connected_endpoint }})
@ -63,7 +63,7 @@
</td>
</tr>
<tr>
<td>Utilization (Allocated)</td>
<th scope="row">Utilization (Allocated)</th>
{% with utilization=object.connected_endpoint.get_power_draw %}
{% if utilization %}
<td>
@ -87,23 +87,23 @@
<div class="card-body">
<table class="table table-hover attr-table">
<tr>
<td>Supply</td>
<th scope="row">Supply</th>
<td>{{ object.get_supply_display }}</td>
</tr>
<tr>
<td>Voltage</td>
<th scope="row">Voltage</th>
<td>{{ object.voltage }}V</td>
</tr>
<tr>
<td>Amperage</td>
<th scope="row">Amperage</th>
<td>{{ object.amperage }}A</td>
</tr>
<tr>
<td>Phase</td>
<th scope="row">Phase</th>
<td>{{ object.get_phase_display }}</td>
</tr>
<tr>
<td>Max Utilization</td>
<th scope="row">Max Utilization</th>
<td>{{ object.max_utilization }}%</td>
</tr>
</table>
@ -126,7 +126,7 @@
{% elif object.cable %}
<table class="table table-hover attr-table">
<tr>
<td>Cable</td>
<th scope="row">Cable</th>
<td>
<a href="{{ object.cable.get_absolute_url }}">{{ object.cable }}</a>
<a href="{% url 'dcim:powerfeed_trace' pk=object.pk %}" class="btn btn-primary btn-xs" title="Trace">
@ -136,27 +136,27 @@
</tr>
{% if object.connected_endpoint %}
<tr>
<td>Device</td>
<th scope="row">Device</th>
<td>
<a href="{{ object.connected_endpoint.device.get_absolute_url }}">{{ object.connected_endpoint.device }}</a>
</td>
</tr>
<tr>
<td>Name</td>
<th scope="row">Name</th>
<td>
<a href="{{ object.connected_endpoint.get_absolute_url }}">{{ object.connected_endpoint.name }}</a>
</td>
</tr>
<tr>
<td>Type</td>
<th scope="row">Type</th>
<td>{{ object.connected_endpoint.get_type_display|placeholder }}</td>
</tr>
<tr>
<td>Description</td>
<th scope="row">Description</th>
<td>{{ object.connected_endpoint.description|placeholder }}</td>
</tr>
<tr>
<td>Path Status</td>
<th scope="row">Path Status</th>
<td>
{% if object.path.is_active %}
<span class="badge bg-success">Reachable</span>
@ -169,15 +169,20 @@
</table>
{% else %}
<div class="text-muted">
{% if perms.dcim.add_cable %}
<a href="{% url 'dcim:powerfeed_connect' termination_a_id=object.pk termination_b_type='power-port' %}?return_url={{ object.get_absolute_url }}" class="btn btn-primary btn-sm pull-right">
<span class="mdi mdi-ethernet-cable" aria-hidden="true"></span> Connect
</a>
{% endif %}
Not connected
</div>
{% endif %}
</div>
{% if not object.mark_connected and not object.cable %}
<div class="card-footer">
{% if perms.dcim.add_cable %}
<a href="{% url 'dcim:powerfeed_connect' termination_a_id=object.pk termination_b_type='power-port' %}?return_url={{ object.get_absolute_url }}"
class="btn btn-primary btn-sm float-end">
<i class="mdi mdi-ethernet-cable" aria-hidden="true"></i> Connect
</a>
{% endif %}
</div>
{% endif %}
</div>
<div class="card">
<h5 class="card-header">

View File

@ -3,39 +3,41 @@
{% load plugins %}
{% block breadcrumbs %}
<li><a href="{% url 'dcim:powerpanel_list' %}">Power Panels</a></li>
<li><a href="{{ object.site.get_absolute_url }}">{{ object.site }}</a></li>
<li class="breadcrumb-item"><a href="{% url 'dcim:powerpanel_list' %}">Power Panels</a></li>
<li class="breadcrumb-item"><a href="{{ object.site.get_absolute_url }}">{{ object.site }}</a></li>
{% if object.location %}
<li><a href="{{ object.location.get_absolute_url }}">{{ object.location }}</a></li>
<li class="breadcrumb-item"><a href="{{ object.location.get_absolute_url }}">{{ object.location }}</a></li>
{% endif %}
<li>{{ object }}</li>
<li class="breadcrumb-item">{{ object }}</li>
{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<strong>Power Panel</strong>
<div class="card">
<h5 class="card-header">
Power Panel
</h5>
<div class="card-body">
<table class="table table-hover attr-table">
<tr>
<th scope="row">Site</th>
<td>
<a href="{{ object.site.get_absolute_url }}">{{ object.site }}</a>
</td>
</tr>
<tr>
<th scope="row">Location</th>
<td>
{% if object.location %}
<a href="{{ object.location.get_absolute_url }}">{{ object.location }}</a>
{% else %}
<span class="text-muted">None</span>
{% endif %}
</td>
</tr>
</table>
</div>
<table class="table table-hover panel-body attr-table">
<tr>
<td>Site</td>
<td>
<a href="{{ object.site.get_absolute_url }}">{{ object.site }}</a>
</td>
</tr>
<tr>
<td>Location</td>
<td>
{% if object.location %}
<a href="{{ object.location.get_absolute_url }}">{{ object.location }}</a>
{% else %}
<span class="text-muted">None</span>
{% endif %}
</td>
</tr>
</table>
</div>
{% plugin_left_page object %}
</div>
@ -45,7 +47,7 @@
{% plugin_right_page object %}
</div>
</div>
<div class="row">
<div class="row my-3">
<div class="col-md-12">
{% include 'panel_table.html' with table=powerfeed_table heading='Connected Feeds' %}
{% plugin_full_width_page object %}

View File

@ -1,6 +1,6 @@
{% extends base_template %}
{% block title %}{{ block.super }} - Change Log{% endblock %}
{% block title %}{{ object }} - Change Log{% endblock %}
{% block content %}
{% include 'panel_table.html' %}

View File

@ -7,8 +7,8 @@
<button
type="button"
class="btn btn-sm btn-outline-secondary"
data-toggle="modal"
data-target="#docs_modal"
data-bs-toggle="modal"
data-bs-target="#docs_modal"
title="Help"
>
<i class="bi bi-question"></i>
@ -22,15 +22,7 @@
{% for field in form.hidden_fields %}{{ field }}{% endfor %}
<div class="row">
<div class="col-md-8 col-md-offset-3">
{% block tabs %}{% endblock %} {% if form.non_field_errors %}
<div class="card bg-danger">
<h5 class="card-header">Errors</h5>
<div class="card-body">
{{ form.non_field_errors }}
</div>
</div>
{% endif %}
{% block tabs %}{% endblock %}
{% block form %}
{% if form.Meta.fieldsets %}

View File

@ -3,8 +3,8 @@
{% load plugins %}
{% block breadcrumbs %}
<li><a href="{% url 'ipam:routetarget_list' %}">Route Targets</a></li>
<li>{{ object }}</li>
<li class="breadcrumb-item"><a href="{% url 'ipam:routetarget_list' %}">Route Targets</a></li>
<li class="breadcrumb-item">{{ object }}</li>
{% endblock %}
{% block content %}

View File

@ -15,23 +15,30 @@
<li class="breadcrumb-item">{{ object }}</li>
{% endblock %}
{% block tabs %}
<ul class="nav nav-tabs" style="margin-bottom: 20px">
<li role="presentation"{% if not active_tab %} class="active"{% endif %}>
<a href="{% url 'ipam:vlan' pk=object.pk %}">VLAN</a>
</li>
<li role="presentation"{% if active_tab == 'interfaces' %} class="active"{% endif %}>
<a href="{% url 'ipam:vlan_interfaces' pk=object.pk %}">Device Interfaces <span class="badge">{{ object.get_interfaces.count }}</span></a>
</li>
<li role="presentation"{% if active_tab == 'vminterfaces' %} class="active"{% endif %}>
<a href="{% url 'ipam:vlan_vminterfaces' pk=object.pk %}">VM Interfaces <span class="badge">{{ object.get_vminterfaces.count }}</span></a>
</li>
{% if perms.extras.view_objectchange %}
<li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
<a href="{% url 'ipam:vlan_changelog' pk=object.pk %}">Change Log</a>
</li>
{% endif %}
</ul>
{% block tab_items %}
<li class="nav-item" role="presentation">
<a class="nav-link{% if not active_tab %} active{% endif %}" href="{% url 'ipam:vlan' pk=object.pk %}">VLAN</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link{% if active_tab == 'interfaces' %} active{% endif %}" href="{% url 'ipam:vlan_interfaces' pk=object.pk %}">
Device Interfaces
{% with count=object.get_interfaces.count %}
{% if count > 0 %}
<span class="badge bg-primary">{{ count }}</span>
{% endif %}
{% endwith %}
</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link{% if active_tab == 'vminterfaces' %} active{% endif %}" href="{% url 'ipam:vlan_vminterfaces' pk=object.pk %}">
VM Interfaces
{% comment %} {% with count=object.get_vminterfaces.count %}
{% if count > 0 %}
<span class="badge bg-primary">{{ count }}</span>
{% endif %}
{% endwith %} {% endcomment %}
</a>
</li>
{% endblock %}
{% block content %}
@ -44,7 +51,12 @@
<div class="card-body">
<table class="table table-hover attr-table">
<tr>
<td>Site</td>
<td colspan="2">
<span class="badge bg-{{ object.get_status_class }}">{{ object.get_status_display }}</span>
</td>
</tr>
<tr>
<th scope="row">Site</th>
<td>
{% if object.site %}
{% if object.site.region %}
@ -57,7 +69,7 @@
</td>
</tr>
<tr>
<td>Group</td>
<th scope="row">Group</th>
<td>
{% if object.group %}
<a href="{{ object.group.get_absolute_url }}">{{ object.group }}</a>
@ -67,15 +79,15 @@
</td>
</tr>
<tr>
<td>VLAN ID</td>
<th scope="row">VLAN ID</th>
<td>{{ object.vid }}</td>
</tr>
<tr>
<td>Name</td>
<th scope="row">Name</th>
<td>{{ object.name }}</td>
</tr>
<tr>
<td>Tenant</td>
<th scope="row">Tenant</th>
<td>
{% if object.tenant %}
{% if object.tenant.group %}
@ -88,13 +100,7 @@
</td>
</tr>
<tr>
<td>Status</td>
<td>
<span class="badge bg-{{ object.get_status_class }}">{{ object.get_status_display }}</span>
</td>
</tr>
<tr>
<td>Role</td>
<th scope="row">Role</th>
<td>
{% if object.role %}
<a href="{% url 'ipam:vlan_list' %}?role={{ object.role.slug }}">{{ object.role }}</a>
@ -104,7 +110,7 @@
</td>
</tr>
<tr>
<td>Description</td>
<th scope="row">Description</th>
<td>{{ object.description|placeholder }}</td>
</tr>
</table>

View File

@ -3,25 +3,45 @@
{% load helpers %}
{% block form %}
<div class="panel panel-default">
<div class="panel-heading"><strong>VLAN Group</strong></div>
<div class="panel-body">
{% render_field form.name %}
{% render_field form.slug %}
{% render_field form.description %}
</div>
<div class="field-group">
<h4>VLAN Group</h4>
{% render_field form.name %}
{% render_field form.slug %}
{% render_field form.description %}
</div>
<div class="panel panel-default">
<div class="panel-heading">
<strong>Scope</strong>
</div>
<div class="panel-body">
{% with virtual_tab_active=form.initial.cluster %}
<ul class="nav nav-tabs" role="tablist">
<li role="presentation"{% if not virtual_tab_active %} class="active"{% endif %}><a href="#physical" role="tab" data-toggle="tab">Physical</a></li>
<li role="presentation"{% if virtual_tab_active %} class="active"{% endif %}><a href="#virtual" role="tab" data-toggle="tab">Virtual</a></li>
</ul>
<div class="tab-content">
<div class="field-group">
<h4>Scope</h4>
{% with virtual_tab_active=form.initial.cluster %}
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item" role="presentation">
<button
role="tab"
type="button"
id="physical_tab"
data-bs-toggle="tab"
aria-controls="physical"
data-bs-target="#physical"
class="nav-link {% if not virtual_tab_active %}active{% endif %}"
>
Physical
</button>
</li>
<li class="nav-item" role="presentation">
<button
role="tab"
type="button"
id="virtual_tab"
data-bs-toggle="tab"
aria-controls="virtual"
data-bs-target="#virtual"
class="nav-link {% if virtual_tab_active %}active{% endif %}"
>
Virtual
</button>
</li>
</ul>
<div class="card my-3">
<div class="card-body tab-content">
<div class="tab-pane{% if not virtual_tab_active %} active{% endif %}" id="physical">
{% render_field form.region %}
{% render_field form.site_group %}
@ -33,17 +53,15 @@
{% render_field form.cluster_group %}
{% render_field form.cluster %}
</div>
<span class="form-text">The VLAN group will be limited in scope to the most-specific object selected above.</span>
</div>
<span class="help-block">The VLAN group will be limited in scope to the most-specific object selected above.</span>
{% endwith %}
</div>
</div>
{% endwith %}
</div>
{% if form.custom_fields %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Custom Fields</strong></div>
<div class="panel-body">
{% render_custom_fields form %}
</div>
<div class="field-group">
<h4>Custom Fields</h4>
{% render_custom_fields form %}
</div>
{% endif %}
{% endblock %}

View File

@ -1,24 +1,41 @@
{% extends 'base.html' %}
{% extends 'generic/object.html' %}
{% block title %}{{ object }} - VLANs{% endblock %}
{% block content %}
<div class="row noprint">
<div class="col-sm-12 col-md-12">
<ol class="breadcrumb">
<li><a href="{% url 'ipam:vlangroup_list' %}">VLAN Groups</a></li>
{% if object.site %}
<li><a href="{{ object.site.get_absolute_url }}">{{ object.site }}</a></li>
{% endif %}
<li>{{ object }}</li>
</ol>
</div>
</div>
{% include 'ipam/inc/vlangroup_header.html' %}
<div class="row">
<div class="col-md-12">
{% include 'utilities/obj_table.html' with table=vlan_table table_template='panel_table.html' heading='VLANs' bulk_edit_url='ipam:vlan_bulk_edit' bulk_delete_url='ipam:vlan_bulk_delete' %}
</div>
</div>
{% block controls %}
{% if perms.ipam.add_vlan and first_available_vlan %}
<a
href="{% url 'ipam:vlan_add' %}?vid={{ first_available_vlan }}&group={{ object.pk }}{% if object.site %}&site={{ object.site.pk }}{% endif %}"
class="btn btn-sm btn-success m-1">
<i class="bi bi-plus" aria-hidden="true"></i> Add a VLAN
</a>
{% endif %}
{% if perms.ipam.change_vlangroup %}
<a
href="{% url 'ipam:vlangroup_edit' pk=object.pk %}"
class="btn btn-sm btn-warning m-1">
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit this VLAN Group
</a>
{% endif %}
{% endblock %}
{% block breadcrumbs %}
<li class="breadcrumb-item">
<a href="{% url 'ipam:vlangroup_list' %}">VLAN Groups</a>
</li>
{% if object.site %}
<li class="breadcrumb-item">
<a href="{{ object.site.get_absolute_url }}">{{ object.site }}</a>
</li>
{% endif %}
<li class="breadcrumb-item">{{ object }}</li>
{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-12">
{% include 'utilities/obj_table.html' with table=vlan_table table_template='panel_table.html' heading='VLANs' bulk_edit_url='ipam:vlan_bulk_edit' bulk_delete_url='ipam:vlan_bulk_delete' %}
</div>
</div>
{% endblock %}

View File

@ -7,8 +7,8 @@
{% block title %}VRF {{ object }}{% endblock %}
{% block breadcrumbs %}
<li><a href="{% url 'ipam:vrf_list' %}">VRFs</a></li>
<li>{{ object }}</li>
<li class="breadcrumb-item"><a href="{% url 'ipam:vrf_list' %}">VRFs</a></li>
<li class="breadcrumb-item">{{ object }}</li>
{% endblock %}
{% block content %}

View File

@ -15,11 +15,12 @@
<a class="text-decoration-none" href="{% url 'admin:index' %}">
<i class="bi bi-gear-wide-connected"></i> Admin
</a>
{% else %}
{% endif %}
</li>
<li class="dropdown-item">
<a class="text-decoration-none" href="{% url 'user:profile' %}">
<i class="bi bi-person-fill"></i> Profile
</a>
{% endif %}
</li>
<li><hr class="dropdown-divider" /></li>
<li class="dropdown-item">

View File

@ -5,54 +5,47 @@
{% load plugins %}
{% block breadcrumbs %}
<li><a href="{% url 'secrets:secret_list' %}">Secrets</a></li>
<li><a href="{% url 'secrets:secret_list' %}?role={{ object.role.slug }}">{{ object.role }}</a></li>
<li><a href="{{ object.assigned_object.get_absolute_url }}">{{ object.assigned_object }}</a></li>
<li>{{ object }}</li>
{% endblock %}
{% block buttons %}
{% if perms.secrets.change_secret %}
{% edit_button object %}
{% endif %}
{% if perms.secrets.delete_secret %}
{% delete_button object %}
{% endif %}
<li class="breadcrumb-item"><a href="{% url 'secrets:secret_list' %}">Secrets</a></li>
<li class="breadcrumb-item"><a href="{% url 'secrets:secret_list' %}?role={{ object.role.slug }}">{{ object.role }}</a></li>
<li class="breadcrumb-item"><a href="{{ object.assigned_object.get_absolute_url }}">{{ object.assigned_object }}</a></li>
<li class="breadcrumb-item">{{ object }}</li>
{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<strong>Secret Attributes</strong>
<div class="card">
<h5 class="card-header">
Secret Attributes
</h5>
<div class="card-body">
<table class="table table-hover">
<tr>
<th scope="row">Assigned object</th>
<td>
<a href="{{ object.assigned_object.get_absolute_url }}">{{ object.assigned_object }}</a>
</td>
</tr>
<tr>
<th scope="row">Role</th>
<td>{{ object.role }}</td>
</tr>
<tr>
<th scope="row">Name</th>
<td>{{ object.name|placeholder }}</td>
</tr>
</table>
</div>
<table class="table table-hover panel-body">
<tr>
<td>Assigned object</td>
<td>
<a href="{{ object.assigned_object.get_absolute_url }}">{{ object.assigned_object }}</a>
</td>
</tr>
<tr>
<td>Role</td>
<td>{{ object.role }}</td>
</tr>
<tr>
<td>Name</td>
<td>{{ object.name|placeholder }}</td>
</tr>
</table>
</div>
{% include 'inc/custom_fields_panel.html' %}
{% plugin_left_page object %}
</div>
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<strong>Secret Data</strong>
</div>
<div class="panel-body">
<div class="card">
<h5 class="card-header">
Secret Data
</h5>
<div class="card-body">
<form id="secret_form">
{% csrf_token %}
</form>
@ -60,13 +53,13 @@
<div class="col-md-2">Secret</div>
<div class="col-md-6" id="secret_{{ object.pk }}">********</div>
<div class="col-md-4 text-right noprint">
<button class="btn btn-xs btn-success unlock-secret" secret-id="{{ object.pk }}">
<button class="btn btn-sm btn-success unlock-secret" secret-id="{{ object.pk }}">
<i class="mdi mdi-lock"></i> Unlock
</button>
<button class="btn btn-xs btn-default copy-secret collapse" secret-id="{{ object.pk }}" data-clipboard-target="#secret_{{ object.pk }}">
<button class="btn btn-sm btn-default copy-secret collapse" secret-id="{{ object.pk }}" data-clipboard-target="#secret_{{ object.pk }}">
<i class="mdi mdi-content-copy"></i> Copy
</button>
<button class="btn btn-xs btn-danger lock-secret collapse" secret-id="{{ object.pk }}">
<button class="btn btn-sm btn-danger lock-secret collapse" secret-id="{{ object.pk }}">
<i class="mdi mdi-lock-open"></i> Lock
</button>
</div>
@ -85,7 +78,3 @@
{% include 'secrets/inc/private_key_modal.html' %}
{% endblock %}
{% block javascript %}
<script src="{% static 'js/secrets.js' %}?v{{ settings.VERSION }}"></script>
{% endblock %}

View File

@ -1,99 +1,88 @@
{% extends 'base.html' %}
{% extends 'generic/object_edit.html' %}
{% load static %}
{% load form_helpers %}
{% block content %}
<form action="." method="post" class="form form-horizontal">
{% csrf_token %}
{{ form.private_key }}
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h3>{% block title %}{% if obj.pk %}Editing {{ obj }}{% else %}Add a Secret{% endif %}{% endblock %}</h3>
{% if form.non_field_errors %}
<div class="panel panel-danger">
<div class="panel-heading"><strong>Errors</strong></div>
<div class="panel-body">
{{ form.non_field_errors }}
</div>
</div>
{% endif %}
<div class="panel panel-default">
<div class="panel-heading">
<strong>Secret Assignment</strong>
</div>
<div class="panel-body">
{% with vm_tab_active=form.initial.virtual_machine %}
<ul class="nav nav-tabs" role="tablist">
<li role="presentation"{% if not vm_tab_active %} class="active"{% endif %}><a href="#device" role="tab" data-toggle="tab">Device</a></li>
<li role="presentation"{% if vm_tab_active %} class="active"{% endif %}><a href="#virtualmachine" role="tab" data-toggle="tab">Virtual Machine</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane{% if not vm_tab_active %} active{% endif %}" id="device">
{% render_field form.device %}
</div>
<div class="tab-pane{% if vm_tab_active %} active{% endif %}" id="virtualmachine">
{% render_field form.virtual_machine %}
</div>
</div>
{% endwith %}
{% render_field form.role %}
{% render_field form.name %}
{% render_field form.userkeys %}
{% render_field form.tags %}
</div>
{% block title %}{% if obj.pk %}Editing {{ obj }}{% else %}Add a Secret{% endif %}{% endblock %}
{% block form %}
{% render_errors form %}
{{ form.private_key }}
<div class="field-group">
<h4>Secret Assignment</h4>
<ul class="nav nav-tabs mb-3" role="tablist">
<li class="nav-item" role="presentation">
<button
role="tab"
type="button"
id="device_tab"
data-bs-toggle="tab"
class="nav-link{% if not vm_tab_active %} active{% endif %}"
data-bs-target="#device"
aria-controls="device"
>
Device
</button>
</li>
<li class="nav-item" role="presentation">
<button
role="tab"
type="button"
id="vm_tab"
data-bs-toggle="tab"
class="nav-link{% if vm_tab_active %} active{% endif %}"
data-bs-target="#virtualmachine"
aria-controls="virtualmachine"
>
Virtual Machine
</button>
</li>
</ul>
{% with vm_tab_active=form.initial.virtual_machine %}
<div class="tab-content">
<div class="tab-pane{% if not vm_tab_active %} active{% endif %}" id="device">
{% render_field form.device %}
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>Secret Data</strong></div>
<div class="panel-body">
{% if obj.pk %}
<div class="form-group">
<label class="col-md-3 control-label required">Current Plaintext</label>
<div class="col-md-7">
<p class="form-control-static" id="secret_{{ obj.pk }}">********</p>
</div>
<div class="col-md-2 text-right">
<button class="btn btn-xs btn-success unlock-secret" secret-id="{{ obj.pk }}">
<i class="mdi mdi-lock"></i> Unlock
</button>
<button class="btn btn-xs btn-danger lock-secret collapse" secret-id="{{ obj.pk }}">
<i class="mdi mdi-lock-open"></i> Lock
</button>
</div>
</div>
{% endif %}
{% render_field form.plaintext %}
{% render_field form.plaintext2 %}
</div>
<div class="tab-pane{% if vm_tab_active %} active{% endif %}" id="virtualmachine">
{% render_field form.virtual_machine %}
</div>
{% if form.custom_fields %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Custom Fields</strong></div>
<div class="panel-body">
{% render_custom_fields form %}
</div>
</div>
{% endif %}
</div>
{% endwith %}
{% render_field form.role %}
{% render_field form.name %}
{% render_field form.userkeys %}
{% render_field form.tags %}
</div>
<div class="field-group">
<h4>Secret Data</h4>
{% if obj.pk %}
<div class="form-floating mb-3">
<input class="form-control" value="********" id="secret_{{ obj.pk }}" />
<label class="required">Current Plain Text</label>
</div>
<div class="col-md-2 text-end">
<button class="btn btn-sm btn-success unlock-secret" data-secret-id="{{ obj.pk }}">
<i class="mdi mdi-lock"></i> Unlock
</button>
<button class="btn btn-sm, btn-danger lock-secret collapse" data-secret-id="{{ obj.pk }}">
<i class="mdi mdi-lock-open"></i> Lock
</button>
</div>
{% endif %}
{% render_field form.plaintext %}
{% render_field form.plaintext2 %}
</div>
{% if form.custom_fields %}
<div class="card">
<h5 class="card-header">Custom Fields</h5>
<div class="card-body">
{% render_custom_fields form %}
</div>
</div>
<div class="row">
<div class="form-group">
<div class="col-md-12 text-center">
{% if obj.pk %}
<button type="submit" name="_update" class="btn btn-primary">Update</button>
<a href="{% url 'secrets:secret' pk=obj.pk %}" class="btn btn-default">Cancel</a>
{% else %}
<button type="submit" name="_create" class="btn btn-primary">Create</button>
<button type="submit" name="_addanother" class="btn btn-primary">Create and Add Another</button>
<a href="{{ return_url }}" class="btn btn-default">Cancel</a>
{% endif %}
</div>
</div>
</div>
</form>
{% endif %}
{% include 'secrets/inc/private_key_modal.html' %}
{% endblock %}
{% block javascript %}
<script src="{% static 'js/secrets.js' %}?v{{ settings.VERSION }}"></script>
{% endblock %}

View File

@ -7,15 +7,15 @@
<div class="row">
<div class="col-md-12">
{% for token in tokens %}
<div class="panel panel-{% if token.is_expired %}danger{% else %}default{% endif %}">
<div class="panel-heading">
<div class="pull-right noprint">
<a class="btn btn-xs btn-success copy-token" data-clipboard-target="#token_{{ token.pk }}">Copy</a>
<div class="card{% if token.is_expired %} bg-danger{% endif %}">
<div class="card-header">
<div class="float-end noprint">
<a class="m-1 btn btn-sm btn-success copy-token" data-clipboard-target="#token_{{ token.pk }}">Copy</a>
{% if perms.users.change_token %}
<a href="{% url 'user:token_edit' pk=token.pk %}" class="btn btn-xs btn-warning">Edit</a>
<a href="{% url 'user:token_edit' pk=token.pk %}" class="m-1 btn btn-sm btn-warning">Edit</a>
{% endif %}
{% if perms.users.delete_token %}
<a href="{% url 'user:token_delete' pk=token.pk %}" class="btn btn-xs btn-danger">Delete</a>
<a href="{% url 'user:token_delete' pk=token.pk %}" class="m-1 btn btn-sm btn-danger">Delete</a>
{% endif %}
</div>
<i class="mdi mdi-key"></i>
@ -24,7 +24,7 @@
<span class="label label-danger">Expired</span>
{% endif %}
</div>
<div class="panel-body">
<div class="card-body">
<div class="row">
<div class="col-md-4">
<small class="text-muted">Created</small><br />
@ -55,22 +55,20 @@
{% empty %}
<p>You do not have any API tokens.</p>
{% endfor %}
{% if perms.users.add_token %}
<a href="{% url 'user:token_add' %}" class="btn btn-primary">
<span class="mdi mdi-plus-thick" aria-hidden="true"></span>
Add a token
</a>
{% else %}
<div class="alert alert-info text-center" role="alert">
You do not have permission to create new API tokens. If needed, ask an administrator to enable token creation for your account or an assigned group.
<div class="row my-3">
<div class="col-md-12">
{% if perms.users.add_token %}
<a href="{% url 'user:token_add' %}" class="btn btn-primary">
<span class="mdi mdi-plus-thick" aria-hidden="true"></span>
Add a Token
</a>
{% else %}
<div class="alert alert-info text-center" role="alert">
You do not have permission to create new API tokens. If needed, ask an administrator to enable token creation for your account or an assigned group.
</div>
{% endif %}
</div>
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% block javascript %}
<script type="text/javascript">
new ClipboardJS('.copy-token');
</script>
{% endblock %}

View File

@ -1,34 +1,21 @@
{% extends 'base.html' %}
{% extends 'layout.html' %}
{% block title %}{% endblock %}
{% block content %}
<div class="row">
<div class="col-sm-12 col-md-10 col-md-offset-1">
<h1>{% block title %}{% endblock %}</h1>
</div>
</div>
<div class="row">
<div class="col-sm-3 col-md-2 col-md-offset-1">
<ul class="nav nav-pills nav-stacked">
<li{% if active_tab == "profile" %} class="active"{% endif %}>
<a href="{% url 'user:profile' %}">Profile</a>
</li>
<li{% if active_tab == "preferences" %} class="active"{% endif %}>
<a href="{% url 'user:preferences' %}">Preferences</a>
</li>
<div class="col-sm-3 col-md-2 col-md-offset-1 border-end">
<nav class="nav nav-pills nav-justified flex-column">
<a class="nav-item nav-link text-start{% if active_tab == 'profile' %} active{% endif %}" href="{% url 'user:profile' %}">Profile</a>
<a class="nav-link nav-item text-start{% if active_tab == 'preferences' %} active{% endif %}" href="{% url 'user:preferences' %}">Preferences</a>
{% if not request.user.ldap_username %}
<li{% if active_tab == "change_password" %} class="active"{% endif %}>
<a href="{% url 'user:change_password' %}">Change Password</a>
</li>
<a class="nav-link nav-item text-start{% if active_tab == 'change-password' %} active{% endif %}" href="{% url 'user:change_password' %}">Change Password</a>
{% endif %}
<li{% if active_tab == "api_tokens" %} class="active"{% endif %}>
<a href="{% url 'user:token_list' %}">API Tokens</a>
</li>
<li{% if active_tab == "userkey" %} class="active"{% endif %}>
<a href="{% url 'user:userkey' %}">User Key</a>
</li>
</ul>
<a class="nav-link nav-item text-start{% if active_tab == 'api-tokens' %} active{% endif %}" href="{% url 'user:token_list' %}">API Tokens</a>
<a class="nav-link nav-item text-start{% if active_tab == 'userkey' %} active{% endif %}" href="{% url 'user:userkey' %}">User Key</a>
</nav>
</div>
<div class="col-sm-9 col-md-8">
<div class="col-sm-9 col-md-8 px-4">
{% block usercontent %}{% endblock %}
</div>
</div>

View File

@ -3,28 +3,20 @@
{% block title %}Change Password{% endblock %}
{% render_errors form %}
{% block usercontent %}
<form action="." method="post" class="form form-horizontal col-md-10 col-md-offset-1">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="panel panel-danger">
<div class="panel-heading"><strong>Errors</strong></div>
<div class="panel-body">
{{ form.non_field_errors }}
</div>
</div>
{% endif %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Password</strong></div>
<div class="panel-body">
{% render_field form.old_password %}
{% render_field form.new_password1 %}
{% render_field form.new_password2 %}
</div>
<div class="field-group">
<h4>Password</h4>
{% render_field form.old_password %}
{% render_field form.new_password1 %}
{% render_field form.new_password2 %}
</div>
<div class="text-right">
<div class="text-end">
<a href="{% url 'user:profile' %}" class="btn btn-outline-danger">Cancel</a>
<button type="submit" name="_update" class="btn btn-primary">Update</button>
<a href="{% url 'user:profile' %}" class="btn btn-default">Cancel</a>
</div>
</form>
{% endblock %}

View File

@ -4,16 +4,34 @@
{% block title %}User Profile{% endblock %}
{% block usercontent %}
<small class="text-muted">User login</small>
<small class="text-muted">User Login</small>
<h5>{{ request.user.username }}</h5>
<small class="text-muted">Full name</small>
<h5>{{ request.user.first_name }} {{ request.user.last_name }}</h5>
<small class="text-muted">Full Name</small>
<h5>
{% if request.user.first_name and request.user.last_name %}
{{ request.user.first_name }} {{ request.user.last_name }}
{% elif request.user.first_name and not request.user.last_name %}
{{ request.user.first_name }}
{% elif request.user.last_name and not request.user.first_name %}
{{ request.user.last_name }}
{% else %}
<span class="text-muted">None</span>
{% endif %}
</h5>
<small class="text-muted">Email</small>
<h5>{{ request.user.email }}</h5>
<small class="text-muted">Registered</small>
<h5>{{ request.user.date_joined }}</h5>
<small class="text-muted">Groups</small>
<h5>{{ request.user.groups.all|join:', ' }}</h5>
<small class="text-muted">Admin access</small>
<h5>
{% if request.user.groups.all %}
{% for group in request.user.groups.all %}
<span class="badge bg-secondary">{{ group }}</span>
{% endfor %}
{% else %}
<span class="text-muted">None</span>
{% endif %}
</h5>
<small class="text-muted">Admin Access</small>
<h5>{{ request.user.is_staff|yesno|capfirst }}</h5>
{% endblock %}

View File

@ -4,18 +4,18 @@
{% block usercontent %}
{% if object %}
<div class="pull-right noprint">
<div class="float-end noprint">
<a href="{% url 'user:userkey_edit' %}" class="btn btn-warning">
<span class="mdi mdi-pencil" aria-hidden="true"></span>
Edit user key
Edit User Key
</a>
</div>
<h4>
Your user key is:
Your user key is
{% if object.is_active %}
<span class="label label-success">Active</span>
<span class="badge bg-success">Active</span>
{% else %}
<span class="label label-danger">Inactive</span>
<span class="badge bg-danger">Inactive</span>
{% endif %}
</h4>
{% include 'inc/created_updated.html' %}
@ -25,7 +25,7 @@
Your user key is inactive. Ask an administrator to enable it for you.
</div>
{% endif %}
<pre>{{ object.public_key }}</pre>
<pre class="border rounded p-3 copyable">{{ object.public_key }}</pre>
<hr />
{% if object.session_key %}
<div class="pull-right noprint">

View File

@ -13,48 +13,43 @@
{% endif %}
<form action="." method="post" class="form">
{% csrf_token %}
<div class="form-group">
<div class="field-group">
{% render_field form.public_key %}
</div>
<div class="row">
<div class="form-group">
<div class="col-sm-6 col-md-6">
<button type="button" class="btn btn-info" id="generate_keypair">Generate a New Key Pair</button>
</div>
<div class="col-sm-6 col-md-6 text-right">
<button type="submit" name="_update" class="btn btn-primary">Save</button>
<a href="{% url 'user:userkey' %}" class="btn btn-default">Cancel</a>
</div>
<div class="row my-3">
<div class="col-4 text-start">
<button type="button" class="btn btn-info" id="generate_keypair" data-bs-toggle="modal" data-bs-target="#new_keypair_modal">Generate a New Key Pair</button>
</div>
<div class="col-8 text-end">
<a href="{% url 'user:userkey' %}" class="btn btn-outline-danger">Cancel</a>
<button type="submit" name="_update" class="btn btn-primary">Save</button>
</div>
</div>
</form>
<div class="modal fade" id="new_keypair_modal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-md" role="document">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="new_keypair_modal_title">
New RSA Key Pair
</h4>
<button type="button" class="btn btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<strong>New Public Key</strong>
<div class="form-group">
<textarea class="form-control" id="new_pubkey" style="height: 250px;"></textarea>
<div class="field-group">
<h5>New Public Key</h5>
<textarea class="form-control" rows="10" id="new_pubkey" style="height: 250px;font-family:var(--bs-font-monospace);"></textarea>
</div>
<strong>New Private Key</strong>
<div class="form-group">
<textarea class="form-control" id="new_privkey" style="height: 250px;"></textarea>
<div class="field-group">
<h5>New Private Key</h5>
<textarea class="form-control" rows="10" id="new_privkey" style="height: 250px;font-family:var(--bs-font-monospace);"></textarea>
</div>
</div>
<div class="modal-footer text-center">
<button type="button" class="btn btn-danger" id="use_new_pubkey" data-dismiss="modal">I have saved my new private key</button>
<button type="button" class="btn btn-danger" id="use_new_pubkey" data-bs-dismiss="modal">I Saved My New Private Key</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block javascript %}
<script src="{% static 'js/secrets.js' %}?v{{ settings.VERSION }}"></script>
{% endblock %}

View File

@ -4,11 +4,11 @@
{% if form.errors or form.non_field_errors %}
<div class="alert alert-danger mt-3" role="alert">
<h4 class="alert-heading">Errors</h4>
{% if form.errors %}
<hr />
{% if form.errors and '__all__' not in form.errors %}
<hr />
{% endif %}
<div class="ps-2">
{% if form.errors %}
{% if form.errors and '__all__' not in form.errors %}
{% for field_name, errors in form.errors.items %}
{% if not field_name|startswith:'__' %}
{% with field=form|getfield:field_name %}

View File

@ -5,72 +5,74 @@
{% load plugins %}
{% block breadcrumbs %}
<li><a href="{{ object.type.get_absolute_url }}">{{ object.type }}</a></li>
<li class="breadcrumb-item"><a href="{{ object.type.get_absolute_url }}">{{ object.type }}</a></li>
{% if object.group %}
<li><a href="{{ object.group.get_absolute_url }}">{{ object.group }}</a></li>
<li class="breadcrumb-item"><a href="{{ object.group.get_absolute_url }}">{{ object.group }}</a></li>
{% endif %}
<li>{{ object }}</li>
<li class="breadcrumb-item">{{ object }}</li>
{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-5">
<div class="panel panel-default">
<div class="panel-heading">
<strong>Cluster</strong>
<div class="card">
<h5 class="card-header">
Cluster
</h5>
<div class="card-body">
<table class="table table-hover attr-table">
<tr>
<th scope="row">Name</th>
<td>{{ object.name }}</td>
</tr>
<tr>
<th scope="row">Type</th>
<td><a href="{{ object.type.get_absolute_url }}">{{ object.type }}</a></td>
</tr>
<tr>
<th scope="row">Group</th>
<td>
{% if object.group %}
<a href="{{ object.group.get_absolute_url }}">{{ object.group }}</a>
{% else %}
<span class="text-muted">None</span>
{% endif %}
</td>
</tr>
<tr>
<th scope="row">Tenant</th>
<td>
{% if object.tenant %}
<a href="{{ object.tenant.get_absolute_url }}">{{ object.tenant }}</a>
{% else %}
<span class="text-muted">None</span>
{% endif %}
</td>
</tr>
<tr>
<th scope="row">Site</th>
<td>
{% if object.site %}
<a href="{{ object.site.get_absolute_url }}">{{ object.site }}</a>
{% else %}
<span class="text-muted">None</span>
{% endif %}
</td>
</tr>
<tr>
<th scope="row">Virtual Machines</th>
<td><a href="{% url 'virtualization:virtualmachine_list' %}?cluster_id={{ object.pk }}">{{ object.virtual_machines.count }}</a></td>
</tr>
</table>
</div>
<table class="table table-hover panel-body attr-table">
<tr>
<td>Name</td>
<td>{{ object.name }}</td>
</tr>
<tr>
<td>Type</td>
<td><a href="{{ object.type.get_absolute_url }}">{{ object.type }}</a></td>
</tr>
<tr>
<td>Group</td>
<td>
{% if object.group %}
<a href="{{ object.group.get_absolute_url }}">{{ object.group }}</a>
{% else %}
<span class="text-muted">None</span>
{% endif %}
</td>
</tr>
<tr>
<td>Tenant</td>
<td>
{% if object.tenant %}
<a href="{{ object.tenant.get_absolute_url }}">{{ object.tenant }}</a>
{% else %}
<span class="text-muted">None</span>
{% endif %}
</td>
</tr>
<tr>
<td>Site</td>
<td>
{% if object.site %}
<a href="{{ object.site.get_absolute_url }}">{{ object.site }}</a>
{% else %}
<span class="text-muted">None</span>
{% endif %}
</td>
</tr>
<tr>
<td>Virtual Machines</td>
<td><a href="{% url 'virtualization:virtualmachine_list' %}?cluster_id={{ object.pk }}">{{ object.virtual_machines.count }}</a></td>
</tr>
</table>
</div>
{% include 'inc/custom_fields_panel.html' %}
{% include 'extras/inc/tags_panel.html' with tags=object.tags.all url='virtualization:cluster_list' %}
<div class="panel panel-default">
<div class="panel-heading">
<strong>Comments</strong>
</div>
<div class="panel-body rendered-markdown">
<div class="card">
<h5 class="card-header">
Comments
</h5>
<div class="card-body rendered-markdown">
{% if object.comments %}
{{ object.comments|render_markdown }}
{% else %}
@ -81,30 +83,30 @@
{% plugin_left_page object %}
</div>
<div class="col-md-7">
<div class="panel panel-default">
<div class="panel-heading">
<strong>Host Devices</strong>
</div>
{% if perms.virtualization.change_cluster %}
<div class="card">
<h5 class="card-header">
Host Devices
</h5>
<div class="card-body">
{% if perms.virtualization.change_cluster %}
<form action="{% url 'virtualization:cluster_remove_devices' pk=object.pk %}" method="post">
{% csrf_token %}
{% endif %}
{% include 'responsive_table.html' with table=device_table %}
{% if perms.virtualization.change_cluster %}
<div class="panel-footer noprint">
<div class="pull-right">
<a href="{% url 'virtualization:cluster_add_devices' pk=object.pk %}?site={{ object.site.pk }}" class="btn btn-primary btn-xs">
{% csrf_token %}
{% include 'responsive_table.html' with table=device_table %}
{% if perms.virtualization.change_cluster %}
<div class="card-footer noprint justify-content-between d-flex">
<button type="submit" name="_remove" class="btn btn-danger primary btn-sm">
<span class="mdi mdi-trash-can-outline" aria-hidden="true"></span>
Remove devices
</button>
<a href="{% url 'virtualization:cluster_add_devices' pk=object.pk %}?site={{ object.site.pk }}" class="btn btn-primary btn-sm">
<span class="mdi mdi-plus-thick" aria-hidden="true"></span>
Add devices
</a>
</div>
<button type="submit" name="_remove" class="btn btn-danger primary btn-xs">
<span class="mdi mdi-trash-can-outline" aria-hidden="true"></span>
Remove devices
</button>
</div>
</form>
{% endif %}
{% endif %}
{% endif %}
</div>
</div>
{% plugin_right_page object %}
</div>

View File

@ -1,36 +1,29 @@
{% extends 'base.html' %}
{% extends 'generic/object_edit.html' %}
{% load static %}
{% load form_helpers %}
{% render_errors form %}
{% block title %}Add Device to Cluster {{ cluster }}{% endblock %}
{% block content %}
<form action="." method="post" class="form form-horizontal">
<form action="." method="post">
{% csrf_token %}
{% for field in form.hidden_fields %}
{{ field }}
{% endfor %}
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h3>{% block title %}Add Devices to Cluster {{ cluster }}{% endblock %}</h3>
{% if form.non_field_errors %}
<div class="panel panel-danger">
<div class="panel-heading"><strong>Errors</strong></div>
<div class="panel-body">
{{ form.non_field_errors }}
</div>
</div>
{% endif %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Device Selection</strong></div>
<div class="panel-body">
{% render_form form %}
</div>
<div class="field-group">
<h4>Device Selection</h4>
{% render_form form %}
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 col-md-offset-3 text-right noprint">
<div class="col-md-6 col-md-offset-3 text-end noprint">
<a href="{{ return_url }}" class="btn btn-outline-danger">Cancel</a>
<button type="submit" name="_add" class="btn btn-primary">Add Devices</button>
<a href="{{ return_url }}" class="btn btn-default">Cancel</a>
</div>
</div>
</form>

View File

@ -2,41 +2,37 @@
{% load form_helpers %}
{% block form %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Interface</strong></div>
<div class="panel-body">
{% if form.instance.virtual_machine %}
<div class="form-group">
<label class="col-md-3 control-label required" for="id_device">Virtual Machine</label>
<div class="col-md-9">
<p class="form-control-static">
<a href="{{ form.instance.virtual_machine.get_absolute_url }}">{{ form.instance.virtual_machine }}</a>
</p>
</div>
</div>
{% endif %}
{% render_field form.name %}
{% render_field form.enabled %}
{% render_field form.mac_address %}
{% render_field form.mtu %}
{% render_field form.description %}
{% render_field form.tags %}
</div>
<div class="field-group">
<h4>Interface</h4>
{% if form.instance.virtual_machine %}
<div class="form-floating">
<input class="form-control" value="{{ form.instance.virtual_machine }}"/>
{% comment %} <div class="col-md-9">
<p class="form-control-static">
<a href="{{ form.instance.virtual_machine.get_absolute_url }}">{{ form.instance.virtual_machine }}</a>
</p>
</div> {% endcomment %}
<label class="col-md-3 control-label required" for="id_device">Virtual Machine</label>
</div>
{% endif %}
{% render_field form.name %}
{% render_field form.enabled %}
{% render_field form.mac_address %}
{% render_field form.mtu %}
{% render_field form.description %}
{% render_field form.tags %}
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>802.1Q Switching</strong></div>
<div class="panel-body">
{% render_field form.mode %}
{% render_field form.untagged_vlan %}
{% render_field form.tagged_vlans %}
</div>
<div class="field-group">
<h4>802.1Q Switching</h4>
{% render_field form.mode %}
{% render_field form.untagged_vlan %}
{% render_field form.tagged_vlans %}
</div>
{% if form.custom_fields %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Custom Fields</strong></div>
<div class="panel-body">
{% render_custom_fields form %}
</div>
<div class="field-group">
<h4>Custom Fields</h4>
{% render_custom_fields form %}
</div>
{% endif %}
{% endblock %}