From c2c9f73e0bee690d3e0fd0ad4f808d4636129cea Mon Sep 17 00:00:00 2001 From: Paige Williams Date: Mon, 13 Apr 2026 10:09:33 -0700 Subject: [PATCH 01/14] delete login templates --- TEKDB/login/templates/create.html | 21 ----------- TEKDB/login/templates/forgot.html | 36 ------------------- .../registration/registration_form.html | 11 ------ 3 files changed, 68 deletions(-) delete mode 100644 TEKDB/login/templates/create.html delete mode 100644 TEKDB/login/templates/forgot.html delete mode 100644 TEKDB/login/templates/registration/registration_form.html diff --git a/TEKDB/login/templates/create.html b/TEKDB/login/templates/create.html deleted file mode 100644 index 78bc3a27..00000000 --- a/TEKDB/login/templates/create.html +++ /dev/null @@ -1,21 +0,0 @@ -
-
- Avatar -
- -
- - - - - - - - Remember me -
- -
- - Forgot password? -
-
diff --git a/TEKDB/login/templates/forgot.html b/TEKDB/login/templates/forgot.html deleted file mode 100644 index 9f19422e..00000000 --- a/TEKDB/login/templates/forgot.html +++ /dev/null @@ -1,36 +0,0 @@ -
-
-
-
-
-
-
-
-

-

Forgot Password?

-

You can reset your password here.

-
- -
-
-
-
- - - -
-
-
- -
-
-
- -
-
-
-
-
-
-
-
diff --git a/TEKDB/login/templates/registration/registration_form.html b/TEKDB/login/templates/registration/registration_form.html deleted file mode 100644 index 78dc5d6a..00000000 --- a/TEKDB/login/templates/registration/registration_form.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "base.html" %} - -{% block content %} -
- {% csrf_token %} - {{ form.as_p }} -
- -
-
-{% endblock %} From 4e73b9c54305eb48396f3446595d678c6221ae7a Mon Sep 17 00:00:00 2001 From: Paige Williams Date: Mon, 13 Apr 2026 11:51:01 -0700 Subject: [PATCH 02/14] change password in modal --- TEKDB/TEKDB/urls.py | 6 ++++- TEKDB/explore/static/explore/css/forms.css | 4 ---- TEKDB/explore/static/explore/js/modals.js | 19 +++++++++++++++ TEKDB/explore/templates/modals.html | 11 ++++----- TEKDB/explore/templates/navbar.html | 3 ++- TEKDB/login/static/login/js/account.js | 23 +++++++++++++++++++ TEKDB/login/templates/change_password.html | 14 +++++++++++ TEKDB/login/templates/registration/login.html | 7 ------ TEKDB/login/views.py | 19 +++++++-------- 9 files changed, 77 insertions(+), 29 deletions(-) create mode 100644 TEKDB/login/templates/change_password.html diff --git a/TEKDB/TEKDB/urls.py b/TEKDB/TEKDB/urls.py index eb694a67..828d0216 100644 --- a/TEKDB/TEKDB/urls.py +++ b/TEKDB/TEKDB/urls.py @@ -35,7 +35,11 @@ urlpatterns = [ - # url(r'^login/', include('login.urls')), + path( + "change_password/", + login_views.PasswordChangeView.as_view(), + name="change_password", + ), path("admin/filebrowser/", tekdb_filebrowser.urls), path("login/", login_views.login, name="login"), path("login_async/", login_views.login_async, name="login_async"), diff --git a/TEKDB/explore/static/explore/css/forms.css b/TEKDB/explore/static/explore/css/forms.css index f0ec63bc..0e03731a 100644 --- a/TEKDB/explore/static/explore/css/forms.css +++ b/TEKDB/explore/static/explore/css/forms.css @@ -2,10 +2,6 @@ text-align: center; } -.registration-form .helptext { - visibility: hidden; -} - input[type="text"], .form-control, input[type="password"] { diff --git a/TEKDB/explore/static/explore/js/modals.js b/TEKDB/explore/static/explore/js/modals.js index 6e46d2b2..9b6fcddd 100644 --- a/TEKDB/explore/static/explore/js/modals.js +++ b/TEKDB/explore/static/explore/js/modals.js @@ -19,3 +19,22 @@ $("#loginModal").on("shown.bs.modal", function () { }); }); }); + +$("#changePasswordModal").on("shown.bs.modal", function () { + $("#currentPasswordInput").focus(); + var changePasswordForm = document.querySelector("#changePasswordModal form"); + const CHANGE_PASSWORD_ERROR_MESSAGE = "Error changing password"; + changePasswordForm.addEventListener("submit", function (event) { + event.preventDefault(); + account.changePassword(event, this, function (success) { + if (success) { + $("#changePasswordModal").modal("hide"); + alert("Password changed successfully"); + } else { + if (!changePasswordForm.innerHTML.includes(CHANGE_PASSWORD_ERROR_MESSAGE)) { + changePasswordForm.append(CHANGE_PASSWORD_ERROR_MESSAGE); + } + } + }); + }); +}); \ No newline at end of file diff --git a/TEKDB/explore/templates/modals.html b/TEKDB/explore/templates/modals.html index a408544e..144539f3 100644 --- a/TEKDB/explore/templates/modals.html +++ b/TEKDB/explore/templates/modals.html @@ -22,19 +22,17 @@ -{% comment %} - -{% endcomment %} diff --git a/TEKDB/explore/templates/navbar.html b/TEKDB/explore/templates/navbar.html index 4e58c8c5..cd7bde93 100644 --- a/TEKDB/explore/templates/navbar.html +++ b/TEKDB/explore/templates/navbar.html @@ -37,13 +37,14 @@ {% if user.is_authenticated %} diff --git a/TEKDB/login/static/login/js/account.js b/TEKDB/login/static/login/js/account.js index c18253fb..914558ef 100644 --- a/TEKDB/login/static/login/js/account.js +++ b/TEKDB/login/static/login/js/account.js @@ -51,5 +51,28 @@ var account = { callback(false); } }); + }, + changePassword: function(event, form, callback) { + var formData = $(form).serialize(); + var url = '/change_password/'; + $.ajax({ + url: url, + type: 'POST', + data: formData, + dataType: 'json', + success: function(response) { + if (!!response.data) { + console.log('%csuccessfully changed password', 'color:green;'); + callback(true); + } else { + console.log('%cerror changing password: %o', 'color: red;', response); + callback(false); + } + }, + error: function(response) { + console.log('%cerror with change password request submission: %o', 'color: red', JSON.stringify(response.responseText)); + callback(false); + } + }); } }; \ No newline at end of file diff --git a/TEKDB/login/templates/change_password.html b/TEKDB/login/templates/change_password.html new file mode 100644 index 00000000..a25cacbf --- /dev/null +++ b/TEKDB/login/templates/change_password.html @@ -0,0 +1,14 @@ +
+ {% csrf_token %} +
+ + + + + + +
+ +
\ No newline at end of file diff --git a/TEKDB/login/templates/registration/login.html b/TEKDB/login/templates/registration/login.html index 81f9d852..12a42281 100644 --- a/TEKDB/login/templates/registration/login.html +++ b/TEKDB/login/templates/registration/login.html @@ -12,10 +12,3 @@
- diff --git a/TEKDB/login/views.py b/TEKDB/login/views.py index 3f92ad70..61f11cc2 100644 --- a/TEKDB/login/views.py +++ b/TEKDB/login/views.py @@ -3,6 +3,7 @@ from django.shortcuts import render from django.contrib.auth import authenticate from django.contrib.auth import login as auth_login +from django.contrib.auth.views import PasswordChangeView, update_session_auth_hash def index(request): @@ -10,15 +11,6 @@ def index(request): "pageTitle": "Login", } return render(request, "index.html", context) - # return HttpResponse("

Server error: Already Logged In") - - -def forgot(request): - context = { - "pageTitle": "Forgot Login", - } - return render(request, "forgot.html", context) - # return HttpResponse("

Forgot Password") def login(request): @@ -66,3 +58,12 @@ def login_async(request): "success": login_user["success"], } return JsonResponse(context) + + +class PasswordChangeView(PasswordChangeView): + def form_valid(self, form): + self.object = form.save() + # prevent user’s auth session to be invalidated + # and user have to log in again after password change + update_session_auth_hash(self.request, self.object) + return JsonResponse({"data": form.is_valid()}, status=200) From 9f19e18b122acfb2b752e7516a6168ecf7e93818 Mon Sep 17 00:00:00 2001 From: Paige Williams Date: Mon, 13 Apr 2026 15:21:54 -0700 Subject: [PATCH 03/14] display success and error messages --- TEKDB/explore/static/explore/js/modals.js | 54 ++++++++++++++++++---- TEKDB/explore/templates/modals.html | 4 +- TEKDB/login/static/login/js/account.js | 8 ++-- TEKDB/login/templates/change_password.html | 2 +- TEKDB/login/views.py | 3 ++ 5 files changed, 56 insertions(+), 15 deletions(-) diff --git a/TEKDB/explore/static/explore/js/modals.js b/TEKDB/explore/static/explore/js/modals.js index 9b6fcddd..7ed7b4b0 100644 --- a/TEKDB/explore/static/explore/js/modals.js +++ b/TEKDB/explore/static/explore/js/modals.js @@ -4,7 +4,7 @@ $("#loginModal").on("shown.bs.modal", function () { const LOGIN_ERROR_MESSAGE = "Invalid username or password"; loginForm.addEventListener("submit", function (event) { event.preventDefault(); - var signIn = account.signIn(event, this, function (success) { + account.signIn(event, this, function (success) { if (success) { $("#loginModal").modal("hide"); const exploreLink = document.querySelector(".nav-explore"); @@ -23,18 +23,56 @@ $("#loginModal").on("shown.bs.modal", function () { $("#changePasswordModal").on("shown.bs.modal", function () { $("#currentPasswordInput").focus(); var changePasswordForm = document.querySelector("#changePasswordModal form"); - const CHANGE_PASSWORD_ERROR_MESSAGE = "Error changing password"; changePasswordForm.addEventListener("submit", function (event) { event.preventDefault(); - account.changePassword(event, this, function (success) { - if (success) { - $("#changePasswordModal").modal("hide"); - alert("Password changed successfully"); + account.changePassword(event, this, function (response) { + console.log('%cresponse from change password request: %o', 'color:blue;', response); + if (response.success) { + // clear any previous error messages + const errorLists = document.querySelectorAll("#changePasswordModal ul"); + errorLists.forEach((list) => list.remove()); + const submitButton = changePasswordForm.querySelector('#changePasswordSubmit'); + const successMessage = document.createElement("p"); + successMessage.setAttribute("id", "changePasswordSuccessMessage"); + successMessage.textContent = "Password successfully changed!"; + successMessage.setAttribute("style", "color: green;"); + successMessage.setAttribute("class", "m-0"); + submitButton.after(successMessage); + // reset form + changePasswordForm.reset(); } else { - if (!changePasswordForm.innerHTML.includes(CHANGE_PASSWORD_ERROR_MESSAGE)) { - changePasswordForm.append(CHANGE_PASSWORD_ERROR_MESSAGE); + for (const elementId in response.data.data) { + const errorList = document.createElement("ul"); + const erroredInput = document.querySelector(`#${elementId}`); + erroredInput.after(errorList); + erroredInput.setAttribute("style", "border-color: red;"); + if (response.data.data[elementId].length > 0) { + for (const error in response.data.data[elementId]) { + const listItem = document.createElement("li"); + listItem.textContent = response.data.data[elementId][error]; + errorList.appendChild(listItem); + } + + } } } }); }); +}); + +$("#changePasswordModal").on("hidden.bs.modal", function () { + const errorLists = document.querySelectorAll("#changePasswordModal ul"); + errorLists.forEach((list) => list.remove()); + // reset input border color + const passwordInputs = document.querySelectorAll("#changePasswordModal input[type='password']"); + passwordInputs.forEach((input) => input.setAttribute("style", "")); + + // clear form + document.querySelector("#changePasswordModal form").reset(); + // clear success message + const successMessage = document.querySelector("#changePasswordSuccessMessage"); + + if (successMessage) { + successMessage.remove(); + } }); \ No newline at end of file diff --git a/TEKDB/explore/templates/modals.html b/TEKDB/explore/templates/modals.html index 144539f3..fc0ffd58 100644 --- a/TEKDB/explore/templates/modals.html +++ b/TEKDB/explore/templates/modals.html @@ -15,7 +15,7 @@ class="btn-close" data-bs-dismiss="modal" aria-label="Close" - > + > @@ -38,7 +38,7 @@ class="btn-close" data-bs-dismiss="modal" aria-label="Close" - > + > diff --git a/TEKDB/login/static/login/js/account.js b/TEKDB/login/static/login/js/account.js index 914558ef..f590f366 100644 --- a/TEKDB/login/static/login/js/account.js +++ b/TEKDB/login/static/login/js/account.js @@ -63,15 +63,15 @@ var account = { success: function(response) { if (!!response.data) { console.log('%csuccessfully changed password', 'color:green;'); - callback(true); + callback({success: true, data: response.data}); } else { - console.log('%cerror changing password: %o', 'color: red;', response); - callback(false); + console.log('%cerror changing password: %o', 'color: red;', response.data); + callback({success: false, data: response.data}); } }, error: function(response) { console.log('%cerror with change password request submission: %o', 'color: red', JSON.stringify(response.responseText)); - callback(false); + callback({success: false, data: response.responseJSON}); } }); } diff --git a/TEKDB/login/templates/change_password.html b/TEKDB/login/templates/change_password.html index a25cacbf..e2bab785 100644 --- a/TEKDB/login/templates/change_password.html +++ b/TEKDB/login/templates/change_password.html @@ -9,6 +9,6 @@ \ No newline at end of file diff --git a/TEKDB/login/views.py b/TEKDB/login/views.py index 61f11cc2..1b6f8b35 100644 --- a/TEKDB/login/views.py +++ b/TEKDB/login/views.py @@ -61,6 +61,9 @@ def login_async(request): class PasswordChangeView(PasswordChangeView): + def form_invalid(self, form): + return JsonResponse({"data": form.errors}, status=400) + def form_valid(self, form): self.object = form.save() # prevent user’s auth session to be invalidated From a41385a8c598401960ad0d4df377f1126146b506 Mon Sep 17 00:00:00 2001 From: Paige Williams Date: Mon, 13 Apr 2026 15:57:12 -0700 Subject: [PATCH 04/14] remove console.log --- TEKDB/explore/static/explore/js/modals.js | 1 - 1 file changed, 1 deletion(-) diff --git a/TEKDB/explore/static/explore/js/modals.js b/TEKDB/explore/static/explore/js/modals.js index 7ed7b4b0..3760cd7e 100644 --- a/TEKDB/explore/static/explore/js/modals.js +++ b/TEKDB/explore/static/explore/js/modals.js @@ -26,7 +26,6 @@ $("#changePasswordModal").on("shown.bs.modal", function () { changePasswordForm.addEventListener("submit", function (event) { event.preventDefault(); account.changePassword(event, this, function (response) { - console.log('%cresponse from change password request: %o', 'color:blue;', response); if (response.success) { // clear any previous error messages const errorLists = document.querySelectorAll("#changePasswordModal ul"); From 3f4aa35d78090ec4c2e15698cadbae61c9913b29 Mon Sep 17 00:00:00 2001 From: Paige Williams Date: Mon, 13 Apr 2026 16:25:44 -0700 Subject: [PATCH 05/14] add tests for login/views --- TEKDB/login/tests/test_views.py | 101 ++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/TEKDB/login/tests/test_views.py b/TEKDB/login/tests/test_views.py index e69de29b..69ac8957 100644 --- a/TEKDB/login/tests/test_views.py +++ b/TEKDB/login/tests/test_views.py @@ -0,0 +1,101 @@ +from django.test import TestCase, Client, RequestFactory +from django.contrib.auth import get_user_model +from django.urls import reverse +from unittest.mock import MagicMock, patch +import json +from login.views import ( + PasswordChangeView as CustomPasswordChangeView, + index, + login_logic, +) + +User = get_user_model() + + +class LoginViewTests(TestCase): + def setUp(self): + self.client = Client() + self.factory = RequestFactory() + self.user = User.objects.create_user( + username="testuser", password="testpass123" + ) + + def test_index_view(self): + request = self.factory.get("/") + response = index(request) + self.assertEqual(response.status_code, 200) + self.assertIn("Login", response.content.decode("utf-8")) + + def test_login_success(self): + response = self.client.post( + reverse("login"), {"username": "testuser", "password": "testpass123"} + ) + self.assertTrue(response.wsgi_request.user.is_authenticated) + self.assertIn("explore", response.content.decode("utf-8").lower()) + self.assertEqual(response.status_code, 200) + + def test_login_invalid_credentials(self): + response = self.client.post( + reverse("login"), {"username": "testuser", "password": "wrongpassword"} + ) + self.assertFalse(response.wsgi_request.user.is_authenticated) + self.assertEqual(response.context["errorcode"], 403) + self.assertIn("incorrect", response.context["error"].lower()) + + def test_login_logic_success(self): + request = MagicMock() + request.POST = {"username": "testuser", "password": "testpass123"} + result = login_logic(request) + self.assertTrue(result["success"]) + self.assertEqual(result["username"], "testuser") + + def test_login_logic_failure(self): + request = MagicMock() + request.POST = {"username": "testuser", "password": "wrongpass"} + result = login_logic(request) + self.assertFalse(result["success"]) + + def test_login_async(self): + response = self.client.post( + reverse("login_async"), {"username": "testuser", "password": "testpass123"} + ) + self.assertEqual(response.status_code, 200) + data = json.loads(response.content) + self.assertTrue(data["success"]) + + def test_login_async_failure(self): + response = self.client.post( + reverse("login_async"), {"username": "testuser", "password": "wrongpass"} + ) + data = json.loads(response.content) + self.assertFalse(data["success"]) + + def test_password_change_form_invalid_returns_json_errors(self): + view = CustomPasswordChangeView() + form = MagicMock() + form.errors = {"old_password": ["This field is required."]} + + response = view.form_invalid(form) + + self.assertEqual(response.status_code, 400) + payload = json.loads(response.content) + self.assertEqual(payload["data"], form.errors) + + @patch("login.views.update_session_auth_hash") + def test_password_change_form_valid_updates_session_and_returns_success( + self, mock_update_session_auth_hash + ): + view = CustomPasswordChangeView() + view.request = self.factory.post("/change_password/") + form = MagicMock() + form.save.return_value = self.user + form.is_valid.return_value = True + + response = view.form_valid(form) + + self.assertEqual(response.status_code, 200) + payload = json.loads(response.content) + self.assertTrue(payload["data"]) + self.assertEqual(view.object, self.user) + form.save.assert_called_once_with() + mock_update_session_auth_hash.assert_called_once_with(view.request, self.user) From 1ee78a8a34bf790f4e8e00dacbd73e3ec17ec005 Mon Sep 17 00:00:00 2001 From: Paige Williams Date: Tue, 14 Apr 2026 09:34:18 -0700 Subject: [PATCH 06/14] clear errors before adding them --- TEKDB/explore/static/explore/js/modals.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/TEKDB/explore/static/explore/js/modals.js b/TEKDB/explore/static/explore/js/modals.js index 3760cd7e..ffc069e5 100644 --- a/TEKDB/explore/static/explore/js/modals.js +++ b/TEKDB/explore/static/explore/js/modals.js @@ -40,6 +40,12 @@ $("#changePasswordModal").on("shown.bs.modal", function () { // reset form changePasswordForm.reset(); } else { + // clear any previous error messages and styling + const errorLists = document.querySelectorAll("#changePasswordModal ul"); + errorLists.forEach((list) => list.remove()); + const passwordInputs = document.querySelectorAll("#changePasswordModal input[type='password']"); + passwordInputs.forEach((input) => input.setAttribute("style", "")); + for (const elementId in response.data.data) { const errorList = document.createElement("ul"); const erroredInput = document.querySelector(`#${elementId}`); From 2c8eb8701334cd58b3257e73e3fe052b6d971ff8 Mon Sep 17 00:00:00 2001 From: Paige Williams Date: Tue, 14 Apr 2026 10:00:32 -0700 Subject: [PATCH 07/14] rename view to TEKDBPasswordChangeView --- TEKDB/TEKDB/urls.py | 2 +- TEKDB/login/tests/test_views.py | 6 +++--- TEKDB/login/views.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/TEKDB/TEKDB/urls.py b/TEKDB/TEKDB/urls.py index 828d0216..ecf23991 100644 --- a/TEKDB/TEKDB/urls.py +++ b/TEKDB/TEKDB/urls.py @@ -37,7 +37,7 @@ urlpatterns = [ path( "change_password/", - login_views.PasswordChangeView.as_view(), + login_views.TEKDBPasswordChangeView.as_view(), name="change_password", ), path("admin/filebrowser/", tekdb_filebrowser.urls), diff --git a/TEKDB/login/tests/test_views.py b/TEKDB/login/tests/test_views.py index 69ac8957..d66f8bf1 100644 --- a/TEKDB/login/tests/test_views.py +++ b/TEKDB/login/tests/test_views.py @@ -4,7 +4,7 @@ from unittest.mock import MagicMock, patch import json from login.views import ( - PasswordChangeView as CustomPasswordChangeView, + TEKDBPasswordChangeView, index, login_logic, ) @@ -71,7 +71,7 @@ def test_login_async_failure(self): self.assertFalse(data["success"]) def test_password_change_form_invalid_returns_json_errors(self): - view = CustomPasswordChangeView() + view = TEKDBPasswordChangeView() form = MagicMock() form.errors = {"old_password": ["This field is required."]} @@ -85,7 +85,7 @@ def test_password_change_form_invalid_returns_json_errors(self): def test_password_change_form_valid_updates_session_and_returns_success( self, mock_update_session_auth_hash ): - view = CustomPasswordChangeView() + view = TEKDBPasswordChangeView() view.request = self.factory.post("/change_password/") form = MagicMock() form.save.return_value = self.user diff --git a/TEKDB/login/views.py b/TEKDB/login/views.py index 1b6f8b35..05adabec 100644 --- a/TEKDB/login/views.py +++ b/TEKDB/login/views.py @@ -60,7 +60,7 @@ def login_async(request): return JsonResponse(context) -class PasswordChangeView(PasswordChangeView): +class TEKDBPasswordChangeView(PasswordChangeView): def form_invalid(self, form): return JsonResponse({"data": form.errors}, status=400) From 4558f847c75ae11a477703d161dcbfe8506bcd32 Mon Sep 17 00:00:00 2001 From: Paige Williams Date: Tue, 14 Apr 2026 11:18:44 -0700 Subject: [PATCH 08/14] fix auth imports --- TEKDB/login/views.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/TEKDB/login/views.py b/TEKDB/login/views.py index 05adabec..57720da3 100644 --- a/TEKDB/login/views.py +++ b/TEKDB/login/views.py @@ -1,9 +1,11 @@ -# Create your views here. from django.http.response import JsonResponse from django.shortcuts import render -from django.contrib.auth import authenticate -from django.contrib.auth import login as auth_login -from django.contrib.auth.views import PasswordChangeView, update_session_auth_hash +from django.contrib.auth import ( + authenticate, + login as auth_login, + update_session_auth_hash, +) +from django.contrib.auth.views import PasswordChangeView def index(request): From 33c488b87a51641e83e2518a14611d8a37aa4e22 Mon Sep 17 00:00:00 2001 From: Paige Williams Date: Tue, 14 Apr 2026 11:40:51 -0700 Subject: [PATCH 09/14] update api response for TEKDBPasswordChangeView --- TEKDB/login/static/login/js/account.js | 4 ++-- TEKDB/login/views.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/TEKDB/login/static/login/js/account.js b/TEKDB/login/static/login/js/account.js index f590f366..c5d1802c 100644 --- a/TEKDB/login/static/login/js/account.js +++ b/TEKDB/login/static/login/js/account.js @@ -61,7 +61,7 @@ var account = { data: formData, dataType: 'json', success: function(response) { - if (!!response.data) { + if (response.success) { console.log('%csuccessfully changed password', 'color:green;'); callback({success: true, data: response.data}); } else { @@ -70,7 +70,7 @@ var account = { } }, error: function(response) { - console.log('%cerror with change password request submission: %o', 'color: red', JSON.stringify(response.responseText)); + console.log('%cerror with change password request submission: %o', 'color: red', response.data); callback({success: false, data: response.responseJSON}); } }); diff --git a/TEKDB/login/views.py b/TEKDB/login/views.py index 57720da3..f2bae458 100644 --- a/TEKDB/login/views.py +++ b/TEKDB/login/views.py @@ -64,11 +64,11 @@ def login_async(request): class TEKDBPasswordChangeView(PasswordChangeView): def form_invalid(self, form): - return JsonResponse({"data": form.errors}, status=400) + return JsonResponse({"data": form.errors, "success": False}, status=400) def form_valid(self, form): self.object = form.save() # prevent user’s auth session to be invalidated # and user have to log in again after password change update_session_auth_hash(self.request, self.object) - return JsonResponse({"data": form.is_valid()}, status=200) + return JsonResponse({"data": None, "success": True}, status=200) From c073d03e6ab6b991eec01f99afaec3c01f7ba25c Mon Sep 17 00:00:00 2001 From: Paige Williams Date: Tue, 14 Apr 2026 12:28:16 -0700 Subject: [PATCH 10/14] prevent showing duplicate success message fix tests --- TEKDB/explore/static/explore/js/modals.js | 39 +++++++++++++---------- TEKDB/login/tests/test_views.py | 3 +- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/TEKDB/explore/static/explore/js/modals.js b/TEKDB/explore/static/explore/js/modals.js index ffc069e5..bc0232c8 100644 --- a/TEKDB/explore/static/explore/js/modals.js +++ b/TEKDB/explore/static/explore/js/modals.js @@ -21,31 +21,36 @@ $("#loginModal").on("shown.bs.modal", function () { }); $("#changePasswordModal").on("shown.bs.modal", function () { - $("#currentPasswordInput").focus(); - var changePasswordForm = document.querySelector("#changePasswordModal form"); + const changePasswordForm = document.querySelector("#changePasswordModal form"); + changePasswordForm.addEventListener("submit", function (event) { event.preventDefault(); account.changePassword(event, this, function (response) { if (response.success) { // clear any previous error messages const errorLists = document.querySelectorAll("#changePasswordModal ul"); - errorLists.forEach((list) => list.remove()); - const submitButton = changePasswordForm.querySelector('#changePasswordSubmit'); - const successMessage = document.createElement("p"); - successMessage.setAttribute("id", "changePasswordSuccessMessage"); - successMessage.textContent = "Password successfully changed!"; - successMessage.setAttribute("style", "color: green;"); - successMessage.setAttribute("class", "m-0"); - submitButton.after(successMessage); + if (errorLists.length) { + errorLists.forEach((list) => list.remove()); + } + // clear any previous success message + const previousSuccessMessage = document.querySelector("#changePasswordSuccessMessage"); + if (!previousSuccessMessage) { + // display success message + const successMessage = document.createElement("p"); + successMessage.setAttribute("id", "changePasswordSuccessMessage"); + successMessage.textContent = "Password successfully changed!"; + successMessage.setAttribute("style", "color: green;"); + successMessage.setAttribute("class", "m-0"); + + // insert success message after submit button + const submitButton = changePasswordForm.querySelector('#changePasswordSubmit'); + submitButton.after(successMessage); + } + // reset form changePasswordForm.reset(); } else { - // clear any previous error messages and styling - const errorLists = document.querySelectorAll("#changePasswordModal ul"); - errorLists.forEach((list) => list.remove()); - const passwordInputs = document.querySelectorAll("#changePasswordModal input[type='password']"); - passwordInputs.forEach((input) => input.setAttribute("style", "")); - + // display error messages for (const elementId in response.data.data) { const errorList = document.createElement("ul"); const erroredInput = document.querySelector(`#${elementId}`); @@ -66,12 +71,12 @@ $("#changePasswordModal").on("shown.bs.modal", function () { }); $("#changePasswordModal").on("hidden.bs.modal", function () { + // clear error messages const errorLists = document.querySelectorAll("#changePasswordModal ul"); errorLists.forEach((list) => list.remove()); // reset input border color const passwordInputs = document.querySelectorAll("#changePasswordModal input[type='password']"); passwordInputs.forEach((input) => input.setAttribute("style", "")); - // clear form document.querySelector("#changePasswordModal form").reset(); // clear success message diff --git a/TEKDB/login/tests/test_views.py b/TEKDB/login/tests/test_views.py index d66f8bf1..2ec703ca 100644 --- a/TEKDB/login/tests/test_views.py +++ b/TEKDB/login/tests/test_views.py @@ -80,6 +80,7 @@ def test_password_change_form_invalid_returns_json_errors(self): self.assertEqual(response.status_code, 400) payload = json.loads(response.content) self.assertEqual(payload["data"], form.errors) + self.assertFalse(payload["success"]) @patch("login.views.update_session_auth_hash") def test_password_change_form_valid_updates_session_and_returns_success( @@ -95,7 +96,7 @@ def test_password_change_form_valid_updates_session_and_returns_success( self.assertEqual(response.status_code, 200) payload = json.loads(response.content) - self.assertTrue(payload["data"]) + self.assertTrue(payload["success"]) self.assertEqual(view.object, self.user) form.save.assert_called_once_with() mock_update_session_auth_hash.assert_called_once_with(view.request, self.user) From a5a6d0073091684b9af26af6212cb1bbee9cd112 Mon Sep 17 00:00:00 2001 From: Paige Williams Date: Tue, 14 Apr 2026 12:32:19 -0700 Subject: [PATCH 11/14] add href=# to anchor tags in navbar --- TEKDB/explore/templates/navbar.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TEKDB/explore/templates/navbar.html b/TEKDB/explore/templates/navbar.html index cd7bde93..292486b2 100644 --- a/TEKDB/explore/templates/navbar.html +++ b/TEKDB/explore/templates/navbar.html @@ -44,13 +44,13 @@ {% if user.is_staff %}
  • Administration
  • {% endif %} -
  • Change Password
  • +
  • Change Password
  • Logout
  • {% else %} From c21e7116c378885bb81b0bc53bfaa50243056a99 Mon Sep 17 00:00:00 2001 From: Paige Williams Date: Mon, 20 Apr 2026 09:57:33 -0700 Subject: [PATCH 12/14] code cleanup; move addEventListener to outside of modal mount --- TEKDB/explore/static/explore/js/modals.js | 137 +++++++++++----------- 1 file changed, 71 insertions(+), 66 deletions(-) diff --git a/TEKDB/explore/static/explore/js/modals.js b/TEKDB/explore/static/explore/js/modals.js index bc0232c8..9b8bc581 100644 --- a/TEKDB/explore/static/explore/js/modals.js +++ b/TEKDB/explore/static/explore/js/modals.js @@ -1,86 +1,91 @@ -$("#loginModal").on("shown.bs.modal", function () { - $("#loginInput").focus(); - var loginForm = document.querySelector("#loginModal form"); - const LOGIN_ERROR_MESSAGE = "Invalid username or password"; - loginForm.addEventListener("submit", function (event) { - event.preventDefault(); - account.signIn(event, this, function (success) { - if (success) { - $("#loginModal").modal("hide"); - const exploreLink = document.querySelector(".nav-explore"); - exploreLink.classList.remove("disabled"); - exploreLink.href = "/explore"; - exploreLink.click(); - } else { - if (!loginForm.innerHTML.includes(LOGIN_ERROR_MESSAGE)) { - loginForm.append(LOGIN_ERROR_MESSAGE); - } +const LOGIN_MODAL_ID = "loginModal"; +const LOGIN_INPUT_ID = "loginInput"; +const loginForm = document.querySelector(`#${LOGIN_MODAL_ID} form`); +const LOGIN_ERROR_MESSAGE = "Invalid username or password"; + +$(`#${LOGIN_MODAL_ID}`).on("shown.bs.modal", function () { + $(`#${LOGIN_INPUT_ID}`).focus(); +}); + +loginForm.addEventListener("submit", function (event) { + event.preventDefault(); + account.signIn(event, this, function (success) { + if (success) { + $(`#${LOGIN_MODAL_ID}`).modal("hide"); + const exploreLink = document.querySelector(".nav-explore"); + exploreLink.classList.remove("disabled"); + exploreLink.href = "/explore"; + exploreLink.click(); + } else { + if (!loginForm.innerHTML.includes(LOGIN_ERROR_MESSAGE)) { + loginForm.append(LOGIN_ERROR_MESSAGE); } - }); + } }); }); -$("#changePasswordModal").on("shown.bs.modal", function () { - const changePasswordForm = document.querySelector("#changePasswordModal form"); - - changePasswordForm.addEventListener("submit", function (event) { - event.preventDefault(); - account.changePassword(event, this, function (response) { - if (response.success) { - // clear any previous error messages - const errorLists = document.querySelectorAll("#changePasswordModal ul"); - if (errorLists.length) { - errorLists.forEach((list) => list.remove()); - } - // clear any previous success message - const previousSuccessMessage = document.querySelector("#changePasswordSuccessMessage"); - if (!previousSuccessMessage) { - // display success message - const successMessage = document.createElement("p"); - successMessage.setAttribute("id", "changePasswordSuccessMessage"); - successMessage.textContent = "Password successfully changed!"; - successMessage.setAttribute("style", "color: green;"); - successMessage.setAttribute("class", "m-0"); +const CHANGE_PASSWORD_MODAL_ID = "changePasswordModal"; +const CHANGE_PASSWORD_SUCCESS_ID = "changePasswordSuccessMessage"; +const CHANGE_PASSWORD_SUCCESS_MESSAGE = "Password successfully changed!"; +const changePasswordForm = document.querySelector(`#${CHANGE_PASSWORD_MODAL_ID} form`); - // insert success message after submit button - const submitButton = changePasswordForm.querySelector('#changePasswordSubmit'); - submitButton.after(successMessage); - } - - // reset form - changePasswordForm.reset(); - } else { - // display error messages - for (const elementId in response.data.data) { - const errorList = document.createElement("ul"); - const erroredInput = document.querySelector(`#${elementId}`); - erroredInput.after(errorList); - erroredInput.setAttribute("style", "border-color: red;"); - if (response.data.data[elementId].length > 0) { - for (const error in response.data.data[elementId]) { - const listItem = document.createElement("li"); - listItem.textContent = response.data.data[elementId][error]; - errorList.appendChild(listItem); - } - +changePasswordForm.addEventListener("submit", function (event) { + event.preventDefault(); + account.changePassword(event, this, function (response) { + if (response.success) { + // clear any previous error messages + const errorLists = document.querySelectorAll(`#${CHANGE_PASSWORD_MODAL_ID} ul`); + if (errorLists.length) { + errorLists.forEach((list) => list.remove()); + } + // clear any previous success message + const previousSuccessMessage = document.querySelector(`#${CHANGE_PASSWORD_SUCCESS_ID}`); + if (!previousSuccessMessage) { + // display success message + const successMessage = document.createElement("p"); + successMessage.setAttribute("id", `${CHANGE_PASSWORD_SUCCESS_ID}`); + successMessage.textContent = CHANGE_PASSWORD_SUCCESS_MESSAGE; + successMessage.setAttribute("style", "color: green;"); + successMessage.setAttribute("class", "m-0"); + + // insert success message after submit button + const submitButton = changePasswordForm.querySelector('#changePasswordSubmit'); + submitButton.after(successMessage); + } + + // reset form + changePasswordForm.reset(); + } else { + // display error messages + for (const elementId in response.data.data) { + const errorList = document.createElement("ul"); + const erroredInput = document.querySelector(`#${elementId}`); + erroredInput.after(errorList); + erroredInput.setAttribute("style", "border-color: red;"); + if (response.data.data[elementId].length > 0) { + for (const error in response.data.data[elementId]) { + const listItem = document.createElement("li"); + listItem.textContent = response.data.data[elementId][error]; + errorList.appendChild(listItem); } + } } - }); + } }); }); -$("#changePasswordModal").on("hidden.bs.modal", function () { +$(`#${CHANGE_PASSWORD_MODAL_ID}`).on("hidden.bs.modal", function () { // clear error messages - const errorLists = document.querySelectorAll("#changePasswordModal ul"); + const errorLists = document.querySelectorAll(`#${CHANGE_PASSWORD_MODAL_ID} ul`); errorLists.forEach((list) => list.remove()); // reset input border color - const passwordInputs = document.querySelectorAll("#changePasswordModal input[type='password']"); + const passwordInputs = document.querySelectorAll(`#${CHANGE_PASSWORD_MODAL_ID} input[type='password']`); passwordInputs.forEach((input) => input.setAttribute("style", "")); // clear form - document.querySelector("#changePasswordModal form").reset(); + document.querySelector(`#${CHANGE_PASSWORD_MODAL_ID} form`).reset(); // clear success message - const successMessage = document.querySelector("#changePasswordSuccessMessage"); + const successMessage = document.querySelector(`#${CHANGE_PASSWORD_SUCCESS_ID}`); if (successMessage) { successMessage.remove(); From f59c1bda2999a0100715af9ca1c6e08343c48c94 Mon Sep 17 00:00:00 2001 From: Paige Williams Date: Mon, 20 Apr 2026 10:12:16 -0700 Subject: [PATCH 13/14] remove unused login urls --- TEKDB/login/urls.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/TEKDB/login/urls.py b/TEKDB/login/urls.py index 34538d68..5119061b 100644 --- a/TEKDB/login/urls.py +++ b/TEKDB/login/urls.py @@ -3,8 +3,5 @@ from . import views urlpatterns = [ - path("create", views.create, name="create"), - path("forgot", views.forgot, name="forgot"), path("", views.index, name="index"), ] -# url(r'^logout$', views.logout, name='logout'), From f7a7e8d11986b55fea90d57813db26c7269e8c1b Mon Sep 17 00:00:00 2001 From: Paige Williams Date: Tue, 21 Apr 2026 16:08:01 -0700 Subject: [PATCH 14/14] bump version to 2.11.0 --- TEKDB/TEKDB/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TEKDB/TEKDB/settings.py b/TEKDB/TEKDB/settings.py index 1706339b..91f8ac9f 100644 --- a/TEKDB/TEKDB/settings.py +++ b/TEKDB/TEKDB/settings.py @@ -262,7 +262,7 @@ TINYMCE_FILEBROWSER = False # Add Version to the admin site header -VERSION = "2.10.0" +VERSION = "2.11.0" ADMIN_SITE_HEADER = os.environ.get( "ADMIN_SITE_HEADER", default="ITK DB Admin v{}".format(VERSION) )