Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
04d58d8
feat: init typescript build
maikschneider Nov 26, 2025
a396957
feat: init new navigation component
maikschneider Nov 26, 2025
01f4da2
feat: add sys_category fixture
maikschneider Dec 18, 2025
c269eb3
feat: add entry point repository methods
maikschneider Dec 18, 2025
9d45719
Merge branch 'v14' into category-tree
maikschneider Jan 6, 2026
0312d0a
feat: filter records by category
maikschneider Jan 6, 2026
6daac97
fix: correct pid on save
maikschneider Jan 11, 2026
7a951f3
feat: sort categories when creating via drag and drop
maikschneider Jan 11, 2026
7945ae6
feat: align more option button, collapse and reload function
maikschneider Jan 11, 2026
b85c950
fix: prevent null reference in context menu handler
maikschneider Jan 17, 2026
5fd0896
Merge branch 'master' into category-tree
maikschneider May 29, 2026
0460220
feat: add root element, support category types
maikschneider May 29, 2026
9cc7257
fix: sorting of inserted recrods
maikschneider May 29, 2026
0859a67
fix: display hidden categories
maikschneider Jun 10, 2026
5901c37
fix: restore undefined $userTsConfig read in category tree config
maikschneider Jun 10, 2026
03b88cf
fix: use mb_strtolower for category tree search
maikschneider Jun 10, 2026
67c2263
refactor: remove dead page-tree port from category tree controller
maikschneider Jun 10, 2026
995b635
fix: drop nullsafe on non-nullable Icon and dedupe icon lookup
maikschneider Jun 10, 2026
17a8b19
perf: avoid discarded root getTree() call when loading a subtree
maikschneider Jun 10, 2026
e56f517
feat: set category tree node editable/deletable from user access
maikschneider Jun 10, 2026
daccb9e
feat: hide category tree create affordance without modify access
maikschneider Jun 10, 2026
1ed4e28
Merge remote-tracking branch 'origin/master' into category-tree
maikschneider Jun 12, 2026
c1bfd67
fix: npm build
maikschneider Jun 12, 2026
abe44d2
Merge branch 'master' into category-tree
maikschneider Jun 17, 2026
3914c9b
Merge branch 'master' into category-tree
maikschneider Jul 13, 2026
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
86 changes: 86 additions & 0 deletions Classes/Controller/AbstractBackendController.php
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ public function processRequest(RequestInterface $request): ResponseInterface
$this->addSearchConstraint();
$this->addFilterConstraint();
$this->addLanguageConstraint();
$this->addCategoryConstraint();
$this->addAdditionalConstraints();
$this->addOrderConstraint();
$this->addBasicQueryConstraints();
Expand Down Expand Up @@ -938,6 +939,91 @@ protected function getModuleDataSetting(string $setting): mixed
return $this->getModuleData()['settings'][$setting] ?? null;
}

protected function addCategoryConstraint(): void
{
$categoryId = $this->getSelectedCategoryId();
if ($categoryId === null) {
return;
}

// Find category columns for the current table
$categoryColumns = $this->getCategoryColumns();
if (empty($categoryColumns)) {
return;
}

// Build OR constraint for all category columns
$categoryConstraints = [];
foreach ($categoryColumns as $columnName) {
$mmTable = $GLOBALS['TCA'][$this->getTableName()]['columns'][$columnName]['config']['MM'] ?? 'sys_category_record_mm';
$mmMatchFields = $GLOBALS['TCA'][$this->getTableName()]['columns'][$columnName]['config']['MM_match_fields'] ?? [];

// Subquery to find records with the selected category
$subQb = $this->connectionPool->getQueryBuilderForTable($mmTable);
$subQb->select('uid_foreign')
->from($mmTable)
->where(
$subQb->expr()->eq('uid_local', $subQb->createNamedParameter($categoryId, Connection::PARAM_INT))
);

if (!empty($mmMatchFields['tablenames'])) {
$subQb->andWhere(
$subQb->expr()->eq('tablenames', $subQb->createNamedParameter($mmMatchFields['tablenames']))
);
}
if (!empty($mmMatchFields['fieldname']) || isset($GLOBALS['TCA'][$this->getTableName()]['columns'][$columnName])) {
$fieldname = $mmMatchFields['fieldname'] ?? $columnName;
$subQb->andWhere(
$subQb->expr()->eq('fieldname', $subQb->createNamedParameter($fieldname))
);
}

$recordUids = $subQb->executeQuery()->fetchFirstColumn();

if (!empty($recordUids)) {
$categoryConstraints[] = $this->queryBuilder->expr()->in(
't1.uid',
$this->queryBuilder->quoteArrayBasedValueListToIntegerList($recordUids)
);
}
}

if (!empty($categoryConstraints)) {
$this->additionalConstraints[] = $this->queryBuilder->expr()->or(...$categoryConstraints);
} else {
// No records found with this category - return empty result
$this->additionalConstraints[] = $this->queryBuilder->expr()->eq('t1.uid', 0);
}
}

protected function getSelectedCategoryId(): ?int
{
$categoryId = $this->request->getQueryParams()['category'] ?? $this->request->getParsedBody()['category'] ?? null;
if ($categoryId !== null && MathUtility::canBeInterpretedAsInteger($categoryId)) {
return (int)$categoryId;
}
return null;
}

protected function getCategoryColumns(): array
{
$categoryColumns = [];
$tableName = $this->getTableName();

if (!isset($GLOBALS['TCA'][$tableName]['columns'])) {
return [];
}

foreach ($GLOBALS['TCA'][$tableName]['columns'] as $columnName => $config) {
if (in_array($config['config']['type'] ?? '', ['select', 'category']) &&
($config['config']['foreign_table'] ?? '') === 'sys_category') {
$categoryColumns[] = $columnName;
}
}

return $categoryColumns;
}

protected function addAdditionalConstraints(): void
{
}
Expand Down
Loading
Loading