agora inbox for pgsql-www@postgresql.orghelp / color / mirror / Atom feed
[PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. 1467+ messages / 1 participants [nested] [flat]
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 24 ++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..1e32b850 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,24 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..5bdf5ea3 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.method == 'post'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
* [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. @ 2026-08-10 09:30 Jonathan Gonzalez V. <jonathan@abdiel.eu> 0 siblings, 0 replies; 1467+ messages in thread From: Jonathan Gonzalez V. @ 2026-08-10 09:30 UTC (permalink / raw) --- media/css/main.css | 12 ++++++++++++ pgweb/account/tests.py | 26 ++++++++++++++++++++++++++ pgweb/util/contexts.py | 2 +- templates/base/page.html | 9 ++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 pgweb/account/tests.py diff --git a/media/css/main.css b/media/css/main.css index 7e9ebf18..4861a348 100644 --- a/media/css/main.css +++ b/media/css/main.css @@ -847,6 +847,18 @@ input#navbar-toggler { color: var(--pg-sidenav-a-fg-color); font-weight: normal; } +#pgSideNav .pg-sidenav-link { + background: none; + border: 0; + color: var(--pg-sidenav-a-fg-color); + cursor: pointer; + font: inherit; + padding: 0; +} + +#pgSideNav .pg-sidenav-link:hover { + text-decoration: underline; +} #pgSideNav ul { list-style-type: circle; diff --git a/pgweb/account/tests.py b/pgweb/account/tests.py new file mode 100644 index 00000000..3cbc59d7 --- /dev/null +++ b/pgweb/account/tests.py @@ -0,0 +1,26 @@ +from django.contrib.auth import SESSION_KEY, get_user_model +from django.test import TestCase, override_settings + + +@override_settings(DO_ESI=True) +class LogoutTests(TestCase): + def setUp(self): + self.user = get_user_model().objects.create_user( + username='logout-test', + email='logout-test@example.com', + ) + self.client.force_login(self.user) + + def test_account_navigation_submits_logout_with_post(self): + response = self.client.get('/account/') + + self.assertContains( + response, + '<form action="/account/logout/" method="post">', + ) + + def test_logout_post_clears_session(self): + response = self.client.post('/account/logout/') + + self.assertRedirects(response, '/', fetch_redirect_response=False) + self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/pgweb/util/contexts.py b/pgweb/util/contexts.py index ca493714..95750c39 100644 --- a/pgweb/util/contexts.py +++ b/pgweb/util/contexts.py @@ -89,7 +89,7 @@ sitenav = { {'title': 'Organisations', 'link': '/account/edit/organisations/'}, ]}, {'title': 'Change password', 'link': '/account/changepwd/'}, - {'title': 'Logout', 'link': '/account/logout/'}, + {'title': 'Logout', 'link': '/account/logout/', 'method': 'post'}, ], } diff --git a/templates/base/page.html b/templates/base/page.html index 523b53d8..7dc9e5f8 100644 --- a/templates/base/page.html +++ b/templates/base/page.html @@ -11,7 +11,14 @@ <ul> {%for m in navmenu%} {%if not forloop.first %}</li>{%endif%} - <li{%if forloop.last%} class="last-child"{%endif%}><a href="{{m.link}}">{{m.title}}</a> + <li{%if forloop.last%} class="last-child"{%endif%}> + {%if m.link == '/account/logout/'%} + <form action="{{m.link}}" method="post">{%csrf_token%} + <button class="pg-sidenav-link" type="submit">{{m.title}}</button> + </form> + {%else%} + <a href="{{m.link}}">{{m.title}}</a> + {%endif%} {%if m.submenu %} <ul> {%for sm in m.submenu %} -- 2.53.0 --=-=-=-- ^ permalink raw reply [nested|flat] 1467+ messages in thread
end of thread, other threads:[~2026-08-10 09:30 UTC | newest] Thread overview: 1467+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu> 2026-08-10 09:30 [PATCH v2 1/1] Turn the logout link into a POST method following Django 5.2 rules. Jonathan Gonzalez V. <jonathan@abdiel.eu>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox