mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-21 11:37:21 -06:00
commit
b7129e1456
2
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@ -14,7 +14,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: NetBox version
|
label: NetBox version
|
||||||
description: What version of NetBox are you currently running?
|
description: What version of NetBox are you currently running?
|
||||||
placeholder: v3.0.11
|
placeholder: v3.0.12
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
|
2
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
2
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
@ -14,7 +14,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: NetBox version
|
label: NetBox version
|
||||||
description: What version of NetBox are you currently running?
|
description: What version of NetBox are you currently running?
|
||||||
placeholder: v3.0.11
|
placeholder: v3.0.12
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
|
@ -1,5 +1,22 @@
|
|||||||
# NetBox v3.0
|
# NetBox v3.0
|
||||||
|
|
||||||
|
## v3.0.12 (2021-12-06)
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
* [#7751](https://github.com/netbox-community/netbox/issues/7751) - Get API user from LDAP only when `FIND_GROUP_PERMS` is enabled
|
||||||
|
* [#7885](https://github.com/netbox-community/netbox/issues/7885) - Linkify VLAN name in VLANs table
|
||||||
|
* [#7892](https://github.com/netbox-community/netbox/issues/7892) - Add L22-30 power port & outlet types
|
||||||
|
* [#7932](https://github.com/netbox-community/netbox/issues/7932) - Improve performance of the "quick find" function
|
||||||
|
* [#7941](https://github.com/netbox-community/netbox/issues/7941) - Add multi-standard ITA power outlet type
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* [#7823](https://github.com/netbox-community/netbox/issues/7823) - Fix issue where `return_url` is not honored when 'Save & Continue' button is present
|
||||||
|
* [#7981](https://github.com/netbox-community/netbox/issues/7981) - Fix Markdown sanitization regex
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## v3.0.11 (2021-11-24)
|
## v3.0.11 (2021-11-24)
|
||||||
|
|
||||||
### Enhancements
|
### Enhancements
|
||||||
|
@ -312,6 +312,7 @@ class PowerPortTypeChoices(ChoiceSet):
|
|||||||
TYPE_NEMA_L1560P = 'nema-l15-60p'
|
TYPE_NEMA_L1560P = 'nema-l15-60p'
|
||||||
TYPE_NEMA_L2120P = 'nema-l21-20p'
|
TYPE_NEMA_L2120P = 'nema-l21-20p'
|
||||||
TYPE_NEMA_L2130P = 'nema-l21-30p'
|
TYPE_NEMA_L2130P = 'nema-l21-30p'
|
||||||
|
TYPE_NEMA_L2230P = 'nema-l22-30p'
|
||||||
# California style
|
# California style
|
||||||
TYPE_CS6361C = 'cs6361c'
|
TYPE_CS6361C = 'cs6361c'
|
||||||
TYPE_CS6365C = 'cs6365c'
|
TYPE_CS6365C = 'cs6365c'
|
||||||
@ -417,6 +418,7 @@ class PowerPortTypeChoices(ChoiceSet):
|
|||||||
(TYPE_NEMA_L1560P, 'NEMA L15-60P'),
|
(TYPE_NEMA_L1560P, 'NEMA L15-60P'),
|
||||||
(TYPE_NEMA_L2120P, 'NEMA L21-20P'),
|
(TYPE_NEMA_L2120P, 'NEMA L21-20P'),
|
||||||
(TYPE_NEMA_L2130P, 'NEMA L21-30P'),
|
(TYPE_NEMA_L2130P, 'NEMA L21-30P'),
|
||||||
|
(TYPE_NEMA_L2230P, 'NEMA L22-30P'),
|
||||||
)),
|
)),
|
||||||
('California Style', (
|
('California Style', (
|
||||||
(TYPE_CS6361C, 'CS6361C'),
|
(TYPE_CS6361C, 'CS6361C'),
|
||||||
@ -533,6 +535,7 @@ class PowerOutletTypeChoices(ChoiceSet):
|
|||||||
TYPE_NEMA_L1560R = 'nema-l15-60r'
|
TYPE_NEMA_L1560R = 'nema-l15-60r'
|
||||||
TYPE_NEMA_L2120R = 'nema-l21-20r'
|
TYPE_NEMA_L2120R = 'nema-l21-20r'
|
||||||
TYPE_NEMA_L2130R = 'nema-l21-30r'
|
TYPE_NEMA_L2130R = 'nema-l21-30r'
|
||||||
|
TYPE_NEMA_L2230R = 'nema-l22-30r'
|
||||||
# California style
|
# California style
|
||||||
TYPE_CS6360C = 'CS6360C'
|
TYPE_CS6360C = 'CS6360C'
|
||||||
TYPE_CS6364C = 'CS6364C'
|
TYPE_CS6364C = 'CS6364C'
|
||||||
@ -552,6 +555,7 @@ class PowerOutletTypeChoices(ChoiceSet):
|
|||||||
TYPE_ITA_M = 'ita-m'
|
TYPE_ITA_M = 'ita-m'
|
||||||
TYPE_ITA_N = 'ita-n'
|
TYPE_ITA_N = 'ita-n'
|
||||||
TYPE_ITA_O = 'ita-o'
|
TYPE_ITA_O = 'ita-o'
|
||||||
|
TYPE_ITA_MULTISTANDARD = 'ita-multistandard'
|
||||||
# USB
|
# USB
|
||||||
TYPE_USB_A = 'usb-a'
|
TYPE_USB_A = 'usb-a'
|
||||||
TYPE_USB_MICROB = 'usb-micro-b'
|
TYPE_USB_MICROB = 'usb-micro-b'
|
||||||
@ -630,6 +634,7 @@ class PowerOutletTypeChoices(ChoiceSet):
|
|||||||
(TYPE_NEMA_L1560R, 'NEMA L15-60R'),
|
(TYPE_NEMA_L1560R, 'NEMA L15-60R'),
|
||||||
(TYPE_NEMA_L2120R, 'NEMA L21-20R'),
|
(TYPE_NEMA_L2120R, 'NEMA L21-20R'),
|
||||||
(TYPE_NEMA_L2130R, 'NEMA L21-30R'),
|
(TYPE_NEMA_L2130R, 'NEMA L21-30R'),
|
||||||
|
(TYPE_NEMA_L2230R, 'NEMA L22-30R'),
|
||||||
)),
|
)),
|
||||||
('California Style', (
|
('California Style', (
|
||||||
(TYPE_CS6360C, 'CS6360C'),
|
(TYPE_CS6360C, 'CS6360C'),
|
||||||
@ -651,6 +656,7 @@ class PowerOutletTypeChoices(ChoiceSet):
|
|||||||
(TYPE_ITA_M, 'ITA Type M (BS 546)'),
|
(TYPE_ITA_M, 'ITA Type M (BS 546)'),
|
||||||
(TYPE_ITA_N, 'ITA Type N'),
|
(TYPE_ITA_N, 'ITA Type N'),
|
||||||
(TYPE_ITA_O, 'ITA Type O'),
|
(TYPE_ITA_O, 'ITA Type O'),
|
||||||
|
(TYPE_ITA_MULTISTANDARD, 'ITA Multistandard'),
|
||||||
)),
|
)),
|
||||||
('USB', (
|
('USB', (
|
||||||
(TYPE_USB_A, 'USB Type A'),
|
(TYPE_USB_A, 'USB Type A'),
|
||||||
|
@ -95,6 +95,9 @@ class VLANTable(BaseTable):
|
|||||||
template_code=VLAN_LINK,
|
template_code=VLAN_LINK,
|
||||||
verbose_name='VID'
|
verbose_name='VID'
|
||||||
)
|
)
|
||||||
|
name = tables.Column(
|
||||||
|
linkify=True
|
||||||
|
)
|
||||||
site = tables.Column(
|
site = tables.Column(
|
||||||
linkify=True
|
linkify=True
|
||||||
)
|
)
|
||||||
|
@ -29,6 +29,9 @@ class TokenAuthentication(authentication.TokenAuthentication):
|
|||||||
if settings.REMOTE_AUTH_BACKEND == 'netbox.authentication.LDAPBackend':
|
if settings.REMOTE_AUTH_BACKEND == 'netbox.authentication.LDAPBackend':
|
||||||
from netbox.authentication import LDAPBackend
|
from netbox.authentication import LDAPBackend
|
||||||
ldap_backend = LDAPBackend()
|
ldap_backend = LDAPBackend()
|
||||||
|
|
||||||
|
# Load from LDAP if FIND_GROUP_PERMS is active
|
||||||
|
if ldap_backend.settings.FIND_GROUP_PERMS:
|
||||||
user = ldap_backend.populate_user(token.user.username)
|
user = ldap_backend.populate_user(token.user.username)
|
||||||
# If the user is found in the LDAP directory use it, if not fallback to the local user
|
# If the user is found in the LDAP directory use it, if not fallback to the local user
|
||||||
if user:
|
if user:
|
||||||
|
@ -17,7 +17,7 @@ from django.core.validators import URLValidator
|
|||||||
# Environment setup
|
# Environment setup
|
||||||
#
|
#
|
||||||
|
|
||||||
VERSION = '3.0.11'
|
VERSION = '3.0.12'
|
||||||
|
|
||||||
# Hostname
|
# Hostname
|
||||||
HOSTNAME = platform.node()
|
HOSTNAME = platform.node()
|
||||||
|
BIN
netbox/project-static/dist/netbox.js
vendored
BIN
netbox/project-static/dist/netbox.js
vendored
Binary file not shown.
BIN
netbox/project-static/dist/netbox.js.map
vendored
BIN
netbox/project-static/dist/netbox.js.map
vendored
Binary file not shown.
@ -1,4 +1,32 @@
|
|||||||
import { getElements, scrollTo } from '../util';
|
import { getElements, scrollTo, isTruthy } from '../util';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When editing an object, it is sometimes desirable to customize the form action *without*
|
||||||
|
* overriding the form's `submit` event. For example, the 'Save & Continue' button. We don't want
|
||||||
|
* to use the `formaction` attribute on that element because it will be included on the form even
|
||||||
|
* if the button isn't clicked.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```html
|
||||||
|
* <button type="button" return-url="/special-url/">
|
||||||
|
* Save & Continue
|
||||||
|
* </button>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param event Click event.
|
||||||
|
*/
|
||||||
|
function handleSubmitWithReturnUrl(event: MouseEvent): void {
|
||||||
|
const element = event.target as HTMLElement;
|
||||||
|
if (element.tagName === 'BUTTON') {
|
||||||
|
const button = element as HTMLButtonElement;
|
||||||
|
const action = button.getAttribute('return-url');
|
||||||
|
const form = button.form;
|
||||||
|
if (form !== null && isTruthy(action)) {
|
||||||
|
form.action = action;
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleFormSubmit(event: Event, form: HTMLFormElement): void {
|
function handleFormSubmit(event: Event, form: HTMLFormElement): void {
|
||||||
// Track the names of each invalid field.
|
// Track the names of each invalid field.
|
||||||
@ -38,6 +66,15 @@ function handleFormSubmit(event: Event, form: HTMLFormElement): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach event listeners to form buttons with the `return-url` attribute present.
|
||||||
|
*/
|
||||||
|
function initReturnUrlSubmitButtons(): void {
|
||||||
|
for (const button of getElements<HTMLButtonElement>('button[return-url]')) {
|
||||||
|
button.addEventListener('click', handleSubmitWithReturnUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attach an event listener to each form's submitter (button[type=submit]). When called, the
|
* Attach an event listener to each form's submitter (button[type=submit]). When called, the
|
||||||
* callback checks the validity of each form field and adds the appropriate Bootstrap CSS class
|
* callback checks the validity of each form field and adds the appropriate Bootstrap CSS class
|
||||||
@ -54,4 +91,5 @@ export function initFormElements(): void {
|
|||||||
submitter.addEventListener('click', (event: Event) => handleFormSubmit(event, form));
|
submitter.addEventListener('click', (event: Event) => handleFormSubmit(event, form));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
initReturnUrlSubmitButtons();
|
||||||
}
|
}
|
||||||
|
@ -107,6 +107,9 @@ function initTableFilter(): void {
|
|||||||
// Create a regex pattern from the input search text to match against.
|
// Create a regex pattern from the input search text to match against.
|
||||||
const filter = new RegExp(target.value.toLowerCase().trim());
|
const filter = new RegExp(target.value.toLowerCase().trim());
|
||||||
|
|
||||||
|
// List of which rows which match the query
|
||||||
|
const matchedRows: Array<HTMLTableRowElement> = [];
|
||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
// Find the row's checkbox and deselect it, so that it is not accidentally included in form
|
// Find the row's checkbox and deselect it, so that it is not accidentally included in form
|
||||||
// submissions.
|
// submissions.
|
||||||
@ -114,19 +117,26 @@ function initTableFilter(): void {
|
|||||||
if (checkBox !== null) {
|
if (checkBox !== null) {
|
||||||
checkBox.checked = false;
|
checkBox.checked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate through each row's cell values
|
// Iterate through each row's cell values
|
||||||
for (const value of getRowValues(row)) {
|
for (const value of getRowValues(row)) {
|
||||||
if (filter.test(value.toLowerCase())) {
|
if (filter.test(value.toLowerCase())) {
|
||||||
// If this row matches the search pattern, but is already hidden, unhide it and stop
|
// If this row matches the search pattern, add it to the list.
|
||||||
// iterating through the rest of the cells.
|
matchedRows.push(row);
|
||||||
row.classList.remove('d-none');
|
|
||||||
break;
|
break;
|
||||||
} else {
|
|
||||||
// If none of the cells in this row match the search pattern, hide the row.
|
|
||||||
row.classList.add('d-none');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Iterate the rows again to set visibility.
|
||||||
|
// This results in a single reflow instead of one for each row.
|
||||||
|
for (const row of rows) {
|
||||||
|
if (matchedRows.indexOf(row) >= 0) {
|
||||||
|
row.classList.remove('d-none');
|
||||||
|
} else {
|
||||||
|
row.classList.add('d-none');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
input.addEventListener('keyup', debounce(handleInput, 300));
|
input.addEventListener('keyup', debounce(handleInput, 300));
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
{% block buttons %}
|
{% block buttons %}
|
||||||
<a href="{{ return_url }}" class="btn btn-outline-danger">Cancel</a>
|
<a href="{{ return_url }}" class="btn btn-outline-danger">Cancel</a>
|
||||||
{% if obj.pk %}
|
{% if obj.pk %}
|
||||||
<button type="submit" formaction="?return_url={% url 'dcim:interface_edit' pk=obj.pk %}" class="btn btn-outline-primary">Save & Continue Editing</button>
|
<button type="button" return-url="?return_url={% url 'dcim:interface_edit' pk=obj.pk %}" class="btn btn-outline-primary">Save & Continue Editing</button>
|
||||||
<button type="submit" name="_update" class="btn btn-primary">Save</button>
|
<button type="submit" name="_update" class="btn btn-primary">Save</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
<button type="submit" name="_addanother" class="btn btn-outline-primary">Create & Add Another</button>
|
<button type="submit" name="_addanother" class="btn btn-outline-primary">Create & Add Another</button>
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
{% block buttons %}
|
{% block buttons %}
|
||||||
<a href="{{ return_url }}" class="btn btn-outline-danger">Cancel</a>
|
<a href="{{ return_url }}" class="btn btn-outline-danger">Cancel</a>
|
||||||
{% if obj.pk %}
|
{% if obj.pk %}
|
||||||
<button type="submit" formaction="?return_url={% url 'virtualization:vminterface_edit' pk=obj.pk %}" class="btn btn-outline-primary">Save & Continue Editing</button>
|
<button type="button" return-url="?return_url={% url 'virtualization:vminterface_edit' pk=obj.pk %}" class="btn btn-outline-primary">Save & Continue Editing</button>
|
||||||
<button type="submit" name="_update" class="btn btn-primary">Save</button>
|
<button type="submit" name="_update" class="btn btn-primary">Save</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
<button type="submit" name="_addanother" class="btn btn-outline-primary">Create & Add Another</button>
|
<button type="submit" name="_addanother" class="btn btn-outline-primary">Create & Add Another</button>
|
||||||
|
@ -51,7 +51,7 @@ def render_markdown(value):
|
|||||||
value = re.sub(pattern, '[\\1](\\3)', value, flags=re.IGNORECASE)
|
value = re.sub(pattern, '[\\1](\\3)', value, flags=re.IGNORECASE)
|
||||||
|
|
||||||
# Sanitize Markdown reference links
|
# Sanitize Markdown reference links
|
||||||
pattern = fr'\[(.+)\]:\w?(?!({schemes})).*:(.+)'
|
pattern = fr'\[(.+)\]:\s*(?!({schemes}))\w*:(.+)'
|
||||||
value = re.sub(pattern, '[\\1]: \\3', value, flags=re.IGNORECASE)
|
value = re.sub(pattern, '[\\1]: \\3', value, flags=re.IGNORECASE)
|
||||||
|
|
||||||
# Render Markdown
|
# Render Markdown
|
||||||
|
Loading…
Reference in New Issue
Block a user