mirror of
https://github.com/netbox-community/netbox.git
synced 2026-03-12 05:22:17 +01:00
Compare commits
8 Commits
21579-scri
...
20934-fix-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c7f5a6bd8 | ||
|
|
cac3c1221c | ||
|
|
3a9d00a537 | ||
|
|
4040e4f266 | ||
|
|
f938309ed9 | ||
|
|
98d898aba9 | ||
|
|
07bb6aa365 | ||
|
|
f3c34b30ec |
32
.github/workflows/claude.yml
vendored
32
.github/workflows/claude.yml
vendored
@@ -30,9 +30,39 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
# Workaround for claude-code-action bug with fork PRs: The action fetches by branch name
|
||||
# (git fetch origin --depth=N <branch>), but fork PR branches don't exist on origin.
|
||||
# Fix: redirect origin to the fork's URL so the action can fetch the branch directly.
|
||||
- name: Configure git remote for fork PRs
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
# Determine PR number based on event type
|
||||
if [ "${{ github.event_name }}" = "issue_comment" ]; then
|
||||
PR_NUMBER="${{ github.event.issue.number }}"
|
||||
elif [ "${{ github.event_name }}" = "pull_request_review_comment" ] || [ "${{ github.event_name }}" = "pull_request_review" ]; then
|
||||
PR_NUMBER="${{ github.event.pull_request.number }}"
|
||||
else
|
||||
exit 0 # issues event — no PR branch to worry about
|
||||
fi
|
||||
|
||||
# Fetch fork info in one API call; silently skip if this is not a PR
|
||||
PR_INFO=$(gh pr view "${PR_NUMBER}" --json isCrossRepository,headRepositoryOwner,headRepository 2>/dev/null || echo "")
|
||||
if [ -z "$PR_INFO" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
IS_FORK=$(echo "$PR_INFO" | jq -r '.isCrossRepository')
|
||||
if [ "$IS_FORK" = "true" ]; then
|
||||
FORK_OWNER=$(echo "$PR_INFO" | jq -r '.headRepositoryOwner.login')
|
||||
FORK_REPO=$(echo "$PR_INFO" | jq -r '.headRepository.name')
|
||||
echo "Fork PR detected from ${FORK_OWNER}/${FORK_REPO}: updating origin to fork URL"
|
||||
git remote set-url origin "https://github.com/${FORK_OWNER}/${FORK_REPO}.git"
|
||||
fi
|
||||
|
||||
- name: Run Claude Code
|
||||
id: claude
|
||||
uses: anthropics/claude-code-action@v1
|
||||
uses: anthropics/claude-code-action@e763fe78de2db7389e04818a00b5ff8ba13d1360 # v1
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
|
||||
|
||||
8
.github/workflows/lock-threads.yml
vendored
8
.github/workflows/lock-threads.yml
vendored
@@ -11,14 +11,14 @@ permissions:
|
||||
pull-requests: write
|
||||
discussions: write
|
||||
|
||||
concurrency:
|
||||
group: lock-threads
|
||||
|
||||
jobs:
|
||||
lock:
|
||||
if: github.repository == 'netbox-community/netbox'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1
|
||||
- uses: dessant/lock-threads@v6.0.0
|
||||
with:
|
||||
issue-inactive-days: 90
|
||||
pr-inactive-days: 30
|
||||
discussion-inactive-days: 180
|
||||
issue-lock-reason: 'resolved'
|
||||
|
||||
@@ -405,6 +405,7 @@ class DeviceViewSet(
|
||||
NetBoxModelViewSet
|
||||
):
|
||||
queryset = Device.objects.prefetch_related(
|
||||
'device_type__manufacturer', # Referenced by Device.__str__() for unnamed devices
|
||||
'parent_bay', # Referenced by DeviceSerializer.get_parent_device()
|
||||
)
|
||||
filterset_class = filtersets.DeviceFilterSet
|
||||
|
||||
@@ -218,7 +218,7 @@ class RackReservationTable(TenancyColumnsMixin, PrimaryModelTable):
|
||||
class Meta(PrimaryModelTable.Meta):
|
||||
model = RackReservation
|
||||
fields = (
|
||||
'pk', 'id', 'reservation', 'site', 'location', 'rack', 'unit_list', 'status', 'user', 'created', 'tenant',
|
||||
'pk', 'id', 'reservation', 'site', 'location', 'rack', 'unit_list', 'status', 'user', 'tenant',
|
||||
'tenant_group', 'description', 'comments', 'tags', 'actions', 'created', 'last_updated',
|
||||
)
|
||||
default_columns = ('pk', 'reservation', 'site', 'rack', 'unit_list', 'status', 'user', 'description')
|
||||
|
||||
@@ -10,6 +10,7 @@ __all__ = (
|
||||
'BooleanAttr',
|
||||
'ChoiceAttr',
|
||||
'ColorAttr',
|
||||
'DateTimeAttr',
|
||||
'GPSCoordinatesAttr',
|
||||
'GenericForeignKeyAttr',
|
||||
'ImageAttr',
|
||||
@@ -367,6 +368,26 @@ class GPSCoordinatesAttr(ObjectAttribute):
|
||||
})
|
||||
|
||||
|
||||
class DateTimeAttr(ObjectAttribute):
|
||||
"""
|
||||
A date or datetime attribute.
|
||||
|
||||
Parameters:
|
||||
spec (str): Controls the rendering format. Use 'date' for date-only rendering,
|
||||
or 'seconds'/'minutes' for datetime rendering with the given precision.
|
||||
"""
|
||||
template_name = 'ui/attrs/datetime.html'
|
||||
|
||||
def __init__(self, *args, spec='seconds', **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.spec = spec
|
||||
|
||||
def get_context(self, obj, context):
|
||||
return {
|
||||
'spec': self.spec,
|
||||
}
|
||||
|
||||
|
||||
class TimezoneAttr(ObjectAttribute):
|
||||
"""
|
||||
A timezone value. Includes the numeric offset from UTC.
|
||||
|
||||
2
netbox/project-static/dist/netbox.css
vendored
2
netbox/project-static/dist/netbox.css
vendored
File diff suppressed because one or more lines are too long
2
netbox/project-static/dist/netbox.js
vendored
2
netbox/project-static/dist/netbox.js
vendored
File diff suppressed because one or more lines are too long
6
netbox/project-static/dist/netbox.js.map
vendored
6
netbox/project-static/dist/netbox.js.map
vendored
File diff suppressed because one or more lines are too long
@@ -20,12 +20,7 @@ function storeColorMode(mode: ColorMode): void {
|
||||
}
|
||||
|
||||
function updateElements(targetMode: ColorMode): void {
|
||||
const body = document.querySelector('body');
|
||||
if (body && targetMode == 'dark') {
|
||||
body.setAttribute('data-bs-theme', 'dark');
|
||||
} else if (body) {
|
||||
body.setAttribute('data-bs-theme', 'light');
|
||||
}
|
||||
document.documentElement.setAttribute('data-bs-theme', targetMode);
|
||||
|
||||
for (const elevation of getElements<HTMLObjectElement>('.rack_elevation')) {
|
||||
const svg = elevation.firstElementChild ?? null;
|
||||
|
||||
@@ -112,7 +112,7 @@ img.plugin-icon {
|
||||
}
|
||||
|
||||
|
||||
body[data-bs-theme=dark] {
|
||||
html[data-bs-theme=dark] {
|
||||
// Assuming icon is black/white line art, invert it and tone down brightness
|
||||
img.plugin-icon {
|
||||
filter: grayscale(100%) invert(100%) brightness(80%);
|
||||
|
||||
@@ -93,7 +93,7 @@ pre {
|
||||
}
|
||||
|
||||
// Dark mode overrides
|
||||
body[data-bs-theme=dark] {
|
||||
html[data-bs-theme=dark] {
|
||||
// Override background color alpha value
|
||||
::selection {
|
||||
background-color: rgba(var(--tblr-primary-rgb),.48);
|
||||
@@ -174,16 +174,11 @@ pre code {
|
||||
}
|
||||
|
||||
// Theme-based visibility utilities
|
||||
// Tabler's .hide-theme-* utilities expect data-bs-theme on :root, but NetBox applies
|
||||
// it to body. These overrides use higher specificity selectors to ensure theme-based
|
||||
// visibility works correctly. The :root:not(.dummy) pattern provides the additional
|
||||
// specificity needed to override Tabler's :root:not() rules.
|
||||
:root:not(.dummy) body[data-bs-theme='light'] .hide-theme-light,
|
||||
:root:not(.dummy) body[data-bs-theme='dark'] .hide-theme-dark {
|
||||
:root:not(.dummy)[data-bs-theme='light'] .hide-theme-light,
|
||||
:root:not(.dummy)[data-bs-theme='dark'] .hide-theme-dark {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
:root:not(.dummy) body[data-bs-theme='dark'] .hide-theme-light,
|
||||
:root:not(.dummy) body[data-bs-theme='light'] .hide-theme-dark {
|
||||
:root:not(.dummy)[data-bs-theme='dark'] .hide-theme-light,
|
||||
:root:not(.dummy)[data-bs-theme='light'] .hide-theme-dark {
|
||||
display: inline-flex !important;
|
||||
}
|
||||
|
||||
@@ -77,13 +77,13 @@
|
||||
}
|
||||
|
||||
// Light theme styling
|
||||
body[data-bs-theme=light] .navbar-vertical.navbar-expand-lg {
|
||||
html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg {
|
||||
// Background Gradient
|
||||
background: linear-gradient(180deg, rgba(0, 133, 125, 0.00) 0%, rgba(0, 133, 125, 0.10) 100%), #FFF;
|
||||
}
|
||||
|
||||
// Dark theme styling
|
||||
body[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg {
|
||||
html[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg {
|
||||
|
||||
// Background Gradient
|
||||
background: linear-gradient(180deg, rgba(0, 242, 212, 0.00) 0%, rgba(0, 242, 212, 0.10) 100%), #001423;
|
||||
|
||||
@@ -59,7 +59,7 @@ table th.orderable a {
|
||||
color: var(--#{$prefix}body-color);
|
||||
}
|
||||
|
||||
body[data-bs-theme=dark] {
|
||||
html[data-bs-theme=dark] {
|
||||
// Adjust table header background color
|
||||
.table thead th, .markdown>table thead th {
|
||||
background: $rich-black !important;
|
||||
|
||||
1
netbox/templates/ui/attrs/datetime.html
Normal file
1
netbox/templates/ui/attrs/datetime.html
Normal file
@@ -0,0 +1 @@
|
||||
{% load helpers %}{% if spec == 'date' %}{{ value|isodate }}{% else %}{{ value|isodatetime:spec }}{% endif %}
|
||||
1
netbox/templates/users/attrs/full_name.html
Normal file
1
netbox/templates/users/attrs/full_name.html
Normal file
@@ -0,0 +1 @@
|
||||
{% load helpers %}{{ object.get_full_name|placeholder }}
|
||||
@@ -1,60 +1,3 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load i18n %}
|
||||
{% load helpers %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block title %}{% trans "Group" %} {{ object.name }}{% endblock %}
|
||||
|
||||
{% block subtitle %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Group" %}</h2>
|
||||
<table class="table table-hover attr-table">
|
||||
<tr>
|
||||
<th scope="row">{% trans "Name" %}</th>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Description" %}</th>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Users" %}</h2>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for user in object.users.all %}
|
||||
<a href="{% url 'users:user' pk=user.pk %}" class="list-group-item list-group-item-action">{{ user }}</a>
|
||||
{% empty %}
|
||||
<div class="list-group-item text-muted">{% trans "None" %}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Assigned Permissions" %}</h2>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for perm in object.object_permissions.all %}
|
||||
<a href="{% url 'users:objectpermission' pk=perm.pk %}" class="list-group-item list-group-item-action">{{ perm }}</a>
|
||||
{% empty %}
|
||||
<div class="list-group-item text-muted">{% trans "None" %}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Owner Membership" %}</h2>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for owner in object.owners.all %}
|
||||
<a href="{% url 'users:owner' pk=owner.pk %}" class="list-group-item list-group-item-action">{{ owner }}</a>
|
||||
{% empty %}
|
||||
<div class="list-group-item text-muted">{% trans "None" %}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,93 +1,5 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load i18n %}
|
||||
{% load helpers %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block title %}{% trans "Permission" %} {{ object.name }}{% endblock %}
|
||||
|
||||
{% block subtitle %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Permission" %}</h2>
|
||||
<table class="table table-hover attr-table">
|
||||
<tr>
|
||||
<th scope="row">{% trans "Name" %}</th>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Description" %}</th>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Enabled" %}</th>
|
||||
<td>{% checkmark object.enabled %}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Actions" %}</h2>
|
||||
<table class="table table-hover attr-table">
|
||||
<tr>
|
||||
<th scope="row">{% trans "View" %}</th>
|
||||
<td>{% checkmark object.can_view %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Add" %}</th>
|
||||
<td>{% checkmark object.can_add %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Change" %}</th>
|
||||
<td>{% checkmark object.can_change %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Delete" %}</th>
|
||||
<td>{% checkmark object.can_delete %}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Constraints" %}</h2>
|
||||
<div class="card-body">
|
||||
{% if object.constraints %}
|
||||
<pre>{{ object.constraints|json }}</pre>
|
||||
{% else %}
|
||||
<span class="text-muted">None</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Object Types" %}</h2>
|
||||
<ul class="list-group list-group-flush">
|
||||
{% for user in object.object_types.all %}
|
||||
<li class="list-group-item">{{ user }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Assigned Users" %}</h2>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for user in object.users.all %}
|
||||
<a href="{% url 'users:user' pk=user.pk %}" class="list-group-item list-group-item-action">{{ user }}</a>
|
||||
{% empty %}
|
||||
<div class="list-group-item text-muted">{% trans "None" %}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Assigned Groups" %}</h2>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for group in object.groups.all %}
|
||||
<a href="{% url 'users:group' pk=group.pk %}" class="list-group-item list-group-item-action">{{ group }}</a>
|
||||
{% empty %}
|
||||
<div class="list-group-item text-muted">{% trans "None" %}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -11,50 +11,3 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block subtitle %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Owner" %}</h2>
|
||||
<table class="table table-hover attr-table">
|
||||
<tr>
|
||||
<th scope="row">{% trans "Name" %}</th>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Group" %}</th>
|
||||
<td>{{ object.group|linkify|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Description" %}</th>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Groups" %}</h2>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for group in object.user_groups.all %}
|
||||
<a href="{% url 'users:group' pk=group.pk %}" class="list-group-item list-group-item-action">{{ group }}</a>
|
||||
{% empty %}
|
||||
<div class="list-group-item text-muted">{% trans "None" %}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Users" %}</h2>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for user in object.users.all %}
|
||||
<a href="{% url 'users:user' pk=user.pk %}" class="list-group-item list-group-item-action">{{ user }}</a>
|
||||
{% empty %}
|
||||
<div class="list-group-item text-muted">{% trans "None" %}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% include 'inc/panels/related_objects.html' with filter_name='owner_id' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,46 +1,3 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load i18n %}
|
||||
{% load helpers %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block subtitle %}{% endblock %}
|
||||
|
||||
{% block extra_controls %}
|
||||
{% if perms.users.add_owner %}
|
||||
<a href="{% url 'users:owner_add' %}?group={{ object.pk }}" class="btn btn-primary">
|
||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Owner" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock extra_controls %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Group" %}</h2>
|
||||
<table class="table table-hover attr-table">
|
||||
<tr>
|
||||
<th scope="row">{% trans "Name" %}</th>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Description" %}</th>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Members" %}</h2>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for owner in object.members.all %}
|
||||
<a href="{% url 'users:owner' pk=owner.pk %}" class="list-group-item list-group-item-action">{{ owner }}</a>
|
||||
{% empty %}
|
||||
<div class="list-group-item text-muted">{% trans "None" %}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
11
netbox/templates/users/panels/object_types.html
Normal file
11
netbox/templates/users/panels/object_types.html
Normal file
@@ -0,0 +1,11 @@
|
||||
{% load i18n %}
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Object Types" %}</h2>
|
||||
<ul class="list-group list-group-flush">
|
||||
{% for object_type in object.object_types.all %}
|
||||
<li class="list-group-item">{{ object_type }}</li>
|
||||
{% empty %}
|
||||
<li class="list-group-item text-muted">{% trans "None" %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -1,85 +1,3 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "User" %} {{ object.username }}{% endblock %}
|
||||
|
||||
{% block subtitle %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "User" %}</h2>
|
||||
<table class="table table-hover attr-table">
|
||||
<tr>
|
||||
<th scope="row">{% trans "Username" %}</th>
|
||||
<td>{{ object.username }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Full Name" %}</th>
|
||||
<td>{{ object.get_full_name|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Email" %}</th>
|
||||
<td>{{ object.email|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Account Created" %}</th>
|
||||
<td>{{ object.date_joined|isodate }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Last Login" %}</th>
|
||||
<td>{{ object.last_login|isodatetime:"minutes"|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Active" %}</th>
|
||||
<td>{% checkmark object.is_active %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Superuser" %}</th>
|
||||
<td>{% checkmark object.is_superuser %}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Assigned Groups" %}</h2>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for group in object.groups.all %}
|
||||
<a href="{% url 'users:group' pk=group.pk %}" class="list-group-item list-group-item-action">{{ group }}</a>
|
||||
{% empty %}
|
||||
<div class="list-group-item text-muted">{% trans "None" %}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Assigned Permissions" %}</h2>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for perm in object.object_permissions.all %}
|
||||
<a href="{% url 'users:objectpermission' pk=perm.pk %}" class="list-group-item list-group-item-action">{{ perm }}</a>
|
||||
{% empty %}
|
||||
<div class="list-group-item text-muted">{% trans "None" %}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Owner Membership" %}</h2>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for owner in object.owners.all %}
|
||||
<a href="{% url 'users:owner' pk=owner.pk %}" class="list-group-item list-group-item-action">{{ owner }}</a>
|
||||
{% empty %}
|
||||
<div class="list-group-item text-muted">{% trans "None" %}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if perms.core.view_objectchange %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{% include 'users/inc/user_activity.html' with user=object table=changelog_table %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-03-10 05:18+0000\n"
|
||||
"POT-Creation-Date: 2026-03-11 05:18+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -62,7 +62,7 @@ msgstr ""
|
||||
#: netbox/ipam/choices.py:31 netbox/ipam/choices.py:49
|
||||
#: netbox/ipam/choices.py:69 netbox/ipam/choices.py:154
|
||||
#: netbox/templates/extras/configcontext.html:29
|
||||
#: netbox/templates/users/user.html:35 netbox/users/forms/bulk_edit.py:41
|
||||
#: netbox/users/forms/bulk_edit.py:41 netbox/users/ui/panels.py:38
|
||||
#: netbox/virtualization/choices.py:22 netbox/virtualization/choices.py:45
|
||||
#: netbox/vpn/choices.py:19 netbox/vpn/choices.py:280
|
||||
#: netbox/wireless/choices.py:25
|
||||
@@ -471,7 +471,7 @@ msgstr ""
|
||||
#: netbox/dcim/tables/devicetypes.py:214 netbox/dcim/tables/devicetypes.py:255
|
||||
#: netbox/dcim/tables/devicetypes.py:274 netbox/dcim/tables/racks.py:30
|
||||
#: netbox/extras/forms/bulk_edit.py:306 netbox/extras/tables/tables.py:552
|
||||
#: netbox/netbox/ui/attrs.py:193 netbox/templates/circuits/circuittype.html:30
|
||||
#: netbox/netbox/ui/attrs.py:194 netbox/templates/circuits/circuittype.html:30
|
||||
#: netbox/templates/circuits/virtualcircuittype.html:30
|
||||
#: netbox/templates/dcim/cable.html:44 netbox/templates/dcim/frontport.html:40
|
||||
#: netbox/templates/dcim/inventoryitemrole.html:26
|
||||
@@ -890,10 +890,6 @@ msgstr ""
|
||||
#: netbox/templates/tenancy/contactrole.html:22
|
||||
#: netbox/templates/tenancy/tenant.html:24
|
||||
#: netbox/templates/tenancy/tenantgroup.html:33
|
||||
#: netbox/templates/users/group.html:21
|
||||
#: netbox/templates/users/objectpermission.html:21
|
||||
#: netbox/templates/users/owner.html:30
|
||||
#: netbox/templates/users/ownergroup.html:27
|
||||
#: netbox/templates/vpn/ikepolicy.html:17
|
||||
#: netbox/templates/vpn/ikeproposal.html:17
|
||||
#: netbox/templates/vpn/ipsecpolicy.html:17
|
||||
@@ -1344,9 +1340,6 @@ msgstr ""
|
||||
#: netbox/templates/ipam/inc/panels/fhrp_groups.html:23
|
||||
#: netbox/templates/ipam/panels/fhrp_groups.html:9
|
||||
#: netbox/templates/ipam/vlan.html:27 netbox/templates/tenancy/tenant.html:20
|
||||
#: netbox/templates/users/group.html:6 netbox/templates/users/group.html:14
|
||||
#: netbox/templates/users/owner.html:26
|
||||
#: netbox/templates/users/ownergroup.html:20
|
||||
#: netbox/templates/vpn/tunnel.html:29
|
||||
#: netbox/templates/wireless/wirelesslan.html:18
|
||||
#: netbox/tenancy/forms/bulk_edit.py:44 netbox/tenancy/forms/bulk_import.py:46
|
||||
@@ -1743,10 +1736,6 @@ msgstr ""
|
||||
#: netbox/templates/tenancy/contactgroup.html:21
|
||||
#: netbox/templates/tenancy/contactrole.html:18
|
||||
#: netbox/templates/tenancy/tenantgroup.html:29
|
||||
#: netbox/templates/users/group.html:17
|
||||
#: netbox/templates/users/objectpermission.html:17
|
||||
#: netbox/templates/users/owner.html:22
|
||||
#: netbox/templates/users/ownergroup.html:23
|
||||
#: netbox/templates/vpn/ikepolicy.html:13
|
||||
#: netbox/templates/vpn/ikeproposal.html:13
|
||||
#: netbox/templates/vpn/ipsecpolicy.html:13
|
||||
@@ -2117,8 +2106,7 @@ msgid "Local"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/data_backends.py:64 netbox/core/tables/change_logging.py:21
|
||||
#: netbox/templates/account/profile.html:13 netbox/templates/users/user.html:15
|
||||
#: netbox/users/tables.py:64
|
||||
#: netbox/templates/account/profile.html:13 netbox/users/tables.py:64
|
||||
msgid "Username"
|
||||
msgstr ""
|
||||
|
||||
@@ -2189,7 +2177,6 @@ msgstr ""
|
||||
#: netbox/templates/extras/eventrule.html:17
|
||||
#: netbox/templates/extras/savedfilter.html:25
|
||||
#: netbox/templates/extras/tableconfig.html:33
|
||||
#: netbox/templates/users/objectpermission.html:25
|
||||
#: netbox/users/forms/bulk_edit.py:87 netbox/users/forms/bulk_edit.py:105
|
||||
#: netbox/users/forms/filtersets.py:67 netbox/users/forms/filtersets.py:133
|
||||
#: netbox/users/tables.py:30 netbox/users/tables.py:113
|
||||
@@ -2302,8 +2289,7 @@ msgstr ""
|
||||
#: netbox/templates/core/objectchange.html:36
|
||||
#: netbox/templates/extras/savedfilter.html:21
|
||||
#: netbox/templates/extras/tableconfig.html:29
|
||||
#: netbox/templates/inc/user_menu.html:31 netbox/templates/users/user.html:4
|
||||
#: netbox/templates/users/user.html:12 netbox/users/filtersets.py:135
|
||||
#: netbox/templates/inc/user_menu.html:31 netbox/users/filtersets.py:135
|
||||
#: netbox/users/filtersets.py:217 netbox/users/forms/filtersets.py:81
|
||||
#: netbox/users/forms/filtersets.py:126 netbox/users/forms/model_forms.py:181
|
||||
#: netbox/users/forms/model_forms.py:221 netbox/users/tables.py:22
|
||||
@@ -2747,7 +2733,7 @@ msgid "Deletion is prevented by a protection rule: {message}"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/tables/change_logging.py:26
|
||||
#: netbox/templates/account/profile.html:17 netbox/templates/users/user.html:19
|
||||
#: netbox/templates/account/profile.html:17
|
||||
msgid "Full Name"
|
||||
msgstr ""
|
||||
|
||||
@@ -5963,8 +5949,7 @@ msgstr ""
|
||||
#: netbox/dcim/forms/object_create.py:312 netbox/dcim/tables/devices.py:1130
|
||||
#: netbox/ipam/tables/fhrp.py:31 netbox/templates/dcim/virtualchassis.html:43
|
||||
#: netbox/templates/dcim/virtualchassis_edit.html:59
|
||||
#: netbox/templates/ipam/fhrpgroup.html:38
|
||||
#: netbox/templates/users/ownergroup.html:35
|
||||
#: netbox/templates/ipam/fhrpgroup.html:38 netbox/users/views.py:347
|
||||
msgid "Members"
|
||||
msgstr ""
|
||||
|
||||
@@ -8828,7 +8813,6 @@ msgstr ""
|
||||
#: netbox/extras/forms/bulk_import.py:318
|
||||
#: netbox/extras/forms/model_forms.py:414 netbox/netbox/navigation/menu.py:415
|
||||
#: netbox/templates/extras/notificationgroup.html:41
|
||||
#: netbox/templates/users/group.html:29 netbox/templates/users/owner.html:46
|
||||
#: netbox/users/forms/filtersets.py:181 netbox/users/forms/model_forms.py:265
|
||||
#: netbox/users/forms/model_forms.py:277 netbox/users/forms/model_forms.py:352
|
||||
#: netbox/users/forms/model_forms.py:483 netbox/users/forms/model_forms.py:498
|
||||
@@ -8845,8 +8829,7 @@ msgstr ""
|
||||
#: netbox/netbox/navigation/menu.py:416
|
||||
#: netbox/templates/extras/notificationgroup.html:31
|
||||
#: netbox/templates/tenancy/contact.html:21
|
||||
#: netbox/templates/users/owner.html:36 netbox/tenancy/forms/bulk_edit.py:121
|
||||
#: netbox/tenancy/forms/filtersets.py:107
|
||||
#: netbox/tenancy/forms/bulk_edit.py:121 netbox/tenancy/forms/filtersets.py:107
|
||||
#: netbox/tenancy/forms/model_forms.py:93 netbox/tenancy/tables/contacts.py:57
|
||||
#: netbox/tenancy/tables/contacts.py:101 netbox/users/forms/filtersets.py:176
|
||||
#: netbox/users/forms/model_forms.py:210 netbox/users/forms/model_forms.py:222
|
||||
@@ -10008,7 +9991,7 @@ msgstr ""
|
||||
#: netbox/extras/tables/tables.py:517 netbox/extras/tables/tables.py:555
|
||||
#: netbox/templates/extras/customfield.html:105
|
||||
#: netbox/templates/extras/eventrule.html:27
|
||||
#: netbox/templates/users/objectpermission.html:64 netbox/users/tables.py:110
|
||||
#: netbox/templates/users/panels/object_types.html:3 netbox/users/tables.py:110
|
||||
msgid "Object Types"
|
||||
msgstr ""
|
||||
|
||||
@@ -10059,7 +10042,7 @@ msgstr ""
|
||||
#: netbox/netbox/forms/mixins.py:162 netbox/netbox/forms/mixins.py:187
|
||||
#: netbox/netbox/tables/tables.py:292 netbox/netbox/tables/tables.py:307
|
||||
#: netbox/netbox/tables/tables.py:322 netbox/templates/generic/object.html:61
|
||||
#: netbox/templates/users/owner.html:19 netbox/users/forms/model_forms.py:481
|
||||
#: netbox/users/forms/model_forms.py:481
|
||||
msgid "Owner"
|
||||
msgstr ""
|
||||
|
||||
@@ -12506,7 +12489,7 @@ msgstr ""
|
||||
#: netbox/templates/dcim/manufacturer.html:8
|
||||
#: netbox/templates/extras/tableconfig_edit.html:29
|
||||
#: netbox/templates/generic/bulk_add_component.html:22
|
||||
#: netbox/templates/users/objectpermission.html:38
|
||||
#: netbox/users/ui/panels.py:52
|
||||
#: netbox/utilities/templates/helpers/table_config_form.html:20
|
||||
#: netbox/utilities/templates/widgets/splitmultiselect.html:11
|
||||
#: netbox/utilities/templatetags/buttons.py:175
|
||||
@@ -12543,8 +12526,7 @@ msgstr ""
|
||||
#: netbox/templates/htmx/delete_form.html:70
|
||||
#: netbox/templates/ipam/inc/panels/fhrp_groups.html:48
|
||||
#: netbox/templates/ipam/panels/fhrp_groups.html:34
|
||||
#: netbox/templates/users/objectpermission.html:46
|
||||
#: netbox/utilities/templatetags/buttons.py:146
|
||||
#: netbox/users/ui/panels.py:54 netbox/utilities/templatetags/buttons.py:146
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
@@ -12801,13 +12783,13 @@ msgstr ""
|
||||
msgid "Copy"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/netbox/ui/attrs.py:212
|
||||
#: netbox/netbox/ui/attrs.py:213
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Invalid decoding option: {decoding}! Must be one of {image_decoding_choices}"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/netbox/ui/attrs.py:343
|
||||
#: netbox/netbox/ui/attrs.py:344
|
||||
msgid "GPS coordinates"
|
||||
msgstr ""
|
||||
|
||||
@@ -13077,26 +13059,25 @@ msgid "Account Details"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/templates/account/profile.html:27
|
||||
#: netbox/templates/tenancy/contact.html:53 netbox/templates/users/user.html:23
|
||||
#: netbox/templates/tenancy/contact.html:53
|
||||
#: netbox/tenancy/forms/bulk_edit.py:104
|
||||
msgid "Email"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/templates/account/profile.html:31 netbox/templates/users/user.html:27
|
||||
#: netbox/templates/account/profile.html:31
|
||||
msgid "Account Created"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/templates/account/profile.html:35 netbox/templates/users/user.html:31
|
||||
#: netbox/templates/account/profile.html:35
|
||||
msgid "Last Login"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/templates/account/profile.html:39 netbox/templates/users/user.html:39
|
||||
#: netbox/templates/account/profile.html:39 netbox/users/ui/panels.py:39
|
||||
msgid "Superuser"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/templates/account/profile.html:47
|
||||
#: netbox/templates/users/objectpermission.html:82
|
||||
#: netbox/templates/users/user.html:47
|
||||
#: netbox/templates/account/profile.html:47 netbox/users/views.py:104
|
||||
#: netbox/users/views.py:283
|
||||
msgid "Assigned Groups"
|
||||
msgstr ""
|
||||
|
||||
@@ -13126,14 +13107,7 @@ msgstr ""
|
||||
#: netbox/templates/ipam/panels/fhrp_groups.html:42
|
||||
#: netbox/templates/ui/panels/comments.html:9
|
||||
#: netbox/templates/ui/panels/related_objects.html:22
|
||||
#: netbox/templates/users/group.html:34 netbox/templates/users/group.html:44
|
||||
#: netbox/templates/users/group.html:54
|
||||
#: netbox/templates/users/objectpermission.html:77
|
||||
#: netbox/templates/users/objectpermission.html:87
|
||||
#: netbox/templates/users/owner.html:41 netbox/templates/users/owner.html:51
|
||||
#: netbox/templates/users/ownergroup.html:40
|
||||
#: netbox/templates/users/user.html:52 netbox/templates/users/user.html:62
|
||||
#: netbox/templates/users/user.html:72
|
||||
#: netbox/templates/users/panels/object_types.html:8
|
||||
msgid "None"
|
||||
msgstr ""
|
||||
|
||||
@@ -13465,8 +13439,7 @@ msgstr ""
|
||||
msgid "every %(interval)s minutes"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/templates/core/objectchange.html:29
|
||||
#: netbox/templates/users/objectpermission.html:42
|
||||
#: netbox/templates/core/objectchange.html:29 netbox/users/ui/panels.py:53
|
||||
msgid "Change"
|
||||
msgstr ""
|
||||
|
||||
@@ -14252,8 +14225,8 @@ msgstr ""
|
||||
|
||||
#: netbox/templates/dcim/virtualchassis_add_member.html:27
|
||||
#: netbox/templates/generic/object_edit.html:78
|
||||
#: netbox/templates/users/objectpermission.html:31
|
||||
#: netbox/users/forms/filtersets.py:64 netbox/users/forms/model_forms.py:373
|
||||
#: netbox/users/ui/panels.py:49
|
||||
msgid "Actions"
|
||||
msgstr ""
|
||||
|
||||
@@ -15392,45 +15365,19 @@ msgstr ""
|
||||
msgid "Local time"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/templates/users/group.html:39 netbox/templates/users/user.html:57
|
||||
msgid "Assigned Permissions"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/templates/users/group.html:49 netbox/templates/users/user.html:67
|
||||
msgid "Owner Membership"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/templates/users/inc/user_activity.html:6
|
||||
#: netbox/templates/users/inc/user_activity.html:6 netbox/users/views.py:118
|
||||
msgid "Recent Activity"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/templates/users/inc/user_activity.html:9
|
||||
#: netbox/templates/users/inc/user_activity.html:9 netbox/users/views.py:123
|
||||
msgid "View All"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/templates/users/objectpermission.html:6
|
||||
#: netbox/templates/users/objectpermission.html:14
|
||||
#: netbox/templates/users/objectpermission.html:4
|
||||
#: netbox/users/forms/filtersets.py:63
|
||||
msgid "Permission"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/templates/users/objectpermission.html:34
|
||||
msgid "View"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/templates/users/objectpermission.html:52
|
||||
#: netbox/users/forms/model_forms.py:363 netbox/users/forms/model_forms.py:376
|
||||
msgid "Constraints"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/templates/users/objectpermission.html:72
|
||||
msgid "Assigned Users"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/templates/users/ownergroup.html:11
|
||||
msgid "Add Owner"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/templates/users/token.html:4 netbox/users/forms/bulk_import.py:48
|
||||
#: netbox/users/forms/filtersets.py:117 netbox/users/forms/model_forms.py:127
|
||||
msgid "Token"
|
||||
@@ -15987,6 +15934,11 @@ msgstr ""
|
||||
msgid "Actions granted in addition to those listed above"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/users/forms/model_forms.py:363 netbox/users/forms/model_forms.py:376
|
||||
#: netbox/users/views.py:275
|
||||
msgid "Constraints"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/users/forms/model_forms.py:365
|
||||
msgid ""
|
||||
"JSON expression of a queryset filter that will return only permitted "
|
||||
@@ -16206,6 +16158,34 @@ msgstr ""
|
||||
msgid "Example Usage"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/users/ui/panels.py:32
|
||||
msgid "Full name"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/users/ui/panels.py:36
|
||||
msgid "Account created"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/users/ui/panels.py:37
|
||||
msgid "Last login"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/users/ui/panels.py:51
|
||||
msgid "View"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/users/views.py:108 netbox/users/views.py:206
|
||||
msgid "Assigned Permissions"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/users/views.py:112 netbox/users/views.py:210
|
||||
msgid "Owner Membership"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/users/views.py:280
|
||||
msgid "Assigned Users"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/utilities/api.py:184
|
||||
#, python-brace-format
|
||||
msgid "Related object not found using the provided attributes: {params}"
|
||||
|
||||
@@ -23,3 +23,38 @@ class TokenExamplePanel(panels.Panel):
|
||||
actions = [
|
||||
actions.CopyContent('token-example')
|
||||
]
|
||||
|
||||
|
||||
class UserPanel(panels.ObjectAttributesPanel):
|
||||
username = attrs.TextAttr('username')
|
||||
full_name = attrs.TemplatedAttr(
|
||||
'get_full_name',
|
||||
label=_('Full name'),
|
||||
template_name='users/attrs/full_name.html',
|
||||
)
|
||||
email = attrs.TextAttr('email')
|
||||
date_joined = attrs.DateTimeAttr('date_joined', label=_('Account created'), spec='date')
|
||||
last_login = attrs.DateTimeAttr('last_login', label=_('Last login'), spec='minutes')
|
||||
is_active = attrs.BooleanAttr('is_active', label=_('Active'))
|
||||
is_superuser = attrs.BooleanAttr('is_superuser', label=_('Superuser'))
|
||||
|
||||
|
||||
class ObjectPermissionPanel(panels.ObjectAttributesPanel):
|
||||
name = attrs.TextAttr('name')
|
||||
description = attrs.TextAttr('description')
|
||||
enabled = attrs.BooleanAttr('enabled')
|
||||
|
||||
|
||||
class ObjectPermissionActionsPanel(panels.ObjectAttributesPanel):
|
||||
title = _('Actions')
|
||||
|
||||
can_view = attrs.BooleanAttr('can_view', label=_('View'))
|
||||
can_add = attrs.BooleanAttr('can_add', label=_('Add'))
|
||||
can_change = attrs.BooleanAttr('can_change', label=_('Change'))
|
||||
can_delete = attrs.BooleanAttr('can_delete', label=_('Delete'))
|
||||
|
||||
|
||||
class OwnerPanel(panels.ObjectAttributesPanel):
|
||||
name = attrs.TextAttr('name')
|
||||
group = attrs.RelatedObjectAttr('group', linkify=True)
|
||||
description = attrs.TextAttr('description')
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
from django.db.models import Count
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from core.models import ObjectChange
|
||||
from core.tables import ObjectChangeTable
|
||||
from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport, BulkRename
|
||||
from netbox.ui import layout
|
||||
from netbox.ui import actions, layout
|
||||
from netbox.ui.panels import (
|
||||
ContextTablePanel,
|
||||
JSONPanel,
|
||||
ObjectsTablePanel,
|
||||
OrganizationalObjectPanel,
|
||||
RelatedObjectsPanel,
|
||||
TemplatePanel,
|
||||
)
|
||||
from netbox.views import generic
|
||||
from users.ui import panels
|
||||
from utilities.query import count_related
|
||||
@@ -86,7 +95,39 @@ class UserListView(generic.ObjectListView):
|
||||
@register_model_view(User)
|
||||
class UserView(generic.ObjectView):
|
||||
queryset = User.objects.all()
|
||||
template_name = 'users/user.html'
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
panels.UserPanel(),
|
||||
],
|
||||
right_panels=[
|
||||
ObjectsTablePanel(
|
||||
'users.Group', title=_('Assigned Groups'), filters={'user_id': lambda ctx: ctx['object'].pk}
|
||||
),
|
||||
ObjectsTablePanel(
|
||||
'users.ObjectPermission',
|
||||
title=_('Assigned Permissions'),
|
||||
filters={'user_id': lambda ctx: ctx['object'].pk},
|
||||
),
|
||||
ObjectsTablePanel(
|
||||
'users.Owner', title=_('Owner Membership'), filters={'user_id': lambda ctx: ctx['object'].pk}
|
||||
),
|
||||
],
|
||||
bottom_panels=[
|
||||
ContextTablePanel(
|
||||
'changelog_table',
|
||||
title=_('Recent Activity'),
|
||||
actions=[
|
||||
actions.LinkAction(
|
||||
view_name='core:objectchange_list',
|
||||
url_params={'user_id': lambda ctx: ctx['object'].pk},
|
||||
label=_('View All'),
|
||||
button_icon='arrow-right-thick',
|
||||
permissions=['core.view_objectchange'],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
changelog = ObjectChange.objects.valid_models().restrict(request.user, 'view').filter(user=instance)[:20]
|
||||
@@ -154,7 +195,22 @@ class GroupListView(generic.ObjectListView):
|
||||
@register_model_view(Group)
|
||||
class GroupView(generic.ObjectView):
|
||||
queryset = Group.objects.all()
|
||||
template_name = 'users/group.html'
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
OrganizationalObjectPanel(),
|
||||
],
|
||||
right_panels=[
|
||||
ObjectsTablePanel('users.User', filters={'group_id': lambda ctx: ctx['object'].pk}),
|
||||
ObjectsTablePanel(
|
||||
'users.ObjectPermission',
|
||||
title=_('Assigned Permissions'),
|
||||
filters={'group_id': lambda ctx: ctx['object'].pk},
|
||||
),
|
||||
ObjectsTablePanel(
|
||||
'users.Owner', title=_('Owner Membership'), filters={'user_group_id': lambda ctx: ctx['object'].pk}
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@register_model_view(Group, 'add', detail=False)
|
||||
@@ -212,7 +268,22 @@ class ObjectPermissionListView(generic.ObjectListView):
|
||||
@register_model_view(ObjectPermission)
|
||||
class ObjectPermissionView(generic.ObjectView):
|
||||
queryset = ObjectPermission.objects.all()
|
||||
template_name = 'users/objectpermission.html'
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
panels.ObjectPermissionPanel(),
|
||||
panels.ObjectPermissionActionsPanel(),
|
||||
JSONPanel('constraints', title=_('Constraints')),
|
||||
],
|
||||
right_panels=[
|
||||
TemplatePanel('users/panels/object_types.html'),
|
||||
ObjectsTablePanel(
|
||||
'users.User', title=_('Assigned Users'), filters={'permission_id': lambda ctx: ctx['object'].pk}
|
||||
),
|
||||
ObjectsTablePanel(
|
||||
'users.Group', title=_('Assigned Groups'), filters={'permission_id': lambda ctx: ctx['object'].pk}
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@register_model_view(ObjectPermission, 'add', detail=False)
|
||||
@@ -255,7 +326,7 @@ class ObjectPermissionBulkDeleteView(generic.BulkDeleteView):
|
||||
@register_model_view(OwnerGroup, 'list', path='', detail=False)
|
||||
class OwnerGroupListView(generic.ObjectListView):
|
||||
queryset = OwnerGroup.objects.annotate(
|
||||
owner_count=count_related(Owner, 'group')
|
||||
owner_count=count_related(Owner, 'group')
|
||||
)
|
||||
filterset = filtersets.OwnerGroupFilterSet
|
||||
filterset_form = forms.OwnerGroupFilterForm
|
||||
@@ -263,14 +334,26 @@ class OwnerGroupListView(generic.ObjectListView):
|
||||
|
||||
|
||||
@register_model_view(OwnerGroup)
|
||||
class OwnerGroupView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
class OwnerGroupView(generic.ObjectView):
|
||||
queryset = OwnerGroup.objects.all()
|
||||
template_name = 'users/ownergroup.html'
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
return {
|
||||
'related_models': self.get_related_models(request, instance),
|
||||
}
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
OrganizationalObjectPanel(),
|
||||
],
|
||||
right_panels=[
|
||||
ObjectsTablePanel(
|
||||
'users.Owner',
|
||||
filters={'group_id': lambda ctx: ctx['object'].pk},
|
||||
title=_('Members'),
|
||||
actions=[
|
||||
actions.AddObject(
|
||||
'users.Owner',
|
||||
url_params={'group': lambda ctx: ctx['object'].pk},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@register_model_view(OwnerGroup, 'add', detail=False)
|
||||
@@ -326,7 +409,16 @@ class OwnerListView(generic.ObjectListView):
|
||||
@register_model_view(Owner)
|
||||
class OwnerView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
queryset = Owner.objects.all()
|
||||
template_name = 'users/owner.html'
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
panels.OwnerPanel(),
|
||||
ObjectsTablePanel('users.Group', filters={'owner_id': lambda ctx: ctx['object'].pk}),
|
||||
ObjectsTablePanel('users.User', filters={'owner_id': lambda ctx: ctx['object'].pk}),
|
||||
],
|
||||
right_panels=[
|
||||
RelatedObjectsPanel(),
|
||||
],
|
||||
)
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user