Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Django==6.0.4 # Base django module
django-bootstrap4==26.1 # CSS base
django-bootstrap5 # CSS base
django-crispy-forms==2.6 # Nice looking forms
django-tables2==3.0.0 # Tables for lists
django-filter==25.2 # Filtering the lists
Expand Down
25 changes: 15 additions & 10 deletions rodatraden/static/rodatraden/block_detail.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,14 +335,19 @@ function main() {
createCategorySumChart();

// Show the name of the file to be uploaded when a new file has been selected.
document.querySelector('.custom-file-input').addEventListener(
'change',
function(e) {
const fileName = document.getElementById("inputExcelFile").files[0].name;
let label = e.target.nextElementSibling;
label.innerText = fileName;
}
);
const inputExcelFile = document.getElementById('inputExcelFile');
if (inputExcelFile) {
inputExcelFile.addEventListener('change', function(e) {
if (!inputExcelFile.files || inputExcelFile.files.length === 0) {
return;
}
const fileName = inputExcelFile.files[0].name;
const label = e.target.nextElementSibling;
if (label) {
label.innerText = fileName;
}
});
}

const courseOccasions = CourseOccasion.fromElement('course-occasions-data');

Expand Down Expand Up @@ -436,9 +441,9 @@ function main() {
return;
}

// Handle clicks on elements with data-dismiss="modal" (close buttons).
// Handle clicks on elements with data-bs-dismiss="modal" (close buttons).
// This makes Bootstrap-style close buttons work with native <dialog>.
const dismissButton = event.target.closest('[data-dismiss="modal"]');
const dismissButton = event.target.closest('[data-bs-dismiss="modal"]');
if (dismissButton) {
event.preventDefault();
closeModalWithAnimation();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -673,13 +673,13 @@ export function updateCourseBlocks(courseContainer,
// The attributes are added to all course blocks, but this doesn't matter
// since the tooltip is only shown for the blocks if the title attribute
// contains something.
// NOTE: The "data-toggle" attribute is missing on purpose for unmet
// NOTE: The "data-bs-toggle" attribute is missing on purpose for unmet
// prerequisites. This is so that the tooltip isn't enabled when
// "$('[data-toggle="tooltip"]').tooltip()" is called somewhere else.
// "$("[data-bs-toggle="tooltip"]").tooltip()" is called somewhere else.
// For retakes, we always show the tooltip.
course
.attr("data-placement", "left")
.attr("data-toggle", course => {
.attr("data-bs-placement", "left")
.attr("data-bs-toggle", course => {
// Only enable tooltip automatically for retakes (not for unmet prerequisites)
if (course.isRetake && course.unmetPrerequisiteIDs.length === 0) {
return "tooltip";
Expand Down Expand Up @@ -757,8 +757,8 @@ export function addFooter(term, isLoggedIn, blockCourseListUrl, blockSchedule) {

// Setup data for the pop-up modal.
newButton
.attr("data-toggle", "tooltip")
.attr("data-placement", "left")
.attr("data-bs-toggle", "tooltip")
.attr("data-bs-placement", "left")
.attr('data-url', button => {
// XXX: Why use the start weeks 0, 10, 20 and 30 when the period number
// would do? The code would be clearer if the server did the week
Expand Down Expand Up @@ -864,10 +864,10 @@ export function updatePrerequisiteWarnings() {
const shouldUseWarningColor = checkbox.checked && hasUnmet && !isManualSkip;

if (shouldShowWarning) {
course.setAttribute('data-toggle', 'tooltip');
course.setAttribute('data-bs-toggle', 'tooltip');
$(course).tooltip('enable');
} else {
course.removeAttribute('data-toggle');
course.removeAttribute('data-bs-toggle');
$(course).tooltip('disable');
}

Expand Down
2 changes: 1 addition & 1 deletion rodatraden/static/rodatraden/category_form.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,5 @@ $(function() {
}
});

$('[data-toggle="tooltip"]').tooltip();
$('[data-bs-toggle="tooltip"]').tooltip();
});
2 changes: 1 addition & 1 deletion rodatraden/static/rodatraden/course/form_categories.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,5 @@ $(function() {
$(this).parent().remove();
});

$('[data-toggle="tooltip"]').tooltip()
$('[data-bs-toggle="tooltip"]').tooltip()
});
48 changes: 44 additions & 4 deletions rodatraden/static/rodatraden/rodatraden.css
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,24 @@
}

.card-columns {
@include media-breakpoint-only(lg) {
column-count: 1;
column-gap: 1.25rem;
}

.card-columns .card {
display: inline-block;
width: 100%;
margin: 0 0 1.25rem;
}

@media (min-width: 768px) {
.card-columns {
column-count: 2;
}
}

@media (min-width: 1200px) {
.card-columns {
column-count: 3;
}
}
Expand Down Expand Up @@ -97,7 +114,7 @@
}

/* Keep page action buttons clickable even when nearby content overlaps. */
.main-template > .text-right {
.main-template > .text-end {
position: relative;
z-index: 3;
}
Expand Down Expand Up @@ -131,16 +148,34 @@
background-color: transparent;
}

/* Red links */
/* Red links (low specificity so contextual rules like .course a can override). */
a:not(.default) {
color: #D6001E;
text-decoration: none;
}

a:not(.default):hover{
a:not(.default):hover {
color: #b30018;
text-decoration: none;
}

/* Keep navbar links using navbar palette rather than global link color. */
.primary-nav .navbar-brand,
.primary-nav .nav-link,
.primary-nav .dropdown-item {
color: inherit;
}

/* Keep dropdown menu items in navbar red as before migration. */
.primary-nav .dropdown-menu .dropdown-item {
color: #D6001E;
}

.primary-nav .dropdown-menu .dropdown-item:hover,
.primary-nav .dropdown-menu .dropdown-item:focus {
color: #fff;
}

/* List without background */
.list-group-item {
background-color: transparent;
Expand Down Expand Up @@ -185,6 +220,11 @@ a:not(.default):hover{
opacity: 0;
}

/* Ensure fetched course info content always has a solid modal background. */
.native-modal .modal-content {
background-color: var(--bs-modal-bg, #fff);
}

.native-modal::backdrop {
background: rgb(0 0 0 / 0.5);
}
Expand Down
49 changes: 45 additions & 4 deletions rodatraden/static/rodatraden/rodatraden.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,48 @@
/* Bootstrap nice tooltips */
$(function () { $('[data-toggle="tooltip"]').tooltip() })
/* BOotstrap dropdown */
$(function(){ $('.dropdown-toggle').dropdown(); });
/* Provide jQuery compatibility wrappers for Bootstrap 5 APIs. */
(function (window) {
if (!window.jQuery || !window.bootstrap) {
return;
}

const $ = window.jQuery;

if (!$.fn.tooltip && window.bootstrap.Tooltip) {
$.fn.tooltip = function (action) {
return this.each(function () {
const instance = window.bootstrap.Tooltip.getOrCreateInstance(this);
if (action === 'dispose') instance.dispose();
else if (action === 'enable') instance.enable();
else if (action === 'disable') instance.disable();
else if (action === 'hide') instance.hide();
else if (action === 'show') instance.show();
});
};
}

if (!$.fn.dropdown && window.bootstrap.Dropdown) {
$.fn.dropdown = function (action) {
return this.each(function () {
const instance = window.bootstrap.Dropdown.getOrCreateInstance(this);
if (action === 'show') instance.show();
else if (action === 'hide') instance.hide();
else if (action === 'toggle') instance.toggle();
});
Comment thread
SpiderQubit marked this conversation as resolved.
if (!$.fn.dropdown && window.bootstrap.Dropdown) {
$.fn.dropdown = function (action) {
return this.each(function () {
const instance = window.bootstrap.Dropdown.getOrCreateInstance(this);
if (action === 'show') instance.show();
else if (action === 'hide') instance.hide();
else if (action === 'toggle') instance.toggle();
});
};
}
})(window);

/* Bootstrap tooltips */
$(function () { $('[data-bs-toggle="tooltip"]').tooltip() });
/* Bootstrap dropdown */
$(function () { $('.dropdown-toggle').dropdown(); });

/* Hide alerts automatically */
$(".alert").fadeTo(2000, 500).slideUp(500, function(){
Expand Down
11 changes: 7 additions & 4 deletions rodatraden/templates/rodatraden/base.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<!DOCTYPE html>

{# Load the tag library #}
{% load bootstrap4 %}
{% load django_bootstrap5 %}

{# Load CSS and JavaScript #}
{% bootstrap_css %}
{% bootstrap_javascript jquery='full' %}
{% bootstrap_javascript %}

{# Display django.contrib.messages as Bootstrap alerts #}
{% bootstrap_messages %}
Expand Down Expand Up @@ -47,7 +47,7 @@
</div>
</div>

<div class="text-right">
<div class="text-end">
{% block menu %}
{% endblock %}
</div>
Expand Down Expand Up @@ -80,8 +80,11 @@ <h1 class="text-center rt-page-title">
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="
crossorigin="anonymous"></script>
<script src="{% static 'rodatraden/rodatraden.js' %}"></script>
<script src="{% static 'js/jquery.bootstrap.modal.forms.js' %}"></script>
<script src="{% static 'js/bootstrap5.modal.forms.js' %}"></script>
Comment on lines +83 to +87

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Locate modal forms plugin assets that actually exist.
fd -i 'modal.forms.js'

# 2) Find script includes that reference modal form assets.
rg -n 'bootstrap5\.modal\.forms\.js|jquery\.bootstrap\.modal\.forms\.js' rodatraden/templates

# 3) Find all modalForm call sites that depend on the plugin.
rg -nP '\.modalForm\s*\(' rodatraden/templates rodatraden/static

Repository: it-amanuens/rodatraden

Length of output: 1951


🏁 Script executed:

# Check if the static/js directory exists and what files are in it
fd -type f -name '*.js' rodatraden/static/

# Also check the full path structure
tree rodatraden/static/ -L 3 2>/dev/null || find rodatraden/static -type f | head -20

Repository: it-amanuens/rodatraden

Length of output: 1100


🏁 Script executed:

# Read the exact lines from base.html around line 87
sed -n '80,95p' rodatraden/templates/rodatraden/base.html

# Search for any bootstrap modal related files anywhere
find . -iname '*modal*' -o -iname '*bootstrap*' 2>/dev/null | grep -v '.git' | head -20

Repository: it-amanuens/rodatraden

Length of output: 1008


🏁 Script executed:

# Search for requirements or package files that might list this dependency
find . -name 'requirements*.txt' -o -name 'package.json' -o -name 'setup.py' -o -name 'pyproject.toml' 2>/dev/null | head -10

# Check if there's any reference to where bootstrap modal forms should come from
rg 'bootstrap.*modal|modal.*form' rodatraden/templates rodatraden/static --type html --type js -i | head -20

Repository: it-amanuens/rodatraden

Length of output: 2211


🏁 Script executed:

# Check package.json for bootstrap modal forms
cat package.json

# Check requirements.txt
cat requirements.txt | grep -i bootstrap || cat requirements.txt

Repository: it-amanuens/rodatraden

Length of output: 589


🌐 Web query:

django-bootstrap-modal-forms 3.0.5 bootstrap5.modal.forms.js static files location

💡 Result:

The static file bootstrap5.modal.forms.js for django-bootstrap-modal-forms version 3.0.5 is located in the package's static directory, specifically at bootstrap_modal_forms/static/js/bootstrap5.modal.forms.js (or bootstrap_modal_forms/static/bootstrap5.modal.forms.js). In Django templates, include it using the static template tag as: <script src="{% static 'js/bootstrap5.modal.forms.js' %}"></script> This matches the official documentation and examples in the GitHub repository and PyPI page for version 3.0.5 (released 2024-09-28), which explicitly show this path for Bootstrap 5 support. A minified version bootstrap5.modal.forms.min.js is also available at the same location. Django's staticfiles app will serve it from your project's collected static directory after running collectstatic, following standard Django static file conventions (app/static/app/...).

Citations:


Asset path is correct, but file is missing from static directory—add defensive guard to prevent runtime breakage.

The path js/bootstrap5.modal.forms.js is correct per the django-bootstrap-modal-forms package (v3.0.5) documentation. However, this file is not present in the repository's static directory, indicating it has not been collected via Django's collectstatic command. The multiple .modalForm() calls throughout templates will fail at runtime if this file is unavailable. Add the defensive guard to gracefully handle its absence:

🛠 Suggested defensive guard
     <script type="text/javascript">
       $(function () {
+        if (typeof $.fn.modalForm !== 'function') {
+          console.warn('modalForm plugin is unavailable; skipping modal bindings.');
+          return;
+        }
+
         /* Send the user report */
         $(".send-report").each(function () {
           $(this).modalForm({formURL: $(this).data('id')});
         });

Ensure static files are collected during deployment and development setup.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rodatraden/templates/rodatraden/base.html` around lines 83 - 87, The template
includes a script tag for js/bootstrap5.modal.forms.js which is missing from
static and causes runtime errors when templates call the .modalForm() plugin;
add a defensive guard immediately after the static script tag in the base
template that checks for the jQuery plugin (e.g., $.fn.modalForm) or the global
(e.g., window.bootstrapModalForms) and defines a safe no-op implementation if
absent so existing calls to .modalForm() and any bootstrapModalForms globals
won't throw; also note to collect the package's static assets
(collectstatic/install) for proper deployment.

<!-- Select 2 -->
<script src="{% static 'assets/js/select2.min.js' %}"></script>
<!-- Fancy icons -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@

<div class="modal-header">
<h3 class="modal-title">Radera blockschema</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>

<div class="modal-body">
Expand Down
6 changes: 2 additions & 4 deletions rodatraden/templates/rodatraden/block/block_create.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@

<div class="modal-header">
<h3 class="modal-title">Skapa blockschema</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>

<div class="modal-body">
Expand All @@ -33,7 +31,7 @@ <h3 class="modal-title">Skapa blockschema</h3>
</div>

<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Stäng</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Stäng</button>
<button type="submit" class="btn btn-rt">Skapa</button>
</div>

Expand Down
28 changes: 13 additions & 15 deletions rodatraden/templates/rodatraden/block/block_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,24 @@
{% if logged_in %}
<a class="btn btn-gone"
href="{% url 'block-list' this_block.user %}"
data-toggle="tooltip" title="Blockscheman">
data-bs-toggle="tooltip" title="Blockscheman">
<span class="fa fa-list"></span>
</a>
<a class="btn btn-gone import-block"
href="{% url 'block-import-list' this_block.user this_block.slug %}"
data-id="{% url 'block-import-list' this_block.user this_block.slug %}"
data-toggle="tooltip" title="Importera blockschema">
data-bs-toggle="tooltip" title="Importera blockschema">
<span class="fa fa-sign-in-alt"></span>
</a>
<a class="btn btn-gone update-block"
data-id="{% url 'block-update' this_block.user this_block.slug %}"
data-toggle="tooltip" title="Ändra">
data-bs-toggle="tooltip" title="Ändra">
<span class="fa fa-edit"></span>
</a>
<a class="btn btn-gone delete-block"
href="{% url 'block-delete' this_block.user this_block.slug %}"
data-id="{% url 'block-delete' this_block.user this_block.slug %}"
data-toggle="tooltip" title="Radera">
data-bs-toggle="tooltip" title="Radera">
<span class="fa fa-trash"></span>
</a>
{% endif %}
Expand Down Expand Up @@ -100,17 +100,15 @@ <h5>Ladda upp din ISP för att få den ifylld</h5>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="input-group mb-3">
<div class="input-group-prepend">
<input id="download"
class="btn btn-outline-secondary"
type="submit"
value="Ladda ner"
for="download">
</input>
</div>
<div class="custom-file">
<input type="file" class="custom-file-input" name="excel_file" id="inputExcelFile">
<label class="custom-file-label" for="inputExcelFile">
<input id="download"
class="btn btn-outline-secondary"
type="submit"
value="Ladda ner"
for="download">
</input>
Comment on lines +103 to +108

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

find . -path "*rodatraden/templates/rodatraden/block/block_detail.html" -type f

Repository: it-amanuens/rodatraden

Length of output: 124


🏁 Script executed:

cat -n rodatraden/templates/rodatraden/block/block_detail.html | sed -n '95,115p'

Repository: it-amanuens/rodatraden

Length of output: 966


🌐 Web query:

HTML5 input void element closing tag specification

💡 Result:

In HTML (HTML5 / HTML Living Standard), is a void element, so it must not have an end (closing) tag; void elements “only have a start tag; end tags must not be specified for void elements.”[1] Therefore, markup like <input...> is invalid HTML, and the correct form is just the start tag, e.g. (or without the trailing slash: ).[1][2] Also note: HTML self-closing syntax with a trailing slash is not “real” self-closing in HTML. The spec says that if a single trailing “/” appears in a start tag, it has no effect on void elements (it’s unnecessary); it’s primarily relevant for foreign elements.[1]

Citations:


🌐 Web query:

HTML for attribute label element only valid locations

💡 Result:

The for attribute of the HTML <label> element is valid only on <label> elements themselves. It is used to explicitly associate the label with a form control by referencing the control's id [1][2][3]. The <label> element (which bears the for attribute) is permitted in any context where phrasing content is expected [4][3][5]. Phrasing content includes locations inside paragraphs (<p>), headings (<h1>-<h6>), <div>, <span>, <li>, form elements like <form> and <fieldset>, and many others, but not block-level contexts that only accept flow content excluding phrasing (e.g., directly in <body> without wrapping, though browsers often parse permissively) [6][7]. The attribute's value must be the ID of a labelable form control (like <input>, <textarea>, <select>) in the same document [1][4][5].

Citations:


Remove invalid </input> closing tag and for attribute from the submit button.

The <input> element is void in HTML and must not have a closing tag. The for attribute is only valid on <label> elements, not on <input>. Both issues cause HTML validation errors.

Proposed fix
-          <input id="download"
-                 class="btn btn-outline-secondary"
-                 type="submit"
-                 value="Ladda ner"
-                 for="download">
-          </input>
+          <input id="download"
+                 class="btn btn-outline-secondary"
+                 type="submit"
+                 value="Ladda ner">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<input id="download"
class="btn btn-outline-secondary"
type="submit"
value="Ladda ner"
for="download">
</input>
<input id="download"
class="btn btn-outline-secondary"
type="submit"
value="Ladda ner">
🧰 Tools
🪛 HTMLHint (1.9.2)

[error] 108-108: Tag must be paired, no start tag: [ ]

(tag-pair)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rodatraden/templates/rodatraden/block/block_detail.html` around lines 103 -
108, The <input id="download"> element is invalidly closed and has an invalid
"for" attribute; remove the stray </input> closing tag and delete the
for="download" attribute from the input element (refer to the input with id
"download") so the submit button is a proper void <input> element.

<div class="flex-grow-1">
<input type="file" class="form-control" name="excel_file" id="inputExcelFile">
<label class="form-text" for="inputExcelFile">
Välj fil eller ladda ner
</label>
</div>
Expand Down
8 changes: 4 additions & 4 deletions rodatraden/templates/rodatraden/block/block_list.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% extends 'rodatraden/index.html' %}

{% load render_table from django_tables2 %}
{% load bootstrap4 %}
{% load django_bootstrap5 %}

{% block title %}
Blockscheman - {{ user }}
Expand All @@ -10,7 +10,7 @@
{% block menu %}
<a class="btn btn-gone add-block"
data-id="{% url 'block-create' user %}"
data-toggle="tooltip" title="Skapa nytt">
data-bs-toggle="tooltip" title="Skapa nytt">
<span class="fa fa-plus"></span>
</a>
{% endblock %}
Expand All @@ -29,9 +29,9 @@ <h2><a href="{% url 'block-detail' user block.slug %}">
<h6>{{ block.start_year }}</h6>

{% if block.private %}
<span class="badge badge-warning">Privat</span>
<span class="badge bg-warning text-dark">Privat</span>
{% else %}
<span class="badge badge-success">Publik</span>
<span class="badge bg-success">Publik</span>
{% endif %}

{% if this_block.description %}
Expand Down
Loading
Loading