---
title: "Feature: Project filter selections have a URL"
manual: "Academic Projects"
version: "main"
permalink: "https://docs.typo3.org/permalink/fgtclb/academic-projects:feature-1790226101@main"
source: "Changelog/3.0/Feature-FilterSelectionsHaveAUrl.rst"
rendered: "2026-10-12T23:12:54+00:00"
---

# Feature: Project filter selections have a URL {#feature-1790226101}

## Description {#description}

The filter, sorting and active state form of both project list plugins submits
by POST, and a filtered list used to be the answer to that POST: it had no URL
of its own. It could not be bookmarked or shared, a reload asked to send the
form again, and no link rendered inside the list could carry the selection.

Both plugins now answer the submission with a `303 See Other` to the same page
and plugin, carrying the selection as GET arguments:

```text
?tx_academicprojects_projectlist[action]=list
&tx_academicprojects_projectlist[controller]=Project
&tx_academicprojects_projectlist[demand][activeState]=completed
&tx_academicprojects_projectlist[demand][filterCollection][categories]=3,6
&tx_academicprojects_projectlist[demand][sortingDirection]=asc
&tx_academicprojects_projectlist[demand][sortingField]=title
&cHash=…
```

-   The URL is built from what the plugin accepted, not from the request: a
    category of another group, a uid no category has, an active state the list
    does not offer and the referrer and request hash fields of the form never
    appear in it; an unknown active state becomes `all`.
-   The categories of every category type are one comma separated list, in
    ascending order, so one selection has exactly one URL.
-   The sorting and the active state are always part of the URL. The content
    element's preset categories, sorting and active state apply only to the
    page URL without any list argument, so a visitor who clears a preset
    category keeps it cleared.
-   A GET request with those arguments renders the list exactly as the POST did
    before, and the form shows the selection again.
-   The selection is read from the submitted form alone. A form that posts to
    the URL of a filtered list replaces its selection rather than adding to it,
    and a POST carrying no demand of the plugin - another plugin's form on the
    page - is not redirected.

Nothing changes in the templates, so overridden filter templates keep working as
long as they submit fields the demand knows: the redirect carries the selection
the plugin accepted and nothing else. A field a project adds to its filter
partial, read by a listener from the request, is lost on the redirect. The list
action stays non-cacheable, and the demand is not part of the cache hash, so
every filter URL of a list shares one page cache entry, see
[Important: The list demand is not part of the cache hash](https://docs.typo3.org/permalink/fgtclb/academic-projects:important-1790226106@main).

## Impact {#impact}

-   A filter or sorting submission answers `303` instead of `200`. Browsers and
    `fetch()` follow it by default; JavaScript that posts the form and reads
    the HTML of the response itself has to follow the redirect. The redirect
    target carries the plugin's own arguments only: a page type, arguments of
    other plugins and campaign parameters of the submitted URL are not kept,
    so a form posted to a page type endpoint lands on the full page.
-   A project that added its own redirect after a POST, in a subclass of the
    controller, can drop it together with the subclass, which no longer loads
    in 3.0, see [Breaking: The project controller is final](https://docs.typo3.org/permalink/fgtclb/academic-projects:breaking-1791043407@main). The URLs that redirect produced
    change their `cHash`, see [Important: The list demand is not part of the cache hash](https://docs.typo3.org/permalink/fgtclb/academic-projects:important-1790226106@main).
-   A route enhancer for these plugins must not declare `defaults` for the
    sorting: a default is left out of a generated path, so the redirect for the
    default sorting would end on the page URL without arguments, where the
    preset applies again.
