/** * Migrate date and datetime db field values to timestamp * * @param array $result * @return array */ public function addData(array $result) { $dateTimeFormats = QueryHelper::getDateTimeFormats(); foreach ($result['processedTca']['columns'] as $column => $columnConfig) { if (isset($columnConfig['config']['dbType']) && ($columnConfig['config']['dbType'] === 'date' || $columnConfig['config']['dbType'] === 'datetime')) { if (!empty($result['databaseRow'][$column]) && $result['databaseRow'][$column] !== $dateTimeFormats[$columnConfig['config']['dbType']]['empty']) { // Create an ISO-8601 date from current field data; the database always contains UTC // The field value is something like "2016-01-01" or "2016-01-01 10:11:12", so appending "UTC" // makes date() treat it as a UTC date (which is what we store in the database). $result['databaseRow'][$column] = date('c', strtotime($result['databaseRow'][$column] . ' UTC')); } else { // Set to 0 timestamp $result['databaseRow'][$column] = 0; } } else { // its a UNIX timestamp! We do not modify this here, as it will only be treated as a datetime because // of eval being set to "date" or "datetime". This is handled in InputTextElement then. } } return $result; }
/** * Prepares the clause by which the result elements are sorted. See description of ORDER BY in * SQL standard for reference. * * @return void */ protected function prepareOrderByStatement() { if (empty($this->config['orderBy'])) { $this->queryBuilder->addOrderBy($GLOBALS['TCA'][$this->table]['ctrl']['label']); } else { foreach (QueryHelper::parseOrderBy($this->config['orderBy']) as $orderPair) { list($fieldName, $order) = $orderPair; $this->queryBuilder->addOrderBy($fieldName, $order); } } }
/** * Returns an array of file mount records, taking workspaces and user home and group home directories into account * Needs to be called AFTER the groups have been loaded. * * @return array * @internal */ public function getFileMountRecords() { static $fileMountRecordCache = []; if (!empty($fileMountRecordCache)) { return $fileMountRecordCache; } $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class); // Processing file mounts (both from the user and the groups) $fileMounts = array_unique(GeneralUtility::intExplode(',', $this->dataLists['filemount_list'], true)); // Limit file mounts if set in workspace record if ($this->workspace > 0 && !empty($this->workspaceRec['file_mountpoints'])) { $workspaceFileMounts = GeneralUtility::intExplode(',', $this->workspaceRec['file_mountpoints'], true); $fileMounts = array_intersect($fileMounts, $workspaceFileMounts); } if (!empty($fileMounts)) { $orderBy = $GLOBALS['TCA']['sys_filemounts']['ctrl']['default_sortby'] ?? 'sorting'; $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_filemounts'); $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class))->add(GeneralUtility::makeInstance(HiddenRestriction::class))->add(GeneralUtility::makeInstance(RootLevelRestriction::class)); $queryBuilder->select('*')->from('sys_filemounts')->where($queryBuilder->expr()->in('uid', $queryBuilder->createNamedParameter($fileMounts, Connection::PARAM_INT_ARRAY))); foreach (QueryHelper::parseOrderBy($orderBy) as $fieldAndDirection) { $queryBuilder->addOrderBy(...$fieldAndDirection); } $fileMountRecords = $queryBuilder->execute()->fetchAll(\PDO::FETCH_ASSOC); if ($fileMountRecords !== false) { foreach ($fileMountRecords as $fileMount) { $fileMountRecordCache[$fileMount['base'] . $fileMount['path']] = $fileMount; } } } // Read-only file mounts $readOnlyMountPoints = trim($GLOBALS['BE_USER']->getTSConfigVal('options.folderTree.altElementBrowserMountPoints')); if ($readOnlyMountPoints) { // We cannot use the API here but need to fetch the default storage record directly // to not instantiate it (which directly applies mount points) before all mount points are resolved! $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_file_storage'); $defaultStorageRow = $queryBuilder->select('uid')->from('sys_file_storage')->where($queryBuilder->expr()->eq('is_default', $queryBuilder->createNamedParameter(1, \PDO::PARAM_INT)))->setMaxResults(1)->execute()->fetch(\PDO::FETCH_ASSOC); $readOnlyMountPointArray = GeneralUtility::trimExplode(',', $readOnlyMountPoints); foreach ($readOnlyMountPointArray as $readOnlyMountPoint) { $readOnlyMountPointConfiguration = GeneralUtility::trimExplode(':', $readOnlyMountPoint); if (count($readOnlyMountPointConfiguration) === 2) { // A storage is passed in the configuration $storageUid = (int) $readOnlyMountPointConfiguration[0]; $path = $readOnlyMountPointConfiguration[1]; } else { if (empty($defaultStorageRow)) { throw new \RuntimeException('Read only mount points have been defined in User TsConfig without specific storage, but a default storage could not be resolved.', 1404472382); } // Backwards compatibility: If no storage is passed, we use the default storage $storageUid = $defaultStorageRow['uid']; $path = $readOnlyMountPointConfiguration[0]; } $fileMountRecordCache[$storageUid . $path] = ['base' => $storageUid, 'title' => $path, 'path' => $path, 'read_only' => true]; } } // Personal or Group filemounts are not accessible if file mount list is set in workspace record if ($this->workspace <= 0 || empty($this->workspaceRec['file_mountpoints'])) { // If userHomePath is set, we attempt to mount it if ($GLOBALS['TYPO3_CONF_VARS']['BE']['userHomePath']) { list($userHomeStorageUid, $userHomeFilter) = explode(':', $GLOBALS['TYPO3_CONF_VARS']['BE']['userHomePath'], 2); $userHomeStorageUid = (int) $userHomeStorageUid; $userHomeFilter = '/' . ltrim($userHomeFilter, '/'); if ($userHomeStorageUid > 0) { // Try and mount with [uid]_[username] $path = $userHomeFilter . $this->user['uid'] . '_' . $this->user['username'] . $GLOBALS['TYPO3_CONF_VARS']['BE']['userUploadDir']; $fileMountRecordCache[$userHomeStorageUid . $path] = ['base' => $userHomeStorageUid, 'title' => $this->user['username'], 'path' => $path, 'read_only' => false, 'user_mount' => true]; // Try and mount with only [uid] $path = $userHomeFilter . $this->user['uid'] . $GLOBALS['TYPO3_CONF_VARS']['BE']['userUploadDir']; $fileMountRecordCache[$userHomeStorageUid . $path] = ['base' => $userHomeStorageUid, 'title' => $this->user['username'], 'path' => $path, 'read_only' => false, 'user_mount' => true]; } } // Mount group home-dirs if ((is_array($this->user) && $this->user['options'] & Permission::PAGE_EDIT) == 2 && $GLOBALS['TYPO3_CONF_VARS']['BE']['groupHomePath'] != '') { // If groupHomePath is set, we attempt to mount it list($groupHomeStorageUid, $groupHomeFilter) = explode(':', $GLOBALS['TYPO3_CONF_VARS']['BE']['groupHomePath'], 2); $groupHomeStorageUid = (int) $groupHomeStorageUid; $groupHomeFilter = '/' . ltrim($groupHomeFilter, '/'); if ($groupHomeStorageUid > 0) { foreach ($this->userGroups as $groupData) { $path = $groupHomeFilter . $groupData['uid']; $fileMountRecordCache[$groupHomeStorageUid . $path] = ['base' => $groupHomeStorageUid, 'title' => $groupData['title'], 'path' => $path, 'read_only' => false, 'user_mount' => true]; } } } } return $fileMountRecordCache; }
/** * Build the MySql where clause by table. * * @param QueryBuilder $queryBuilder * @param string $tableName Record table name * @param array $fieldsToSearchWithin User right based visible fields where we can search within. * @return CompositeExpression */ protected function makeQuerySearchByTable(QueryBuilder &$queryBuilder, $tableName, array $fieldsToSearchWithin) { $constraints = []; // If the search string is a simple integer, assemble an equality comparison if (MathUtility::canBeInterpretedAsInteger($this->queryString)) { foreach ($fieldsToSearchWithin as $fieldName) { if ($fieldName !== 'uid' && $fieldName !== 'pid' && !isset($GLOBALS['TCA'][$tableName]['columns'][$fieldName])) { continue; } $fieldConfig = $GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config']; $fieldType = $fieldConfig['type']; $evalRules = $fieldConfig['eval'] ?: ''; // Assemble the search condition only if the field is an integer, or is uid or pid if ($fieldName === 'uid' || $fieldName === 'pid' || $fieldType === 'input' && $evalRules && GeneralUtility::inList($evalRules, 'int')) { $constraints[] = $queryBuilder->expr()->eq($fieldName, $queryBuilder->createNamedParameter($this->queryString, \PDO::PARAM_INT)); } elseif ($fieldType === 'text' || $fieldType === 'flex' || $fieldType === 'input' && (!$evalRules || !preg_match('/date|time|int/', $evalRules))) { // Otherwise and if the field makes sense to be searched, assemble a like condition $constraints[] = $constraints[] = $queryBuilder->expr()->like($fieldName, $queryBuilder->createNamedParameter('%' . $queryBuilder->escapeLikeWildcards((int) $this->queryString) . '%', \PDO::PARAM_STR)); } } } else { $like = '%' . $queryBuilder->escapeLikeWildcards($this->queryString) . '%'; foreach ($fieldsToSearchWithin as $fieldName) { if (!isset($GLOBALS['TCA'][$tableName]['columns'][$fieldName])) { continue; } $fieldConfig =& $GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config']; $fieldType = $fieldConfig['type']; $evalRules = $fieldConfig['eval'] ?: ''; // Check whether search should be case-sensitive or not $searchConstraint = $queryBuilder->expr()->andX($queryBuilder->expr()->comparison('LOWER(' . $queryBuilder->quoteIdentifier($fieldName) . ')', 'LIKE', $queryBuilder->createNamedParameter(strtolower($like), \PDO::PARAM_STR))); if (is_array($fieldConfig['search'])) { if (in_array('case', $fieldConfig['search'], true)) { // Replace case insensitive default constraint $searchConstraint = $queryBuilder->expr()->andX($queryBuilder->expr()->like($fieldName, $queryBuilder->createNamedParameter($like, \PDO::PARAM_STR))); } // Apply additional condition, if any if ($fieldConfig['search']['andWhere']) { $searchConstraint->add(QueryHelper::stripLogicalOperatorPrefix($fieldConfig['search']['andWhere'])); } } // Assemble the search condition only if the field makes sense to be searched if ($fieldType === 'text' || $fieldType === 'flex' || $fieldType === 'input' && (!$evalRules || !preg_match('/date|time|int/', $evalRules))) { if ($searchConstraint->count() !== 0) { $constraints[] = $searchConstraint; } } } } // If no search field conditions have been build ensure no results are returned if (empty($constraints)) { return '0=1'; } return $queryBuilder->expr()->orX(...$constraints); }
/** * Generates a list of Page-uid's from $id. List does not include $id itself * The only pages excluded from the list are deleted pages. * * @param int $id Start page id * @param int $depth Depth to traverse down the page tree. * @param int $begin Is an optional integer that determines at which level in the tree to start collecting uid's. Zero means 'start right away', 1 = 'next level and out' * @param string $perms_clause Perms clause * @return string Returns the list with a comma in the end (if any pages selected!) */ public function extGetTreeList($id, $depth, $begin = 0, $perms_clause) { /** @var QueryBuilder $queryBuilder */ $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages'); $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class)); $depth = (int) $depth; $begin = (int) $begin; $id = (int) $id; $theList = ''; if ($id && $depth > 0) { $result = $queryBuilder->select('uid', 'title')->from('pages')->where($queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)), $queryBuilder->expr()->in('doktype', $queryBuilder->createNamedParameter($GLOBALS['TYPO3_CONF_VARS']['FE']['content_doktypes'], \PDO::PARAM_INT)), QueryHelper::stripLogicalOperatorPrefix($perms_clause))->execute(); while ($row = $result->fetch()) { if ($begin <= 0) { $theList .= $row['uid'] . ','; $this->extPageInTreeInfo[] = [$row['uid'], htmlspecialchars($row['title'], $depth)]; } if ($depth > 1) { $theList .= $this->extGetTreeList($row['uid'], $depth - 1, $begin - 1, $perms_clause); } } } return $theList; }
/** * Make value list * * @param string $fieldName * @param string $fieldValue * @param array $conf * @param string $table * @param string $splitString * @return string */ public function makeValueList($fieldName, $fieldValue, $conf, $table, $splitString) { $fieldSetup = $conf; $out = ''; if ($fieldSetup['type'] == 'files') { $d = dir(PATH_site . $fieldSetup['uploadfolder']); while (false !== ($entry = $d->read())) { if ($entry == '.' || $entry == '..') { continue; } $fileArray[] = $entry; } $d->close(); natcasesort($fileArray); foreach ($fileArray as $fileName) { if (GeneralUtility::inList($fieldValue, $fileName) || $fieldValue == $fileName) { if (!$out) { $out = htmlspecialchars($fileName); } else { $out .= $splitString . htmlspecialchars($fileName); } } } } if ($fieldSetup['type'] == 'multiple') { foreach ($fieldSetup['items'] as $key => $val) { if (substr($val[0], 0, 4) == 'LLL:') { $value = $this->languageService->sL($val[0]); } else { $value = $val[0]; } if (GeneralUtility::inList($fieldValue, $val[1]) || $fieldValue == $val[1]) { if (!$out) { $out = htmlspecialchars($value); } else { $out .= $splitString . htmlspecialchars($value); } } } } if ($fieldSetup['type'] == 'binary') { foreach ($fieldSetup['items'] as $Key => $val) { if (substr($val[0], 0, 4) == 'LLL:') { $value = $this->languageService->sL($val[0]); } else { $value = $val[0]; } if (!$out) { $out = htmlspecialchars($value); } else { $out .= $splitString . htmlspecialchars($value); } } } if ($fieldSetup['type'] == 'relation') { $dontPrefixFirstTable = 0; $useTablePrefix = 0; if ($fieldSetup['items']) { foreach ($fieldSetup['items'] as $key => $val) { if (substr($val[0], 0, 4) == 'LLL:') { $value = $this->languageService->sL($val[0]); } else { $value = $val[0]; } if (GeneralUtility::inList($fieldValue, $value) || $fieldValue == $value) { if (!$out) { $out = htmlspecialchars($value); } else { $out .= $splitString . htmlspecialchars($value); } } } } if (stristr($fieldSetup['allowed'], ',')) { $from_table_Arr = explode(',', $fieldSetup['allowed']); $useTablePrefix = 1; if (!$fieldSetup['prepend_tname']) { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class)); $statement = $queryBuilder->select($fieldName)->from($table)->execute(); while ($row = $statement->fetch()) { if (stristr($row[$fieldName], ',')) { $checkContent = explode(',', $row[$fieldName]); foreach ($checkContent as $singleValue) { if (!stristr($singleValue, '_')) { $dontPrefixFirstTable = 1; } } } else { $singleValue = $row[$fieldName]; if ($singleValue !== '' && !stristr($singleValue, '_')) { $dontPrefixFirstTable = 1; } } } } } else { $from_table_Arr[0] = $fieldSetup['allowed']; } if ($fieldSetup['prepend_tname']) { $useTablePrefix = 1; } if ($fieldSetup['foreign_table']) { $from_table_Arr[0] = $fieldSetup['foreign_table']; } $counter = 0; $useSelectLabels = 0; $useAltSelectLabels = 0; $tablePrefix = ''; $labelFieldSelect = []; foreach ($from_table_Arr as $from_table) { if ($useTablePrefix && !$dontPrefixFirstTable && $counter != 1 || $counter == 1) { $tablePrefix = $from_table . '_'; } $counter = 1; if (is_array($GLOBALS['TCA'][$from_table])) { $labelField = $GLOBALS['TCA'][$from_table]['ctrl']['label']; $altLabelField = $GLOBALS['TCA'][$from_table]['ctrl']['label_alt']; if ($GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items']) { $items = $GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items']; foreach ($items as $labelArray) { if (substr($labelArray[0], 0, 4) == 'LLL:') { $labelFieldSelect[$labelArray[1]] = $this->languageService->sL($labelArray[0]); } else { $labelFieldSelect[$labelArray[1]] = $labelArray[0]; } } $useSelectLabels = 1; } if ($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items']) { $items = $GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items']; foreach ($items as $altLabelArray) { if (substr($altLabelArray[0], 0, 4) == 'LLL:') { $altLabelFieldSelect[$altLabelArray[1]] = $this->languageService->sL($altLabelArray[0]); } else { $altLabelFieldSelect[$altLabelArray[1]] = $altLabelArray[0]; } } $useAltSelectLabels = 1; } if (!$this->tableArray[$from_table]) { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($from_table); $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class)); $selectFields = ['uid', $labelField]; if ($altLabelField) { $selectFields[] = $altLabelField; } $queryBuilder->select(...$selectFields)->from($from_table)->orderBy('uid'); if (!$this->backendUserAuthentication->isAdmin() && $GLOBALS['TYPO3_CONF_VARS']['BE']['lockBeUserToDBmounts']) { $webMounts = $this->backendUserAuthentication->returnWebmounts(); $perms_clause = $this->backendUserAuthentication->getPagePermsClause(1); $webMountPageTree = ''; $webMountPageTreePrefix = ''; foreach ($webMounts as $webMount) { if ($webMountPageTree) { $webMountPageTreePrefix = ','; } $webMountPageTree .= $webMountPageTreePrefix . $this->getTreeList($webMount, 999, $begin = 0, $perms_clause); } if ($from_table === 'pages') { $queryBuilder->where(QueryHelper::stripLogicalOperatorPrefix($perms_clause), $queryBuilder->expr()->in('uid', $queryBuilder->createNamedParameter(GeneralUtility::intExplode(',', $webMountPageTree), Connection::PARAM_INT_ARRAY))); } else { $queryBuilder->where($queryBuilder->expr()->in('pid', $queryBuilder->createNamedParameter(GeneralUtility::intExplode(',', $webMountPageTree), Connection::PARAM_INT_ARRAY))); } } $statement = $queryBuilder->execute(); $this->tableArray[$from_table] = []; while ($row = $statement->fetch()) { $this->tableArray[$from_table][] = $row; } } foreach ($this->tableArray[$from_table] as $key => $val) { $GLOBALS['SOBE']->MOD_SETTINGS['labels_noprefix'] = $GLOBALS['SOBE']->MOD_SETTINGS['labels_noprefix'] == 1 ? 'on' : $GLOBALS['SOBE']->MOD_SETTINGS['labels_noprefix']; $prefixString = $GLOBALS['SOBE']->MOD_SETTINGS['labels_noprefix'] == 'on' ? '' : ' [' . $tablePrefix . $val['uid'] . '] '; if (GeneralUtility::inList($fieldValue, $tablePrefix . $val['uid']) || $fieldValue == $tablePrefix . $val['uid']) { if ($useSelectLabels) { if (!$out) { $out = htmlspecialchars($prefixString . $labelFieldSelect[$val[$labelField]]); } else { $out .= $splitString . htmlspecialchars($prefixString . $labelFieldSelect[$val[$labelField]]); } } elseif ($val[$labelField]) { if (!$out) { $out = htmlspecialchars($prefixString . $val[$labelField]); } else { $out .= $splitString . htmlspecialchars($prefixString . $val[$labelField]); } } elseif ($useAltSelectLabels) { if (!$out) { $out = htmlspecialchars($prefixString . $altLabelFieldSelect[$val[$altLabelField]]); } else { $out .= $splitString . htmlspecialchars($prefixString . $altLabelFieldSelect[$val[$altLabelField]]); } } else { if (!$out) { $out = htmlspecialchars($prefixString . $val[$altLabelField]); } else { $out .= $splitString . htmlspecialchars($prefixString . $val[$altLabelField]); } } } } } } } return $out; }
/** * Sets the Doctrine where clause for fetching pages * * @param QueryBuilder $queryBuilder * @param int $id * @param string $searchFilter * @return QueryBuilder */ protected function setWhereClause(QueryBuilder $queryBuilder, $id, $searchFilter = '') : QueryBuilder { $expressionBuilder = $queryBuilder->expr(); $queryBuilder->where(QueryHelper::stripLogicalOperatorPrefix($GLOBALS['BE_USER']->getPagePermsClause(1))); if (is_numeric($id) && $id >= 0) { $queryBuilder->andWhere($expressionBuilder->eq('pid', $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT))); } $excludedDoktypes = $GLOBALS['BE_USER']->getTSConfigVal('options.pageTree.excludeDoktypes'); if (!empty($excludedDoktypes)) { $queryBuilder->andWhere($expressionBuilder->notIn('doktype', $queryBuilder->createNamedParameter(GeneralUtility::intExplode(',', $excludedDoktypes, true), Connection::PARAM_INT_ARRAY))); } if ($searchFilter !== '') { $searchParts = $expressionBuilder->orX(); if (is_numeric($searchFilter) && $searchFilter > 0) { $searchParts->add($expressionBuilder->eq('uid', $queryBuilder->createNamedParameter($searchFilter, \PDO::PARAM_INT))); } $searchFilter = '%' . $queryBuilder->escapeLikeWildcards($searchFilter) . '%'; $useNavTitle = $GLOBALS['BE_USER']->getTSConfigVal('options.pageTree.showNavTitle'); $useAlias = $GLOBALS['BE_USER']->getTSConfigVal('options.pageTree.searchInAlias'); $aliasExpression = ''; if ($useAlias) { $aliasExpression = $expressionBuilder->like('alias', $queryBuilder->createNamedParameter($searchFilter, \PDO::PARAM_STR)); } if ($useNavTitle) { $searchWhereAlias = $expressionBuilder->orX($expressionBuilder->like('nav_title', $queryBuilder->createNamedParameter($searchFilter, \PDO::PARAM_STR)), $expressionBuilder->andX($expressionBuilder->eq('nav_title', $queryBuilder->createNamedParameter('', \PDO::PARAM_STR)), $expressionBuilder->like('title', $queryBuilder->createNamedParameter($searchFilter, \PDO::PARAM_STR)))); if (strlen($aliasExpression)) { $searchWhereAlias->add($aliasExpression); } $searchParts->add($searchWhereAlias); } else { $searchParts->add($expressionBuilder->like('title', $queryBuilder->createNamedParameter($searchFilter, \PDO::PARAM_STR))); if (strlen($aliasExpression)) { $searchParts->add($aliasExpression); } } $queryBuilder->andWhere($searchParts); } return $queryBuilder; }
/** * Getting the tree data: Selecting/Initializing data pointer to items for a certain parent id. * For tables: This will make a database query to select all children to "parent" * For arrays: This will return key to the ->dataLookup array * * @param int $parentId parent item id * * @return mixed Data handle (Tables: An sql-resource, arrays: A parentId integer. -1 is returned if there were NO subLevel.) * @access private */ public function getDataInit($parentId) { if (is_array($this->data)) { if (!is_array($this->dataLookup[$parentId][$this->subLevelID])) { $parentId = -1; } else { reset($this->dataLookup[$parentId][$this->subLevelID]); } return $parentId; } else { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table); $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class))->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); $queryBuilder->select(...$this->fieldArray)->from($this->table)->where($queryBuilder->expr()->eq($this->parentField, $queryBuilder->createNamedParameter($parentId, \PDO::PARAM_INT)), QueryHelper::stripLogicalOperatorPrefix($this->clause)); foreach (QueryHelper::parseOrderBy($this->orderByFields) as $orderPair) { list($fieldName, $order) = $orderPair; $queryBuilder->addOrderBy($fieldName, $order); } return $queryBuilder->execute(); } }
/** * Creates the listing of records from a single table * * @param string $table Table name * @param int $id Page id * @param string $rowList List of fields to show in the listing. Pseudo fields will be added including the record header. * @throws \UnexpectedValueException * @return string HTML table with the listing for the record. */ public function getTable($table, $id, $rowList = '') { $rowListArray = GeneralUtility::trimExplode(',', $rowList, true); // if no columns have been specified, show description (if configured) if (!empty($GLOBALS['TCA'][$table]['ctrl']['descriptionColumn']) && empty($rowListArray)) { array_push($rowListArray, $GLOBALS['TCA'][$table]['ctrl']['descriptionColumn']); } $backendUser = $this->getBackendUserAuthentication(); $lang = $this->getLanguageService(); // Init $addWhere = ''; $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); $titleCol = $GLOBALS['TCA'][$table]['ctrl']['label']; $thumbsCol = $GLOBALS['TCA'][$table]['ctrl']['thumbnail']; $l10nEnabled = $GLOBALS['TCA'][$table]['ctrl']['languageField'] && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] && $tableName !== 'pages_language_overlay'; $tableCollapsed = (bool) $this->tablesCollapsed[$table]; // prepare space icon $this->spaceIcon = '<span class="btn btn-default disabled">' . $this->iconFactory->getIcon('empty-empty', Icon::SIZE_SMALL)->render() . '</span>'; // Cleaning rowlist for duplicates and place the $titleCol as the first column always! $this->fieldArray = []; // title Column // Add title column $this->fieldArray[] = $titleCol; // Control-Panel if (!GeneralUtility::inList($rowList, '_CONTROL_')) { $this->fieldArray[] = '_CONTROL_'; } // Clipboard if ($this->showClipboard) { $this->fieldArray[] = '_CLIPBOARD_'; } // Ref if (!$this->dontShowClipControlPanels) { $this->fieldArray[] = '_REF_'; } // Path if ($this->searchLevels) { $this->fieldArray[] = '_PATH_'; } // Localization if ($this->localizationView && $l10nEnabled) { $this->fieldArray[] = '_LOCALIZATION_'; $this->fieldArray[] = '_LOCALIZATION_b'; // Only restrict to the default language if no search request is in place if ($this->searchString === '') { $addWhere = (string) $queryBuilder->expr()->orX($queryBuilder->expr()->lte($GLOBALS['TCA'][$table]['ctrl']['languageField'], 0), $queryBuilder->expr()->eq($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], 0)); } } // Cleaning up: $this->fieldArray = array_unique(array_merge($this->fieldArray, $rowListArray)); if ($this->noControlPanels) { $tempArray = array_flip($this->fieldArray); unset($tempArray['_CONTROL_']); unset($tempArray['_CLIPBOARD_']); $this->fieldArray = array_keys($tempArray); } // Creating the list of fields to include in the SQL query: $selectFields = $this->fieldArray; $selectFields[] = 'uid'; $selectFields[] = 'pid'; // adding column for thumbnails if ($thumbsCol) { $selectFields[] = $thumbsCol; } if ($table == 'pages') { $selectFields[] = 'module'; $selectFields[] = 'extendToSubpages'; $selectFields[] = 'nav_hide'; $selectFields[] = 'doktype'; $selectFields[] = 'shortcut'; $selectFields[] = 'shortcut_mode'; $selectFields[] = 'mount_pid'; } if (is_array($GLOBALS['TCA'][$table]['ctrl']['enablecolumns'])) { $selectFields = array_merge($selectFields, $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']); } foreach (['type', 'typeicon_column', 'editlock'] as $field) { if ($GLOBALS['TCA'][$table]['ctrl'][$field]) { $selectFields[] = $GLOBALS['TCA'][$table]['ctrl'][$field]; } } if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) { $selectFields[] = 't3ver_id'; $selectFields[] = 't3ver_state'; $selectFields[] = 't3ver_wsid'; } if ($l10nEnabled) { $selectFields[] = $GLOBALS['TCA'][$table]['ctrl']['languageField']; $selectFields[] = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']; } if ($GLOBALS['TCA'][$table]['ctrl']['label_alt']) { $selectFields = array_merge($selectFields, GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['ctrl']['label_alt'], true)); } // Unique list! $selectFields = array_unique($selectFields); $fieldListFields = $this->makeFieldList($table, 1); if (empty($fieldListFields) && $GLOBALS['TYPO3_CONF_VARS']['BE']['debug']) { $message = sprintf(htmlspecialchars($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_mod_web_list.xlf:missingTcaColumnsMessage')), $table, $table); $messageTitle = htmlspecialchars($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_mod_web_list.xlf:missingTcaColumnsMessageTitle')); /** @var FlashMessage $flashMessage */ $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $message, $messageTitle, FlashMessage::WARNING, true); /** @var $flashMessageService FlashMessageService */ $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class); /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */ $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier(); $defaultFlashMessageQueue->enqueue($flashMessage); } // Making sure that the fields in the field-list ARE in the field-list from TCA! $selectFields = array_intersect($selectFields, $fieldListFields); // Implode it into a list of fields for the SQL-statement. $selFieldList = implode(',', $selectFields); $this->selFieldList = $selFieldList; if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['getTable'])) { foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['getTable'] as $classData) { $hookObject = GeneralUtility::getUserObj($classData); if (!$hookObject instanceof RecordListGetTableHookInterface) { throw new \UnexpectedValueException($classData . ' must implement interface ' . RecordListGetTableHookInterface::class, 1195114460); } $hookObject->getDBlistQuery($table, $id, $addWhere, $selFieldList, $this); } } $additionalConstraints = empty($addWhere) ? [] : [QueryHelper::stripLogicalOperatorPrefix($addWhere)]; $selFieldList = GeneralUtility::trimExplode(',', $selFieldList, true); // Create the SQL query for selecting the elements in the listing: // do not do paging when outputting as CSV if ($this->csvOutput) { $this->iLimit = 0; } if ($this->firstElementNumber > 2 && $this->iLimit > 0) { // Get the two previous rows for sorting if displaying page > 1 $this->firstElementNumber = $this->firstElementNumber - 2; $this->iLimit = $this->iLimit + 2; // (API function from TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRecordList) $queryBuilder = $this->getQueryBuilder($table, $id, $additionalConstraints); $this->firstElementNumber = $this->firstElementNumber + 2; $this->iLimit = $this->iLimit - 2; } else { // (API function from TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRecordList) $queryBuilder = $this->getQueryBuilder($table, $id, $additionalConstraints); } // Finding the total amount of records on the page // (API function from TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRecordList) $this->setTotalItems($table, $id, $additionalConstraints); // Init: $queryResult = $queryBuilder->execute(); $dbCount = 0; $out = ''; $tableHeader = ''; $listOnlyInSingleTableMode = $this->listOnlyInSingleTableMode && !$this->table; // If the count query returned any number of records, we perform the real query, // selecting records. if ($this->totalItems) { // Fetch records only if not in single table mode if ($listOnlyInSingleTableMode) { $dbCount = $this->totalItems; } else { // Set the showLimit to the number of records when outputting as CSV if ($this->csvOutput) { $this->showLimit = $this->totalItems; $this->iLimit = $this->totalItems; } $dbCount = $queryResult->rowCount(); } } // If any records was selected, render the list: if ($dbCount) { $tableTitle = htmlspecialchars($lang->sL($GLOBALS['TCA'][$table]['ctrl']['title'])); if ($tableTitle === '') { $tableTitle = $table; } // Header line is drawn $theData = []; if ($this->disableSingleTableView) { $theData[$titleCol] = '<span class="c-table">' . BackendUtility::wrapInHelp($table, '', $tableTitle) . '</span> (<span class="t3js-table-total-items">' . $this->totalItems . '</span>)'; } else { $icon = $this->table ? '<span title="' . htmlspecialchars($lang->getLL('contractView')) . '">' . $this->iconFactory->getIcon('actions-view-table-collapse', Icon::SIZE_SMALL)->render() . '</span>' : '<span title="' . htmlspecialchars($lang->getLL('expandView')) . '">' . $this->iconFactory->getIcon('actions-view-table-expand', Icon::SIZE_SMALL)->render() . '</span>'; $theData[$titleCol] = $this->linkWrapTable($table, $tableTitle . ' (<span class="t3js-table-total-items">' . $this->totalItems . '</span>) ' . $icon); } if ($listOnlyInSingleTableMode) { $tableHeader .= BackendUtility::wrapInHelp($table, '', $theData[$titleCol]); } else { // Render collapse button if in multi table mode $collapseIcon = ''; if (!$this->table) { $href = htmlspecialchars($this->listURL() . '&collapse[' . $table . ']=' . ($tableCollapsed ? '0' : '1')); $title = $tableCollapsed ? htmlspecialchars($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.expandTable')) : htmlspecialchars($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.collapseTable')); $icon = '<span class="collapseIcon">' . $this->iconFactory->getIcon($tableCollapsed ? 'actions-view-list-expand' : 'actions-view-list-collapse', Icon::SIZE_SMALL)->render() . '</span>'; $collapseIcon = '<a href="' . $href . '" title="' . $title . '" class="pull-right t3js-toggle-recordlist" data-table="' . htmlspecialchars($table) . '" data-toggle="collapse" data-target="#recordlist-' . htmlspecialchars($table) . '">' . $icon . '</a>'; } $tableHeader .= $theData[$titleCol] . $collapseIcon; } // Render table rows only if in multi table view or if in single table view $rowOutput = ''; if (!$listOnlyInSingleTableMode || $this->table) { // Fixing an order table for sortby tables $this->currentTable = []; $currentIdList = []; $doSort = $GLOBALS['TCA'][$table]['ctrl']['sortby'] && !$this->sortField; $prevUid = 0; $prevPrevUid = 0; // Get first two rows and initialize prevPrevUid and prevUid if on page > 1 if ($this->firstElementNumber > 2 && $this->iLimit > 0) { $row = $queryResult->fetch(); $prevPrevUid = -(int) $row['uid']; $row = $queryResult->fetch(); $prevUid = $row['uid']; } $accRows = []; // Accumulate rows here while ($row = $queryResult->fetch()) { if (!$this->isRowListingConditionFulfilled($table, $row)) { continue; } // In offline workspace, look for alternative record: BackendUtility::workspaceOL($table, $row, $backendUser->workspace, true); if (is_array($row)) { $accRows[] = $row; $currentIdList[] = $row['uid']; if ($doSort) { if ($prevUid) { $this->currentTable['prev'][$row['uid']] = $prevPrevUid; $this->currentTable['next'][$prevUid] = '-' . $row['uid']; $this->currentTable['prevUid'][$row['uid']] = $prevUid; } $prevPrevUid = isset($this->currentTable['prev'][$row['uid']]) ? -$prevUid : $row['pid']; $prevUid = $row['uid']; } } } $this->totalRowCount = count($accRows); // CSV initiated if ($this->csvOutput) { $this->initCSV(); } // Render items: $this->CBnames = []; $this->duplicateStack = []; $this->eCounter = $this->firstElementNumber; $cc = 0; foreach ($accRows as $row) { // Render item row if counter < limit if ($cc < $this->iLimit) { $cc++; $this->translations = false; $rowOutput .= $this->renderListRow($table, $row, $cc, $titleCol, $thumbsCol); // If localization view is enabled and no search happened it means that the selected // records are either default or All language and here we will not select translations // which point to the main record: if ($this->localizationView && $l10nEnabled && $this->searchString === '') { // For each available translation, render the record: if (is_array($this->translations)) { foreach ($this->translations as $lRow) { // $lRow isn't always what we want - if record was moved we've to work with the // placeholder records otherwise the list is messed up a bit if ($row['_MOVE_PLH_uid'] && $row['_MOVE_PLH_pid']) { $where = 't3ver_move_id="' . (int) $lRow['uid'] . '" AND pid="' . $row['_MOVE_PLH_pid'] . '" AND t3ver_wsid=' . $row['t3ver_wsid'] . BackendUtility::deleteClause($table); $tmpRow = BackendUtility::getRecordRaw($table, $where, $selFieldList); $lRow = is_array($tmpRow) ? $tmpRow : $lRow; } // In offline workspace, look for alternative record: BackendUtility::workspaceOL($table, $lRow, $backendUser->workspace, true); if (is_array($lRow) && $backendUser->checkLanguageAccess($lRow[$GLOBALS['TCA'][$table]['ctrl']['languageField']])) { $currentIdList[] = $lRow['uid']; $rowOutput .= $this->renderListRow($table, $lRow, $cc, $titleCol, $thumbsCol, 18); } } } } } // Counter of total rows incremented: $this->eCounter++; } // Record navigation is added to the beginning and end of the table if in single // table mode if ($this->table) { $rowOutput = $this->renderListNavigation('top') . $rowOutput . $this->renderListNavigation('bottom'); } else { // Show that there are more records than shown if ($this->totalItems > $this->itemsLimitPerTable) { $countOnFirstPage = $this->totalItems > $this->itemsLimitSingleTable ? $this->itemsLimitSingleTable : $this->totalItems; $hasMore = $this->totalItems > $this->itemsLimitSingleTable; $colspan = $this->showIcon ? count($this->fieldArray) + 1 : count($this->fieldArray); $rowOutput .= '<tr><td colspan="' . $colspan . '"> <a href="' . htmlspecialchars($this->listURL() . '&table=' . rawurlencode($table)) . '" class="btn btn-default">' . '<span class="t3-icon fa fa-chevron-down"></span> <i>[1 - ' . $countOnFirstPage . ($hasMore ? '+' : '') . ']</i></a> </td></tr>'; } } // The header row for the table is now created: $out .= $this->renderListHeader($table, $currentIdList); } $collapseClass = $tableCollapsed && !$this->table ? 'collapse' : 'collapse in'; $dataState = $tableCollapsed && !$this->table ? 'collapsed' : 'expanded'; // The list of records is added after the header: $out .= $rowOutput; // ... and it is all wrapped in a table: $out = ' <!-- DB listing of elements: "' . htmlspecialchars($table) . '" --> <div class="panel panel-space panel-default recordlist"> <div class="panel-heading"> ' . $tableHeader . ' </div> <div class="' . $collapseClass . '" data-state="' . $dataState . '" id="recordlist-' . htmlspecialchars($table) . '"> <div class="table-fit"> <table data-table="' . htmlspecialchars($table) . '" class="table table-striped table-hover' . ($listOnlyInSingleTableMode ? ' typo3-dblist-overview' : '') . '"> ' . $out . ' </table> </div> </div> </div> '; // Output csv if... // This ends the page with exit. if ($this->csvOutput) { $this->outputCSV($table); } } // Return content: return $out; }
/** * Get select query * * @param string $qString * @return bool|\mysqli_result|object */ public function getSelectQuery($qString = '') { $backendUserAuthentication = $this->getBackendUserAuthentication(); $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table); if ($this->getModule()->MOD_SETTINGS['show_deleted']) { $queryBuilder->getRestrictions()->removeAll(); } else { $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class)); } $fieldList = GeneralUtility::trimExplode(',', $this->extFieldLists['queryFields'] . ',pid' . ($GLOBALS['TCA'][$this->table]['ctrl']['delete'] ? ',' . $GLOBALS['TCA'][$this->table]['ctrl']['delete'] : '')); $queryBuilder->select(...$fieldList)->from($this->table); if ($this->extFieldLists['queryGroup']) { $queryBuilder->groupBy(...QueryHelper::parseGroupBy($this->extFieldLists['queryGroup'])); } if ($this->extFieldLists['queryOrder']) { foreach (QueryHelper::parseOrderBy($this->extFieldLists['queryOrder_SQL']) as $orderPair) { list($fieldName, $order) = $orderPair; $queryBuilder->addOrderBy($fieldName, $order); } } if ($this->extFieldLists['queryLimit']) { $queryBuilder->setMaxResults((int) $this->extFieldLists['queryLimit']); } if (!$backendUserAuthentication->isAdmin() && $GLOBALS['TYPO3_CONF_VARS']['BE']['lockBeUserToDBmounts']) { $webMounts = $backendUserAuthentication->returnWebmounts(); $perms_clause = $backendUserAuthentication->getPagePermsClause(1); $webMountPageTree = ''; $webMountPageTreePrefix = ''; foreach ($webMounts as $webMount) { if ($webMountPageTree) { $webMountPageTreePrefix = ','; } $webMountPageTree .= $webMountPageTreePrefix . $this->getTreeList($webMount, 999, $begin = 0, $perms_clause); } if ($this->table === 'pages') { $queryBuilder->where(QueryHelper::stripLogicalOperatorPrefix($perms_clause), $queryBuilder->expr()->in('uid', $queryBuilder->createNamedParameter(GeneralUtility::intExplode(',', $webMountPageTree), Connection::PARAM_INT_ARRAY))); } else { $queryBuilder->where($queryBuilder->expr()->in('pid', $queryBuilder->createNamedParameter(GeneralUtility::intExplode(',', $webMountPageTree), Connection::PARAM_INT_ARRAY))); } } if (!$qString) { $qString = $this->getQuery($this->queryConfig); } $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($qString)); return $queryBuilder->getSQL(); }
/** * Sets sys_page where-clause * * @return void * @access private */ public function setSysPageWhereClause() { $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages')->getExpressionBuilder(); $this->sys_page->where_hid_del = ' AND ' . (string) $expressionBuilder->andX(QueryHelper::stripLogicalOperatorPrefix($this->sys_page->where_hid_del), $expressionBuilder->lt('pages.doktype', 200)); $this->sys_page->where_groupAccess = $this->sys_page->getMultipleGroupsWhereClause('pages.fe_group', 'pages'); }
/** * Evaluate "input" type values. * * @param string $value The value to set. * @param array $tcaFieldConf Field configuration from TCA * @param string $table Table name * @param int $id UID of record * @param int $realPid The real PID value of the record. For updates, this is just the pid of the record. For new records this is the PID of the page where it is inserted. If $realPid is -1 it means that a new version of the record is being inserted. * @param string $field Field name * @return array $res The result array. The processed value (if any!) is set in the "value" key. */ protected function checkValueForInput($value, $tcaFieldConf, $table, $id, $realPid, $field) { // Handle native date/time fields $isDateOrDateTimeField = false; $format = ''; $emptyValue = ''; // normal integer "date" fields (timestamps) are handled in checkValue_input_Eval if (isset($tcaFieldConf['dbType']) && ($tcaFieldConf['dbType'] === 'date' || $tcaFieldConf['dbType'] === 'datetime')) { if (empty($value)) { $value = 0; } else { $isDateOrDateTimeField = true; $dateTimeFormats = QueryHelper::getDateTimeFormats(); $format = $dateTimeFormats[$tcaFieldConf['dbType']]['format']; // Convert the date/time into a timestamp for the sake of the checks $emptyValue = $dateTimeFormats[$tcaFieldConf['dbType']]['empty']; // We store UTC timestamps in the database, which is what getTimestamp() returns. $dateTime = new \DateTime($value); $value = $value === $emptyValue ? 0 : $dateTime->getTimestamp(); } } // Secures the string-length to be less than max. if ((int) $tcaFieldConf['max'] > 0) { $value = mb_substr((string) $value, 0, (int) $tcaFieldConf['max'], 'utf-8'); } // Checking range of value: // @todo: The "checkbox" option was removed for type=input, this check could be probably relaxed? if ($tcaFieldConf['range'] && $value != $tcaFieldConf['checkbox'] && (int) $value !== (int) $tcaFieldConf['default']) { if (isset($tcaFieldConf['range']['upper']) && (int) $value > (int) $tcaFieldConf['range']['upper']) { $value = $tcaFieldConf['range']['upper']; } if (isset($tcaFieldConf['range']['lower']) && (int) $value < (int) $tcaFieldConf['range']['lower']) { $value = $tcaFieldConf['range']['lower']; } } if (empty($tcaFieldConf['eval'])) { $res = ['value' => $value]; } else { // Process evaluation settings: $cacheId = $this->getFieldEvalCacheIdentifier($tcaFieldConf['eval']); if ($this->runtimeCache->has($cacheId)) { $evalCodesArray = $this->runtimeCache->get($cacheId); } else { $evalCodesArray = GeneralUtility::trimExplode(',', $tcaFieldConf['eval'], true); $this->runtimeCache->set($cacheId, $evalCodesArray); } $res = $this->checkValue_input_Eval($value, $evalCodesArray, $tcaFieldConf['is_in']); // Process UNIQUE settings: // Field is NOT set for flexForms - which also means that uniqueInPid and unique is NOT available for flexForm fields! Also getUnique should not be done for versioning and if PID is -1 ($realPid<0) then versioning is happening... if ($field && $realPid >= 0 && !empty($res['value'])) { if (in_array('uniqueInPid', $evalCodesArray, true)) { $res['value'] = $this->getUnique($table, $field, $res['value'], $id, $realPid); } if ($res['value'] && in_array('unique', $evalCodesArray, true)) { $res['value'] = $this->getUnique($table, $field, $res['value'], $id); } } } // Handle native date/time fields if ($isDateOrDateTimeField) { // Convert the timestamp back to a date/time $res['value'] = $res['value'] ? date($format, $res['value']) : $emptyValue; } return $res; }
/** * Returns a part of a WHERE clause which will filter out records with start/end * times or hidden/fe_groups fields set to values that should de-select them * according to the current time, preview settings or user login. Definitely a * frontend function. * * Is using the $GLOBALS['TCA'] arrays "ctrl" part where the key "enablefields" * determines for each table which of these features applies to that table. * * @param string $table Table name found in the $GLOBALS['TCA'] array * @param int $show_hidden If $show_hidden is set (0/1), any hidden-fields in records are ignored. NOTICE: If you call this function, consider what to do with the show_hidden parameter. Maybe it should be set? See ContentObjectRenderer->enableFields where it's implemented correctly. * @param array $ignore_array Array you can pass where keys can be "disabled", "starttime", "endtime", "fe_group" (keys from "enablefields" in TCA) and if set they will make sure that part of the clause is not added. Thus disables the specific part of the clause. For previewing etc. * @param bool $noVersionPreview If set, enableFields will be applied regardless of any versioning preview settings which might otherwise disable enableFields * @throws \InvalidArgumentException * @return string The clause starting like " AND ...=... AND ...=... * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::enableFields(), deleteClause() */ public function enableFields($table, $show_hidden = -1, $ignore_array = [], $noVersionPreview = false) { if ($show_hidden === -1 && is_object($this->getTypoScriptFrontendController())) { // If show_hidden was not set from outside and if TSFE is an object, set it // based on showHiddenPage and showHiddenRecords from TSFE $show_hidden = $table === 'pages' || $table === 'pages_language_overlay' ? $this->getTypoScriptFrontendController()->showHiddenPage : $this->getTypoScriptFrontendController()->showHiddenRecords; } if ($show_hidden === -1) { $show_hidden = 0; } // If show_hidden was not changed during the previous evaluation, do it here. $ctrl = $GLOBALS['TCA'][$table]['ctrl']; $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table)->expr(); $constraints = []; if (is_array($ctrl)) { // Delete field check: if ($ctrl['delete']) { $constraints[] = $expressionBuilder->eq($table . '.' . $ctrl['delete'], 0); } if ($ctrl['versioningWS']) { if (!$this->versioningPreview) { // Filter out placeholder records (new/moved/deleted items) // in case we are NOT in a versioning preview (that means we are online!) $constraints[] = $expressionBuilder->lte($table . '.t3ver_state', new VersionState(VersionState::DEFAULT_STATE)); } elseif ($table !== 'pages') { // show only records of live and of the current workspace // in case we are in a versioning preview $constraints[] = $expressionBuilder->orX($expressionBuilder->eq($table . '.t3ver_wsid', 0), $expressionBuilder->eq($table . '.t3ver_wsid', (int) $this->versioningWorkspaceId)); } // Filter out versioned records if (!$noVersionPreview && empty($ignore_array['pid'])) { $constraints[] = $expressionBuilder->neq($table . '.pid', -1); } } // Enable fields: if (is_array($ctrl['enablecolumns'])) { // In case of versioning-preview, enableFields are ignored (checked in // versionOL()) if (!$this->versioningPreview || !$ctrl['versioningWS'] || $noVersionPreview) { if ($ctrl['enablecolumns']['disabled'] && !$show_hidden && !$ignore_array['disabled']) { $field = $table . '.' . $ctrl['enablecolumns']['disabled']; $constraints[] = $expressionBuilder->eq($field, 0); } if ($ctrl['enablecolumns']['starttime'] && !$ignore_array['starttime']) { $field = $table . '.' . $ctrl['enablecolumns']['starttime']; $constraints[] = $expressionBuilder->lte($field, (int) $GLOBALS['SIM_ACCESS_TIME']); } if ($ctrl['enablecolumns']['endtime'] && !$ignore_array['endtime']) { $field = $table . '.' . $ctrl['enablecolumns']['endtime']; $constraints[] = $expressionBuilder->orX($expressionBuilder->eq($field, 0), $expressionBuilder->gt($field, (int) $GLOBALS['SIM_ACCESS_TIME'])); } if ($ctrl['enablecolumns']['fe_group'] && !$ignore_array['fe_group']) { $field = $table . '.' . $ctrl['enablecolumns']['fe_group']; $constraints[] = QueryHelper::stripLogicalOperatorPrefix($this->getMultipleGroupsWhereClause($field, $table)); } // Call hook functions for additional enableColumns // It is used by the extension ingmar_accessctrl which enables assigning more // than one usergroup to content and page records if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['addEnableColumns'])) { $_params = ['table' => $table, 'show_hidden' => $show_hidden, 'ignore_array' => $ignore_array, 'ctrl' => $ctrl]; foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['addEnableColumns'] as $_funcRef) { $constraints[] = QueryHelper::stripLogicalOperatorPrefix(GeneralUtility::callUserFunction($_funcRef, $_params, $this)); } } } } } else { throw new \InvalidArgumentException('There is no entry in the $TCA array for the table "' . $table . '". This means that the function enableFields() is ' . 'called with an invalid table name as argument.', 1283790586); } return empty($constraints) ? '' : ' AND ' . $expressionBuilder->andX(...$constraints); }
/** * Returns the date and time formats compatible with the given database table. * * @param string $table Table name for which to return an empty date. Just enter the table that the field-value is selected from (and any DBAL will look up which handler to use and then how date and time should be formatted). * @return array */ public function getDateTimeFormats($table) { $this->logDeprecation(); return QueryHelper::getDateTimeFormats(); }
/** * Helper function for getQuery(), creating the WHERE clause of the SELECT query * * @param string $table The table name * @param array $conf The TypoScript configuration properties * @return array Associative array containing the prepared data for WHERE, ORDER BY and GROUP BY fragments * @throws \InvalidArgumentException * @see getQuery() */ protected function getQueryConstraints(string $table, array $conf) : array { // Init: $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); $expressionBuilder = $queryBuilder->expr(); $tsfe = $this->getTypoScriptFrontendController(); $constraints = []; $pid_uid_flag = 0; $enableFieldsIgnore = []; $queryParts = ['where' => null, 'groupBy' => null, 'orderBy' => null]; $considerMovePlaceholders = $tsfe->sys_page->versioningPreview && $table !== 'pages' && !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS']); if (trim($conf['uidInList'])) { $listArr = GeneralUtility::intExplode(',', str_replace('this', $tsfe->contentPid, $conf['uidInList'])); // If move placeholder shall be considered, select via t3ver_move_id if ($considerMovePlaceholders) { $constraints[] = (string) $expressionBuilder->orX($expressionBuilder->in($table . '.uid', $listArr), $expressionBuilder->andX($expressionBuilder->eq($table . '.t3ver_state', (int) (string) VersionState::cast(VersionState::MOVE_PLACEHOLDER)), $expressionBuilder->in($table . '.t3ver_move_id', $listArr))); } else { $constraints[] = (string) $expressionBuilder->in($table . '.uid', $listArr); } $pid_uid_flag++; } // Static_* tables are allowed to be fetched from root page if (strpos($table, 'static_') === 0) { $pid_uid_flag++; } if (trim($conf['pidInList'])) { $listArr = GeneralUtility::intExplode(',', str_replace('this', $tsfe->contentPid, $conf['pidInList'])); // Removes all pages which are not visible for the user! $listArr = $this->checkPidArray($listArr); if (GeneralUtility::inList($conf['pidInList'], 'root')) { $listArr[] = 0; } if (GeneralUtility::inList($conf['pidInList'], '-1')) { $listArr[] = -1; $enableFieldsIgnore['pid'] = true; } if (!empty($listArr)) { $constraints[] = $expressionBuilder->in($table . '.pid', array_map('intval', $listArr)); $pid_uid_flag++; } else { // If not uid and not pid then uid is set to 0 - which results in nothing!! $pid_uid_flag = 0; } } // If not uid and not pid then uid is set to 0 - which results in nothing!! if (!$pid_uid_flag) { $constraints[] = $expressionBuilder->eq($table . '.uid', 0); } $where = isset($conf['where.']) ? trim($this->stdWrap($conf['where'], $conf['where.'])) : trim($conf['where']); if ($where) { $constraints[] = QueryHelper::stripLogicalOperatorPrefix($where); } // Check if the table is translatable, and set the language field by default from the TCA information $languageField = ''; if (!empty($conf['languageField']) || !isset($conf['languageField'])) { if (isset($conf['languageField']) && !empty($GLOBALS['TCA'][$table]['columns'][$conf['languageField']])) { $languageField = $conf['languageField']; } elseif (!empty($GLOBALS['TCA'][$table]['ctrl']['languageField']) && !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'])) { $languageField = $table . '.' . $GLOBALS['TCA'][$table]['ctrl']['languageField']; } } if (!empty($languageField)) { // The sys_language record UID of the content of the page $sys_language_content = (int) $tsfe->sys_language_content; if ($tsfe->sys_language_contentOL && !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'])) { // Sys language content is set to zero/-1 - and it is expected that whatever routine processes the output will // OVERLAY the records with localized versions! $languageQuery = $expressionBuilder->in($languageField, [0, -1]); // Use this option to include records that don't have a default translation // (originalpointerfield is 0 and the language field contains the requested language) $includeRecordsWithoutDefaultTranslation = isset($conf['includeRecordsWithoutDefaultTranslation.']) ? $this->stdWrap($conf['includeRecordsWithoutDefaultTranslation'], $conf['includeRecordsWithoutDefaultTranslation.']) : $conf['includeRecordsWithoutDefaultTranslation']; if (trim($includeRecordsWithoutDefaultTranslation) !== '') { $languageQuery = $expressionBuilder->orX($languageQuery, $expressionBuilder->andX($expressionBuilder->eq($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], 0), $expressionBuilder->eq($languageField, $sys_language_content))); } } else { $languageQuery = $expressionBuilder->eq($languageField, $sys_language_content); } $constraints[] = $languageQuery; } // Enablefields if ($table === 'pages') { $constraints[] = QueryHelper::stripLogicalOperatorPrefix($tsfe->sys_page->where_hid_del); $constraints[] = QueryHelper::stripLogicalOperatorPrefix($tsfe->sys_page->where_groupAccess); } else { $constraints[] = QueryHelper::stripLogicalOperatorPrefix($this->enableFields($table, false, $enableFieldsIgnore)); } // MAKE WHERE: if (count($constraints) !== 0) { $queryParts['where'] = $expressionBuilder->andX(...$constraints); } // GROUP BY if (trim($conf['groupBy'])) { $groupBy = isset($conf['groupBy.']) ? trim($this->stdWrap($conf['groupBy'], $conf['groupBy.'])) : trim($conf['groupBy']); $queryParts['groupBy'] = QueryHelper::parseGroupBy($groupBy); } // ORDER BY if (trim($conf['orderBy'])) { $orderByString = isset($conf['orderBy.']) ? trim($this->stdWrap($conf['orderBy'], $conf['orderBy.'])) : trim($conf['orderBy']); $queryParts['orderBy'] = QueryHelper::parseOrderBy($orderByString); } // Return result: return $queryParts; }
/** * @test * @dataProvider parseGroupByDataProvider * @param string $input * @param array $expectedResult */ public function parseGroupByTest(string $input, array $expectedResult) { $this->assertSame($expectedResult, QueryHelper::parseGroupBy($input)); }
/** * Execute final query, based on phash integer list. The main point is sorting the result in the right order. * * @param string $list List of phash integers which match the search. * @param int $freeIndexUid Pointer to which indexing configuration you want to search in. -1 means no filtering. 0 means only regular indexed content. * @return Statement */ protected function execFinalQuery($list, $freeIndexUid = -1) { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_words'); $queryBuilder->select('ISEC.*', 'IP.*')->from('index_phash', 'IP')->from('index_section', 'ISEC')->where($queryBuilder->expr()->in('IP.phash', $queryBuilder->createNamedParameter(GeneralUtility::intExplode(',', $list, true), Connection::PARAM_INT_ARRAY)), QueryHelper::stripLogicalOperatorPrefix($this->mediaTypeWhere()), QueryHelper::stripLogicalOperatorPrefix($this->languageWhere()), QueryHelper::stripLogicalOperatorPrefix($this->freeIndexUidWhere($freeIndexUid)), $queryBuilder->expr()->eq('ISEC.phash', $queryBuilder->quoteIdentifier('IP.phash')))->groupBy('IP.phash', 'ISEC.phash', 'ISEC.phash_t3', 'ISEC.rl0', 'ISEC.rl1', 'ISEC.rl2', 'ISEC.page_id', 'ISEC.uniqid', 'IP.phash_grouping', 'IP.data_filename', 'IP.data_page_id', 'IP.data_page_reg1', 'IP.data_page_type', 'IP.data_page_mp', 'IP.gr_list', 'IP.item_type', 'IP.item_title', 'IP.item_description', 'IP.item_mtime', 'IP.tstamp', 'IP.item_size', 'IP.contentHash', 'IP.crdate', 'IP.parsetime', 'IP.sys_language_uid', 'IP.item_crdate', 'IP.cHashParams', 'IP.externalUrl', 'IP.recordUid', 'IP.freeIndexUid', 'IP.freeIndexSetId'); // Setting up methods of filtering results // based on page types, access, etc. if ($hookObj = $this->hookRequest('execFinalQuery_idList')) { // Calling hook for alternative creation of page ID list $hookWhere = QueryHelper::stripLogicalOperatorPrefix($hookObj->execFinalQuery_idList($list)); if (!empty($hookWhere)) { $queryBuilder->andWhere($hookWhere); } } elseif ($this->joinPagesForQuery) { // Alternative to getting all page ids by ->getTreeList() where // "excludeSubpages" is NOT respected. $queryBuilder->getRestrictions()->removeAll(); $queryBuilder->from('pages'); $queryBuilder->andWhere($queryBuilder->expr()->eq('pages.uid', $queryBuilder->quoteIdentifier('ISEC.page_id')), QueryHelper::stripLogicalOperatorPrefix($this->enableFields('pages')), $queryBuilder->expr()->eq('pages.no_search', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)), $queryBuilder->expr()->lt('pages.doktype', $queryBuilder->createNamedParameter(200, \PDO::PARAM_INT))); } elseif ($this->searchRootPageIdList >= 0) { // Collecting all pages IDs in which to search; // filtering out ALL pages that are not accessible due to enableFields. // Does NOT look for "no_search" field! $siteIdNumbers = GeneralUtility::intExplode(',', $this->searchRootPageIdList); $pageIdList = []; foreach ($siteIdNumbers as $rootId) { $pageIdList[] = $this->getTypoScriptFrontendController()->cObj->getTreeList(-1 * $rootId, 9999); } $queryBuilder->andWhere($queryBuilder->expr()->in('ISEC.page_id', $queryBuilder->createNamedParameter(array_unique(GeneralUtility::intExplode(',', implode(',', $pageIdList), true)), Connection::PARAM_INT_ARRAY))); } // otherwise select all / disable everything // If any of the ranking sortings are selected, we must make a // join with the word/rel-table again, because we need to // calculate ranking based on all search-words found. if (substr($this->sortOrder, 0, 5) === 'rank_') { $queryBuilder->from('index_words', 'IW')->from('index_rel', 'IR')->andWhere($queryBuilder->expr()->eq('IW.wid', $queryBuilder->quoteIdentifier('IR.wid')), $queryBuilder->expr()->eq('ISEC.phash', $queryBuilder->quoteIdentifier('IR.phash'))); switch ($this->sortOrder) { case 'rank_flag': // This gives priority to word-position (max-value) so that words in title, keywords, description counts more than in content. // The ordering is refined with the frequency sum as well. $queryBuilder->addSelectLiteral($queryBuilder->expr()->max('IR.flags', 'order_val1'), $queryBuilder->expr()->sum('IR.freq', 'order_val2'))->orderBy('order_val1', $this->getDescendingSortOrderFlag())->addOrderBy('order_val2', $this->getDescendingSortOrderFlag()); break; case 'rank_first': // Results in average position of search words on page. // Must be inversely sorted (low numbers are closer to top) $queryBuilder->addSelectLiteral($queryBuilder->expr()->avg('IR.first', 'order_val'))->orderBy('order_val', $this->getDescendingSortOrderFlag(true)); break; case 'rank_count': // Number of words found $queryBuilder->addSelectLiteral($queryBuilder->expr()->sum('IR.count', 'order_val'))->orderBy('order_val', $this->getDescendingSortOrderFlag()); break; default: // Frequency sum. I'm not sure if this is the best way to do // it (make a sum...). Or should it be the average? $queryBuilder->addSelectLiteral($queryBuilder->expr()->sum('IR.freq', 'order_val'))->orderBy('order_val', $this->getDescendingSortOrderFlag()); } if (!empty($this->wSelClauses)) { // So, words are combined in an OR statement // (no "sentence search" should be done here - may deselect results) $wordSel = $queryBuilder->expr()->orX(); foreach ($this->wSelClauses as $wSelClause) { $wordSel->add(QueryHelper::stripLogicalOperatorPrefix($wSelClause)); } $queryBuilder->andWhere($wordSel); } } else { // Otherwise, if sorting are done with the pages table or other fields, // there is no need for joining with the rel/word tables: switch ((string) $this->sortOrder) { case 'title': $queryBuilder->orderBy('IP.item_title', $this->getDescendingSortOrderFlag()); break; case 'crdate': $queryBuilder->orderBy('IP.item_crdate', $this->getDescendingSortOrderFlag()); break; case 'mtime': $queryBuilder->orderBy('IP.item_mtime', $this->getDescendingSortOrderFlag()); break; } } return $queryBuilder->execute(); }
/** * Gets the icon for the shortcut * * @param array $row * @param array $shortcut * @return string Shortcut icon as img tag * @throws \InvalidArgumentException */ protected function getShortcutIcon($row, $shortcut) { switch ($row['module_name']) { case 'xMOD_alt_doc.php': $table = $shortcut['table']; $recordid = $shortcut['recordid']; $icon = ''; if ($shortcut['type'] === 'edit') { // Creating the list of fields to include in the SQL query: $selectFields = $this->fieldArray; $selectFields[] = 'uid'; $selectFields[] = 'pid'; if ($table === 'pages') { $selectFields[] = 'module'; $selectFields[] = 'extendToSubpages'; $selectFields[] = 'doktype'; } if (is_array($GLOBALS['TCA'][$table]['ctrl']['enablecolumns'])) { $selectFields = array_merge($selectFields, $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']); } if ($GLOBALS['TCA'][$table]['ctrl']['type']) { $selectFields[] = $GLOBALS['TCA'][$table]['ctrl']['type']; } if ($GLOBALS['TCA'][$table]['ctrl']['typeicon_column']) { $selectFields[] = $GLOBALS['TCA'][$table]['ctrl']['typeicon_column']; } if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) { $selectFields[] = 't3ver_state'; } $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); $queryBuilder->select(...array_unique(array_values($selectFields)))->from($table)->where($queryBuilder->expr()->in('uid', $queryBuilder->createNamedParameter($recordid, \PDO::PARAM_INT))); if ($table === 'pages' && $this->perms_clause) { $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($this->perms_clause)); } $row = $queryBuilder->execute()->fetch(); $icon = $this->iconFactory->getIconForRecord($table, (array) $row, Icon::SIZE_SMALL)->render(); } elseif ($shortcut['type'] === 'new') { $icon = $this->iconFactory->getIconForRecord($table, [], Icon::SIZE_SMALL)->render(); } break; case 'file_edit': $icon = $this->iconFactory->getIcon('mimetypes-text-html', Icon::SIZE_SMALL)->render(); break; case 'wizard_rte': $icon = $this->iconFactory->getIcon('mimetypes-word', Icon::SIZE_SMALL)->render(); break; default: $iconIdentifier = ''; $moduleName = $row['module_name']; if (strpos($moduleName, '_') !== false) { list($mainModule, $subModule) = explode('_', $moduleName, 2); $iconIdentifier = $this->moduleLoader->modules[$mainModule]['sub'][$subModule]['iconIdentifier']; } elseif (!empty($moduleName)) { $iconIdentifier = $this->moduleLoader->modules[$moduleName]['iconIdentifier']; } if (!$iconIdentifier) { $iconIdentifier = 'empty-empty'; } $icon = $this->iconFactory->getIcon($iconIdentifier, Icon::SIZE_SMALL)->render(); } return $icon; }
/** * Get general statistic * * @param string $additionalWhere * @param int $pageUid * @param int $max * @return array|NULL */ public function getGeneralSearchStatistic($additionalWhere, $pageUid, $max = 50) { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_stat_word'); $queryBuilder->select('word')->from('index_stat_word')->addSelectLiteral($queryBuilder->expr()->count('*', 'c'))->where($queryBuilder->expr()->eq('pageid', $queryBuilder->createNamedParameter($pageUid, \PDO::PARAM_INT)))->groupBy('word')->setMaxResults((int) $max); if (!empty($additionalWhere)) { $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($additionalWhere)); } $result = $queryBuilder->execute(); $count = (int) $result->rowCount(); $result->closeCursor(); // exist several statistics for this page? if ($count === 0) { // Limit access to pages of the current site $queryBuilder->where($queryBuilder->expr()->in('pageid', $queryBuilder->createNamedParameter(GeneralUtility::intExplode(',', $this->extGetTreeList((int) $pageUid, 100, 0, '1=1'), true), Connection::PARAM_INT_ARRAY)), QueryHelper::stripLogicalOperatorPrefix($additionalWhere)); } return $queryBuilder->execute()->fetchAll(); }
/** * Get a user from DB by username * provided for usage from services * * @param array $dbUser User db table definition: $this->db_user * @param string $username user name * @param string $extraWhere Additional WHERE clause: " AND ... * @return mixed User array or FALSE */ public function fetchUserRecord($dbUser, $username, $extraWhere = '') { $user = false; if ($username || $extraWhere) { $query = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($dbUser['table']); $constraints = array_filter([QueryHelper::stripLogicalOperatorPrefix($dbUser['check_pid_clause']), QueryHelper::stripLogicalOperatorPrefix($dbUser['enable_clause']), QueryHelper::stripLogicalOperatorPrefix($extraWhere)]); if (!empty($username)) { array_unshift($constraints, $query->expr()->eq($dbUser['username_column'], $query->createNamedParameter($username))); } $user = $query->select('*')->from($dbUser['table'])->where(...$constraints)->execute()->fetch(); } return $user; }
/** * Will select all records from the "category table", $table, and return them in an array. * * @param string $table The name of the category table to select from. * @param int $pid The page from where to select the category records. * @param string $whereClause Optional additional WHERE clauses put in the end of the query. DO NOT PUT IN GROUP BY, ORDER BY or LIMIT! * @param string $groupBy Optional GROUP BY field(s), if none, supply blank string. * @param string $orderBy Optional ORDER BY field(s), if none, supply blank string. * @param string $limit Optional LIMIT value ([begin,]max), if none, supply blank string. * @return array The array with the category records in. */ public function pi_getCategoryTableContents($table, $pid, $whereClause = '', $groupBy = '', $orderBy = '', $limit = '') { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class)); $queryBuilder->select('*')->from($table)->where($queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pid, \PDO::PARAM_INT)), QueryHelper::stripLogicalOperatorPrefix($whereClause)); if (!empty($orderBy)) { foreach (QueryHelper::parseOrderBy($orderBy) as $fieldNameAndSorting) { list($fieldName, $sorting) = $fieldNameAndSorting; $queryBuilder->addOrderBy($fieldName, $sorting); } } if (!empty($groupBy)) { $queryBuilder->groupBy(...QueryHelper::parseGroupBy($groupBy)); } if (!empty($limit)) { $limitValues = GeneralUtility::intExplode(',', $limit, true); if (count($limitValues) === 1) { $queryBuilder->setMaxResults($limitValues[0]); } else { $queryBuilder->setFirstResult($limitValues[0])->setMaxResults($limitValues[1]); } } $result = $queryBuilder->execute(); $outArr = []; while ($row = $result->fetch()) { $outArr[$row['uid']] = $row; } return $outArr; }
/** * Creates part of query for searching after a word ($this->searchString) * fields in input table. * * @param string $table Table, in which the fields are being searched. * @param int $currentPid Page id for the possible search limit. -1 only if called from an old XCLASS. * @return string Returns part of WHERE-clause for searching, if applicable. */ public function makeSearchString($table, $currentPid = -1) { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); $expressionBuilder = $queryBuilder->expr(); $constraints = []; $currentPid = (int) $currentPid; $tablePidField = $table === 'pages' ? 'uid' : 'pid'; // Make query, only if table is valid and a search string is actually defined: if (empty($this->searchString)) { return ''; } $searchableFields = $this->getSearchFields($table); if (empty($searchableFields)) { return ''; } if (MathUtility::canBeInterpretedAsInteger($this->searchString)) { $constraints[] = $expressionBuilder->eq('uid', (int) $this->searchString); foreach ($searchableFields as $fieldName) { if (!isset($GLOBALS['TCA'][$table]['columns'][$fieldName])) { continue; } $fieldConfig = $GLOBALS['TCA'][$table]['columns'][$fieldName]['config']; $fieldType = $fieldConfig['type']; $evalRules = $fieldConfig['eval'] ?: ''; if ($fieldType === 'input' && $evalRules && GeneralUtility::inList($evalRules, 'int')) { if (is_array($fieldConfig['search']) && in_array('pidonly', $fieldConfig['search'], true) && $currentPid > 0) { $constraints[] = $expressionBuilder->andX($expressionBuilder->eq($fieldName, (int) $this->searchString), $expressionBuilder->eq($tablePidField, (int) $currentPid)); } } elseif ($fieldType === 'text' || $fieldType === 'flex' || $fieldType === 'input' && (!$evalRules || !preg_match('/date|time|int/', $evalRules))) { $constraints[] = $expressionBuilder->like($fieldName, $queryBuilder->quote('%' . (int) $this->searchString . '%')); } } } else { $like = $queryBuilder->quote('%' . $queryBuilder->escapeLikeWildcards($this->searchString) . '%'); foreach ($searchableFields as $fieldName) { if (!isset($GLOBALS['TCA'][$table]['columns'][$fieldName])) { continue; } $fieldConfig = $GLOBALS['TCA'][$table]['columns'][$fieldName]['config']; $fieldType = $fieldConfig['type']; $evalRules = $fieldConfig['eval'] ?: ''; $searchConstraint = $expressionBuilder->andX($expressionBuilder->comparison('LOWER(' . $queryBuilder->quoteIdentifier($fieldName) . ')', 'LIKE', 'LOWER(' . $like . ')')); if (is_array($fieldConfig['search'])) { $searchConfig = $fieldConfig['search']; if (in_array('case', $searchConfig)) { // Replace case insensitive default constraint $searchConstraint = $expressionBuilder->andX($expressionBuilder->like($fieldName, $like)); } if (in_array('pidonly', $searchConfig) && $currentPid > 0) { $searchConstraint->add($expressionBuilder->eq($tablePidField, (int) $currentPid)); } if ($searchConfig['andWhere']) { $searchConstraint->add(QueryHelper::stripLogicalOperatorPrefix($fieldConfig['search']['andWhere'])); } } if ($fieldType === 'text' || $fieldType === 'flex' || $fieldType === 'input' && (!$evalRules || !preg_match('/date|time|int/', $evalRules))) { if ($searchConstraint->count() !== 0) { $constraints[] = $searchConstraint; } } } } // If no search field conditions have been build ensure no results are returned if (empty($constraints)) { return '0=1'; } return $expressionBuilder->orX(...$constraints); }
/** * Calls TYPO3\CMS\Backend\FrontendBackendUserAuthentication::extGetTreeList. * Although this duplicates the function TYPO3\CMS\Backend\FrontendBackendUserAuthentication::extGetTreeList * this is necessary to create the object that is used recursively by the original function. * * Generates a list of page uids from $id. List does not include $id itself. * The only pages excluded from the list are deleted pages. * * @param int $id Start page id * @param int $depth Depth to traverse down the page tree. * @param int $begin is an optional integer that determines at which * @param string $permsClause Perms clause * @param bool $considerHidden Whether to consider hidden pages or not * @return string Returns the list with a comma in the end (if any pages selected!) */ public function extGetTreeList($id, $depth, $begin = 0, $permsClause, $considerHidden = false) { $depth = (int) $depth; $begin = (int) $begin; $id = (int) $id; $theList = ''; if ($depth > 0) { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages'); $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class)); $result = $queryBuilder->select('uid', 'title', 'hidden', 'extendToSubpages')->from('pages')->where($queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)), QueryHelper::stripLogicalOperatorPrefix($permsClause))->execute(); while ($row = $result->fetch()) { if ($begin <= 0 && ($row['hidden'] == 0 || $considerHidden)) { $theList .= $row['uid'] . ','; $this->extPageInTreeInfo[] = [$row['uid'], htmlspecialchars($row['title'], $depth)]; } if ($depth > 1 && (!($row['hidden'] == 1 && $row['extendToSubpages'] == 1) || $considerHidden)) { $theList .= $this->extGetTreeList($row['uid'], $depth - 1, $begin - 1, $permsClause, $considerHidden); } } } return $theList; }