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

Merge branch 'develop' into feature

This commit is contained in:
jeremystretch
2021-12-06 12:10:31 -05:00
13 changed files with 98 additions and 25 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -1,6 +1,19 @@
# NetBox v3.0 # NetBox v3.0
## v3.0.12 (FUTURE) ## 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
--- ---

View File

@@ -331,6 +331,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'
@@ -436,6 +437,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'),
@@ -552,6 +554,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'
@@ -571,6 +574,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'
@@ -649,6 +653,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'),
@@ -670,6 +675,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'),

View File

@@ -98,6 +98,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
) )

View File

@@ -29,10 +29,13 @@ 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()
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 # Load from LDAP if FIND_GROUP_PERMS is active
if user: if ldap_backend.settings.FIND_GROUP_PERMS:
return user, token 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 user:
return user, token
return token.user, token return token.user, token

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -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();
} }

View File

@@ -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));
} }

View File

@@ -86,7 +86,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>

View File

@@ -53,7 +53,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>

View File

@@ -53,7 +53,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