diff --git a/netbox/extras/tests/test_views.py b/netbox/extras/tests/test_views.py
index acfdcf1e3..49b256b0a 100644
--- a/netbox/extras/tests/test_views.py
+++ b/netbox/extras/tests/test_views.py
@@ -261,7 +261,7 @@ class BookmarkTestCase(
def _get_url(self, action, instance=None):
if action == 'list':
- return reverse('users:bookmarks')
+ return reverse('account:bookmarks')
return super()._get_url(action, instance)
def test_list_objects_anonymous(self):
diff --git a/netbox/netbox/urls.py b/netbox/netbox/urls.py
index 4162fd382..e44e9e08e 100644
--- a/netbox/netbox/urls.py
+++ b/netbox/netbox/urls.py
@@ -32,10 +32,13 @@ _patterns = [
path('extras/', include('extras.urls')),
path('ipam/', include('ipam.urls')),
path('tenancy/', include('tenancy.urls')),
- path('user/', include('users.urls')),
+ path('users/', include('users.urls')),
path('virtualization/', include('virtualization.urls')),
path('wireless/', include('wireless.urls')),
+ # Current user views
+ path('user/', include('users.account_urls')),
+
# HTMX views
path('htmx/object-selector/', htmx.ObjectSelectorView.as_view(), name='htmx_object_selector'),
diff --git a/netbox/templates/inc/profile_button.html b/netbox/templates/inc/profile_button.html
index a5d8cef61..c21b59e3b 100644
--- a/netbox/templates/inc/profile_button.html
+++ b/netbox/templates/inc/profile_button.html
@@ -19,22 +19,22 @@
{% endif %}
-
+
Profile
-
+
Bookmarks
-
+
Preferences
-
+
API Tokens
diff --git a/netbox/templates/users/account/base.html b/netbox/templates/users/account/base.html
index 9ac61bced..6c1e9f028 100644
--- a/netbox/templates/users/account/base.html
+++ b/netbox/templates/users/account/base.html
@@ -4,21 +4,21 @@
{% block tabs %}
{% endblock %}
diff --git a/netbox/templates/users/account/bookmarks.html b/netbox/templates/users/account/bookmarks.html
index fa3c28c7c..3867e7cdb 100644
--- a/netbox/templates/users/account/bookmarks.html
+++ b/netbox/templates/users/account/bookmarks.html
@@ -9,7 +9,7 @@
diff --git a/netbox/templates/users/account/preferences.html b/netbox/templates/users/account/preferences.html
index 59cca302c..0fdafb6f5 100644
--- a/netbox/templates/users/account/preferences.html
+++ b/netbox/templates/users/account/preferences.html
@@ -79,7 +79,7 @@
diff --git a/netbox/templates/users/account/token.html b/netbox/templates/users/account/token.html
index 6df1d1367..d83e13ff5 100644
--- a/netbox/templates/users/account/token.html
+++ b/netbox/templates/users/account/token.html
@@ -5,7 +5,7 @@
{% load plugins %}
{% block breadcrumbs %}
- {% trans "My API Tokens" %}
+ {% trans "My API Tokens" %}
{% endblock breadcrumbs %}
{% block title %}{% trans "Token" %} {{ object }}{% endblock %}
diff --git a/netbox/templates/users/account/token_list.html b/netbox/templates/users/account/token_list.html
index e30b1ae96..9865cbe7c 100644
--- a/netbox/templates/users/account/token_list.html
+++ b/netbox/templates/users/account/token_list.html
@@ -7,7 +7,7 @@
{% block content %}
diff --git a/netbox/users/account_urls.py b/netbox/users/account_urls.py
new file mode 100644
index 000000000..bcb031003
--- /dev/null
+++ b/netbox/users/account_urls.py
@@ -0,0 +1,18 @@
+from django.urls import include, path
+
+from utilities.urls import get_model_urls
+from . import views
+
+app_name = 'account'
+urlpatterns = [
+
+ # Account views
+ path('profile/', views.ProfileView.as_view(), name='profile'),
+ path('bookmarks/', views.BookmarkListView.as_view(), name='bookmarks'),
+ path('preferences/', views.UserConfigView.as_view(), name='preferences'),
+ path('password/', views.ChangePasswordView.as_view(), name='change_password'),
+ path('api-tokens/', views.UserTokenListView.as_view(), name='usertoken_list'),
+ path('api-tokens/add/', views.UserTokenEditView.as_view(), name='usertoken_add'),
+ path('api-tokens/
/', include(get_model_urls('users', 'usertoken'))),
+
+]
diff --git a/netbox/users/models.py b/netbox/users/models.py
index 71434d5ce..0c95559ff 100644
--- a/netbox/users/models.py
+++ b/netbox/users/models.py
@@ -331,7 +331,7 @@ class UserToken(Token):
verbose_name = 'token'
def get_absolute_url(self):
- return reverse('users:usertoken', args=[self.pk])
+ return reverse('account:usertoken', args=[self.pk])
#
diff --git a/netbox/users/urls.py b/netbox/users/urls.py
index ed3db4661..210d8a2c7 100644
--- a/netbox/users/urls.py
+++ b/netbox/users/urls.py
@@ -6,15 +6,6 @@ from . import views
app_name = 'users'
urlpatterns = [
- # Account views
- path('profile/', views.ProfileView.as_view(), name='profile'),
- path('bookmarks/', views.BookmarkListView.as_view(), name='bookmarks'),
- path('preferences/', views.UserConfigView.as_view(), name='preferences'),
- path('password/', views.ChangePasswordView.as_view(), name='change_password'),
- path('api-tokens/', views.UserTokenListView.as_view(), name='usertoken_list'),
- path('api-tokens/add/', views.UserTokenEditView.as_view(), name='usertoken_add'),
- path('api-tokens//', include(get_model_urls('users', 'usertoken'))),
-
# Tokens
path('tokens/', views.TokenListView.as_view(), name='token_list'),
path('tokens/add/', views.TokenEditView.as_view(), name='token_add'),
diff --git a/netbox/users/views.py b/netbox/users/views.py
index bc5cf1eeb..3796d9af1 100644
--- a/netbox/users/views.py
+++ b/netbox/users/views.py
@@ -193,7 +193,7 @@ class UserConfigView(LoginRequiredMixin, View):
form.save()
messages.success(request, "Your preferences have been updated.")
- return redirect('users:preferences')
+ return redirect('account:preferences')
return render(request, self.template_name, {
'form': form,
@@ -208,7 +208,7 @@ class ChangePasswordView(LoginRequiredMixin, View):
# LDAP users cannot change their password here
if getattr(request.user, 'ldap_username', None):
messages.warning(request, "LDAP-authenticated user credentials cannot be changed within NetBox.")
- return redirect('users:profile')
+ return redirect('account:profile')
form = forms.PasswordChangeForm(user=request.user)
@@ -223,7 +223,7 @@ class ChangePasswordView(LoginRequiredMixin, View):
form.save()
update_session_auth_hash(request, form.user)
messages.success(request, "Your password has been changed successfully.")
- return redirect('users:profile')
+ return redirect('account:profile')
return render(request, self.template_name, {
'form': form,
@@ -292,7 +292,7 @@ class UserTokenEditView(LoginRequiredMixin, View):
return render(request, 'generic/object_edit.html', {
'object': token,
'form': form,
- 'return_url': reverse('users:usertoken_list'),
+ 'return_url': reverse('account:usertoken_list'),
})
def post(self, request, pk=None):
@@ -320,12 +320,12 @@ class UserTokenEditView(LoginRequiredMixin, View):
elif '_addanother' in request.POST:
return redirect(request.path)
else:
- return redirect('users:usertoken_list')
+ return redirect('account:usertoken_list')
return render(request, 'generic/object_edit.html', {
'object': token,
'form': form,
- 'return_url': reverse('users:usertoken_list'),
+ 'return_url': reverse('account:usertoken_list'),
'disable_addanother': not settings.ALLOW_TOKEN_RETRIEVAL
})
@@ -339,7 +339,7 @@ class UserTokenDeleteView(LoginRequiredMixin, View):
return render(request, 'generic/object_delete.html', {
'object': token,
'form': ConfirmationForm(),
- 'return_url': reverse('users:usertoken_list'),
+ 'return_url': reverse('account:usertoken_list'),
})
def post(self, request, pk):
@@ -349,12 +349,12 @@ class UserTokenDeleteView(LoginRequiredMixin, View):
if form.is_valid():
token.delete()
messages.success(request, "Token deleted")
- return redirect('users:usertoken_list')
+ return redirect('account:usertoken_list')
return render(request, 'generic/object_delete.html', {
'object': token,
'form': form,
- 'return_url': reverse('users:usertoken_list'),
+ 'return_url': reverse('account:usertoken_list'),
})