'or',
'AND' => 'and',
'comparison' => [
// Type = text offset = 0
'0_' => 'contains',
'1_' => 'does not contain',
'2_' => 'starts with',
'3_' => 'does not start with',
'4_' => 'ends with',
'5_' => 'does not end with',
'6_' => 'equals',
'7_' => 'does not equal',
// Type = number , offset = 32
'32_' => 'equals',
'33_' => 'does not equal',
'34_' => 'is greater than',
'35_' => 'is less than',
'36_' => 'is between',
'37_' => 'is not between',
'38_' => 'is in list',
'39_' => 'is not in list',
'40_' => 'binary AND equals',
'41_' => 'binary AND does not equal',
'42_' => 'binary OR equals',
'43_' => 'binary OR does not equal',
// Type = multiple, relation, offset = 64
'64_' => 'equals',
'65_' => 'does not equal',
'66_' => 'contains',
'67_' => 'does not contain',
'68_' => 'is in list',
'69_' => 'is not in list',
'70_' => 'binary AND equals',
'71_' => 'binary AND does not equal',
'72_' => 'binary OR equals',
'73_' => 'binary OR does not equal',
// Type = date,time offset = 96
'96_' => 'equals',
'97_' => 'does not equal',
'98_' => 'is greater than',
'99_' => 'is less than',
'100_' => 'is between',
'101_' => 'is not between',
'102_' => 'binary AND equals',
'103_' => 'binary AND does not equal',
'104_' => 'binary OR equals',
'105_' => 'binary OR does not equal',
// Type = boolean, offset = 128
'128_' => 'is True',
'129_' => 'is False',
// Type = binary , offset = 160
'160_' => 'equals',
'161_' => 'does not equal',
'162_' => 'contains',
'163_' => 'does not contain'
]
];
/**
* @var array
*/
protected $compSQL = [
// Type = text offset = 0
'0' => '#FIELD# LIKE \'%#VALUE#%\'',
'1' => '#FIELD# NOT LIKE \'%#VALUE#%\'',
'2' => '#FIELD# LIKE \'#VALUE#%\'',
'3' => '#FIELD# NOT LIKE \'#VALUE#%\'',
'4' => '#FIELD# LIKE \'%#VALUE#\'',
'5' => '#FIELD# NOT LIKE \'%#VALUE#\'',
'6' => '#FIELD# = \'#VALUE#\'',
'7' => '#FIELD# != \'#VALUE#\'',
// Type = number, offset = 32
'32' => '#FIELD# = \'#VALUE#\'',
'33' => '#FIELD# != \'#VALUE#\'',
'34' => '#FIELD# > #VALUE#',
'35' => '#FIELD# < #VALUE#',
'36' => '#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#',
'37' => 'NOT (#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#)',
'38' => '#FIELD# IN (#VALUE#)',
'39' => '#FIELD# NOT IN (#VALUE#)',
'40' => '(#FIELD# & #VALUE#)=#VALUE#',
'41' => '(#FIELD# & #VALUE#)!=#VALUE#',
'42' => '(#FIELD# | #VALUE#)=#VALUE#',
'43' => '(#FIELD# | #VALUE#)!=#VALUE#',
// Type = multiple, relation, offset = 64
'64' => '#FIELD# = \'#VALUE#\'',
'65' => '#FIELD# != \'#VALUE#\'',
'66' => '#FIELD# LIKE \'%#VALUE#%\' AND #FIELD# LIKE \'%#VALUE1#%\'',
'67' => '(#FIELD# NOT LIKE \'%#VALUE#%\' OR #FIELD# NOT LIKE \'%#VALUE1#%\')',
'68' => '#FIELD# IN (#VALUE#)',
'69' => '#FIELD# NOT IN (#VALUE#)',
'70' => '(#FIELD# & #VALUE#)=#VALUE#',
'71' => '(#FIELD# & #VALUE#)!=#VALUE#',
'72' => '(#FIELD# | #VALUE#)=#VALUE#',
'73' => '(#FIELD# | #VALUE#)!=#VALUE#',
// Type = date, offset = 32
'96' => '#FIELD# = \'#VALUE#\'',
'97' => '#FIELD# != \'#VALUE#\'',
'98' => '#FIELD# > #VALUE#',
'99' => '#FIELD# < #VALUE#',
'100' => '#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#',
'101' => 'NOT (#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#)',
'102' => '(#FIELD# & #VALUE#)=#VALUE#',
'103' => '(#FIELD# & #VALUE#)!=#VALUE#',
'104' => '(#FIELD# | #VALUE#)=#VALUE#',
'105' => '(#FIELD# | #VALUE#)!=#VALUE#',
// Type = boolean, offset = 128
'128' => '#FIELD# = \'1\'',
'129' => '#FIELD# != \'1\'',
// Type = binary = 160
'160' => '#FIELD# = \'#VALUE#\'',
'161' => '#FIELD# != \'#VALUE#\'',
'162' => '(#FIELD# & #VALUE#)=#VALUE#',
'163' => '(#FIELD# & #VALUE#)=0'
];
/**
* @var array
*/
protected $comp_offsets = [
'text' => 0,
'number' => 1,
'multiple' => 2,
'relation' => 2,
'date' => 3,
'time' => 3,
'boolean' => 4,
'binary' => 5
];
/**
* Form data name prefix
*
* @var string
*/
protected $name;
/**
* Table for the query
*
* @var string
*/
protected $table;
/**
* Field list
*
* @var string
*/
protected $fieldList;
/**
* Array of the fields possible
*
* @var array
*/
protected $fields = [];
/**
* @var array
*/
protected $extFieldLists = [];
/**
* The query config
*
* @var array
*/
protected $queryConfig = [];
/**
* @var bool
*/
protected $enablePrefix = false;
/**
* @var bool
*/
protected $enableQueryParts = false;
/**
* @var string
*/
protected $fieldName;
/**
* If the current user is an admin and $GLOBALS['TYPO3_CONF_VARS']['BE']['debug']
* is set to true, the names of fields and tables are displayed.
*
* @var bool
*/
protected $showFieldAndTableNames = false;
public function __construct(array $settings, array $menuItems, string $moduleName)
{
$this->getLanguageService()->includeLLFile('EXT:core/Resources/Private/Language/locallang_t3lib_fullsearch.xlf');
$this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
$this->settings = $settings;
$this->menuItems = $menuItems;
$this->moduleName = $moduleName;
$this->showFieldAndTableNames = ($GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] ?? false)
&& $this->getBackendUserAuthentication()->isAdmin();
}
/**
* Get form
*
* @return string
*/
public function form()
{
$markup = [];
$markup[] = '
';
$markup[] = '';
$markup[] = '
';
$markup[] = '';
$markup[] = '';
$markup[] = '
';
return implode(LF, $markup);
}
/**
* Query marker
*
* @return string
*/
public function queryMaker()
{
$output = '';
$this->hookArray = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3lib_fullsearch'] ?? [];
$msg = $this->procesStoreControl();
$userTsConfig = $this->getBackendUserAuthentication()->getTSConfig();
if (!($userTsConfig['mod.']['dbint.']['disableStoreControl'] ?? false)) {
$output .= 'Load/Save Query
';
$output .= '' . $this->makeStoreControl() . '
';
$output .= $msg;
}
// Query Maker:
$this->init('queryConfig', $this->settings['queryTable'] ?? '', '', $this->settings);
if ($this->formName) {
$this->setFormName($this->formName);
}
$tmpCode = $this->makeSelectorTable($this->settings);
$output .= 'Make query
' . $tmpCode . '
';
$mQ = $this->settings['search_query_makeQuery'];
// Make form elements:
if ($this->table && is_array($GLOBALS['TCA'][$this->table])) {
if ($mQ) {
// Show query
$this->enablePrefix = true;
$queryString = $this->getQuery($this->queryConfig);
$selectQueryString = $this->getSelectQuery($queryString);
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
$isConnectionMysql = strpos($connection->getServerVersion(), 'MySQL') === 0;
$fullQueryString = '';
try {
if ($mQ === 'explain' && $isConnectionMysql) {
// EXPLAIN is no ANSI SQL, for now this is only executed on mysql
// @todo: Move away from getSelectQuery() or model differently
$fullQueryString = 'EXPLAIN ' . $selectQueryString;
$dataRows = $connection->executeQuery('EXPLAIN ' . $selectQueryString)->fetchAllAssociative();
} elseif ($mQ === 'count') {
$queryBuilder = $connection->createQueryBuilder();
$queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$queryBuilder->count('*')
->from($this->table)
->where(QueryHelper::stripLogicalOperatorPrefix($queryString));
$fullQueryString = $queryBuilder->getSQL();
$dataRows = [$queryBuilder->execute()->fetchOne()];
} else {
$fullQueryString = $selectQueryString;
$dataRows = $connection->executeQuery($selectQueryString)->fetchAllAssociative();
}
if (!($userTsConfig['mod.']['dbint.']['disableShowSQLQuery'] ?? false)) {
$output .= 'SQL query
' . htmlspecialchars($fullQueryString) . '
';
}
$cPR = $this->getQueryResultCode($mQ, $dataRows, $this->table);
$output .= '' . $cPR['header'] . '
' . $cPR['content'] . '
';
} catch (DBALException $e) {
if (!$userTsConfig['mod.']['dbint.']['disableShowSQLQuery']) {
$output .= 'SQL query
' . htmlspecialchars($fullQueryString) . '
';
}
$out = 'Error: '
. htmlspecialchars($e->getMessage())
. '
';
$output .= 'SQL error
' . $out . '
';
}
}
}
return '' . $output . '
';
}
/**
* Search
*
* @return string
*/
public function search()
{
$swords = $this->settings['sword'] ?? '';
$out = '';
if ($swords) {
foreach ($GLOBALS['TCA'] as $table => $value) {
// Get fields list
$conf = $GLOBALS['TCA'][$table];
// Avoid querying tables with no columns
if (empty($conf['columns'])) {
continue;
}
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table);
$tableColumns = $connection->getSchemaManager()->listTableColumns($table);
$fieldsInDatabase = [];
foreach ($tableColumns as $column) {
$fieldsInDatabase[] = $column->getName();
}
$fields = array_intersect(array_keys($conf['columns']), $fieldsInDatabase);
$queryBuilder = $connection->createQueryBuilder();
$queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$queryBuilder->count('*')->from($table);
$likes = [];
$escapedLikeString = '%' . $queryBuilder->escapeLikeWildcards($swords) . '%';
foreach ($fields as $field) {
$likes[] = $queryBuilder->expr()->like(
$field,
$queryBuilder->createNamedParameter($escapedLikeString, \PDO::PARAM_STR)
);
}
$count = $queryBuilder->orWhere(...$likes)->execute()->fetchOne();
if ($count > 0) {
$queryBuilder = $connection->createQueryBuilder();
$queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$queryBuilder->select('uid', $conf['ctrl']['label'])
->from($table)
->setMaxResults(200);
$likes = [];
foreach ($fields as $field) {
$likes[] = $queryBuilder->expr()->like(
$field,
$queryBuilder->createNamedParameter($escapedLikeString, \PDO::PARAM_STR)
);
}
$statement = $queryBuilder->orWhere(...$likes)->execute();
$lastRow = null;
$rowArr = [];
while ($row = $statement->fetchAssociative()) {
$rowArr[] = $this->resultRowDisplay($row, $conf, $table);
$lastRow = $row;
}
$markup = [];
$markup[] = '';
$markup[] = '
';
$markup[] = htmlspecialchars($this->getLanguageService()->sL($conf['ctrl']['title'])) . ' (' . $count . ')';
$markup[] = '
';
$markup[] = '
';
$markup[] = $this->resultRowTitles($lastRow, $conf);
$markup[] = implode(LF, $rowArr);
$markup[] = '
';
$markup[] = '
';
$out .= implode(LF, $markup);
}
}
}
return $out;
}
/**
* Sets the current name of the input form.
*
* @param string $formName The name of the form.
*/
public function setFormName($formName)
{
$this->formName = trim($formName);
}
/**
* Make store control
*
* @return string
*/
protected function makeStoreControl()
{
// Load/Save
$storeArray = $this->initStoreArray();
$opt = [];
foreach ($storeArray as $k => $v) {
$opt[] = '';
}
// Actions:
if (ExtensionManagementUtility::isLoaded('sys_action') && $this->getBackendUserAuthentication()->isAdmin()) {
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_action');
$queryBuilder->getRestrictions()->removeAll();
$statement = $queryBuilder->select('uid', 'title')
->from('sys_action')
->where($queryBuilder->expr()->eq('type', $queryBuilder->createNamedParameter(2, \PDO::PARAM_INT)))
->orderBy('title')
->execute();
$opt[] = '';
while ($row = $statement->fetchAssociative()) {
$opt[] = '';
}
}
$markup = [];
$markup[] = '';
$markup[] = '
';
$markup[] = '
';
return implode(LF, $markup);
}
/**
* Init store array
*
* @return array
*/
protected function initStoreArray()
{
$storeArray = [
'0' => '[New]'
];
$savedStoreArray = unserialize($this->settings['storeArray'] ?? '', ['allowed_classes' => false]);
if (is_array($savedStoreArray)) {
$storeArray = array_merge($storeArray, $savedStoreArray);
}
return $storeArray;
}
/**
* Clean store query configs
*
* @param array $storeQueryConfigs
* @param array $storeArray
* @return array
*/
protected function cleanStoreQueryConfigs($storeQueryConfigs, $storeArray)
{
if (is_array($storeQueryConfigs)) {
foreach ($storeQueryConfigs as $k => $v) {
if (!isset($storeArray[$k])) {
unset($storeQueryConfigs[$k]);
}
}
}
return $storeQueryConfigs;
}
/**
* Add to store query configs
*
* @param array $storeQueryConfigs
* @param int $index
* @return array
*/
protected function addToStoreQueryConfigs($storeQueryConfigs, $index)
{
$keyArr = explode(',', $this->storeList);
$storeQueryConfigs[$index] = [];
foreach ($keyArr as $k) {
$storeQueryConfigs[$index][$k] = $this->settings[$k];
}
return $storeQueryConfigs;
}
/**
* Save query in action
*
* @param int $uid
* @return bool
*/
protected function saveQueryInAction($uid)
{
if (ExtensionManagementUtility::isLoaded('sys_action')) {
$keyArr = explode(',', $this->storeList);
$saveArr = [];
foreach ($keyArr as $k) {
$saveArr[$k] = $this->settings[$k];
}
// Show query
if ($saveArr['queryTable']) {
$this->init('queryConfig', $saveArr['queryTable'], '', $this->settings);
$this->makeSelectorTable($saveArr);
$this->enablePrefix = true;
$queryString = $this->getQuery($this->queryConfig);
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable($this->table);
$queryBuilder->getRestrictions()->removeAll()
->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$rowCount = $queryBuilder->count('*')
->from($this->table)
->where(QueryHelper::stripLogicalOperatorPrefix($queryString))
->execute()->fetchOne();
$t2DataValue = [
'qC' => $saveArr,
'qCount' => $rowCount,
'qSelect' => $this->getSelectQuery($queryString),
'qString' => $queryString
];
GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_action')
->update(
'sys_action',
['t2_data' => serialize($t2DataValue)],
['uid' => (int)$uid],
['t2_data' => Connection::PARAM_LOB]
);
}
return true;
}
return false;
}
/**
* Load store query configs
*
* @param array $storeQueryConfigs
* @param int $storeIndex
* @param array $writeArray
* @return array
*/
protected function loadStoreQueryConfigs($storeQueryConfigs, $storeIndex, $writeArray)
{
if ($storeQueryConfigs[$storeIndex]) {
$keyArr = explode(',', $this->storeList);
foreach ($keyArr as $k) {
$writeArray[$k] = $storeQueryConfigs[$storeIndex][$k];
}
}
return $writeArray;
}
/**
* Process store control
*
* @return string
*/
protected function procesStoreControl()
{
$languageService = $this->getLanguageService();
$flashMessage = null;
$storeArray = $this->initStoreArray();
$storeQueryConfigs = unserialize($this->settings['storeQueryConfigs'] ?? '', ['allowed_classes' => false]);
$storeControl = GeneralUtility::_GP('storeControl');
$storeIndex = (int)($storeControl['STORE'] ?? 0);
$saveStoreArray = 0;
$writeArray = [];
$msg = '';
if (is_array($storeControl)) {
if ($storeControl['LOAD'] ?? false) {
if ($storeIndex > 0) {
$writeArray = $this->loadStoreQueryConfigs($storeQueryConfigs, $storeIndex, $writeArray);
$saveStoreArray = 1;
$flashMessage = GeneralUtility::makeInstance(
FlashMessage::class,
sprintf($languageService->getLL('query_loaded'), $storeArray[$storeIndex])
);
} elseif ($storeIndex < 0 && ExtensionManagementUtility::isLoaded('sys_action')) {
$actionRecord = BackendUtility::getRecord('sys_action', abs($storeIndex));
if (is_array($actionRecord)) {
$dA = unserialize($actionRecord['t2_data'], ['allowed_classes' => false]);
$dbSC = [];
if (is_array($dA['qC'])) {
$dbSC[0] = $dA['qC'];
}
$writeArray = $this->loadStoreQueryConfigs($dbSC, 0, $writeArray);
$saveStoreArray = 1;
$flashMessage = GeneralUtility::makeInstance(
FlashMessage::class,
sprintf($languageService->getLL('query_from_action_loaded'), $actionRecord['title'])
);
}
}
} elseif ($storeControl['SAVE'] ?? false) {
if ($storeIndex < 0) {
$qOK = $this->saveQueryInAction(abs($storeIndex));
if ($qOK) {
$flashMessage = GeneralUtility::makeInstance(
FlashMessage::class,
$languageService->getLL('query_saved')
);
} else {
$flashMessage = GeneralUtility::makeInstance(
FlashMessage::class,
$languageService->getLL('query_notsaved'),
'',
FlashMessage::ERROR
);
}
} else {
if (trim($storeControl['title'])) {
if ($storeIndex > 0) {
$storeArray[$storeIndex] = $storeControl['title'];
} else {
$storeArray[] = $storeControl['title'];
end($storeArray);
$storeIndex = key($storeArray);
}
$storeQueryConfigs = $this->addToStoreQueryConfigs($storeQueryConfigs, (int)$storeIndex);
$saveStoreArray = 1;
$flashMessage = GeneralUtility::makeInstance(
FlashMessage::class,
$languageService->getLL('query_saved')
);
}
}
} elseif ($storeControl['REMOVE'] ?? false) {
if ($storeIndex > 0) {
$flashMessage = GeneralUtility::makeInstance(
FlashMessage::class,
sprintf($languageService->getLL('query_removed'), $storeArray[$storeControl['STORE']])
);
// Removing
unset($storeArray[$storeControl['STORE']]);
$saveStoreArray = 1;
}
}
if (!empty($flashMessage)) {
$msg = GeneralUtility::makeInstance(FlashMessageRendererResolver::class)
->resolve()
->render([$flashMessage]);
}
}
if ($saveStoreArray) {
// Making sure, index 0 is not set!
unset($storeArray[0]);
$writeArray['storeArray'] = serialize($storeArray);
$writeArray['storeQueryConfigs'] =
serialize($this->cleanStoreQueryConfigs($storeQueryConfigs, $storeArray));
$this->settings = BackendUtility::getModuleData(
$this->menuItems,
$writeArray,
$this->moduleName,
'ses'
);
}
return $msg;
}
/**
* Get query result code
*
* @param string $type
* @param array $dataRows Rows to display
* @param string $table
* @return array HTML-code for "header" and "content"
* @throws \TYPO3\CMS\Core\Exception
*/
protected function getQueryResultCode($type, array $dataRows, $table)
{
$out = '';
$cPR = [];
switch ($type) {
case 'count':
$cPR['header'] = 'Count';
$cPR['content'] = '
' . (int)$dataRows[0] . ' records selected.';
break;
case 'all':
$rowArr = [];
$dataRow = null;
foreach ($dataRows as $dataRow) {
$rowArr[] = $this->resultRowDisplay($dataRow, $GLOBALS['TCA'][$table], $table);
}
if (is_array($this->hookArray['beforeResultTable'] ?? false)) {
foreach ($this->hookArray['beforeResultTable'] as $_funcRef) {
$out .= GeneralUtility::callUserFunction($_funcRef, $this->settings);
}
}
if (!empty($rowArr)) {
$cPR['header'] = 'Result';
$out .= ''
. $this->resultRowTitles($dataRow, $GLOBALS['TCA'][$table]) . implode(LF, $rowArr)
. '
';
} else {
$this->renderNoResultsFoundMessage();
}
$cPR['content'] = $out;
break;
case 'csv':
$rowArr = [];
$first = 1;
foreach ($dataRows as $dataRow) {
if ($first) {
$rowArr[] = $this->csvValues(array_keys($dataRow), ',', '');
$first = 0;
}
$rowArr[] = $this->csvValues($dataRow, ',', '"', $GLOBALS['TCA'][$table], $table);
}
if (!empty($rowArr)) {
$cPR['header'] = 'Result';
$out .= '';
if (!$this->noDownloadB) {
$out .= '
';
}
// Downloads file:
// @todo: args. routing anyone?
if (GeneralUtility::_GP('download_file')) {
$filename = 'TYPO3_' . $table . '_export_' . date('dmy-Hi') . '.csv';
$mimeType = 'application/octet-stream';
header('Content-Type: ' . $mimeType);
header('Content-Disposition: attachment; filename=' . $filename);
echo implode(CRLF, $rowArr);
die;
}
} else {
$this->renderNoResultsFoundMessage();
}
$cPR['content'] = $out;
break;
case 'explain':
default:
foreach ($dataRows as $dataRow) {
$out .= '
' . DebugUtility::viewArray($dataRow);
}
$cPR['header'] = 'Explain SQL query';
$cPR['content'] = $out;
}
return $cPR;
}
/**
* CSV values
*
* @param array $row
* @param string $delim
* @param string $quote
* @param array $conf
* @param string $table
* @return string A single line of CSV
*/
protected function csvValues($row, $delim = ',', $quote = '"', $conf = [], $table = '')
{
$valueArray = $row;
if ($this->settings['search_result_labels'] && $table) {
foreach ($valueArray as $key => $val) {
$valueArray[$key] = $this->getProcessedValueExtra($table, $key, $val, $conf, ';');
}
}
return CsvUtility::csvValues($valueArray, $delim, $quote);
}
/**
* Result row display
*
* @param array $row
* @param array $conf
* @param string $table
* @return string
*/
protected function resultRowDisplay($row, $conf, $table)
{
$languageService = $this->getLanguageService();
$out = '';
foreach ($row as $fieldName => $fieldValue) {
if (GeneralUtility::inList($this->settings['queryFields'] ?? '', $fieldName)
|| !($this->settings['queryFields'] ?? false)
&& $fieldName !== 'pid'
&& $fieldName !== 'deleted'
) {
if ($this->settings['search_result_labels'] ?? false) {
$fVnew = $this->getProcessedValueExtra($table, $fieldName, $fieldValue, $conf, '
');
} else {
$fVnew = htmlspecialchars($fieldValue);
}
$out .= '' . $fVnew . ' | ';
}
}
$out .= '';
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
if (!($row['deleted'] ?? false)) {
$out .= '';
$url = (string)$uriBuilder->buildUriFromRoute('record_edit', [
'edit' => [
$table => [
$row['uid'] => 'edit'
]
],
'returnUrl' => $GLOBALS['TYPO3_REQUEST']->getAttribute('normalizedParams')->getRequestUri()
. HttpUtility::buildQueryString(['SET' => (array)GeneralUtility::_POST('SET')], '&')
]);
$out .= ' '
. $this->iconFactory->getIcon('actions-open', Icon::SIZE_SMALL)->render() . '';
$out .= ' ';
$out .= sprintf(
' %s',
'TYPO3.InfoWindow.showItem',
htmlspecialchars($table . ',' . $row['uid']),
$this->iconFactory->getIcon('actions-document-info', Icon::SIZE_SMALL)->render()
);
$out .= ' ';
} else {
$out .= '';
}
$_params = [$table => $row];
if (is_array($this->hookArray['additionalButtons'] ?? false)) {
foreach ($this->hookArray['additionalButtons'] as $_funcRef) {
$out .= GeneralUtility::callUserFunction($_funcRef, $_params);
}
}
$out .= ' |
';
return $out;
}
/**
* Get processed value extra
*
* @param string $table
* @param string $fieldName
* @param string $fieldValue
* @param array $conf Not used
* @param string $splitString
* @return string
*/
protected function getProcessedValueExtra($table, $fieldName, $fieldValue, $conf, $splitString)
{
$out = '';
$fields = [];
// Analysing the fields in the table.
if (is_array($GLOBALS['TCA'][$table])) {
$fC = $GLOBALS['TCA'][$table]['columns'][$fieldName];
$fields = $fC['config'];
$fields['exclude'] = $fC['exclude'];
if (is_array($fC) && $fC['label']) {
$fields['label'] = preg_replace('/:$/', '', trim($this->getLanguageService()->sL($fC['label'])));
switch ($fields['type']) {
case 'input':
if (preg_match('/int|year/i', $fields['eval'])) {
$fields['type'] = 'number';
} elseif (preg_match('/time/i', $fields['eval'])) {
$fields['type'] = 'time';
} elseif (preg_match('/date/i', $fields['eval'])) {
$fields['type'] = 'date';
} else {
$fields['type'] = 'text';
}
break;
case 'check':
if (!$fields['items']) {
$fields['type'] = 'boolean';
} else {
$fields['type'] = 'binary';
}
break;
case 'radio':
$fields['type'] = 'multiple';
break;
case 'select':
$fields['type'] = 'multiple';
if ($fields['foreign_table']) {
$fields['type'] = 'relation';
}
if ($fields['special']) {
$fields['type'] = 'text';
}
break;
case 'group':
if ($fields['internal_type'] === 'db') {
$fields['type'] = 'relation';
}
break;
case 'user':
case 'flex':
case 'passthrough':
case 'none':
case 'text':
default:
$fields['type'] = 'text';
}
} else {
$fields['label'] = '[FIELD: ' . $fieldName . ']';
switch ($fieldName) {
case 'pid':
$fields['type'] = 'relation';
$fields['allowed'] = 'pages';
break;
case 'cruser_id':
$fields['type'] = 'relation';
$fields['allowed'] = 'be_users';
break;
case 'tstamp':
case 'crdate':
$fields['type'] = 'time';
break;
default:
$fields['type'] = 'number';
}
}
}
switch ($fields['type']) {
case 'date':
if ($fieldValue != -1) {
$out = (string)strftime('%d-%m-%Y', (int)$fieldValue);
}
break;
case 'time':
if ($fieldValue != -1) {
if ($splitString === '
') {
$out = (string)strftime('%H:%M' . $splitString . '%d-%m-%Y', (int)$fieldValue);
} else {
$out = (string)strftime('%H:%M %d-%m-%Y', (int)$fieldValue);
}
}
break;
case 'multiple':
case 'binary':
case 'relation':
$out = $this->makeValueList($fieldName, $fieldValue, $fields, $table, $splitString);
break;
case 'boolean':
$out = $fieldValue ? 'True' : 'False';
break;
default:
$out = htmlspecialchars($fieldValue);
}
return $out;
}
/**
* Recursively fetch all descendants of a given page
*
* @param int $id uid of the page
* @param int $depth
* @param int $begin
* @param string $permsClause
* @return string comma separated list of descendant pages
*/
protected function getTreeList($id, $depth, $begin = 0, $permsClause = '')
{
$depth = (int)$depth;
$begin = (int)$begin;
$id = (int)$id;
if ($id < 0) {
$id = abs($id);
}
if ($begin == 0) {
$theList = (string)$id;
} else {
$theList = '';
}
if ($id && $depth > 0) {
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
$queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$statement = $queryBuilder->select('uid')
->from('pages')
->where(
$queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)),
$queryBuilder->expr()->eq('sys_language_uid', 0)
)
->orderBy('uid');
if ($permsClause !== '') {
$queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($permsClause));
}
$statement = $queryBuilder->execute();
while ($row = $statement->fetchAssociative()) {
if ($begin <= 0) {
$theList .= ',' . $row['uid'];
}
if ($depth > 1) {
$theSubList = $this->getTreeList($row['uid'], $depth - 1, $begin - 1, $permsClause);
if (!empty($theList) && !empty($theSubList) && ($theSubList[0] !== ',')) {
$theList .= ',';
}
$theList .= $theSubList;
}
}
}
return $theList;
}
/**
* Make value list
*
* @param string $fieldName
* @param string $fieldValue
* @param array $conf
* @param string $table
* @param string $splitString
* @return string
*/
protected function makeValueList($fieldName, $fieldValue, $conf, $table, $splitString)
{
$backendUserAuthentication = $this->getBackendUserAuthentication();
$languageService = $this->getLanguageService();
$from_table_Arr = [];
$fieldSetup = $conf;
$out = '';
if ($fieldSetup['type'] === 'multiple') {
foreach ($fieldSetup['items'] as $key => $val) {
if (strpos($val[0], 'LLL:') === 0) {
$value = $languageService->sL($val[0]);
} else {
$value = $val[0];
}
if (GeneralUtility::inList($fieldValue, $val[1]) || $fieldValue == $val[1]) {
if ($out !== '') {
$out .= $splitString;
}
$out .= htmlspecialchars($value);
}
}
}
if ($fieldSetup['type'] === 'binary') {
foreach ($fieldSetup['items'] as $Key => $val) {
if (strpos($val[0], 'LLL:') === 0) {
$value = $languageService->sL($val[0]);
} else {
$value = $val[0];
}
if ($out !== '') {
$out .= $splitString;
}
$out .= htmlspecialchars($value);
}
}
if ($fieldSetup['type'] === 'relation') {
$dontPrefixFirstTable = 0;
$useTablePrefix = 0;
if ($fieldSetup['items']) {
foreach ($fieldSetup['items'] as $key => $val) {
if (strpos($val[0], 'LLL:') === 0) {
$value = $languageService->sL($val[0]);
} else {
$value = $val[0];
}
if (GeneralUtility::inList($fieldValue, $value) || $fieldValue == $value) {
if ($out !== '') {
$out .= $splitString;
}
$out .= htmlspecialchars($value);
}
}
}
if (strpos($fieldSetup['allowed'], ',') !== false) {
$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->fetchAssociative()) {
if (strpos($row[$fieldName], ',') !== false) {
$checkContent = explode(',', $row[$fieldName]);
foreach ($checkContent as $singleValue) {
if (strpos($singleValue, '_') === false) {
$dontPrefixFirstTable = 1;
}
}
} else {
$singleValue = $row[$fieldName];
if ($singleValue !== '' && strpos($singleValue, '_') === false) {
$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 (strpos($labelArray[0], 'LLL:') === 0) {
$labelFieldSelect[$labelArray[1]] = $languageService->sL($labelArray[0]);
} else {
$labelFieldSelect[$labelArray[1]] = $labelArray[0];
}
}
$useSelectLabels = 1;
}
$altLabelFieldSelect = [];
if ($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items']) {
$items = $GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items'];
foreach ($items as $altLabelArray) {
if (strpos($altLabelArray[0], 'LLL:') === 0) {
$altLabelFieldSelect[$altLabelArray[1]] = $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 (!$backendUserAuthentication->isAdmin()) {
$webMounts = $backendUserAuthentication->returnWebmounts();
$perms_clause = $backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
$webMountPageTree = '';
$webMountPageTreePrefix = '';
foreach ($webMounts as $webMount) {
if ($webMountPageTree) {
$webMountPageTreePrefix = ',';
}
$webMountPageTree .= $webMountPageTreePrefix
. $this->getTreeList($webMount, 999, 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->fetchAssociative()) {
$this->tableArray[$from_table][] = $row;
}
}
foreach ($this->tableArray[$from_table] as $key => $val) {
$this->settings['labels_noprefix'] =
$this->settings['labels_noprefix'] == 1
? 'on'
: $this->settings['labels_noprefix'];
$prefixString =
$this->settings['labels_noprefix'] === 'on'
? ''
: ' [' . $tablePrefix . $val['uid'] . '] ';
if ($out !== '') {
$out .= $splitString;
}
if (GeneralUtility::inList($fieldValue, $tablePrefix . $val['uid'])
|| $fieldValue == $tablePrefix . $val['uid']) {
if ($useSelectLabels) {
$out .= htmlspecialchars($prefixString . $labelFieldSelect[$val[$labelField]]);
} elseif ($val[$labelField]) {
$out .= htmlspecialchars($prefixString . $val[$labelField]);
} elseif ($useAltSelectLabels) {
$out .= htmlspecialchars($prefixString . $altLabelFieldSelect[$val[$altLabelField]]);
} else {
$out .= htmlspecialchars($prefixString . $val[$altLabelField]);
}
}
}
}
}
}
return $out;
}
/**
* Render table header
*
* @param array $row Table columns
* @param array $conf Table TCA
* @return string HTML of table header
*/
protected function resultRowTitles($row, $conf)
{
$languageService = $this->getLanguageService();
$tableHeader = [];
// Start header row
$tableHeader[] = '';
// Iterate over given columns
foreach ($row as $fieldName => $fieldValue) {
if (GeneralUtility::inList($this->settings['queryFields'] ?? '', $fieldName)
|| !($this->settings['queryFields'] ?? false)
&& $fieldName !== 'pid'
&& $fieldName !== 'deleted'
) {
if ($this->settings['search_result_labels'] ?? false) {
$title = $languageService->sL($conf['columns'][$fieldName]['label']
?: $fieldName);
} else {
$title = $languageService->sL($fieldName);
}
$tableHeader[] = '' . htmlspecialchars($title) . ' | ';
}
}
// Add empty icon column
$tableHeader[] = ' | ';
// Close header row
$tableHeader[] = '
';
return implode(LF, $tableHeader);
}
/**
* @throws \InvalidArgumentException
* @throws \TYPO3\CMS\Core\Exception
*/
private function renderNoResultsFoundMessage()
{
$flashMessage = GeneralUtility::makeInstance(FlashMessage::class, 'No rows selected!', '', FlashMessage::INFO);
$flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
$defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
$defaultFlashMessageQueue->enqueue($flashMessage);
}
/**
* Make a list of fields for current table
*
* @return string Separated list of fields
*/
protected function makeFieldList()
{
$fieldListArr = [];
if (is_array($GLOBALS['TCA'][$this->table])) {
$fieldListArr = array_keys($GLOBALS['TCA'][$this->table]['columns'] ?? []);
$fieldListArr[] = 'uid';
$fieldListArr[] = 'pid';
$fieldListArr[] = 'deleted';
if ($GLOBALS['TCA'][$this->table]['ctrl']['tstamp'] ?? false) {
$fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['tstamp'];
}
if ($GLOBALS['TCA'][$this->table]['ctrl']['crdate'] ?? false) {
$fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['crdate'];
}
if ($GLOBALS['TCA'][$this->table]['ctrl']['cruser_id'] ?? false) {
$fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['cruser_id'];
}
if ($GLOBALS['TCA'][$this->table]['ctrl']['sortby'] ?? false) {
$fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['sortby'];
}
}
return implode(',', $fieldListArr);
}
/**
* Init function
*
* @param string $name The name
* @param string $table The table name
* @param string $fieldList The field list
* @param array $settings Module settings like checkboxes in the interface
*/
protected function init($name, $table, $fieldList = '', array $settings = [])
{
// Analysing the fields in the table.
if (is_array($GLOBALS['TCA'][$table] ?? false)) {
$this->name = $name;
$this->table = $table;
$this->fieldList = $fieldList ?: $this->makeFieldList();
$this->settings = $settings;
$fieldArr = GeneralUtility::trimExplode(',', $this->fieldList, true);
foreach ($fieldArr as $fieldName) {
$fC = $GLOBALS['TCA'][$this->table]['columns'][$fieldName] ?? [];
$this->fields[$fieldName] = $fC['config'] ?? [];
$this->fields[$fieldName]['exclude'] = $fC['exclude'] ?? '';
if (($this->fields[$fieldName]['type'] ?? '') === 'user' && !isset($this->fields[$fieldName]['type']['userFunc'])
|| ($this->fields[$fieldName]['type'] ?? '') === 'none'
) {
// Do not list type=none "virtual" fields or query them from db,
// and if type is user without defined userFunc
unset($this->fields[$fieldName]);
continue;
}
if (is_array($fC) && ($fC['label'] ?? false)) {
$this->fields[$fieldName]['label'] = rtrim(trim($this->getLanguageService()->sL($fC['label'])), ':');
switch ($this->fields[$fieldName]['type']) {
case 'input':
if (preg_match('/int|year/i', $this->fields[$fieldName]['eval'])) {
$this->fields[$fieldName]['type'] = 'number';
} elseif (preg_match('/time/i', $this->fields[$fieldName]['eval'])) {
$this->fields[$fieldName]['type'] = 'time';
} elseif (preg_match('/date/i', $this->fields[$fieldName]['eval'])) {
$this->fields[$fieldName]['type'] = 'date';
} else {
$this->fields[$fieldName]['type'] = 'text';
}
break;
case 'check':
if (!$this->fields[$fieldName]['items'] || count($this->fields[$fieldName]['items']) <= 1) {
$this->fields[$fieldName]['type'] = 'boolean';
} else {
$this->fields[$fieldName]['type'] = 'binary';
}
break;
case 'radio':
$this->fields[$fieldName]['type'] = 'multiple';
break;
case 'select':
$this->fields[$fieldName]['type'] = 'multiple';
if ($this->fields[$fieldName]['foreign_table'] ?? false) {
$this->fields[$fieldName]['type'] = 'relation';
}
if ($this->fields[$fieldName]['special'] ?? false) {
$this->fields[$fieldName]['type'] = 'text';
}
break;
case 'group':
if ($this->fields[$fieldName]['internal_type'] === 'db') {
$this->fields[$fieldName]['type'] = 'relation';
}
break;
case 'user':
case 'flex':
case 'passthrough':
case 'none':
case 'text':
default:
$this->fields[$fieldName]['type'] = 'text';
}
} else {
$this->fields[$fieldName]['label'] = '[FIELD: ' . $fieldName . ']';
switch ($fieldName) {
case 'pid':
$this->fields[$fieldName]['type'] = 'relation';
$this->fields[$fieldName]['allowed'] = 'pages';
break;
case 'cruser_id':
$this->fields[$fieldName]['type'] = 'relation';
$this->fields[$fieldName]['allowed'] = 'be_users';
break;
case 'tstamp':
case 'crdate':
$this->fields[$fieldName]['type'] = 'time';
break;
case 'deleted':
$this->fields[$fieldName]['type'] = 'boolean';
break;
default:
$this->fields[$fieldName]['type'] = 'number';
}
}
}
}
/* // EXAMPLE:
$this->queryConfig = array(
array(
'operator' => 'AND',
'type' => 'FIELD_space_before_class',
),
array(
'operator' => 'AND',
'type' => 'FIELD_records',
'negate' => 1,
'inputValue' => 'foo foo'
),
array(
'type' => 'newlevel',
'nl' => array(
array(
'operator' => 'AND',
'type' => 'FIELD_space_before_class',
'negate' => 1,
'inputValue' => 'foo foo'
),
array(
'operator' => 'AND',
'type' => 'FIELD_records',
'negate' => 1,
'inputValue' => 'foo foo'
)
)
),
array(
'operator' => 'OR',
'type' => 'FIELD_maillist',
)
);
*/
}
/**
* Set and clean up external lists
*
* @param string $name The name
* @param string $list The list
* @param string $force
*/
protected function setAndCleanUpExternalLists($name, $list, $force = '')
{
$fields = array_unique(GeneralUtility::trimExplode(',', $list . ',' . $force, true));
$reList = [];
foreach ($fields as $fieldName) {
if ($this->fields[$fieldName]) {
$reList[] = $fieldName;
}
}
$this->extFieldLists[$name] = implode(',', $reList);
}
/**
* Process data
*
* @param array $qC Query config
*/
protected function procesData($qC = [])
{
$this->queryConfig = $qC;
$POST = GeneralUtility::_POST();
// If delete...
if ($POST['qG_del'] ?? false) {
// Initialize array to work on, save special parameters
$ssArr = $this->getSubscript($POST['qG_del']);
$workArr = &$this->queryConfig;
$ssArrSize = count($ssArr) - 1;
$i = 0;
for (; $i < $ssArrSize; $i++) {
$workArr = &$workArr[$ssArr[$i]];
}
// Delete the entry and move the other entries
unset($workArr[$ssArr[$i]]);
$workArrSize = count((array)$workArr);
for ($j = $ssArr[$i]; $j < $workArrSize; $j++) {
$workArr[$j] = $workArr[$j + 1];
unset($workArr[$j + 1]);
}
}
// If insert...
if ($POST['qG_ins'] ?? false) {
// Initialize array to work on, save special parameters
$ssArr = $this->getSubscript($POST['qG_ins']);
$workArr = &$this->queryConfig;
$ssArrSize = count($ssArr) - 1;
$i = 0;
for (; $i < $ssArrSize; $i++) {
$workArr = &$workArr[$ssArr[$i]];
}
// Move all entries above position where new entry is to be inserted
$workArrSize = count((array)$workArr);
for ($j = $workArrSize; $j > $ssArr[$i]; $j--) {
$workArr[$j] = $workArr[$j - 1];
}
// Clear new entry position
unset($workArr[$ssArr[$i] + 1]);
$workArr[$ssArr[$i] + 1]['type'] = 'FIELD_';
}
// If move up...
if ($POST['qG_up'] ?? false) {
// Initialize array to work on
$ssArr = $this->getSubscript($POST['qG_up']);
$workArr = &$this->queryConfig;
$ssArrSize = count($ssArr) - 1;
$i = 0;
for (; $i < $ssArrSize; $i++) {
$workArr = &$workArr[$ssArr[$i]];
}
// Swap entries
$qG_tmp = $workArr[$ssArr[$i]];
$workArr[$ssArr[$i]] = $workArr[$ssArr[$i] - 1];
$workArr[$ssArr[$i] - 1] = $qG_tmp;
}
// If new level...
if ($POST['qG_nl'] ?? false) {
// Initialize array to work on
$ssArr = $this->getSubscript($POST['qG_nl']);
$workArr = &$this->queryConfig;
$ssArraySize = count($ssArr) - 1;
$i = 0;
for (; $i < $ssArraySize; $i++) {
$workArr = &$workArr[$ssArr[$i]];
}
// Do stuff:
$tempEl = $workArr[$ssArr[$i]];
if (is_array($tempEl)) {
if ($tempEl['type'] !== 'newlevel') {
$workArr[$ssArr[$i]] = [
'type' => 'newlevel',
'operator' => $tempEl['operator'],
'nl' => [$tempEl]
];
}
}
}
// If collapse level...
if ($POST['qG_remnl'] ?? false) {
// Initialize array to work on
$ssArr = $this->getSubscript($POST['qG_remnl']);
$workArr = &$this->queryConfig;
$ssArrSize = count($ssArr) - 1;
$i = 0;
for (; $i < $ssArrSize; $i++) {
$workArr = &$workArr[$ssArr[$i]];
}
// Do stuff:
$tempEl = $workArr[$ssArr[$i]];
if (is_array($tempEl)) {
if ($tempEl['type'] === 'newlevel' && is_array($workArr)) {
$a1 = array_slice($workArr, 0, $ssArr[$i]);
$a2 = array_slice($workArr, $ssArr[$i]);
array_shift($a2);
$a3 = $tempEl['nl'];
$a3[0]['operator'] = $tempEl['operator'];
$workArr = array_merge($a1, $a3, $a2);
}
}
}
}
/**
* Clean up query config
*
* @param array $queryConfig Query config
* @return array
*/
protected function cleanUpQueryConfig($queryConfig)
{
// Since we don't traverse the array using numeric keys in the upcoming while-loop make sure it's fresh and clean before displaying
if (is_array($queryConfig)) {
ksort($queryConfig);
} else {
// queryConfig should never be empty!
if (!isset($queryConfig[0]) || empty($queryConfig[0]['type'])) {
// Make sure queryConfig is an array
$queryConfig = [];
$queryConfig[0] = ['type' => 'FIELD_'];
}
}
// Traverse:
foreach ($queryConfig as $key => $conf) {
$fieldName = '';
if (strpos($conf['type'], 'FIELD_') === 0) {
$fieldName = substr($conf['type'], 6);
$fieldType = $this->fields[$fieldName]['type'];
} elseif ($conf['type'] === 'newlevel') {
$fieldType = $conf['type'];
} else {
$fieldType = 'ignore';
}
switch ($fieldType) {
case 'newlevel':
if (!$queryConfig[$key]['nl']) {
$queryConfig[$key]['nl'][0]['type'] = 'FIELD_';
}
$queryConfig[$key]['nl'] = $this->cleanUpQueryConfig($queryConfig[$key]['nl']);
break;
case 'userdef':
break;
case 'ignore':
default:
$verifiedName = $this->verifyType($fieldName);
$queryConfig[$key]['type'] = 'FIELD_' . $this->verifyType($verifiedName);
if ($conf['comparison'] >> 5 != $this->comp_offsets[$fieldType]) {
$conf['comparison'] = $this->comp_offsets[$fieldType] << 5;
}
$queryConfig[$key]['comparison'] = $this->verifyComparison($conf['comparison'], $conf['negate'] ? 1 : 0);
$queryConfig[$key]['inputValue'] = $this->cleanInputVal($queryConfig[$key]);
$queryConfig[$key]['inputValue1'] = $this->cleanInputVal($queryConfig[$key], '1');
}
}
return $queryConfig;
}
/**
* Get form elements
*
* @param int $subLevel
* @param string $queryConfig
* @param string $parent
* @return array
*/
protected function getFormElements($subLevel = 0, $queryConfig = '', $parent = '')
{
$codeArr = [];
if (!is_array($queryConfig)) {
$queryConfig = $this->queryConfig;
}
$c = 0;
$arrCount = 0;
$loopCount = 0;
foreach ($queryConfig as $key => $conf) {
$fieldName = '';
$subscript = $parent . '[' . $key . ']';
$lineHTML = [];
$lineHTML[] = $this->mkOperatorSelect($this->name . $subscript, $conf['operator'], (bool)$c, $conf['type'] !== 'FIELD_');
if (strpos($conf['type'], 'FIELD_') === 0) {
$fieldName = substr($conf['type'], 6);
$this->fieldName = $fieldName;
$fieldType = $this->fields[$fieldName]['type'];
if ($conf['comparison'] >> 5 != $this->comp_offsets[$fieldType]) {
$conf['comparison'] = $this->comp_offsets[$fieldType] << 5;
}
//nasty nasty...
//make sure queryConfig contains _actual_ comparevalue.
//mkCompSelect don't care, but getQuery does.
$queryConfig[$key]['comparison'] += isset($conf['negate']) - $conf['comparison'] % 2;
} elseif ($conf['type'] === 'newlevel') {
$fieldType = $conf['type'];
} else {
$fieldType = 'ignore';
}
$fieldPrefix = htmlspecialchars($this->name . $subscript);
switch ($fieldType) {
case 'ignore':
break;
case 'newlevel':
if (!$queryConfig[$key]['nl']) {
$queryConfig[$key]['nl'][0]['type'] = 'FIELD_';
}
$lineHTML[] = '';
$codeArr[$arrCount]['sub'] = $this->getFormElements($subLevel + 1, $queryConfig[$key]['nl'], $subscript . '[nl]');
break;
case 'userdef':
$lineHTML[] = '';
break;
case 'date':
$lineHTML[] = '';
$lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
if ($conf['comparison'] === 100 || $conf['comparison'] === 101) {
// between
$lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'date');
$lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue1]', $conf['inputValue1'], 'date');
} else {
$lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'date');
}
$lineHTML[] = '
';
break;
case 'time':
$lineHTML[] = '';
$lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
if ($conf['comparison'] === 100 || $conf['comparison'] === 101) {
// between:
$lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'datetime');
$lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue1]', $conf['inputValue1'], 'datetime');
} else {
$lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'datetime');
}
$lineHTML[] = '
';
break;
case 'multiple':
case 'binary':
case 'relation':
$lineHTML[] = '';
$lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
if ($conf['comparison'] === 68 || $conf['comparison'] === 69 || $conf['comparison'] === 162 || $conf['comparison'] === 163) {
$lineHTML[] = '
';
break;
case 'boolean':
$lineHTML[] = '';
$lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
$lineHTML[] = '';
$lineHTML[] = '
';
break;
default:
$lineHTML[] = '';
$lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
if ($conf['comparison'] === 37 || $conf['comparison'] === 36) {
// between:
$lineHTML[] = '';
$lineHTML[] = '';
} else {
$lineHTML[] = '';
}
$lineHTML[] = '
';
}
if ($fieldType !== 'ignore') {
$lineHTML[] = '';
$lineHTML[] = $this->updateIcon();
if ($loopCount) {
$lineHTML[] = '';
}
$lineHTML[] = '';
if ($c != 0) {
$lineHTML[] = '';
}
if ($c != 0 && $fieldType !== 'newlevel') {
$lineHTML[] = '';
}
if ($fieldType === 'newlevel') {
$lineHTML[] = '';
}
$lineHTML[] = '
';
$codeArr[$arrCount]['html'] = implode(LF, $lineHTML);
$codeArr[$arrCount]['query'] = $this->getQuerySingle($conf, $c === 0);
$arrCount++;
$c++;
}
$loopCount = 1;
}
$this->queryConfig = $queryConfig;
return $codeArr;
}
/**
* @param string $subscript
* @param string $fieldName
* @param array $conf
*
* @return string
*/
protected function makeComparisonSelector($subscript, $fieldName, $conf)
{
$fieldPrefix = $this->name . $subscript;
$lineHTML = [];
$lineHTML[] = $this->mkTypeSelect($fieldPrefix . '[type]', $fieldName);
$lineHTML[] = ' ';
return implode(LF, $lineHTML);
}
/**
* Make option list
*
* @param string $fieldName
* @param array $conf
* @param string $table
* @return string
*/
protected function makeOptionList($fieldName, $conf, $table)
{
$backendUserAuthentication = $this->getBackendUserAuthentication();
$from_table_Arr = [];
$out = [];
$fieldSetup = $this->fields[$fieldName];
$languageService = $this->getLanguageService();
if ($fieldSetup['type'] === 'multiple') {
$optGroupOpen = false;
foreach ($fieldSetup['items'] as $key => $val) {
if (strpos($val[0], 'LLL:') === 0) {
$value = $languageService->sL($val[0]);
} else {
$value = $val[0];
}
if ($val[1] === '--div--') {
if ($optGroupOpen) {
$out[] = '';
}
$optGroupOpen = true;
$out[] = '';
}
}
if ($fieldSetup['type'] === 'binary') {
foreach ($fieldSetup['items'] as $key => $val) {
if (strpos($val[0], 'LLL:') === 0) {
$value = $languageService->sL($val[0]);
} else {
$value = $val[0];
}
if (GeneralUtility::inList($conf['inputValue'], (string)(2 ** $key))) {
$out[] = '';
} else {
$out[] = '';
}
}
}
if ($fieldSetup['type'] === 'relation') {
$useTablePrefix = 0;
$dontPrefixFirstTable = 0;
if ($fieldSetup['items']) {
foreach ($fieldSetup['items'] as $key => $val) {
if (strpos($val[0], 'LLL:') === 0) {
$value = $languageService->sL($val[0]);
} else {
$value = $val[0];
}
if (GeneralUtility::inList($conf['inputValue'], $val[1])) {
$out[] = '';
} else {
$out[] = '';
}
}
}
if (strpos($fieldSetup['allowed'], ',') !== false) {
$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->fetchAssociative()) {
if (strpos($row[$fieldName], ',') !== false) {
$checkContent = explode(',', $row[$fieldName]);
foreach ($checkContent as $singleValue) {
if (strpos($singleValue, '_') === false) {
$dontPrefixFirstTable = 1;
}
}
} else {
$singleValue = $row[$fieldName];
if ($singleValue !== '' && strpos($singleValue, '_') === false) {
$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;
$tablePrefix = '';
$outArray = [];
$labelFieldSelect = [];
foreach ($from_table_Arr as $from_table) {
$useSelectLabels = false;
$useAltSelectLabels = false;
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']) {
foreach ($GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items'] as $labelArray) {
if (strpos($labelArray[0], 'LLL:') === 0) {
$labelFieldSelect[$labelArray[1]] = $languageService->sL($labelArray[0]);
} else {
$labelFieldSelect[$labelArray[1]] = $labelArray[0];
}
}
$useSelectLabels = true;
}
$altLabelFieldSelect = [];
if ($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items']) {
foreach ($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items'] as $altLabelArray) {
if (strpos($altLabelArray[0], 'LLL:') === 0) {
$altLabelFieldSelect[$altLabelArray[1]] = $languageService->sL($altLabelArray[0]);
} else {
$altLabelFieldSelect[$altLabelArray[1]] = $altLabelArray[0];
}
}
$useAltSelectLabels = true;
}
if (!$this->tableArray[$from_table]) {
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($from_table);
if (isset($this->settings['show_deleted']) && $this->settings['show_deleted']) {
$queryBuilder->getRestrictions()->removeAll();
} else {
$queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
}
$selectFields = ['uid', $labelField];
if ($altLabelField) {
$selectFields[] = $altLabelField;
}
$queryBuilder->select(...$selectFields)
->from($from_table)
->orderBy('uid');
if (!$backendUserAuthentication->isAdmin()) {
$webMounts = $backendUserAuthentication->returnWebmounts();
$perms_clause = $backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
$webMountPageTree = '';
$webMountPageTreePrefix = '';
foreach ($webMounts as $webMount) {
if ($webMountPageTree) {
$webMountPageTreePrefix = ',';
}
$webMountPageTree .= $webMountPageTreePrefix
. $this->getTreeList($webMount, 999, 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] = $statement->fetchAllAssociative();
}
foreach ($this->tableArray[$from_table] as $key => $val) {
if ($useSelectLabels) {
$outArray[$tablePrefix . $val['uid']] = htmlspecialchars($labelFieldSelect[$val[$labelField]]);
} elseif ($val[$labelField]) {
$outArray[$tablePrefix . $val['uid']] = htmlspecialchars($val[$labelField]);
} elseif ($useAltSelectLabels) {
$outArray[$tablePrefix . $val['uid']] = htmlspecialchars($altLabelFieldSelect[$val[$altLabelField]]);
} else {
$outArray[$tablePrefix . $val['uid']] = htmlspecialchars($val[$altLabelField]);
}
}
if (isset($this->settings['options_sortlabel']) && $this->settings['options_sortlabel'] && is_array($outArray)) {
natcasesort($outArray);
}
}
}
foreach ($outArray as $key2 => $val2) {
if (GeneralUtility::inList($conf['inputValue'], $key2)) {
$out[] = '';
} else {
$out[] = '';
}
}
}
return implode(LF, $out);
}
/**
* Print code array
*
* @param array $codeArr
* @param int $recursionLevel
* @return string
*/
protected function printCodeArray($codeArr, $recursionLevel = 0)
{
$out = [];
foreach ($codeArr as $k => $v) {
$out[] = '';
$out[] = '
';
$out[] = $v['html'];
if ($this->enableQueryParts) {
$out[] = '
';
$out[] = htmlspecialchars($v['query']);
$out[] = '
';
}
if (is_array($v['sub'])) {
$out[] = '
';
$out[] = $this->printCodeArray($v['sub'], $recursionLevel + 1);
$out[] = '
';
}
$out[] = '
';
$out[] = '
';
}
return implode(LF, $out);
}
/**
* Make operator select
*
* @param string $name
* @param string $op
* @param bool $draw
* @param bool $submit
* @return string
*/
protected function mkOperatorSelect($name, $op, $draw, $submit)
{
$out = [];
if ($draw) {
$out[] = '';
$out[] = '';
$out[] = ' ';
$out[] = ' ';
$out[] = '';
$out[] = '
';
} else {
$out[] = '';
}
return implode(LF, $out);
}
/**
* Make type select
*
* @param string $name
* @param string $fieldName
* @param string $prepend
* @return string
*/
protected function mkTypeSelect($name, $fieldName, $prepend = 'FIELD_')
{
$out = [];
$out[] = '';
$out[] = '';
foreach ($this->fields as $key => $value) {
if (!$value['exclude'] || $this->getBackendUserAuthentication()->check('non_exclude_fields', $this->table . ':' . $key)) {
$label = $this->fields[$key]['label'];
if ($this->showFieldAndTableNames) {
$label .= ' [' . $key . ']';
}
$out[] = '';
}
}
$out[] = '';
return implode(LF, $out);
}
/**
* Verify type
*
* @param string $fieldName
* @return string
*/
protected function verifyType($fieldName)
{
$first = '';
foreach ($this->fields as $key => $value) {
if (!$first) {
$first = $key;
}
if ($key === $fieldName) {
return $key;
}
}
return $first;
}
/**
* Verify comparison
*
* @param string $comparison
* @param int $neg
* @return int
*/
protected function verifyComparison($comparison, $neg)
{
$compOffSet = $comparison >> 5;
$first = -1;
for ($i = 32 * $compOffSet + $neg; $i < 32 * ($compOffSet + 1); $i += 2) {
if ($first === -1) {
$first = $i;
}
if ($i >> 1 === $comparison >> 1) {
return $i;
}
}
return $first;
}
/**
* Make field to input select
*
* @param string $name
* @param string $fieldName
* @return string
*/
protected function mkFieldToInputSelect($name, $fieldName)
{
$out = [];
$out[] = '';
$out[] = ' ';
$out[] = $this->updateIcon();
$out[] = ' ';
$out[] = ' ';
$out[] = '
';
$out[] = '';
foreach ($this->fields as $key => $value) {
if (!$value['exclude'] || $this->getBackendUserAuthentication()->check('non_exclude_fields', $this->table . ':' . $key)) {
$label = $this->fields[$key]['label'];
if ($this->showFieldAndTableNames) {
$label .= ' [' . $key . ']';
}
$out[] = '';
}
}
$out[] = '';
return implode(LF, $out);
}
/**
* Make table select
*
* @param string $name
* @param string $cur
* @return string
*/
protected function mkTableSelect($name, $cur)
{
$out = [];
$out[] = '';
$out[] = '';
foreach ($GLOBALS['TCA'] as $tN => $value) {
if ($this->getBackendUserAuthentication()->check('tables_select', $tN)) {
$label = $this->getLanguageService()->sL($GLOBALS['TCA'][$tN]['ctrl']['title']);
if ($this->showFieldAndTableNames) {
$label .= ' [' . $tN . ']';
}
$out[] = '';
}
}
$out[] = '';
return implode(LF, $out);
}
/**
* Make comparison select
*
* @param string $name
* @param string $comparison
* @param int $neg
* @return string
*/
protected function mkCompSelect($name, $comparison, $neg)
{
$compOffSet = $comparison >> 5;
$out = [];
$out[] = '';
for ($i = 32 * $compOffSet + $neg; $i < 32 * ($compOffSet + 1); $i += 2) {
if ($this->lang['comparison'][$i . '_']) {
$out[] = '';
}
}
$out[] = '';
return implode(LF, $out);
}
/**
* Get subscript
*
* @param array $arr
* @return array
*/
protected function getSubscript($arr): array
{
$retArr = [];
while (\is_array($arr)) {
reset($arr);
$key = key($arr);
$retArr[] = $key;
if (isset($arr[$key])) {
$arr = $arr[$key];
} else {
break;
}
}
return $retArr;
}
/**
* Get query
*
* @param array $queryConfig
* @param string $pad
* @return string
*/
protected function getQuery($queryConfig, $pad = '')
{
$qs = '';
// Since we don't traverse the array using numeric keys in the upcoming whileloop make sure it's fresh and clean
ksort($queryConfig);
$first = true;
foreach ($queryConfig as $key => $conf) {
$conf = $this->convertIso8601DatetimeStringToUnixTimestamp($conf);
switch ($conf['type']) {
case 'newlevel':
$qs .= LF . $pad . trim($conf['operator']) . ' (' . $this->getQuery(
$queryConfig[$key]['nl'],
$pad . ' '
) . LF . $pad . ')';
break;
default:
$qs .= LF . $pad . $this->getQuerySingle($conf, $first);
}
$first = false;
}
return $qs;
}
/**
* Convert ISO-8601 timestamp (string) into unix timestamp (int)
*
* @param array $conf
* @return array
*/
protected function convertIso8601DatetimeStringToUnixTimestamp(array $conf): array
{
if ($this->isDateOfIso8601Format($conf['inputValue'])) {
$conf['inputValue'] = strtotime($conf['inputValue']);
if ($this->isDateOfIso8601Format($conf['inputValue1'])) {
$conf['inputValue1'] = strtotime($conf['inputValue1']);
}
}
return $conf;
}
/**
* Checks if the given value is of the ISO 8601 format.
*
* @param mixed $date
* @return bool
*/
protected function isDateOfIso8601Format($date): bool
{
if (!is_int($date) && !is_string($date)) {
return false;
}
$format = 'Y-m-d\\TH:i:s\\Z';
$formattedDate = \DateTime::createFromFormat($format, (string)$date);
return $formattedDate && $formattedDate->format($format) === $date;
}
/**
* Get single query
*
* @param array $conf
* @param bool $first
* @return string
*/
protected function getQuerySingle($conf, $first)
{
$qs = '';
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
$prefix = $this->enablePrefix ? $this->table . '.' : '';
if (!$first) {
// Is it OK to insert the AND operator if none is set?
$operator = strtoupper(trim($conf['operator']));
if (!in_array($operator, ['AND', 'OR'], true)) {
$operator = 'AND';
}
$qs .= $operator . ' ';
}
$qsTmp = str_replace('#FIELD#', $prefix . trim(substr($conf['type'], 6)), $this->compSQL[$conf['comparison']]);
$inputVal = $this->cleanInputVal($conf);
if ($conf['comparison'] === 68 || $conf['comparison'] === 69) {
$inputVal = explode(',', $inputVal);
foreach ($inputVal as $key => $fileName) {
$inputVal[$key] = $queryBuilder->quote($fileName);
}
$inputVal = implode(',', $inputVal);
$qsTmp = str_replace('#VALUE#', $inputVal, $qsTmp);
} elseif ($conf['comparison'] === 162 || $conf['comparison'] === 163) {
$inputValArray = explode(',', $inputVal);
$inputVal = 0;
foreach ($inputValArray as $fileName) {
$inputVal += (int)$fileName;
}
$qsTmp = str_replace('#VALUE#', (string)$inputVal, $qsTmp);
} else {
if (is_array($inputVal)) {
$inputVal = $inputVal[0];
}
$qsTmp = str_replace('#VALUE#', trim($queryBuilder->quote($inputVal), '\''), $qsTmp);
}
if ($conf['comparison'] === 37 || $conf['comparison'] === 36 || $conf['comparison'] === 66 || $conf['comparison'] === 67 || $conf['comparison'] === 100 || $conf['comparison'] === 101) {
// between:
$inputVal = $this->cleanInputVal($conf, '1');
$qsTmp = str_replace('#VALUE1#', trim($queryBuilder->quote($inputVal), '\''), $qsTmp);
}
$qs .= trim((string)$qsTmp);
return $qs;
}
/**
* Clear input value
*
* @param array $conf
* @param string $suffix
* @return string
*/
protected function cleanInputVal($conf, $suffix = '')
{
if ($conf['comparison'] >> 5 === 0 || ($conf['comparison'] === 32 || $conf['comparison'] === 33 || $conf['comparison'] === 64 || $conf['comparison'] === 65 || $conf['comparison'] === 66 || $conf['comparison'] === 67 || $conf['comparison'] === 96 || $conf['comparison'] === 97)) {
$inputVal = $conf['inputValue' . $suffix];
} elseif ($conf['comparison'] === 39 || $conf['comparison'] === 38) {
// in list:
$inputVal = implode(',', GeneralUtility::intExplode(',', $conf['inputValue' . $suffix]));
} elseif ($conf['comparison'] === 68 || $conf['comparison'] === 69 || $conf['comparison'] === 162 || $conf['comparison'] === 163) {
// in list:
if (is_array($conf['inputValue' . $suffix])) {
$inputVal = implode(',', $conf['inputValue' . $suffix]);
} elseif ($conf['inputValue' . $suffix]) {
$inputVal = $conf['inputValue' . $suffix];
} else {
$inputVal = 0;
}
} elseif (!is_array($conf['inputValue' . $suffix]) && strtotime($conf['inputValue' . $suffix])) {
$inputVal = $conf['inputValue' . $suffix];
} elseif (!is_array($conf['inputValue' . $suffix]) && MathUtility::canBeInterpretedAsInteger($conf['inputValue' . $suffix])) {
$inputVal = (int)$conf['inputValue' . $suffix];
} else {
// TODO: Six eyes looked at this code and nobody understood completely what is going on here and why we
// fallback to float casting, the whole class smells like it needs a refactoring.
$inputVal = (float)$conf['inputValue' . $suffix];
}
return $inputVal;
}
/**
* Update icon
*
* @return string
*/
protected function updateIcon()
{
return '';
}
/**
* Get label column
*
* @return string
*/
protected function getLabelCol()
{
return $GLOBALS['TCA'][$this->table]['ctrl']['label'];
}
/**
* Make selector table
*
* @param array $modSettings
* @param string $enableList
* @return string
*/
protected function makeSelectorTable($modSettings, $enableList = 'table,fields,query,group,order,limit')
{
$out = [];
$enableArr = explode(',', $enableList);
$userTsConfig = $this->getBackendUserAuthentication()->getTSConfig();
// Make output
if (in_array('table', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableSelectATable'] ?? false)) {
$out[] = '';
$out[] = ' ';
$out[] = $this->mkTableSelect('SET[queryTable]', $this->table);
$out[] = '
';
}
if ($this->table) {
// Init fields:
$this->setAndCleanUpExternalLists('queryFields', $modSettings['queryFields'] ?? '', 'uid,' . $this->getLabelCol());
$this->setAndCleanUpExternalLists('queryGroup', $modSettings['queryGroup'] ?? '');
$this->setAndCleanUpExternalLists('queryOrder', ($modSettings['queryOrder'] ?? '') . ',' . ($modSettings['queryOrder2'] ?? ''));
// Limit:
$this->extFieldLists['queryLimit'] = $modSettings['queryLimit'] ?? '';
if (!$this->extFieldLists['queryLimit']) {
$this->extFieldLists['queryLimit'] = 100;
}
$parts = GeneralUtility::intExplode(',', $this->extFieldLists['queryLimit']);
$limitBegin = 0;
$limitLength = (int)($this->extFieldLists['queryLimit'] ?? 0);
if ($parts[1]) {
$limitBegin = (int)$parts[0];
$limitLength = (int)$parts[1];
}
$this->extFieldLists['queryLimit'] = implode(',', array_slice($parts, 0, 2));
// Insert Descending parts
if ($this->extFieldLists['queryOrder']) {
$descParts = explode(',', $modSettings['queryOrderDesc'] . ',' . $modSettings['queryOrder2Desc']);
$orderParts = explode(',', $this->extFieldLists['queryOrder']);
$reList = [];
foreach ($orderParts as $kk => $vv) {
$reList[] = $vv . ($descParts[$kk] ? ' DESC' : '');
}
$this->extFieldLists['queryOrder_SQL'] = implode(',', $reList);
}
// Query Generator:
$this->procesData(($modSettings['queryConfig'] ?? false) ? unserialize($modSettings['queryConfig'] ?? '', ['allowed_classes' => false]) : []);
$this->queryConfig = $this->cleanUpQueryConfig($this->queryConfig);
$this->enableQueryParts = (bool)$modSettings['search_query_smallparts'];
$codeArr = $this->getFormElements();
$queryCode = $this->printCodeArray($codeArr);
if (in_array('fields', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableSelectFields'] ?? false)) {
$out[] = '';
$out[] = ' ';
$out[] = $this->mkFieldToInputSelect('SET[queryFields]', $this->extFieldLists['queryFields']);
$out[] = '
';
}
if (in_array('query', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableMakeQuery'] ?? false)) {
$out[] = '';
$out[] = ' ';
$out[] = $queryCode;
$out[] = '
';
}
if (in_array('group', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableGroupBy'] ?? false)) {
$out[] = '';
$out[] = ' ';
$out[] = $this->mkTypeSelect('SET[queryGroup]', $this->extFieldLists['queryGroup'], '');
$out[] = '
';
}
if (in_array('order', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableOrderBy'] ?? false)) {
$orderByArr = explode(',', $this->extFieldLists['queryOrder']);
$orderBy = [];
$orderBy[] = $this->mkTypeSelect('SET[queryOrder]', $orderByArr[0], '');
$orderBy[] = '';
$orderBy[] = BackendUtility::getFuncCheck(0, 'SET[queryOrderDesc]', $modSettings['queryOrderDesc'] ?? '', '', '', 'id="checkQueryOrderDesc"');
$orderBy[] = ' ';
$orderBy[] = '
';
if ($orderByArr[0]) {
$orderBy[] = $this->mkTypeSelect('SET[queryOrder2]', $orderByArr[1], '');
$orderBy[] = '';
$orderBy[] = BackendUtility::getFuncCheck(0, 'SET[queryOrder2Desc]', $modSettings['queryOrder2Desc'], '', '', 'id="checkQueryOrder2Desc"') . ' Descending';
$orderBy[] = ' ';
$orderBy[] = '
';
}
$out[] = '';
$out[] = ' ';
$out[] = implode(LF, $orderBy);
$out[] = '
';
}
if (in_array('limit', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableLimit'] ?? false)) {
$limit = [];
$limit[] = '';
$limit[] = ' ';
$limit[] = $this->updateIcon();
$limit[] = ' ';
$limit[] = ' ';
$limit[] = '
';
$prevLimit = $limitBegin - $limitLength < 0 ? 0 : $limitBegin - $limitLength;
$prevButton = '';
$nextButton = '';
if ($limitBegin) {
$prevButton = '';
}
if (!$limitLength) {
$limitLength = 100;
}
$nextLimit = $limitBegin + $limitLength;
if ($nextLimit < 0) {
$nextLimit = 0;
}
if ($nextLimit) {
$nextButton = '';
}
$out[] = '';
}
}
return implode(LF, $out);
}
/**
* Get select query
*
* @param string $qString
* @return string
*/
protected function getSelectQuery($qString = ''): string
{
$backendUserAuthentication = $this->getBackendUserAuthentication();
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
if (isset($this->settings['show_deleted']) && $this->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) {
[$fieldName, $order] = $orderPair;
$queryBuilder->addOrderBy($fieldName, $order);
}
}
if ($this->extFieldLists['queryLimit']) {
$queryBuilder->setMaxResults((int)$this->extFieldLists['queryLimit']);
}
if (!$backendUserAuthentication->isAdmin()) {
$webMounts = $backendUserAuthentication->returnWebmounts();
$perms_clause = $backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
$webMountPageTree = '';
$webMountPageTreePrefix = '';
foreach ($webMounts as $webMount) {
if ($webMountPageTree) {
$webMountPageTreePrefix = ',';
}
$webMountPageTree .= $webMountPageTreePrefix
. $this->getTreeList($webMount, 999, 0, $perms_clause);
}
// createNamedParameter() is not used here because the SQL fragment will only include
// the :dcValueX placeholder when the query is returned as a string. The value for the
// placeholder would be lost in the process.
if ($this->table === 'pages') {
$queryBuilder->where(
QueryHelper::stripLogicalOperatorPrefix($perms_clause),
$queryBuilder->expr()->in(
'uid',
GeneralUtility::intExplode(',', $webMountPageTree)
)
);
} else {
$queryBuilder->where(
$queryBuilder->expr()->in(
'pid',
GeneralUtility::intExplode(',', $webMountPageTree)
)
);
}
}
if (!$qString) {
$qString = $this->getQuery($this->queryConfig);
}
$queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($qString));
return $queryBuilder->getSQL();
}
/**
* @param string $name the field name
* @param string $timestamp ISO-8601 timestamp
* @param string $type [datetime, date, time, timesec, year]
*
* @return string
*/
protected function getDateTimePickerField($name, $timestamp, $type)
{
$value = strtotime($timestamp) ? date($GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], (int)strtotime($timestamp)) : '';
$id = StringUtility::getUniqueId('dt_');
$html = [];
$html[] = '';
$html[] = ' ';
$html[] = ' ';
$html[] = ' ';
$html[] = ' ';
$html[] = ' ';
$html[] = '
';
return implode(LF, $html);
}
protected function getBackendUserAuthentication(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
}