iconFactory = GeneralUtility::makeInstance(IconFactory::class); $this->queryBuilder = $this->getQueryBuilderForTable($table); $this->queryBuilder->getRestrictions() ->removeAll() ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) // if table is versionized, only get the records from the Live Workspace // the overlay itself of WS-records is done below ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class, 0)); $this->table = $table; $this->config = $config; // get a list of all the pages that should be looked on if (isset($config['pidList'])) { $allowedPages = ($pageIds = GeneralUtility::trimExplode(',', $config['pidList'])); $depth = (int)$config['pidDepth']; foreach ($pageIds as $pageId) { if ($pageId > 0) { \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($allowedPages, $this->getAllSubpagesOfPage($pageId, $depth)); } } $this->allowedPages = array_unique($allowedPages); } if (isset($config['maxItemsInResultList'])) { $this->maxItems = $config['maxItemsInResultList']; } if ($this->table === 'pages') { $this->queryBuilder->andWhere( QueryHelper::stripLogicalOperatorPrefix($GLOBALS['BE_USER']->getPagePermsClause(Permission::PAGE_SHOW)) ); } if (isset($config['addWhere'])) { $this->queryBuilder->andWhere( QueryHelper::stripLogicalOperatorPrefix($config['addWhere']) ); } } /** * Queries a table for records and completely processes them * * Returns a two-dimensional array of almost finished records; the only need to be put into a
  • -structure * * If you subclass this class, you will most likely only want to overwrite the functions called from here, but not * this function itself * * @param array $params * @param int $recursionCounter The parent object * @return array Array of rows or FALSE if nothing found */ public function queryTable(&$params, $recursionCounter = 0) { $rows = []; $this->params = &$params; $start = $recursionCounter * 50; $this->prepareSelectStatement(); $this->prepareOrderByStatement(); $result = $this->queryBuilder->select('*') ->from($this->table) ->setFirstResult($start) ->setMaxResults(50) ->execute(); $allRowsCount = $result->rowCount(); if ($allRowsCount) { while ($row = $result->fetch()) { // check if we already have collected the maximum number of records if (count($rows) > $this->maxItems) { break; } $this->manipulateRecord($row); $this->makeWorkspaceOverlay($row); // check if the user has access to the record if (!$this->checkRecordAccess($row, $row['uid'])) { continue; } $spriteIcon = $this->iconFactory->getIconForRecord($this->table, $row, Icon::SIZE_SMALL)->render(); $uid = $row['t3ver_oid'] > 0 ? $row['t3ver_oid'] : $row['uid']; $path = $this->getRecordPath($row, $uid); if (mb_strlen($path, 'utf-8') > 30) { $croppedPath = '' . htmlspecialchars( mb_substr($path, 0, 10, 'utf-8') . '...' . mb_substr($path, -20, null, 'utf-8') ) . ''; } else { $croppedPath = htmlspecialchars($path); } $label = $this->getLabel($row); $entry = [ 'text' => '' . $label . '[' . $uid . ']
    ' . $croppedPath . '', 'table' => $this->mmForeignTable ? $this->mmForeignTable : $this->table, 'label' => $label, 'path' => $path, 'uid' => $uid, 'style' => '', 'class' => isset($this->config['cssClass']) ? $this->config['cssClass'] : '', 'sprite' => $spriteIcon ]; $rows[$this->table . '_' . $uid] = $this->renderRecord($row, $entry); } // if there are less records than we need, call this function again to get more records if (count($rows) < $this->maxItems && $allRowsCount >= 50 && $recursionCounter < $this->maxItems) { $tmp = self::queryTable($params, ++$recursionCounter); $rows = array_merge($tmp, $rows); } } return $rows; } /** * Prepare the statement for selecting the records which will be returned to the selector. May also return some * other records (e.g. from a mm-table) which will be used later on to select the real records */ protected function prepareSelectStatement() { $expressionBuilder = $this->queryBuilder->expr(); $searchWholePhrase = !isset($this->config['searchWholePhrase']) || $this->config['searchWholePhrase']; $searchString = $this->params['value']; $searchUid = (int)$searchString; if ($searchString !== '') { $likeCondition = ($searchWholePhrase ? '%' : '') . $searchString . '%'; // Search in all fields given by label or label_alt $selectFieldsList = $GLOBALS['TCA'][$this->table]['ctrl']['label'] . ',' . $GLOBALS['TCA'][$this->table]['ctrl']['label_alt'] . ',' . $this->config['additionalSearchFields']; $selectFields = GeneralUtility::trimExplode(',', $selectFieldsList, true); $selectFields = array_unique($selectFields); $selectParts = $expressionBuilder->orX(); foreach ($selectFields as $field) { $selectParts->add($expressionBuilder->like($field, $this->queryBuilder->createPositionalParameter($likeCondition))); } $searchClause = $expressionBuilder->orX($selectParts); if ($searchUid > 0 && $searchUid == $searchString) { $searchClause->add($expressionBuilder->eq('uid', $searchUid)); } $this->queryBuilder->andWhere($expressionBuilder->orX($searchClause)); } if (!empty($this->allowedPages)) { $pidList = array_map('intval', $this->allowedPages); if (!empty($pidList)) { $this->queryBuilder->andWhere( $expressionBuilder->in('pid', $pidList) ); } } // add an additional search condition comment if (isset($this->config['searchCondition']) && $this->config['searchCondition'] !== '') { $this->queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($this->config['searchCondition'])); } } /** * Selects all subpages of one page, optionally only up to a certain level * * @param int $uid The uid of the page * @param int $depth The depth to select up to. Defaults to 99 * @return array of page IDs */ protected function getAllSubpagesOfPage($uid, $depth = 99) { $pageIds = [$uid]; $level = 0; $pages = [$uid]; $queryBuilder = $this->getQueryBuilderForTable('pages'); $queryBuilder->select('uid') ->from('pages'); // fetch all while ($depth - $level > 0 && !empty($pageIds)) { ++$level; $rows = $queryBuilder ->where( $queryBuilder->expr()->in( 'pid', $queryBuilder->createNamedParameter($pageIds, Connection::PARAM_INT_ARRAY) ) ) ->execute() ->fetchAll(); $rows = array_column(($rows ?: []), 'uid', 'uid'); if (!count($rows)) { $pageIds = array_keys($rows); $pages = array_merge($pages, $pageIds); } else { break; } } return $pages; } /** * Prepares the clause by which the result elements are sorted. See description of ORDER BY in * SQL standard for reference. */ 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); } } } /** * Manipulate a record before using it to render the selector; may be used to replace a MM-relation etc. * * @param array $row */ protected function manipulateRecord(&$row) { } /** * Selects whether the logged in Backend User is allowed to read a specific record * * @param array $row * @param int $uid * @return bool */ protected function checkRecordAccess($row, $uid) { $retValue = true; $table = $this->mmForeignTable ?: $this->table; if ($table === 'pages') { if (!BackendUtility::readPageAccess($uid, $GLOBALS['BE_USER']->getPagePermsClause(Permission::PAGE_SHOW))) { $retValue = false; } } elseif (isset($GLOBALS['TCA'][$table]['ctrl']['is_static']) && (bool)$GLOBALS['TCA'][$table]['ctrl']['is_static']) { $retValue = true; } else { if (!is_array(BackendUtility::readPageAccess($row['pid'], $GLOBALS['BE_USER']->getPagePermsClause(Permission::PAGE_SHOW)))) { $retValue = false; } } return $retValue; } /** * Overlay the given record with its workspace-version, if any * * @param array $row The record to get the workspace version for */ protected function makeWorkspaceOverlay(&$row) { // Check for workspace-versions if ($GLOBALS['BE_USER']->workspace != 0 && $GLOBALS['TCA'][$this->table]['ctrl']['versioningWS'] == true) { BackendUtility::workspaceOL($this->mmForeignTable ? $this->mmForeignTable : $this->table, $row); } } /** * Returns the path for a record. Is the whole path for all records except pages - for these the last part is cut * off, because it contains the pagetitle itself, which would be double information * * The path is returned uncut, cutting has to be done by calling function. * * @param array $row The row * @param array $record The record * @return string The record-path */ protected function getRecordPath(&$row, $uid) { $titleLimit = max($this->config['maxPathTitleLength'], 0); if (($this->mmForeignTable ? $this->mmForeignTable : $this->table) === 'pages') { $path = BackendUtility::getRecordPath($uid, '', $titleLimit); // For pages we only want the first (n-1) parts of the path, // because the n-th part is the page itself $path = substr($path, 0, strrpos($path, '/', -2)) . '/'; } else { $path = BackendUtility::getRecordPath($row['pid'], '', $titleLimit); } return $path; } /** * Returns a label for a given record; usually only a wrapper for \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordTitle * * @param array $row The record to get the label for * @return string The label */ protected function getLabel($row) { return BackendUtility::getRecordTitle($this->mmForeignTable ? $this->mmForeignTable : $this->table, $row, true); } /** * Calls a user function for rendering the page. * * This user function should manipulate $entry, especially $entry['text']. * * @param array $row The row * @param array $entry The entry to render * @return array The rendered entry (will be put into a
  • later on */ protected function renderRecord($row, $entry) { // Call renderlet if available (normal pages etc. usually don't have one) if ($this->config['renderFunc'] != '') { $params = [ 'table' => $this->table, 'uid' => $row['uid'], 'row' => $row, 'entry' => &$entry ]; GeneralUtility::callUserFunction($this->config['renderFunc'], $params, $this); } return $entry; } /** * @return LanguageService */ protected function getLanguageService() { return $GLOBALS['LANG']; } /** * @param string $table * @return QueryBuilder */ protected function getQueryBuilderForTable($table) { return GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); } }