* Add GitHub Releases modal to backoffice
Shows release notes (fetched from the GitHub API, cached for an hour,
Markdown rendered via league/commonmark) from a top-right nav button
available whether logged in or out, with the current version shown in
the modal header. Also clears cache.app on container start so the
release cache can't carry stale data across redeploys of the
var/ Docker volume.
* Render release notes markdown via twig/markdown-extra instead of manual CommonMark
twig/extra-bundle already wires the markdown filter to league/commonmark
automatically once twig/markdown-extra is installed, so the service no
longer needs to instantiate and call CommonMarkConverter itself.
* Explicitly disallow unsafe link protocols in rendered markdown
twig/extra-bundle's default (allow_unsafe_links: true) permits
javascript:/vbscript:/file:/data: URLs in rendered links, kept for
backwards compatibility. Disable it explicitly since we now render
release note markdown through this filter.
* Autolink bare URLs in rendered release notes
The Twig CommonMark converter only registers CommonMarkCoreExtension by
default, which doesn't turn bare URLs into clickable links (unlike
GitHub's own renderer). Register CommonMark's AutolinkExtension via the
twig.markdown.league_extension tag so PR/compare URLs in release notes
render as actual hyperlinks instead of plain text.
* Address review feedback on GitHubReleasesService and tests
- Extract the cache callback into a named fetchReleases() method instead
of an inline closure with all the fetch/parse logic.
- Simplify the User-Agent header to just "TijdVoorDeTest".
- Add a 5s HTTP timeout so a slow GitHub API can't block the request.
- Cache HTTP failures for only 60s instead of the full hour, so a brief
GitHub outage doesn't suppress releases for as long.
- Replace deprecated word-wrap with overflow-wrap in release-notes CSS.
- Extract duplicated mock setup in ReleasesControllerTest into a helper.
- Reword the "could not load" translation ("Kan" instead of "Kon").
* Address second round of CodeRabbit feedback
- Escape raw HTML embedded in release-note markdown instead of passing
it through, via twig_extra.commonmark.html_input: escape.
- Don't cache GitHub API failures at all (set $save = false in the
cache callback) instead of caching them for a short TTL, so the very
next request retries immediately.
- Sort releases by published_at descending before mapping, so the
first entry (used for the current-version badge and default-expanded
accordion item) is guaranteed to be the newest regardless of the
order GitHub's API happens to return.
* feat: quiz page question rework (#181)
- Replace Bootstrap accordion with flat card list for questions
- Add HTML5 drag-and-drop reordering with placeholder-between-cards UX
and amber/green/red save status indicator next to the heading
- Add edit button per question opening a Bootstrap modal (bo--modal-form
Stimulus controller with X-Modal-Request header pattern)
- Show read-only view button instead of edit for locked/finalized quizzes
- Add BankQuestion edit modal in question bank tab using same infrastructure
- Move modal action buttons into modal-footer via <template data-modal-footer>
- Fix IS_AUTHENTICATED_FULLY 403: replace ROLE_USER with IS_AUTHENTICATED
on all backoffice controllers and in security.yaml access_control
* feat: answer field UX improvements
- Auto-add one empty answer field when opening a blank question form
- Auto-append new empty field when typing in the last answer field
- Strip empty answer rows before submit (novalidate + JS cleanup)
- Tab key skips correct/delete buttons, jumping straight to next answer
* feat: add bank question via modal + dirty modal guard
- Add question in question bank now opens a modal instead of navigating
to a full-page form, consistent with the edit modal pattern
- Modal closes are blocked by static backdrop once the user has made any
change (input, checkbox, drag-reorder, sort, randomize, remove answer)
- Dirty state resets when the modal is fully hidden
* fix: modal save button + missing translations
- Replace form="id" cross-element approach with requestSubmit() for
reliable save button wiring in modal footer
- Re-call _bindDirty after validation-error re-render so dirty guard
is preserved across save attempts
- Translate missing Dutch strings: Order saved, Error saving order,
Question details, View
* feat: replace fetch-based modal forms with Turbo Frames (#181)
Enable @hotwired/turbo with Drive explicitly disabled, then migrate the
bo--modal-form Stimulus controller (custom fetch + X-Modal-Request pattern)
to a thin bo--modal controller that lets Turbo handle HTTP and DOM swap.
Adds frame templates for quiz questions and bank questions; controllers now
detect Turbo-Frame header instead of X-Modal-Request.
* fix: exclude auto-generated reference.php from pre-commit CS-fixer
* fix: use correct Turbo v8 session export to disable Drive
* fix: address CodeRabbit review findings on PR #183
- Add SeasonVoter::EDIT guard to QuizQuestionController::view()
- Add full-count check in reorder() to reject partial ordering payloads
- Retry once in _persistOrder(); lock drag and show dismissible alert on failure
- Remove tabindex="-1" from correct-answer toggle button (accessibility)
- Replace 'EMPTY'|trans placeholder with proper copy
- Add data-modal-title to question bank Edit button to prevent stale title
* fix: correct Dutch translations flagged in review
- 'Error saving order': 'Fout bij het opslaan van de volgorde' (consistent with sibling strings)
- 'Owner(s)': 'Eigenaar/Eigenaren' (unambiguous, correct Dutch plural)
* fix: set explicit form action URLs for Turbo Frame modal forms
When a form has no explicit action, Symfony renders action="" which the
browser resolves to the page URL, not the URL the Turbo Frame was
fetched from. This caused edits submitted via the modal to POST to the
wrong route and silently discard changes.
* fix: preserve answer ordering on save and add coverage for all sort operations
- Enable Turbo Drive and use visit() in modal controller so submit redirects to the page under the modal
- Add removeAnswer() to Question entity so Symfony form can manage the collection with PHP 8.5 private(set)
- Remove applyAnswerOrdering() from QuizQuestionController and QuestionBankController — it iterated the Doctrine collection in its old DB order and overwrote the ordering values submitted from the form
- Add QuizQuestionControllerTest covering answer ordering preservation and question reordering within a quiz
- Extend QuestionBankControllerTest with answer ordering tests for both new and edit bank questions
* fix: translate three missing Dutch strings in nl.xliff
* Updated CLAUDE.md
* refactor: replace addEventListener with Stimulus data-action in modal and question-list controllers
* refactor: replace addEventListener with Stimulus data-action in form-collection controller
Move drag-and-drop and auto-expand event handling from imperative addEventListener
calls to declarative data-action descriptors in answer_row.html.twig and the
collection target templates. Stimulus MutationObserver picks up the descriptors on
dynamically added rows, removing the need for _makeDraggable(). The ancestor-form
submit listener stays as addEventListener since Stimulus data-action cannot reach
elements outside the controller's subtree.
* feat: show label colour badges in question bank edit form
Replace plain checkbox text with coloured Bootstrap badge pills in the
labels section of the bank question edit form (both standalone and modal
frame variants). Adds choice_attr to pass data-colour to each checkbox,
then renders the input manually so the label can hold the badge without
the Bootstrap 5 form theme wrapping in a second plain-text label.