From 8c216c7e566c7a39a17412d04edafdf38ab75cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniele=20Vigan=C3=B2?= Date: Thu, 15 Jan 2026 15:17:20 +0100 Subject: [PATCH] Fix search form validation --- ram/portal/static/js/main.min.js | 2 +- ram/portal/static/js/src/tabs_selector.js | 6 ++++-- ram/portal/static/js/src/validators.js | 15 +++++++++++++++ ram/portal/templates/_includes/search.html | 18 ------------------ ram/portal/views.py | 3 +++ 5 files changed, 23 insertions(+), 21 deletions(-) create mode 100644 ram/portal/static/js/src/validators.js diff --git a/ram/portal/static/js/main.min.js b/ram/portal/static/js/main.min.js index aa7ed63..83d54c0 100644 --- a/ram/portal/static/js/main.min.js +++ b/ram/portal/static/js/main.min.js @@ -3,4 +3,4 @@ * Copyright 2011-2023 The Bootstrap Authors * Licensed under the Creative Commons Attribution 3.0 Unported License. */ -(()=>{"use strict";const e=()=>localStorage.getItem("theme"),t=()=>{const t=e();return t||(window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light")},a=e=>{"auto"===e&&window.matchMedia("(prefers-color-scheme: dark)").matches?document.documentElement.setAttribute("data-bs-theme","dark"):document.documentElement.setAttribute("data-bs-theme",e)};a(t());const r=(e,t=!1)=>{const a=document.querySelector("#bd-theme");if(!a)return;const r=document.querySelector(".theme-icon-active i"),o=document.querySelector(`[data-bs-theme-value="${e}"]`),s=o.querySelector(".theme-icon i").getAttribute("class");document.querySelectorAll("[data-bs-theme-value]").forEach(e=>{e.classList.remove("active"),e.setAttribute("aria-pressed","false")}),o.classList.add("active"),o.setAttribute("aria-pressed","true"),r.setAttribute("class",s),t&&a.focus()};window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{const r=e();"light"!==r&&"dark"!==r&&a(t())}),window.addEventListener("DOMContentLoaded",()=>{r(t()),document.querySelectorAll("[data-bs-theme-value]").forEach(e=>{e.addEventListener("click",()=>{const t=e.getAttribute("data-bs-theme-value");(e=>{localStorage.setItem("theme",e)})(t),a(t),r(t,!0)})})})})(),document.addEventListener("DOMContentLoaded",function(){const e=document.getElementById("tabSelector"),t=window.location.hash.substring(1);if(t){const a=`#nav-${t}`,r=document.querySelector(`[data-bs-target="${a}"]`);r&&(bootstrap.Tab.getOrCreateInstance(r).show(),e.value=a)}document.querySelectorAll('button[data-bs-toggle="tab"]').forEach(e=>{e.addEventListener("shown.bs.tab",e=>{const t=e.target.getAttribute("data-bs-target").replace("nav-","");history.replaceState(null,null,t)})}),e&&(e.addEventListener("change",function(){const e=this.value,t=document.querySelector(`[data-bs-target="${e}"]`);if(t){bootstrap.Tab.getOrCreateInstance(t).show()}}),document.querySelectorAll('[data-bs-toggle="tab"]').forEach(t=>{t.addEventListener("shown.bs.tab",t=>{const a=t.target.getAttribute("data-bs-target");e.value=a})}))}); \ No newline at end of file +(()=>{"use strict";const e=()=>localStorage.getItem("theme"),t=()=>{const t=e();return t||(window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light")},a=e=>{"auto"===e&&window.matchMedia("(prefers-color-scheme: dark)").matches?document.documentElement.setAttribute("data-bs-theme","dark"):document.documentElement.setAttribute("data-bs-theme",e)};a(t());const r=(e,t=!1)=>{const a=document.querySelector("#bd-theme");if(!a)return;const r=document.querySelector(".theme-icon-active i"),o=document.querySelector(`[data-bs-theme-value="${e}"]`),s=o.querySelector(".theme-icon i").getAttribute("class");document.querySelectorAll("[data-bs-theme-value]").forEach(e=>{e.classList.remove("active"),e.setAttribute("aria-pressed","false")}),o.classList.add("active"),o.setAttribute("aria-pressed","true"),r.setAttribute("class",s),t&&a.focus()};window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{const r=e();"light"!==r&&"dark"!==r&&a(t())}),window.addEventListener("DOMContentLoaded",()=>{r(t()),document.querySelectorAll("[data-bs-theme-value]").forEach(e=>{e.addEventListener("click",()=>{const t=e.getAttribute("data-bs-theme-value");(e=>{localStorage.setItem("theme",e)})(t),a(t),r(t,!0)})})})})(),document.addEventListener("DOMContentLoaded",function(){"use strict";const e=document.getElementById("tabSelector"),t=window.location.hash.substring(1);if(t){const a=`#nav-${t}`,r=document.querySelector(`[data-bs-target="${a}"]`);r&&(bootstrap.Tab.getOrCreateInstance(r).show(),e.value=a)}document.querySelectorAll('button[data-bs-toggle="tab"]').forEach(e=>{e.addEventListener("shown.bs.tab",e=>{const t=e.target.getAttribute("data-bs-target").replace("nav-","");history.replaceState(null,null,t)})}),e&&(e.addEventListener("change",function(){const e=this.value,t=document.querySelector(`[data-bs-target="${e}"]`);if(t){bootstrap.Tab.getOrCreateInstance(t).show()}}),document.querySelectorAll('[data-bs-toggle="tab"]').forEach(t=>{t.addEventListener("shown.bs.tab",t=>{const a=t.target.getAttribute("data-bs-target");e.value=a})}))}),document.addEventListener("DOMContentLoaded",function(){"use strict";const e=document.querySelectorAll(".needs-validation");Array.from(e).forEach(e=>{e.addEventListener("submit",t=>{e.checkValidity()||(t.preventDefault(),t.stopPropagation()),e.classList.add("was-validated")},!1)})}); \ No newline at end of file diff --git a/ram/portal/static/js/src/tabs_selector.js b/ram/portal/static/js/src/tabs_selector.js index a0b8b5d..69d7a3f 100644 --- a/ram/portal/static/js/src/tabs_selector.js +++ b/ram/portal/static/js/src/tabs_selector.js @@ -1,5 +1,7 @@ // use Bootstrap 5's Tab component to manage tab navigation and synchronize with URL hash document.addEventListener("DOMContentLoaded", function () { + 'use strict'; + const selectElement = document.getElementById('tabSelector'); // code to handle tab selection and URL hash synchronization const hash = window.location.hash.substring(1) // remove the '#' prefix @@ -8,7 +10,7 @@ document.addEventListener("DOMContentLoaded", function () { const trigger = document.querySelector(`[data-bs-target="${target}"]`); if (trigger) { bootstrap.Tab.getOrCreateInstance(trigger).show(); - selectElement.value = target // keep the dropdown in sync + selectElement.value = target; // keep the dropdown in sync } } @@ -35,7 +37,7 @@ document.addEventListener("DOMContentLoaded", function () { document.querySelectorAll('[data-bs-toggle="tab"]').forEach(btn => { btn.addEventListener('shown.bs.tab', event => { const target = event.target.getAttribute('data-bs-target'); - selectElement.value = target + selectElement.value = target; }); }); }); diff --git a/ram/portal/static/js/src/validators.js b/ram/portal/static/js/src/validators.js new file mode 100644 index 0000000..d0080bf --- /dev/null +++ b/ram/portal/static/js/src/validators.js @@ -0,0 +1,15 @@ +document.addEventListener("DOMContentLoaded", function () { + 'use strict' + + const forms = document.querySelectorAll('.needs-validation') + Array.from(forms).forEach(form => { + form.addEventListener('submit', event => { + if (!form.checkValidity()) { + event.preventDefault() + event.stopPropagation() + } + + form.classList.add('was-validated') + }, false) + }) +}); diff --git a/ram/portal/templates/_includes/search.html b/ram/portal/templates/_includes/search.html index 6c58850..f0e2f59 100644 --- a/ram/portal/templates/_includes/search.html +++ b/ram/portal/templates/_includes/search.html @@ -10,21 +10,3 @@ - diff --git a/ram/portal/views.py b/ram/portal/views.py index 3d167f0..4ced5b5 100644 --- a/ram/portal/views.py +++ b/ram/portal/views.py @@ -277,6 +277,9 @@ class SearchObjects(View): return _filter, search def get(self, request, search, page=1): + if not search: + return HttpResponseBadRequest() + try: encoded_search = search search = base64.b64decode(search.encode()).decode()