---
title: "Feature: Change the project lists by event"
manual: "Academic Projects"
version: "main"
source: "Changelog/3.0/Feature-ListPluginEvents.rst"
rendered: "2026-09-22T20:39:25+00:00"
---

# Feature: Change the project lists by event {#feature-list-plugin-events}

## Description {#description}

`\FGTCLB\AcademicProjects\Event\ModifyProjectDemandEvent` and
`\FGTCLB\AcademicProjects\Event\ModifyProjectListEvent` are PSR-14 events
dispatched by both project list plugins.

The demand event fires after the demand has been built from the content element
settings and the request and before the projects are queried; the demand a
listener hands back is the one that is queried and assigned to the view. The
list event fires after the query and before the view variables are assigned; a
listener replaces the projects, replaces the applicable categories, or assigns
further view variables.

Both carry
`\FGTCLB\AcademicBase\Domain\Model\Dto\PluginControllerActionContextInterface`,
so a listener knows the request, the site, the content element, its settings and
which of the two list plugins is rendering.

**EXT:my_extension/Classes/EventListener/ShowRunningProjectsOnly.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\EventListener;

use FGTCLB\AcademicProjects\Event\ModifyProjectDemandEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;

final class ShowRunningProjectsOnly
{
    #[AsEventListener(identifier: 'my-extension/show-running-projects-only')]
    public function __invoke(ModifyProjectDemandEvent $event): void
    {
        if ($event->getPluginControllerActionContext()->getPluginName() === 'ProjectList') {
            $event->getDemand()->setActiveState('active');
        }
    }
}
```

## Impact {#impact}

Nothing changes in an installation that has no listener. A project that
subclasses `ProjectController` to adjust the demand, the result or the view
variables can drop the subclass and the plugin re-registration that goes with
it, and listen instead.

One detail is worth knowing: the applicable categories are computed once, before
the list event, and not recomputed afterwards - a listener that replaces the
projects and wants the filter to match them sets the categories as well.

> [!WARNING]
> A demand listener widens as easily as it narrows. The demand *is* the
> query, so `setShowHiddenRecords(true)` shows hidden project pages to
> every visitor, `setPages([])` drops the storage restriction the editor
> chose, and `setSorting()` overrides the editor's ordering. What a
> listener cannot undo is the page type, the enable fields other than
> `disabled` and the `uid` tiebreaker of the ordering. A result
> handed to `setProjects()` is rendered in the order it carries - the
> ordering of the repository is not reapplied - and a demand built from
> scratch rather than mutated drops the editor's settings, the category
> filter the visitor submitted and, with `showSelected`, the meaning of
> `pages`.
