getParsedBody(); $table = (string)($parsedBody['table'] ?? ''); $selectedColumns = $parsedBody['selectedColumns'] ?? []; if ($table === '' || !is_array($selectedColumns)) { return $this->jsonResponse([ 'success' => false, 'message' => htmlspecialchars( $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_column_selector.xlf:updateColumnView.nothingUpdated') ), ]); } $backendUser = $this->getBackendUserAuthentication(); $displayFields = $backendUser->getModuleData('list/displayFields'); $displayFields[$table] = $selectedColumns; $backendUser->pushModuleData('list/displayFields', $displayFields); return $this->jsonResponse(['success' => true]); } /** * Generate the show columns selector form */ public function showColumnsSelectorAction(ServerRequestInterface $request): ResponseInterface { $queryParams = $request->getQueryParams(); $table = (string)($queryParams['table'] ?? ''); if ($table === '') { throw new \RuntimeException('No table was given for selecting columns', 1625169125); } $view = $this->backendViewFactory->create($request); $view->assignMultiple([ 'table' => $table, 'columns' => $this->getColumns($table, (int)($queryParams['id'] ?? 0)), ]); return $this->htmlResponse($view); } /** * Retrieve all columns for the table, which can be selected */ protected function getColumns(string $table, int $pageId): array { $tsConfig = BackendUtility::getPagesTSconfig($pageId); // Current fields selection $displayFields = $this->getBackendUserAuthentication()->getModuleData('list/displayFields')[$table] ?? []; if ($table === '_FILE') { // Special handling for _FILE (merging sys_file and sys_file_metadata together) $fields = $this->getFileFields(); } else { // Request fields from table and add pseudo fields $fields = array_merge(BackendUtility::getAllowedFieldsForTable($table), self::PSEUDO_FIELDS); } $columns = $specialColumns = $disabledColumns = []; foreach ($fields as $fieldName) { $concreteTableName = $table; // In case we deal with _FILE, the field name is prefixed with the // concrete table name, which is either sys_file or sys_file_metadata. if ($table === '_FILE') { [$concreteTableName, $fieldName] = explode('|', $fieldName); } // Hide field if disabled if ($tsConfig['TCEFORM.'][$concreteTableName . '.'][$fieldName . '.']['disabled'] ?? false) { continue; } $schema = $this->tcaSchemaFactory->get($concreteTableName); $labelFieldName = false; if ($schema->hasCapability(TcaSchemaCapability::Label)) { $labelFieldName = $schema->getCapability(TcaSchemaCapability::Label)->getPrimaryFieldName(); } // Determine if the column should be disabled (Meaning it is always selected and can not be turned off) $isDisabled = $fieldName === $labelFieldName; // Determine field label $label = ($schema->hasField($fieldName) ? $schema->getField($fieldName)->getLabel() : '') ?: null; $label = $this->getLanguageService()->translateLabel( $tsConfig['TCEFORM.'][$concreteTableName . '.'][$fieldName . '.']['label.'] ?? [], $tsConfig['TCEFORM.'][$concreteTableName . '.'][$fieldName . '.']['label'] ?? $label ?? 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.' . $fieldName ); // Add configuration for this column $columnConfiguration = [ 'name' => $fieldName, 'selected' => $isDisabled || in_array($fieldName, $displayFields, true), 'disabled' => $isDisabled, 'pseudo' => in_array($fieldName, self::PSEUDO_FIELDS, true), 'label' => $label, ]; // Add column configuration to the correct group if ($columnConfiguration['disabled']) { $disabledColumns[] = $columnConfiguration; } elseif (!$columnConfiguration['label']) { $specialColumns[] = $columnConfiguration; } else { $columns[] = $columnConfiguration; } } // Sort standard columns by their resolved label usort($columns, static fn($a, $b) => $a['label'] <=> $b['label']); // Disabled columns go first, followed by standard columns // and special columns, which do not have a label. return array_merge($disabledColumns, $columns, $specialColumns); } /** * Get file related fields by merging sys_file and sys_file_metadata together * and adding the corresponding table as prefix (needed for labels processing). */ protected function getFileFields(): array { // Get all sys_file fields expect excluded ones $fileFields = array_filter( BackendUtility::getAllowedFieldsForTable('sys_file'), static fn(string $field): bool => !in_array($field, self::EXCLUDE_FILE_FIELDS, true) ); // Always add crdate and tstamp fields for files $fileFields = array_unique(array_merge($fileFields, ['crdate', 'tstamp'])); // Update the exclude fields with the fields, already added through sys_file, since those take precedence $excludeFields = array_merge($fileFields, self::EXCLUDE_FILE_FIELDS); // Get all sys_file_metadata fields expect excluded ones $fileMetaDataFields = array_filter( BackendUtility::getAllowedFieldsForTable('sys_file_metadata'), static fn(string $field): bool => !in_array($field, $excludeFields, true) ); // Merge sys_file and sys_file_metadata fields together, while adding the table name as prefix return array_merge( array_map(static fn(string $value): string => 'sys_file|' . $value, $fileFields), array_map(static fn(string $value): string => 'sys_file_metadata|' . $value, $fileMetaDataFields), ); } protected function htmlResponse(ViewInterface $view): ResponseInterface { $response = $this->responseFactory ->createResponse() ->withHeader('Content-Type', 'text/html; charset=utf-8'); $response->getBody()->write($view->render('ColumnSelector')); return $response; } protected function jsonResponse(array $data): ResponseInterface { $response = $this->responseFactory ->createResponse() ->withAddedHeader('Content-Type', 'application/json; charset=utf-8'); $response->getBody()->write(json_encode($data)); return $response; } protected function getBackendUserAuthentication(): BackendUserAuthentication { return $GLOBALS['BE_USER']; } protected function getLanguageService(): LanguageService { return $GLOBALS['LANG']; } }