TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
backend/Classes/Utility/BackendUtility.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Backend\Utility;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
46 
56 {
63  protected static $tcaTableTypeConfigurationCache = [];
64 
65  /*******************************************
66  *
67  * SQL-related, selecting records, searching
68  *
69  *******************************************/
84  public static function deleteClause($table, $tableAlias = '')
85  {
86  if (empty($GLOBALS['TCA'][$table]['ctrl']['delete'])) {
87  return '';
88  }
89  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
90  ->getQueryBuilderForTable($table)
91  ->expr();
92  return ' AND ' . $expressionBuilder->eq(
93  ($tableAlias ?: $table) . '.' . $GLOBALS['TCA'][$table]['ctrl']['delete'],
94  0
95  );
96  }
97 
112  public static function getRecord($table, $uid, $fields = '*', $where = '', $useDeleteClause = true)
113  {
114  // Ensure we have a valid uid (not 0 and not NEWxxxx) and a valid TCA
115  if ((int)$uid && !empty($GLOBALS['TCA'][$table])) {
116  $queryBuilder = static::getQueryBuilderForTable($table);
117 
118  // do not use enabled fields here
119  $queryBuilder->getRestrictions()->removeAll();
120 
121  // should the delete clause be used
122  if ($useDeleteClause) {
123  $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
124  }
125 
126  // set table and where clause
127  $queryBuilder
128  ->select(...GeneralUtility::trimExplode(',', $fields, true))
129  ->from($table)
130  ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)));
131 
132  // add custom where clause
133  if ($where) {
134  $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($where));
135  }
136 
137  $row = $queryBuilder->execute()->fetch();
138  if ($row) {
139  return $row;
140  }
141  }
142  return null;
143  }
144 
156  public static function getRecordWSOL(
157  $table,
158  $uid,
159  $fields = '*',
160  $where = '',
161  $useDeleteClause = true,
162  $unsetMovePointers = false
163  ) {
164  if ($fields !== '*') {
165  $internalFields = GeneralUtility::uniqueList($fields . ',uid,pid');
166  $row = self::getRecord($table, $uid, $internalFields, $where, $useDeleteClause);
167  self::workspaceOL($table, $row, -99, $unsetMovePointers);
168  if (is_array($row)) {
169  foreach ($row as $key => $_) {
170  if (!GeneralUtility::inList($fields, $key) && $key[0] !== '_') {
171  unset($row[$key]);
172  }
173  }
174  }
175  } else {
176  $row = self::getRecord($table, $uid, $fields, $where, $useDeleteClause);
177  self::workspaceOL($table, $row, -99, $unsetMovePointers);
178  }
179  return $row;
180  }
181 
194  public static function getRecordRaw($table, $where = '', $fields = '*')
195  {
196  $queryBuilder = static::getQueryBuilderForTable($table);
197  $queryBuilder->getRestrictions()->removeAll();
198 
199  $row = $queryBuilder
200  ->select(...GeneralUtility::trimExplode(',', $fields, true))
201  ->from($table)
203  ->execute()
204  ->fetch();
205 
206  return $row ?: false;
207  }
208 
225  public static function getRecordsByField(
226  $theTable,
227  $theField,
228  $theValue,
229  $whereClause = '',
230  $groupBy = '',
231  $orderBy = '',
232  $limit = '',
233  $useDeleteClause = true,
234  $queryBuilder = null
235  ) {
236  if (is_array($GLOBALS['TCA'][$theTable])) {
237  if (null === $queryBuilder) {
238  $queryBuilder = static::getQueryBuilderForTable($theTable);
239  }
240 
241  // Show all records except versioning placeholders
242  $queryBuilder->getRestrictions()
243  ->removeAll()
244  ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
245 
246  // Remove deleted records from the query result
247  if ($useDeleteClause) {
248  $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
249  }
250 
251  // build fields to select
252  $queryBuilder
253  ->select('*')
254  ->from($theTable)
255  ->where($queryBuilder->expr()->eq($theField, $queryBuilder->createNamedParameter($theValue)));
256 
257  // additional where
258  if ($whereClause) {
259  $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($whereClause));
260  }
261 
262  // group by
263  if ($groupBy !== '') {
264  $queryBuilder->groupBy(QueryHelper::parseGroupBy($groupBy));
265  }
266 
267  // order by
268  if ($orderBy !== '') {
269  foreach (QueryHelper::parseOrderBy($orderBy) as $orderPair) {
270  list($fieldName, $order) = $orderPair;
271  $queryBuilder->addOrderBy($fieldName, $order);
272  }
273  }
274 
275  // limit
276  if ($limit !== '') {
277  if (strpos($limit, ',')) {
278  $limitOffsetAndMax = GeneralUtility::intExplode(',', $limit);
279  $queryBuilder->setFirstResult((int)$limitOffsetAndMax[0]);
280  $queryBuilder->setMaxResults((int)$limitOffsetAndMax[1]);
281  } else {
282  $queryBuilder->setMaxResults((int)$limit);
283  }
284  }
285 
286  $rows = $queryBuilder->execute()->fetchAll();
287  return $rows;
288  }
289  return null;
290  }
291 
299  public static function splitTable_Uid($str)
300  {
301  list($uid, $table) = explode('_', strrev($str), 2);
302  return [strrev($table), strrev($uid)];
303  }
304 
315  public static function getSQLselectableList($in_list, $tablename, $default_tablename)
316  {
318  $list = [];
319  if ((string)trim($in_list) != '') {
320  $tempItemArray = explode(',', trim($in_list));
321  foreach ($tempItemArray as $key => $val) {
322  $val = strrev($val);
323  $parts = explode('_', $val, 2);
324  if ((string)trim($parts[0]) != '') {
325  $theID = (int)strrev($parts[0]);
326  $theTable = trim($parts[1]) ? strrev(trim($parts[1])) : $default_tablename;
327  if ($theTable == $tablename) {
328  $list[] = $theID;
329  }
330  }
331  }
332  }
333  return implode(',', $list);
334  }
335 
346  public static function BEenableFields($table, $inv = false)
347  {
348  $ctrl = $GLOBALS['TCA'][$table]['ctrl'];
349  $query = [];
350  $invQuery = [];
351  if (is_array($ctrl)) {
352  if (is_array($ctrl['enablecolumns'])) {
353  if ($ctrl['enablecolumns']['disabled']) {
354  $field = $table . '.' . $ctrl['enablecolumns']['disabled'];
355  $query[] = $field . '=0';
356  $invQuery[] = $field . '<>0';
357  }
358  if ($ctrl['enablecolumns']['starttime']) {
359  $field = $table . '.' . $ctrl['enablecolumns']['starttime'];
360  $query[] = '(' . $field . '<=' . $GLOBALS['SIM_ACCESS_TIME'] . ')';
361  $invQuery[] = '(' . $field . '<>0 AND ' . $field . '>' . $GLOBALS['SIM_ACCESS_TIME'] . ')';
362  }
363  if ($ctrl['enablecolumns']['endtime']) {
364  $field = $table . '.' . $ctrl['enablecolumns']['endtime'];
365  $query[] = '(' . $field . '=0 OR ' . $field . '>' . $GLOBALS['SIM_ACCESS_TIME'] . ')';
366  $invQuery[] = '(' . $field . '<>0 AND ' . $field . '<=' . $GLOBALS['SIM_ACCESS_TIME'] . ')';
367  }
368  }
369  }
370  $outQ = $inv ? '(' . implode(' OR ', $invQuery) . ')' : implode(' AND ', $query);
371  return $outQ ? ' AND ' . $outQ : '';
372  }
373 
383  public static function getRecordLocalization($table, $uid, $language, $andWhereClause = '')
384  {
385  $recordLocalization = false;
386 
387  // Pages still stores translations in the pages_language_overlay table, all other tables store in themself
388  if ($table === 'pages') {
389  $table = 'pages_language_overlay';
390  }
391 
392  if (self::isTableLocalizable($table)) {
393  $tcaCtrl = $GLOBALS['TCA'][$table]['ctrl'];
394 
395  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
396  ->getQueryBuilderForTable($table);
397 
398  $constraint = $queryBuilder->expr()->andX(
399  $queryBuilder->expr()->eq(
400  $tcaCtrl['languageField'],
401  $queryBuilder->createNamedParameter($language, \PDO::PARAM_INT)
402  ),
404  );
405 
406  $recordLocalization = self::getRecordsByField(
407  $table,
408  $tcaCtrl['transOrigPointerField'],
409  $uid,
410  (string)$constraint,
411  '',
412  '',
413  1,
414  true,
415  $queryBuilder
416  );
417  }
418  return $recordLocalization;
419  }
420 
421  /*******************************************
422  *
423  * Page tree, TCA related
424  *
425  *******************************************/
440  public static function BEgetRootLine($uid, $clause = '', $workspaceOL = false)
441  {
442  static $BEgetRootLine_cache = [];
443  $output = [];
444  $pid = $uid;
445  $ident = $pid . '-' . $clause . '-' . $workspaceOL;
446  if (is_array($BEgetRootLine_cache[$ident])) {
447  $output = $BEgetRootLine_cache[$ident];
448  } else {
449  $loopCheck = 100;
450  $theRowArray = [];
451  while ($uid != 0 && $loopCheck) {
452  $loopCheck--;
453  $row = self::getPageForRootline($uid, $clause, $workspaceOL);
454  if (is_array($row)) {
455  $uid = $row['pid'];
456  $theRowArray[] = $row;
457  } else {
458  break;
459  }
460  }
461  if ($uid == 0) {
462  $theRowArray[] = ['uid' => 0, 'title' => ''];
463  }
464  $c = count($theRowArray);
465  foreach ($theRowArray as $val) {
466  $c--;
467  $output[$c] = [
468  'uid' => $val['uid'],
469  'pid' => $val['pid'],
470  'title' => $val['title'],
471  'doktype' => $val['doktype'],
472  'tsconfig_includes' => $val['tsconfig_includes'],
473  'TSconfig' => $val['TSconfig'],
474  'is_siteroot' => $val['is_siteroot'],
475  't3ver_oid' => $val['t3ver_oid'],
476  't3ver_wsid' => $val['t3ver_wsid'],
477  't3ver_state' => $val['t3ver_state'],
478  't3ver_stage' => $val['t3ver_stage'],
479  'backend_layout_next_level' => $val['backend_layout_next_level']
480  ];
481  if (isset($val['_ORIG_pid'])) {
482  $output[$c]['_ORIG_pid'] = $val['_ORIG_pid'];
483  }
484  }
485  $BEgetRootLine_cache[$ident] = $output;
486  }
487  return $output;
488  }
489 
499  protected static function getPageForRootline($uid, $clause, $workspaceOL)
500  {
501  static $getPageForRootline_cache = [];
502  $ident = $uid . '-' . $clause . '-' . $workspaceOL;
503  if (is_array($getPageForRootline_cache[$ident])) {
504  $row = $getPageForRootline_cache[$ident];
505  } else {
506  $queryBuilder = static::getQueryBuilderForTable('pages');
507  $queryBuilder->getRestrictions()
508  ->removeAll()
509  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
510 
511  $row = $queryBuilder
512  ->select(
513  'pid',
514  'uid',
515  'title',
516  'doktype',
517  'tsconfig_includes',
518  'TSconfig',
519  'is_siteroot',
520  't3ver_oid',
521  't3ver_wsid',
522  't3ver_state',
523  't3ver_stage',
524  'backend_layout_next_level'
525  )
526  ->from('pages')
527  ->where(
528  $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)),
530  )
531  ->execute()
532  ->fetch();
533 
534  if ($row) {
535  $newLocation = false;
536  if ($workspaceOL) {
537  self::workspaceOL('pages', $row);
538  $newLocation = self::getMovePlaceholder('pages', $row['uid'], 'pid');
539  }
540  if (is_array($row)) {
541  if ($newLocation !== false) {
542  $row['pid'] = $newLocation['pid'];
543  } else {
544  self::fixVersioningPid('pages', $row);
545  }
546  $getPageForRootline_cache[$ident] = $row;
547  }
548  }
549  }
550  return $row;
551  }
552 
560  public static function openPageTree($pid, $clearExpansion)
561  {
562  $beUser = static::getBackendUserAuthentication();
563  // Get current expansion data:
564  if ($clearExpansion) {
565  $expandedPages = [];
566  } else {
567  $expandedPages = unserialize($beUser->uc['browseTrees']['browsePages']);
568  }
569  // Get rootline:
570  $rL = self::BEgetRootLine($pid);
571  // First, find out what mount index to use (if more than one DB mount exists):
572  $mountIndex = 0;
573  $mountKeys = array_flip($beUser->returnWebmounts());
574  foreach ($rL as $rLDat) {
575  if (isset($mountKeys[$rLDat['uid']])) {
576  $mountIndex = $mountKeys[$rLDat['uid']];
577  break;
578  }
579  }
580  // Traverse rootline and open paths:
581  foreach ($rL as $rLDat) {
582  $expandedPages[$mountIndex][$rLDat['uid']] = 1;
583  }
584  // Write back:
585  $beUser->uc['browseTrees']['browsePages'] = serialize($expandedPages);
586  $beUser->writeUC();
587  }
588 
600  public static function getRecordPath($uid, $clause, $titleLimit, $fullTitleLimit = 0)
601  {
602  if (!$titleLimit) {
603  $titleLimit = 1000;
604  }
605  $output = $fullOutput = '/';
606  $clause = trim($clause);
607  if ($clause !== '' && substr($clause, 0, 3) !== 'AND') {
608  $clause = 'AND ' . $clause;
609  }
610  $data = self::BEgetRootLine($uid, $clause);
611  foreach ($data as $record) {
612  if ($record['uid'] === 0) {
613  continue;
614  }
615  $output = '/' . GeneralUtility::fixed_lgd_cs(strip_tags($record['title']), $titleLimit) . $output;
616  if ($fullTitleLimit) {
617  $fullOutput = '/' . GeneralUtility::fixed_lgd_cs(strip_tags($record['title']), $fullTitleLimit) . $fullOutput;
618  }
619  }
620  if ($fullTitleLimit) {
621  return [$output, $fullOutput];
622  } else {
623  return $output;
624  }
625  }
626 
635  public static function getOriginalTranslationTable($table)
636  {
637  return $table === 'pages_language_overlay' ? 'pages' : $table;
638  }
639 
646  public static function isTableLocalizable($table)
647  {
648  $isLocalizable = false;
649  if (isset($GLOBALS['TCA'][$table]['ctrl']) && is_array($GLOBALS['TCA'][$table]['ctrl'])) {
650  $tcaCtrl = $GLOBALS['TCA'][$table]['ctrl'];
651  $isLocalizable = isset($tcaCtrl['languageField']) && $tcaCtrl['languageField'] && isset($tcaCtrl['transOrigPointerField']) && $tcaCtrl['transOrigPointerField'];
652  }
653  return $isLocalizable;
654  }
655 
665  public static function getInlineLocalizationMode($table, $fieldOrConfig)
666  {
667  $localizationMode = false;
668  $config = null;
669  if (is_array($fieldOrConfig) && !empty($fieldOrConfig)) {
670  $config = $fieldOrConfig;
671  } elseif (is_string($fieldOrConfig) && isset($GLOBALS['TCA'][$table]['columns'][$fieldOrConfig]['config'])) {
672  $config = $GLOBALS['TCA'][$table]['columns'][$fieldOrConfig]['config'];
673  }
674  if (is_array($config) && isset($config['type']) && $config['type'] === 'inline' && self::isTableLocalizable($table)) {
675  $localizationMode = isset($config['behaviour']['localizationMode']) && $config['behaviour']['localizationMode']
676  ? $config['behaviour']['localizationMode']
677  : 'select';
678  // The mode 'select' is not possible when child table is not localizable at all:
679  if ($localizationMode === 'select' && !self::isTableLocalizable($config['foreign_table'])) {
680  $localizationMode = false;
681  }
682  }
683  return $localizationMode;
684  }
685 
695  public static function readPageAccess($id, $perms_clause)
696  {
697  if ((string)$id !== '') {
698  $id = (int)$id;
699  if (!$id) {
700  if (static::getBackendUserAuthentication()->isAdmin()) {
701  $path = '/';
702  $pageinfo['_thePath'] = $path;
703  return $pageinfo;
704  }
705  } else {
706  $pageinfo = self::getRecord('pages', $id, '*', $perms_clause);
707  if ($pageinfo['uid'] && static::getBackendUserAuthentication()->isInWebMount($id, $perms_clause)) {
708  self::workspaceOL('pages', $pageinfo);
709  if (is_array($pageinfo)) {
710  self::fixVersioningPid('pages', $pageinfo);
711  list($pageinfo['_thePath'], $pageinfo['_thePathFull']) = self::getRecordPath((int)$pageinfo['uid'], $perms_clause, 15, 1000);
712  return $pageinfo;
713  }
714  }
715  }
716  }
717  return false;
718  }
719 
728  public static function getTCAtypes($table, $rec, $useFieldNameAsKey = false)
729  {
730  if ($GLOBALS['TCA'][$table]) {
731  // Get type value:
732  $fieldValue = self::getTCAtypeValue($table, $rec);
733  $cacheIdentifier = $table . '-type-' . $fieldValue . '-fnk-' . $useFieldNameAsKey;
734 
735  // Fetch from first-level-cache if available
736  if (isset(self::$tcaTableTypeConfigurationCache[$cacheIdentifier])) {
737  return self::$tcaTableTypeConfigurationCache[$cacheIdentifier];
738  }
739 
740  // Get typesConf
741  $typesConf = $GLOBALS['TCA'][$table]['types'][$fieldValue];
742  // Get fields list and traverse it
743  $fieldList = explode(',', $typesConf['showitem']);
744 
745  // Add subtype fields e.g. for a valid RTE transformation
746  // The RTE runs the DB -> RTE transformation only, if the RTE field is part of the getTCAtypes array
747  if (isset($typesConf['subtype_value_field'])) {
748  $subType = $rec[$typesConf['subtype_value_field']];
749  if (isset($typesConf['subtypes_addlist'][$subType])) {
750  $subFields = GeneralUtility::trimExplode(',', $typesConf['subtypes_addlist'][$subType], true);
751  $fieldList = array_merge($fieldList, $subFields);
752  }
753  }
754 
755  // Add palette fields e.g. for a valid RTE transformation
756  $paletteFieldList = [];
757  foreach ($fieldList as $fieldData) {
758  list($pFieldName, $pAltTitle, $pPalette) = GeneralUtility::trimExplode(';', $fieldData);
759  if ($pPalette
760  && isset($GLOBALS['TCA'][$table]['palettes'][$pPalette])
761  && is_array($GLOBALS['TCA'][$table]['palettes'][$pPalette])
762  && isset($GLOBALS['TCA'][$table]['palettes'][$pPalette]['showitem'])
763  ) {
764  $paletteFields = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['palettes'][$pPalette]['showitem'], true);
765  foreach ($paletteFields as $paletteField) {
766  if ($paletteField !== '--linebreak--') {
767  $paletteFieldList[] = $paletteField;
768  }
769  }
770  }
771  }
772  $fieldList = array_merge($fieldList, $paletteFieldList);
773 
774  $altFieldList = [];
775  // Traverse fields in types config and parse the configuration into a nice array:
776  foreach ($fieldList as $k => $v) {
777  list($pFieldName, $pAltTitle, $pPalette) = GeneralUtility::trimExplode(';', $v);
778  $defaultExtras = '';
779  if (!empty($typesConf['columnsOverrides'][$pFieldName]['defaultExtras'])) {
780  // Use defaultExtras from columnsOverrides if given
781  $defaultExtras = $typesConf['columnsOverrides'][$pFieldName]['defaultExtras'];
782  } elseif (!empty($GLOBALS['TCA'][$table]['columns'][$pFieldName]['defaultExtras'])) {
783  // Use defaultExtras from columns if given
784  $defaultExtras = $GLOBALS['TCA'][$table]['columns'][$pFieldName]['defaultExtras'];
785  }
786  $specConfParts = self::getSpecConfParts($defaultExtras);
787  $fieldList[$k] = [
788  'field' => $pFieldName,
789  'title' => $pAltTitle,
790  'palette' => $pPalette,
791  'spec' => $specConfParts,
792  'origString' => $v
793  ];
794  if ($useFieldNameAsKey) {
795  $altFieldList[$fieldList[$k]['field']] = $fieldList[$k];
796  }
797  }
798  if ($useFieldNameAsKey) {
799  $fieldList = $altFieldList;
800  }
801 
802  // Add to first-level-cache
803  self::$tcaTableTypeConfigurationCache[$cacheIdentifier] = $fieldList;
804 
805  // Return array:
806  return $fieldList;
807  }
808  return null;
809  }
810 
828  public static function getTCAtypeValue($table, $row)
829  {
830  $typeNum = 0;
831  if ($GLOBALS['TCA'][$table]) {
832  $field = $GLOBALS['TCA'][$table]['ctrl']['type'];
833  if (strpos($field, ':') !== false) {
834  list($pointerField, $foreignTableTypeField) = explode(':', $field);
835  // Get field value from database if field is not in the $row array
836  if (!isset($row[$pointerField])) {
837  $localRow = self::getRecord($table, $row['uid'], $pointerField);
838  $foreignUid = $localRow[$pointerField];
839  } else {
840  $foreignUid = $row[$pointerField];
841  }
842  if ($foreignUid) {
843  $fieldConfig = $GLOBALS['TCA'][$table]['columns'][$pointerField]['config'];
844  $relationType = $fieldConfig['type'];
845  if ($relationType === 'select') {
846  $foreignTable = $fieldConfig['foreign_table'];
847  } elseif ($relationType === 'group') {
848  $allowedTables = explode(',', $fieldConfig['allowed']);
849  $foreignTable = $allowedTables[0];
850  } else {
851  throw new \RuntimeException(
852  'TCA foreign field pointer fields are only allowed to be used with group or select field types.',
853  1325862240
854  );
855  }
856  $foreignRow = self::getRecord($foreignTable, $foreignUid, $foreignTableTypeField);
857  if ($foreignRow[$foreignTableTypeField]) {
858  $typeNum = $foreignRow[$foreignTableTypeField];
859  }
860  }
861  } else {
862  $typeNum = $row[$field];
863  }
864  // If that value is an empty string, set it to "0" (zero)
865  if (empty($typeNum)) {
866  $typeNum = 0;
867  }
868  }
869  // If current typeNum doesn't exist, set it to 0 (or to 1 for historical reasons, if 0 doesn't exist)
870  if (!$GLOBALS['TCA'][$table]['types'][$typeNum]) {
871  $typeNum = $GLOBALS['TCA'][$table]['types']['0'] ? 0 : 1;
872  }
873  // Force to string. Necessary for eg '-1' to be recognized as a type value.
874  $typeNum = (string)$typeNum;
875  return $typeNum;
876  }
877 
887  public static function getSpecConfParts($defaultExtrasString)
888  {
889  $specConfParts = GeneralUtility::trimExplode(':', $defaultExtrasString, true);
890  $reg = [];
891  if (!empty($specConfParts)) {
892  foreach ($specConfParts as $k2 => $v2) {
893  unset($specConfParts[$k2]);
894  if (preg_match('/(.*)\\[(.*)\\]/', $v2, $reg)) {
895  $specConfParts[trim($reg[1])] = [
896  'parameters' => GeneralUtility::trimExplode('|', $reg[2], true)
897  ];
898  } else {
899  $specConfParts[trim($v2)] = 1;
900  }
901  }
902  } else {
903  $specConfParts = [];
904  }
905  return $specConfParts;
906  }
907 
915  public static function getSpecConfParametersFromArray($pArr)
916  {
917  $out = [];
918  if (is_array($pArr)) {
919  foreach ($pArr as $k => $v) {
920  $parts = explode('=', $v, 2);
921  if (count($parts) === 2) {
922  $out[trim($parts[0])] = trim($parts[1]);
923  } else {
924  $out[$k] = $v;
925  }
926  }
927  }
928  return $out;
929  }
930 
959  public static function getFlexFormDS($conf, $row, $table, $fieldName = '', $WSOL = true, $newRecordPidValue = 0)
960  {
962  // Get pointer field etc from TCA-config:
963  $ds_pointerField = $conf['ds_pointerField'];
964  $ds_array = $conf['ds'];
965  $ds_tableField = $conf['ds_tableField'];
966  $ds_searchParentField = $conf['ds_pointerField_searchParent'];
967  // If there is a data source array, that takes precedence
968  if (is_array($ds_array)) {
969  // If a pointer field is set, take the value from that field in the $row array and use as key.
970  if ($ds_pointerField) {
971  // Up to two pointer fields can be specified in a comma separated list.
972  $pointerFields = GeneralUtility::trimExplode(',', $ds_pointerField);
973  // If we have two pointer fields, the array keys should contain both field values separated by comma.
974  // The asterisk "*" catches all values. For backwards compatibility, it's also possible to specify only
975  // the value of the first defined ds_pointerField.
976  if (count($pointerFields) === 2) {
977  if ($ds_array[$row[$pointerFields[0]] . ',' . $row[$pointerFields[1]]]) {
978  // Check if we have a DS for the combination of both pointer fields values
979  $srcPointer = $row[$pointerFields[0]] . ',' . $row[$pointerFields[1]];
980  } elseif ($ds_array[$row[$pointerFields[1]] . ',*']) {
981  // Check if we have a DS for the value of the first pointer field suffixed with ",*"
982  $srcPointer = $row[$pointerFields[1]] . ',*';
983  } elseif ($ds_array['*,' . $row[$pointerFields[1]]]) {
984  // Check if we have a DS for the value of the second pointer field prefixed with "*,"
985  $srcPointer = '*,' . $row[$pointerFields[1]];
986  } elseif ($ds_array[$row[$pointerFields[0]]]) {
987  // Check if we have a DS for just the value of the first pointer field (mainly for backwards compatibility)
988  $srcPointer = $row[$pointerFields[0]];
989  } else {
990  $srcPointer = null;
991  }
992  } else {
993  $srcPointer = $row[$pointerFields[0]];
994  }
995  $srcPointer = $srcPointer !== null && isset($ds_array[$srcPointer]) ? $srcPointer : 'default';
996  } else {
997  $srcPointer = 'default';
998  }
999  // Get Data Source: Detect if it's a file reference and in that case read the file and parse as XML. Otherwise the value is expected to be XML.
1000  if (substr($ds_array[$srcPointer], 0, 5) == 'FILE:') {
1001  $file = GeneralUtility::getFileAbsFileName(substr($ds_array[$srcPointer], 5));
1002  if ($file && @is_file($file)) {
1003  $dataStructArray = GeneralUtility::xml2array(file_get_contents($file));
1004  } else {
1005  $dataStructArray = 'The file "' . substr($ds_array[$srcPointer], 5) . '" in ds-array key "' . $srcPointer . '" was not found ("' . $file . '")';
1006  }
1007  } else {
1008  $dataStructArray = GeneralUtility::xml2array($ds_array[$srcPointer]);
1009  }
1010  } elseif ($ds_pointerField) {
1011  // If pointer field AND possibly a table/field is set:
1012  // Value of field pointed to:
1013  $srcPointer = $row[$ds_pointerField];
1014  // Searching recursively back if 'ds_pointerField_searchParent' is defined (typ. a page rootline, or maybe a tree-table):
1015  if ($ds_searchParentField && !$srcPointer) {
1016  $rr = self::getRecord($table, $row['uid'], 'uid,' . $ds_searchParentField);
1017  // Get the "pid" field - we cannot know that it is in the input record! ###NOTE_A###
1018  if ($WSOL) {
1019  self::workspaceOL($table, $rr);
1020  self::fixVersioningPid($table, $rr, true);
1021  }
1022 
1023  $queryBuilder = static::getQueryBuilderForTable($table);
1024  $queryBuilder->getRestrictions()
1025  ->removeAll()
1026  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1027 
1028  $uidAcc = [];
1029  // Used to avoid looping, if any should happen.
1030  $subFieldPointer = $conf['ds_pointerField_searchParent_subField'];
1031  while (!$srcPointer) {
1032  // select fields
1033  $queryBuilder
1034  ->select('uid', $ds_pointerField, $ds_searchParentField)
1035  ->from($table)
1036  ->where(
1037  $queryBuilder->expr()->eq(
1038  'uid',
1039  $queryBuilder->createNamedParameter(
1040  ($newRecordPidValue ?: $rr[$ds_searchParentField]),
1041  \PDO::PARAM_INT
1042  )
1043  )
1044  );
1045  if ($subFieldPointer) {
1046  $queryBuilder->addSelect($subFieldPointer);
1047  }
1048 
1049  $rr = $queryBuilder->execute()->fetch();
1050 
1051  $newRecordPidValue = 0;
1052  // Break if no result from SQL db or if looping...
1053  if (!is_array($rr) || isset($uidAcc[$rr['uid']])) {
1054  break;
1055  }
1056  $uidAcc[$rr['uid']] = 1;
1057  if ($WSOL) {
1058  self::workspaceOL($table, $rr);
1059  self::fixVersioningPid($table, $rr, true);
1060  }
1061  $srcPointer = $subFieldPointer && $rr[$subFieldPointer] ? $rr[$subFieldPointer] : $rr[$ds_pointerField];
1062  }
1063  }
1064  // If there is a srcPointer value:
1065  if ($srcPointer) {
1066  if (MathUtility::canBeInterpretedAsInteger($srcPointer)) {
1067  // If integer, then its a record we will look up:
1068  list($tName, $fName) = explode(':', $ds_tableField, 2);
1069  if ($tName && $fName && is_array($GLOBALS['TCA'][$tName])) {
1070  $dataStructRec = self::getRecord($tName, $srcPointer);
1071  if ($WSOL) {
1072  self::workspaceOL($tName, $dataStructRec);
1073  }
1074  if (strpos($dataStructRec[$fName], '<') === false) {
1075  if (is_file(PATH_site . $dataStructRec[$fName])) {
1076  // The value is a pointer to a file
1077  $dataStructArray = GeneralUtility::xml2array(file_get_contents(PATH_site . $dataStructRec[$fName]));
1078  } else {
1079  $dataStructArray = sprintf('File \'%s\' was not found', $dataStructRec[$fName]);
1080  }
1081  } else {
1082  // No file pointer, handle as being XML (default behaviour)
1083  $dataStructArray = GeneralUtility::xml2array($dataStructRec[$fName]);
1084  }
1085  } else {
1086  $dataStructArray = 'No tablename (' . $tName . ') or fieldname (' . $fName . ') was found an valid!';
1087  }
1088  } else {
1089  // Otherwise expect it to be a file:
1090  $file = GeneralUtility::getFileAbsFileName($srcPointer);
1091  if ($file && @is_file($file)) {
1092  $dataStructArray = GeneralUtility::xml2array(file_get_contents($file));
1093  } else {
1094  // Error message.
1095  $dataStructArray = 'The file "' . $srcPointer . '" was not found ("' . $file . '")';
1096  }
1097  }
1098  } else {
1099  // Error message.
1100  $dataStructArray = 'No source value in fieldname "' . $ds_pointerField . '"';
1101  }
1102  } else {
1103  $dataStructArray = 'No proper configuration!';
1104  }
1105  // Hook for post-processing the Flexform DS. Introduces the possibility to configure Flexforms via TSConfig
1106  // This hook isn't called anymore from within the core, the whole method is deprecated.
1107  // There are alternative hooks, see FlexFormTools->getDataStructureIdentifier() and ->parseDataStructureByIdentifier()
1108  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['getFlexFormDSClass'])) {
1109  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['getFlexFormDSClass'] as $classRef) {
1110  $hookObj = GeneralUtility::getUserObj($classRef);
1111  if (method_exists($hookObj, 'getFlexFormDS_postProcessDS')) {
1112  $hookObj->getFlexFormDS_postProcessDS($dataStructArray, $conf, $row, $table, $fieldName);
1113  }
1114  }
1115  }
1116  return $dataStructArray;
1117  }
1118 
1119  /*******************************************
1120  *
1121  * Caching related
1122  *
1123  *******************************************/
1135  public static function storeHash($hash, $data, $ident)
1136  {
1138  $cacheManager = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class);
1139  $cacheManager->getCache('cache_hash')->set($hash, $data, ['ident_' . $ident], 0);
1140  }
1141 
1151  public static function getHash($hash)
1152  {
1154  $cacheManager = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class);
1155  $cacheEntry = $cacheManager->getCache('cache_hash')->get($hash);
1156  $hashContent = null;
1157  if ($cacheEntry) {
1158  $hashContent = $cacheEntry;
1159  }
1160  return $hashContent;
1161  }
1162 
1163  /*******************************************
1164  *
1165  * TypoScript related
1166  *
1167  *******************************************/
1177  public static function getPagesTSconfig($id, $rootLine = null, $returnPartArray = false)
1178  {
1179  static $pagesTSconfig_cacheReference = [];
1180  static $combinedTSconfig_cache = [];
1181 
1182  $id = (int)$id;
1183  if ($returnPartArray === false
1184  && $rootLine === null
1185  && isset($pagesTSconfig_cacheReference[$id])
1186  ) {
1187  return $combinedTSconfig_cache[$pagesTSconfig_cacheReference[$id]];
1188  } else {
1189  $TSconfig = [];
1190  if (!is_array($rootLine)) {
1191  $useCacheForCurrentPageId = true;
1192  $rootLine = self::BEgetRootLine($id, '', true);
1193  } else {
1194  $useCacheForCurrentPageId = false;
1195  }
1196 
1197  // Order correctly
1198  ksort($rootLine);
1199  $TSdataArray = [];
1200  // Setting default configuration
1201  $TSdataArray['defaultPageTSconfig'] = $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPageTSconfig'];
1202  foreach ($rootLine as $k => $v) {
1203  if (trim($v['tsconfig_includes'])) {
1204  $includeTsConfigFileList = GeneralUtility::trimExplode(',', $v['tsconfig_includes'], true);
1205  // Traversing list
1206  foreach ($includeTsConfigFileList as $key => $includeTsConfigFile) {
1207  if (strpos($includeTsConfigFile, 'EXT:') === 0) {
1208  list($includeTsConfigFileExtensionKey, $includeTsConfigFilename) = explode(
1209  '/',
1210  substr($includeTsConfigFile, 4),
1211  2
1212  );
1213  if ((string)$includeTsConfigFileExtensionKey !== ''
1214  && ExtensionManagementUtility::isLoaded($includeTsConfigFileExtensionKey)
1215  && (string)$includeTsConfigFilename !== ''
1216  ) {
1217  $includeTsConfigFileAndPath = ExtensionManagementUtility::extPath($includeTsConfigFileExtensionKey) .
1218  $includeTsConfigFilename;
1219  if (file_exists($includeTsConfigFileAndPath)) {
1220  $TSdataArray['uid_' . $v['uid'] . '_static_' . $key] = file_get_contents($includeTsConfigFileAndPath);
1221  }
1222  }
1223  }
1224  }
1225  }
1226  $TSdataArray['uid_' . $v['uid']] = $v['TSconfig'];
1227  }
1228  $TSdataArray = static::emitGetPagesTSconfigPreIncludeSignal($TSdataArray, $id, $rootLine, $returnPartArray);
1229  $TSdataArray = TypoScriptParser::checkIncludeLines_array($TSdataArray);
1230  if ($returnPartArray) {
1231  return $TSdataArray;
1232  }
1233  // Parsing the page TS-Config
1234  $pageTS = implode(LF . '[GLOBAL]' . LF, $TSdataArray);
1235  /* @var $parseObj \TYPO3\CMS\Backend\Configuration\TsConfigParser */
1236  $parseObj = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Configuration\TsConfigParser::class);
1237  $res = $parseObj->parseTSconfig($pageTS, 'PAGES', $id, $rootLine);
1238  if ($res) {
1239  $TSconfig = $res['TSconfig'];
1240  }
1241  $cacheHash = $res['hash'];
1242  // Get User TSconfig overlay
1243  $userTSconfig = static::getBackendUserAuthentication()->userTS['page.'];
1244  if (is_array($userTSconfig)) {
1245  ArrayUtility::mergeRecursiveWithOverrule($TSconfig, $userTSconfig);
1246  $cacheHash .= '_user' . $GLOBALS['BE_USER']->user['uid'];
1247  }
1248 
1249  if ($useCacheForCurrentPageId) {
1250  if (!isset($combinedTSconfig_cache[$cacheHash])) {
1251  $combinedTSconfig_cache[$cacheHash] = $TSconfig;
1252  }
1253  $pagesTSconfig_cacheReference[$id] = $cacheHash;
1254  }
1255  }
1256  return $TSconfig;
1257  }
1258 
1259  /*******************************************
1260  *
1261  * Users / Groups related
1262  *
1263  *******************************************/
1272  public static function getUserNames($fields = 'username,usergroup,usergroup_cached_list,uid', $where = '')
1273  {
1274  return self::getRecordsSortedByTitle(
1276  'be_users',
1277  'username',
1278  'AND pid=0 ' . $where
1279  );
1280  }
1281 
1289  public static function getGroupNames($fields = 'title,uid', $where = '')
1290  {
1291  return self::getRecordsSortedByTitle(
1293  'be_groups',
1294  'title',
1295  'AND pid=0 ' . $where
1296  );
1297  }
1298 
1310  protected static function getRecordsSortedByTitle(array $fields, $table, $titleField, $where = '')
1311  {
1312  $fieldsIndex = array_flip($fields);
1313  // Make sure the titleField is amongst the fields when getting sorted
1314  $fieldsIndex[$titleField] = 1;
1315 
1316  $result = [];
1317 
1318  $queryBuilder = static::getQueryBuilderForTable($table);
1319  $queryBuilder->getRestrictions()
1320  ->removeAll()
1321  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1322 
1323  $res = $queryBuilder
1324  ->select('*')
1325  ->from($table)
1327  ->execute();
1328 
1329  while ($record = $res->fetch()) {
1330  // store the uid, because it might be unset if it's not among the requested $fields
1331  $recordId = $record['uid'];
1332  $record[$titleField] = self::getRecordTitle($table, $record);
1333 
1334  // include only the requested fields in the result
1335  $result[$recordId] = array_intersect_key($record, $fieldsIndex);
1336  }
1337 
1338  // sort records by $sortField. This is not done in the query because the title might have been overwritten by
1339  // self::getRecordTitle();
1340  return ArrayUtility::sortArraysByKey($result, $titleField);
1341  }
1342 
1350  public static function getListGroupNames($fields = 'title, uid')
1351  {
1352  $beUser = static::getBackendUserAuthentication();
1353  $exQ = ' AND hide_in_lists=0';
1354  if (!$beUser->isAdmin()) {
1355  $exQ .= ' AND uid IN (' . ($beUser->user['usergroup_cached_list'] ?: 0) . ')';
1356  }
1357  return self::getGroupNames($fields, $exQ);
1358  }
1359 
1370  public static function blindUserNames($usernames, $groupArray, $excludeBlindedFlag = false)
1371  {
1372  if (is_array($usernames) && is_array($groupArray)) {
1373  foreach ($usernames as $uid => $row) {
1374  $userN = $uid;
1375  $set = 0;
1376  if ($row['uid'] != static::getBackendUserAuthentication()->user['uid']) {
1377  foreach ($groupArray as $v) {
1378  if ($v && GeneralUtility::inList($row['usergroup_cached_list'], $v)) {
1379  $userN = $row['username'];
1380  $set = 1;
1381  }
1382  }
1383  } else {
1384  $userN = $row['username'];
1385  $set = 1;
1386  }
1387  $usernames[$uid]['username'] = $userN;
1388  if ($excludeBlindedFlag && !$set) {
1389  unset($usernames[$uid]);
1390  }
1391  }
1392  }
1393  return $usernames;
1394  }
1395 
1404  public static function blindGroupNames($groups, $groupArray, $excludeBlindedFlag = false)
1405  {
1406  if (is_array($groups) && is_array($groupArray)) {
1407  foreach ($groups as $uid => $row) {
1408  $groupN = $uid;
1409  $set = 0;
1410  if (ArrayUtility::inArray($groupArray, $uid)) {
1411  $groupN = $row['title'];
1412  $set = 1;
1413  }
1414  $groups[$uid]['title'] = $groupN;
1415  if ($excludeBlindedFlag && !$set) {
1416  unset($groups[$uid]);
1417  }
1418  }
1419  }
1420  return $groups;
1421  }
1422 
1423  /*******************************************
1424  *
1425  * Output related
1426  *
1427  *******************************************/
1434  public static function daysUntil($tstamp)
1435  {
1436  $delta_t = $tstamp - $GLOBALS['EXEC_TIME'];
1437  return ceil($delta_t / (3600 * 24));
1438  }
1439 
1446  public static function date($tstamp)
1447  {
1448  return date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], (int)$tstamp);
1449  }
1450 
1457  public static function datetime($value)
1458  {
1459  return date(
1460  $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'],
1461  $value
1462  );
1463  }
1464 
1473  public static function time($value, $withSeconds = true)
1474  {
1475  $hh = floor($value / 3600);
1476  $min = floor(($value - $hh * 3600) / 60);
1477  $sec = $value - $hh * 3600 - $min * 60;
1478  $l = sprintf('%02d', $hh) . ':' . sprintf('%02d', $min);
1479  if ($withSeconds) {
1480  $l .= ':' . sprintf('%02d', $sec);
1481  }
1482  return $l;
1483  }
1484 
1492  public static function calcAge($seconds, $labels = ' min| hrs| days| yrs| min| hour| day| year')
1493  {
1494  $labelArr = explode('|', $labels);
1495  $absSeconds = abs($seconds);
1496  $sign = $seconds < 0 ? -1 : 1;
1497  if ($absSeconds < 3600) {
1498  $val = round($absSeconds / 60);
1499  $seconds = $sign * $val . ($val == 1 ? $labelArr[4] : $labelArr[0]);
1500  } elseif ($absSeconds < 24 * 3600) {
1501  $val = round($absSeconds / 3600);
1502  $seconds = $sign * $val . ($val == 1 ? $labelArr[5] : $labelArr[1]);
1503  } elseif ($absSeconds < 365 * 24 * 3600) {
1504  $val = round($absSeconds / (24 * 3600));
1505  $seconds = $sign * $val . ($val == 1 ? $labelArr[6] : $labelArr[2]);
1506  } else {
1507  $val = round($absSeconds / (365 * 24 * 3600));
1508  $seconds = $sign * $val . ($val == 1 ? $labelArr[7] : $labelArr[3]);
1509  }
1510  return $seconds;
1511  }
1512 
1522  public static function dateTimeAge($tstamp, $prefix = 1, $date = '')
1523  {
1524  if (!$tstamp) {
1525  return '';
1526  }
1527  $label = static::getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.minutesHoursDaysYears');
1528  $age = ' (' . self::calcAge($prefix * ($GLOBALS['EXEC_TIME'] - $tstamp), $label) . ')';
1529  return ($date === 'date' ? self::date($tstamp) : self::datetime($tstamp)) . $age;
1530  }
1531 
1539  public static function titleAltAttrib($content)
1540  {
1542  $out = '';
1543  $out .= ' alt="' . htmlspecialchars($content) . '"';
1544  $out .= ' title="' . htmlspecialchars($content) . '"';
1545  return $out;
1546  }
1547 
1557  public static function resolveFileReferences($tableName, $fieldName, $element, $workspaceId = null)
1558  {
1559  if (empty($GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config'])) {
1560  return null;
1561  }
1562  $configuration = $GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config'];
1563  if (empty($configuration['type']) || $configuration['type'] !== 'inline'
1564  || empty($configuration['foreign_table']) || $configuration['foreign_table'] !== 'sys_file_reference'
1565  ) {
1566  return null;
1567  }
1568 
1569  $fileReferences = [];
1571  $relationHandler = GeneralUtility::makeInstance(RelationHandler::class);
1572  if ($workspaceId !== null) {
1573  $relationHandler->setWorkspaceId($workspaceId);
1574  }
1575  $relationHandler->start(
1576  $element[$fieldName],
1577  $configuration['foreign_table'],
1578  $configuration['MM'],
1579  $element['uid'],
1580  $tableName,
1581  $configuration
1582  );
1583  $relationHandler->processDeletePlaceholder();
1584  $referenceUids = $relationHandler->tableArray[$configuration['foreign_table']];
1585 
1586  foreach ($referenceUids as $referenceUid) {
1587  try {
1588  $fileReference = ResourceFactory::getInstance()->getFileReferenceObject(
1589  $referenceUid,
1590  [],
1591  ($workspaceId === 0)
1592  );
1593  $fileReferences[$fileReference->getUid()] = $fileReference;
1594  } catch (\TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException $e) {
1599  } catch (\InvalidArgumentException $e) {
1604  $logMessage = $e->getMessage() . ' (table: "' . $tableName . '", fieldName: "' . $fieldName . '", referenceUid: ' . $referenceUid . ')';
1605  GeneralUtility::sysLog($logMessage, 'core', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1606  }
1607  }
1608 
1609  return $fileReferences;
1610  }
1611 
1629  public static function thumbCode(
1630  $row,
1631  $table,
1632  $field,
1633  $backPath = '',
1634  $thumbScript = '',
1635  $uploaddir = null,
1636  $abs = 0,
1637  $tparams = '',
1638  $size = '',
1639  $linkInfoPopup = true
1640  ) {
1641  // Check and parse the size parameter
1642  $size = trim($size);
1643  $sizeParts = [64, 64];
1644  if ($size) {
1645  $sizeParts = explode('x', $size . 'x' . $size);
1646  }
1647  $thumbData = '';
1648  $fileReferences = static::resolveFileReferences($table, $field, $row);
1649  // FAL references
1650  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
1651  if ($fileReferences !== null) {
1652  foreach ($fileReferences as $fileReferenceObject) {
1653  // Do not show previews of hidden references
1654  if ($fileReferenceObject->getProperty('hidden')) {
1655  continue;
1656  }
1657  $fileObject = $fileReferenceObject->getOriginalFile();
1658 
1659  if ($fileObject->isMissing()) {
1660  $thumbData .= '<span class="label label-danger">'
1661  . htmlspecialchars(
1662  static::getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:warning.file_missing')
1663  )
1664  . '</span>&nbsp;' . htmlspecialchars($fileObject->getName()) . '<br />';
1665  continue;
1666  }
1667 
1668  // Preview web image or media elements
1669  if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['thumbnails']
1671  $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'] . ',' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['mediafile_ext'],
1672  $fileReferenceObject->getExtension()
1673  )
1674  ) {
1675  $processedImage = $fileObject->process(
1677  [
1678  'width' => $sizeParts[0],
1679  'height' => $sizeParts[1] . 'c',
1680  'crop' => $fileReferenceObject->getProperty('crop')
1681  ]
1682  );
1683  $imageUrl = $processedImage->getPublicUrl(true);
1684  $imgTag = '<img src="' . $imageUrl . '" '
1685  . 'width="' . $processedImage->getProperty('width') . '" '
1686  . 'height="' . $processedImage->getProperty('height') . '" '
1687  . 'alt="' . htmlspecialchars($fileReferenceObject->getName()) . '" />';
1688  } else {
1689  // Icon
1690  $imgTag = '<span title="' . htmlspecialchars($fileObject->getName()) . '">'
1691  . $iconFactory->getIconForResource($fileObject, Icon::SIZE_SMALL)->render()
1692  . '</span>';
1693  }
1694  if ($linkInfoPopup) {
1695  $onClick = 'top.launchView(\'_FILE\',\'' . (int)$fileObject->getUid() . '\'); return false;';
1696  $thumbData .= '<a href="#" onclick="' . htmlspecialchars($onClick) . '">' . $imgTag . '</a> ';
1697  } else {
1698  $thumbData .= $imgTag;
1699  }
1700  }
1701  } else {
1702  // Find uploaddir automatically
1703  if (is_null($uploaddir)) {
1704  $uploaddir = $GLOBALS['TCA'][$table]['columns'][$field]['config']['uploadfolder'];
1705  }
1706  $uploaddir = rtrim($uploaddir, '/');
1707  // Traverse files:
1708  $thumbs = GeneralUtility::trimExplode(',', $row[$field], true);
1709  $thumbData = '';
1710  foreach ($thumbs as $theFile) {
1711  if ($theFile) {
1712  $fileName = trim($uploaddir . '/' . $theFile, '/');
1713  try {
1715  $fileObject = ResourceFactory::getInstance()->retrieveFileOrFolderObject($fileName);
1716  // Skip the resource if it's not of type AbstractFile. One case where this can happen if the
1717  // storage has been externally modified and the field value now points to a folder
1718  // instead of a file.
1719  if (!$fileObject instanceof AbstractFile) {
1720  continue;
1721  }
1722  if ($fileObject->isMissing()) {
1723  $thumbData .= '<span class="label label-danger">'
1724  . htmlspecialchars(static::getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:warning.file_missing'))
1725  . '</span>&nbsp;' . htmlspecialchars($fileObject->getName()) . '<br />';
1726  continue;
1727  }
1728  } catch (ResourceDoesNotExistException $exception) {
1729  $thumbData .= '<span class="label label-danger">'
1730  . htmlspecialchars(static::getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:warning.file_missing'))
1731  . '</span>&nbsp;' . htmlspecialchars($fileName) . '<br />';
1732  continue;
1733  }
1734 
1735  $fileExtension = $fileObject->getExtension();
1736  if ($fileExtension == 'ttf'
1737  || GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileExtension)
1738  ) {
1739  $imageUrl = $fileObject->process(
1741  [
1742  'width' => $sizeParts[0],
1743  'height' => $sizeParts[1]
1744  ]
1745  )->getPublicUrl(true);
1746 
1747  $image = '<img src="' . htmlspecialchars($imageUrl) . '" hspace="2" border="0" title="' . htmlspecialchars($fileObject->getName()) . '"' . $tparams . ' alt="" />';
1748  if ($linkInfoPopup) {
1749  $onClick = 'top.launchView(\'_FILE\', ' . GeneralUtility::quoteJSvalue($fileName) . ',\'\');return false;';
1750  $thumbData .= '<a href="#" onclick="' . htmlspecialchars($onClick) . '">' . $image . '</a> ';
1751  } else {
1752  $thumbData .= $image;
1753  }
1754  } else {
1755  // Gets the icon
1756  $fileIcon = '<span title="' . htmlspecialchars($fileObject->getName()) . '">'
1757  . $iconFactory->getIconForResource($fileObject, Icon::SIZE_SMALL)->render()
1758  . '</span>';
1759  if ($linkInfoPopup) {
1760  $onClick = 'top.launchView(\'_FILE\', ' . GeneralUtility::quoteJSvalue($fileName) . ',\'\'); return false;';
1761  $thumbData .= '<a href="#" onclick="' . htmlspecialchars($onClick) . '">' . $fileIcon . '</a> ';
1762  } else {
1763  $thumbData .= $fileIcon;
1764  }
1765  }
1766  }
1767  }
1768  }
1769  return $thumbData;
1770  }
1771 
1780  public static function titleAttribForPages($row, $perms_clause = '', $includeAttrib = true)
1781  {
1782  $lang = static::getLanguageService();
1783  $parts = [];
1784  $parts[] = 'id=' . $row['uid'];
1785  if ($row['alias']) {
1786  $parts[] = $lang->sL($GLOBALS['TCA']['pages']['columns']['alias']['label']) . ' ' . $row['alias'];
1787  }
1788  if ($row['pid'] < 0) {
1789  $parts[] = 'v#1.' . $row['t3ver_id'];
1790  }
1791  switch (VersionState::cast($row['t3ver_state'])) {
1793  $parts[] = 'PLH WSID#' . $row['t3ver_wsid'];
1794  break;
1796  $parts[] = 'Deleted element!';
1797  break;
1799  $parts[] = 'NEW LOCATION (PLH) WSID#' . $row['t3ver_wsid'];
1800  break;
1802  $parts[] = 'OLD LOCATION (PNT) WSID#' . $row['t3ver_wsid'];
1803  break;
1805  $parts[] = 'New element!';
1806  break;
1807  }
1808  if ($row['doktype'] == PageRepository::DOKTYPE_LINK) {
1809  $parts[] = $lang->sL($GLOBALS['TCA']['pages']['columns']['url']['label']) . ' ' . $row['url'];
1810  } elseif ($row['doktype'] == PageRepository::DOKTYPE_SHORTCUT) {
1811  if ($perms_clause) {
1812  $label = self::getRecordPath((int)$row['shortcut'], $perms_clause, 20);
1813  } else {
1814  $row['shortcut'] = (int)$row['shortcut'];
1815  $lRec = self::getRecordWSOL('pages', $row['shortcut'], 'title');
1816  $label = $lRec['title'] . ' (id=' . $row['shortcut'] . ')';
1817  }
1818  if ($row['shortcut_mode'] != PageRepository::SHORTCUT_MODE_NONE) {
1819  $label .= ', ' . $lang->sL($GLOBALS['TCA']['pages']['columns']['shortcut_mode']['label']) . ' '
1820  . $lang->sL(self::getLabelFromItemlist('pages', 'shortcut_mode', $row['shortcut_mode']));
1821  }
1822  $parts[] = $lang->sL($GLOBALS['TCA']['pages']['columns']['shortcut']['label']) . ' ' . $label;
1823  } elseif ($row['doktype'] == PageRepository::DOKTYPE_MOUNTPOINT) {
1824  if ($perms_clause) {
1825  $label = self::getRecordPath((int)$row['mount_pid'], $perms_clause, 20);
1826  } else {
1827  $lRec = self::getRecordWSOL('pages', (int)$row['mount_pid'], 'title');
1828  $label = $lRec['title'] . ' (id=' . $row['mount_pid'] . ')';
1829  }
1830  $parts[] = $lang->sL($GLOBALS['TCA']['pages']['columns']['mount_pid']['label']) . ' ' . $label;
1831  if ($row['mount_pid_ol']) {
1832  $parts[] = $lang->sL($GLOBALS['TCA']['pages']['columns']['mount_pid_ol']['label']);
1833  }
1834  }
1835  if ($row['nav_hide']) {
1836  $parts[] = rtrim($lang->sL($GLOBALS['TCA']['pages']['columns']['nav_hide']['label']), ':');
1837  }
1838  if ($row['hidden']) {
1839  $parts[] = $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.hidden');
1840  }
1841  if ($row['starttime']) {
1842  $parts[] = $lang->sL($GLOBALS['TCA']['pages']['columns']['starttime']['label'])
1843  . ' ' . self::dateTimeAge($row['starttime'], -1, 'date');
1844  }
1845  if ($row['endtime']) {
1846  $parts[] = $lang->sL($GLOBALS['TCA']['pages']['columns']['endtime']['label']) . ' '
1847  . self::dateTimeAge($row['endtime'], -1, 'date');
1848  }
1849  if ($row['fe_group']) {
1850  $fe_groups = [];
1851  foreach (GeneralUtility::intExplode(',', $row['fe_group']) as $fe_group) {
1852  if ($fe_group < 0) {
1853  $fe_groups[] = $lang->sL(self::getLabelFromItemlist('pages', 'fe_group', $fe_group));
1854  } else {
1855  $lRec = self::getRecordWSOL('fe_groups', $fe_group, 'title');
1856  $fe_groups[] = $lRec['title'];
1857  }
1858  }
1859  $label = implode(', ', $fe_groups);
1860  $parts[] = $lang->sL($GLOBALS['TCA']['pages']['columns']['fe_group']['label']) . ' ' . $label;
1861  }
1862  $out = htmlspecialchars(implode(' - ', $parts));
1863  return $includeAttrib ? 'title="' . $out . '"' : $out;
1864  }
1865 
1873  public static function getRecordToolTip(array $row, $table = 'pages')
1874  {
1875  $toolTipText = self::getRecordIconAltText($row, $table);
1876  $toolTipCode = 'data-toggle="tooltip" data-title=" '
1877  . str_replace(' - ', '<br>', $toolTipText)
1878  . '" data-html="true" data-placement="right"';
1879  return $toolTipCode;
1880  }
1881 
1891  public static function getRecordIconAltText($row, $table = 'pages')
1892  {
1893  if ($table == 'pages') {
1894  $out = self::titleAttribForPages($row, '', 0);
1895  } else {
1896  $out = !empty(trim($GLOBALS['TCA'][$table]['ctrl']['descriptionColumn'])) ? $row[$GLOBALS['TCA'][$table]['ctrl']['descriptionColumn']] . ' ' : '';
1897  $ctrl = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns'];
1898  // Uid is added
1899  $out .= 'id=' . $row['uid'];
1900  if ($table == 'pages' && $row['alias']) {
1901  $out .= ' / ' . $row['alias'];
1902  }
1903  if (static::isTableWorkspaceEnabled($table) && $row['pid'] < 0) {
1904  $out .= ' - v#1.' . $row['t3ver_id'];
1905  }
1906  if (static::isTableWorkspaceEnabled($table)) {
1907  switch (VersionState::cast($row['t3ver_state'])) {
1909  $out .= ' - PLH WSID#' . $row['t3ver_wsid'];
1910  break;
1912  $out .= ' - Deleted element!';
1913  break;
1915  $out .= ' - NEW LOCATION (PLH) WSID#' . $row['t3ver_wsid'];
1916  break;
1918  $out .= ' - OLD LOCATION (PNT) WSID#' . $row['t3ver_wsid'];
1919  break;
1921  $out .= ' - New element!';
1922  break;
1923  }
1924  }
1925  // Hidden
1926  $lang = static::getLanguageService();
1927  if ($ctrl['disabled']) {
1928  $out .= $row[$ctrl['disabled']] ? ' - ' . $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.hidden') : '';
1929  }
1930  if ($ctrl['starttime']) {
1931  if ($row[$ctrl['starttime']] > $GLOBALS['EXEC_TIME']) {
1932  $out .= ' - ' . $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.starttime') . ':' . self::date($row[$ctrl['starttime']]) . ' (' . self::daysUntil($row[$ctrl['starttime']]) . ' ' . $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.days') . ')';
1933  }
1934  }
1935  if ($row[$ctrl['endtime']]) {
1936  $out .= ' - ' . $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.endtime') . ': ' . self::date($row[$ctrl['endtime']]) . ' (' . self::daysUntil($row[$ctrl['endtime']]) . ' ' . $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.days') . ')';
1937  }
1938  }
1939  return htmlspecialchars($out);
1940  }
1941 
1950  public static function getLabelFromItemlist($table, $col, $key)
1951  {
1952  // Check, if there is an "items" array:
1953  if (is_array($GLOBALS['TCA'][$table])
1954  && is_array($GLOBALS['TCA'][$table]['columns'][$col])
1955  && is_array($GLOBALS['TCA'][$table]['columns'][$col]['config']['items'])
1956  ) {
1957  // Traverse the items-array...
1958  foreach ($GLOBALS['TCA'][$table]['columns'][$col]['config']['items'] as $v) {
1959  // ... and return the first found label where the value was equal to $key
1960  if ((string)$v[1] === (string)$key) {
1961  return $v[0];
1962  }
1963  }
1964  }
1965  return '';
1966  }
1967 
1977  public static function getLabelFromItemListMerged($pageId, $table, $column, $key)
1978  {
1979  $pageTsConfig = static::getPagesTSconfig($pageId);
1980  $label = '';
1981  if (is_array($pageTsConfig['TCEFORM.'])
1982  && is_array($pageTsConfig['TCEFORM.'][$table . '.'])
1983  && is_array($pageTsConfig['TCEFORM.'][$table . '.'][$column . '.'])
1984  ) {
1985  if (is_array($pageTsConfig['TCEFORM.'][$table . '.'][$column . '.']['addItems.'])
1986  && isset($pageTsConfig['TCEFORM.'][$table . '.'][$column . '.']['addItems.'][$key])
1987  ) {
1988  $label = $pageTsConfig['TCEFORM.'][$table . '.'][$column . '.']['addItems.'][$key];
1989  } elseif (is_array($pageTsConfig['TCEFORM.'][$table . '.'][$column . '.']['altLabels.'])
1990  && isset($pageTsConfig['TCEFORM.'][$table . '.'][$column . '.']['altLabels.'][$key])
1991  ) {
1992  $label = $pageTsConfig['TCEFORM.'][$table . '.'][$column . '.']['altLabels.'][$key];
1993  }
1994  }
1995  if (empty($label)) {
1996  $tcaValue = self::getLabelFromItemlist($table, $column, $key);
1997  if (!empty($tcaValue)) {
1998  $label = $tcaValue;
1999  }
2000  }
2001  return $label;
2002  }
2003 
2014  public static function getLabelsFromItemsList($table, $column, $keyList, array $columnTsConfig = [])
2015  {
2016  // Check if there is an "items" array
2017  if (
2018  !isset($GLOBALS['TCA'][$table]['columns'][$column]['config']['items'])
2019  || !is_array($GLOBALS['TCA'][$table]['columns'][$column]['config']['items'])
2020  || $keyList === ''
2021  ) {
2022  return '';
2023  }
2024 
2025  $keys = GeneralUtility::trimExplode(',', $keyList, true);
2026  $labels = [];
2027  // Loop on all selected values
2028  foreach ($keys as $key) {
2029  $label = null;
2030  if ($columnTsConfig) {
2031  // Check if label has been defined or redefined via pageTsConfig
2032  if (isset($columnTsConfig['addItems.'][$key])) {
2033  $label = $columnTsConfig['addItems.'][$key];
2034  } elseif (isset($columnTsConfig['altLabels.'][$key])) {
2035  $label = $columnTsConfig['altLabels.'][$key];
2036  }
2037  }
2038  if ($label === null) {
2039  // Otherwise lookup the label in TCA items list
2040  foreach ($GLOBALS['TCA'][$table]['columns'][$column]['config']['items'] as $itemConfiguration) {
2041  list($currentLabel, $currentKey) = $itemConfiguration;
2042  if ((string)$key === (string)$currentKey) {
2043  $label = $currentLabel;
2044  break;
2045  }
2046  }
2047  }
2048  if ($label !== null) {
2049  $labels[] = static::getLanguageService()->sL($label);
2050  }
2051  }
2052  return implode(', ', $labels);
2053  }
2054 
2063  public static function getItemLabel($table, $col)
2064  {
2065  // Check if column exists
2066  if (is_array($GLOBALS['TCA'][$table]) && is_array($GLOBALS['TCA'][$table]['columns'][$col])) {
2067  return $GLOBALS['TCA'][$table]['columns'][$col]['label'];
2068  }
2069 
2070  return null;
2071  }
2072 
2081  protected static function replaceL10nModeFields($table, array $row)
2082  {
2083  $originalUidField = isset($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'])
2084  ? $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']
2085  : '';
2086  if (empty($row[$originalUidField])) {
2087  return $row;
2088  }
2089 
2090  $originalTable = self::getOriginalTranslationTable($table);
2091  $originalRow = self::getRecord($originalTable, $row[$originalUidField]);
2092  foreach ($row as $field => $_) {
2093  $l10n_mode = isset($GLOBALS['TCA'][$originalTable]['columns'][$field]['l10n_mode'])
2094  ? $GLOBALS['TCA'][$originalTable]['columns'][$field]['l10n_mode']
2095  : '';
2096  if ($l10n_mode === 'exclude' || ($l10n_mode === 'mergeIfNotBlank' && trim($row[$field]) === '')) {
2097  $row[$field] = $originalRow[$field];
2098  }
2099  }
2100  return $row;
2101  }
2102 
2113  public static function getRecordTitle($table, $row, $prep = false, $forceResult = true)
2114  {
2115  $recordTitle = '';
2116  if (is_array($GLOBALS['TCA'][$table])) {
2117  // If configured, call userFunc
2118  if ($GLOBALS['TCA'][$table]['ctrl']['label_userFunc']) {
2119  $params['table'] = $table;
2120  $params['row'] = $row;
2121  $params['title'] = '';
2122  $params['options'] = $GLOBALS['TCA'][$table]['ctrl']['label_userFunc_options'] ?? [];
2123 
2124  // Create NULL-reference
2125  $null = null;
2126  GeneralUtility::callUserFunction($GLOBALS['TCA'][$table]['ctrl']['label_userFunc'], $params, $null);
2127  $recordTitle = $params['title'];
2128  } else {
2129  if (is_array($row)) {
2130  $row = self::replaceL10nModeFields($table, $row);
2131  }
2132 
2133  // No userFunc: Build label
2134  $recordTitle = self::getProcessedValue(
2135  $table,
2136  $GLOBALS['TCA'][$table]['ctrl']['label'],
2137  $row[$GLOBALS['TCA'][$table]['ctrl']['label']],
2138  0,
2139  0,
2140  false,
2141  $row['uid'],
2142  $forceResult
2143  );
2144  if ($GLOBALS['TCA'][$table]['ctrl']['label_alt']
2145  && ($GLOBALS['TCA'][$table]['ctrl']['label_alt_force'] || (string)$recordTitle === '')
2146  ) {
2147  $altFields = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['ctrl']['label_alt'], true);
2148  $tA = [];
2149  if (!empty($recordTitle)) {
2150  $tA[] = $recordTitle;
2151  }
2152  foreach ($altFields as $fN) {
2153  $recordTitle = trim(strip_tags($row[$fN]));
2154  if ((string)$recordTitle !== '') {
2155  $recordTitle = self::getProcessedValue($table, $fN, $recordTitle, 0, 0, false, $row['uid']);
2156  if (!$GLOBALS['TCA'][$table]['ctrl']['label_alt_force']) {
2157  break;
2158  }
2159  $tA[] = $recordTitle;
2160  }
2161  }
2162  if ($GLOBALS['TCA'][$table]['ctrl']['label_alt_force']) {
2163  $recordTitle = implode(', ', $tA);
2164  }
2165  }
2166  }
2167  // If the current result is empty, set it to '[No title]' (localized) and prepare for output if requested
2168  if ($prep || $forceResult) {
2169  if ($prep) {
2170  $recordTitle = self::getRecordTitlePrep($recordTitle);
2171  }
2172  if (trim($recordTitle) === '') {
2173  $recordTitle = self::getNoRecordTitle($prep);
2174  }
2175  }
2176  }
2177 
2178  return $recordTitle;
2179  }
2180 
2189  public static function getRecordTitlePrep($title, $titleLength = 0)
2190  {
2191  // If $titleLength is not a valid positive integer, use BE_USER->uc['titleLen']:
2192  if (!$titleLength || !MathUtility::canBeInterpretedAsInteger($titleLength) || $titleLength < 0) {
2193  $titleLength = static::getBackendUserAuthentication()->uc['titleLen'];
2194  }
2195  $titleOrig = htmlspecialchars($title);
2196  $title = htmlspecialchars(GeneralUtility::fixed_lgd_cs($title, $titleLength));
2197  // If title was cropped, offer a tooltip:
2198  if ($titleOrig != $title) {
2199  $title = '<span title="' . $titleOrig . '">' . $title . '</span>';
2200  }
2201  return $title;
2202  }
2203 
2210  public static function getNoRecordTitle($prep = false)
2211  {
2212  $noTitle = '[' .
2213  htmlspecialchars(static::getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.no_title'))
2214  . ']';
2215  if ($prep) {
2216  $noTitle = '<em>' . $noTitle . '</em>';
2217  }
2218  return $noTitle;
2219  }
2220 
2239  public static function getProcessedValue(
2240  $table,
2241  $col,
2242  $value,
2243  $fixed_lgd_chars = 0,
2244  $defaultPassthrough = false,
2245  $noRecordLookup = false,
2246  $uid = 0,
2247  $forceResult = true,
2248  $pid = 0
2249  ) {
2250  if ($col === 'uid') {
2251  // uid is not in TCA-array
2252  return $value;
2253  }
2254  // Check if table and field is configured
2255  if (!is_array($GLOBALS['TCA'][$table]) || !is_array($GLOBALS['TCA'][$table]['columns'][$col])) {
2256  return null;
2257  }
2258  // Depending on the fields configuration, make a meaningful output value.
2259  $theColConf = $GLOBALS['TCA'][$table]['columns'][$col]['config'];
2260  /*****************
2261  *HOOK: pre-processing the human readable output from a record
2262  ****************/
2263  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['preProcessValue'])) {
2264  // Create NULL-reference
2265  $null = null;
2266  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['preProcessValue'] as $_funcRef) {
2267  GeneralUtility::callUserFunction($_funcRef, $theColConf, $null);
2268  }
2269  }
2270  $l = '';
2271  $lang = static::getLanguageService();
2272  switch ((string)$theColConf['type']) {
2273  case 'radio':
2274  $l = self::getLabelFromItemlist($table, $col, $value);
2275  $l = $lang->sL($l);
2276  break;
2277  case 'inline':
2278  case 'select':
2279  if ($theColConf['MM']) {
2280  if ($uid) {
2281  // Display the title of MM related records in lists
2282  if ($noRecordLookup) {
2283  $MMfield = $theColConf['foreign_table'] . '.uid';
2284  } else {
2285  $MMfields = [$theColConf['foreign_table'] . '.' . $GLOBALS['TCA'][$theColConf['foreign_table']]['ctrl']['label']];
2286  foreach (GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$theColConf['foreign_table']]['ctrl']['label_alt'], true) as $f) {
2287  $MMfields[] = $theColConf['foreign_table'] . '.' . $f;
2288  }
2289  $MMfield = implode(',', $MMfields);
2290  }
2292  $dbGroup = GeneralUtility::makeInstance(RelationHandler::class);
2293  $dbGroup->start(
2294  $value,
2295  $theColConf['foreign_table'],
2296  $theColConf['MM'],
2297  $uid,
2298  $table,
2299  $theColConf
2300  );
2301  $selectUids = $dbGroup->tableArray[$theColConf['foreign_table']];
2302  if (is_array($selectUids) && !empty($selectUids)) {
2303  $queryBuilder = static::getQueryBuilderForTable($theColConf['foreign_table']);
2304  $queryBuilder->getRestrictions()
2305  ->removeAll()
2306  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
2307 
2308  $result = $queryBuilder
2309  ->select('uid', $MMfield)
2310  ->from($theColConf['foreign_table'])
2311  ->where(
2312  $queryBuilder->expr()->in(
2313  'uid',
2314  $queryBuilder->createNamedParameter($selectUids, Connection::PARAM_INT_ARRAY)
2315  )
2316  )
2317  ->execute();
2318 
2319  $mmlA = [];
2320  while ($MMrow = $result->fetch()) {
2321  // Keep sorting of $selectUids
2322  $selectedUid = array_search($MMrow['uid'], $selectUids);
2323  $mmlA[$selectedUid] = $MMrow['uid'];
2324  if (!$noRecordLookup) {
2325  $mmlA[$selectedUid] = static::getRecordTitle(
2326  $theColConf['foreign_table'],
2327  $MMrow,
2328  false,
2329  $forceResult
2330  );
2331  }
2332  }
2333 
2334  if (!empty($mmlA)) {
2335  ksort($mmlA);
2336  $l = implode('; ', $mmlA);
2337  } else {
2338  $l = 'N/A';
2339  }
2340  } else {
2341  $l = 'N/A';
2342  }
2343  } else {
2344  $l = 'N/A';
2345  }
2346  } else {
2347  $columnTsConfig = [];
2348  if ($pid) {
2349  $pageTsConfig = self::getPagesTSconfig($pid);
2350  if (isset($pageTsConfig['TCEFORM.'][$table . '.'][$col . '.']) && is_array($pageTsConfig['TCEFORM.'][$table . '.'][$col . '.'])) {
2351  $columnTsConfig = $pageTsConfig['TCEFORM.'][$table . '.'][$col . '.'];
2352  }
2353  }
2354  $l = self::getLabelsFromItemsList($table, $col, $value, $columnTsConfig);
2355  if ($theColConf['foreign_table'] && !$l && $GLOBALS['TCA'][$theColConf['foreign_table']]) {
2356  if ($noRecordLookup) {
2357  $l = $value;
2358  } else {
2359  $rParts = [];
2360  if ($uid && isset($theColConf['foreign_field']) && $theColConf['foreign_field'] !== '') {
2361  $queryBuilder = static::getQueryBuilderForTable($theColConf['foreign_table']);
2362  $queryBuilder->getRestrictions()
2363  ->removeAll()
2364  ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
2365  $constraints = [
2366  $queryBuilder->expr()->eq(
2367  $theColConf['foreign_field'],
2368  $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
2369  )
2370  ];
2371 
2372  if (!empty($theColConf['foreign_table_field'])) {
2373  $constraints[] = $queryBuilder->expr()->eq(
2374  $theColConf['foreign_table_field'],
2375  $queryBuilder->createNamedParameter($table, \PDO::PARAM_STR)
2376  );
2377  }
2378 
2379  // Add additional where clause if foreign_match_fields are defined
2380  $foreignMatchFields = [];
2381  if (is_array($theColConf['foreign_match_fields'])) {
2382  $foreignMatchFields = $theColConf['foreign_match_fields'];
2383  }
2384 
2385  foreach ($foreignMatchFields as $matchField => $matchValue) {
2386  $constraints[] = $queryBuilder->expr()->eq(
2387  $matchField,
2388  $queryBuilder->createNamedParameter($matchValue)
2389  );
2390  }
2391 
2392  $result = $queryBuilder
2393  ->select('*')
2394  ->from($theColConf['foreign_table'])
2395  ->where(...$constraints)
2396  ->execute();
2397 
2398  while ($record = $result->fetch()) {
2399  $rParts[] = $record['uid'];
2400  }
2401  }
2402  if (empty($rParts)) {
2403  $rParts = GeneralUtility::trimExplode(',', $value, true);
2404  }
2405  $lA = [];
2406  foreach ($rParts as $rVal) {
2407  $rVal = (int)$rVal;
2408  $r = self::getRecordWSOL($theColConf['foreign_table'], $rVal);
2409  if (is_array($r)) {
2410  $lA[] = $lang->sL($theColConf['foreign_table_prefix'])
2411  . self::getRecordTitle($theColConf['foreign_table'], $r, false, $forceResult);
2412  } else {
2413  $lA[] = $rVal ? '[' . $rVal . '!]' : '';
2414  }
2415  }
2416  $l = implode(', ', $lA);
2417  }
2418  }
2419  if (empty($l) && !empty($value)) {
2420  // Use plain database value when label is empty
2421  $l = $value;
2422  }
2423  }
2424  break;
2425  case 'group':
2426  // resolve the titles for DB records
2427  if ($theColConf['internal_type'] === 'db') {
2428  if ($theColConf['MM']) {
2429  if ($uid) {
2430  // Display the title of MM related records in lists
2431  if ($noRecordLookup) {
2432  $MMfield = $theColConf['foreign_table'] . '.uid';
2433  } else {
2434  $MMfields = [$theColConf['foreign_table'] . '.' . $GLOBALS['TCA'][$theColConf['foreign_table']]['ctrl']['label']];
2435  $altLabelFields = explode(
2436  ',',
2437  $GLOBALS['TCA'][$theColConf['foreign_table']]['ctrl']['label_alt']
2438  );
2439  foreach ($altLabelFields as $f) {
2440  $f = trim($f);
2441  if ($f !== '') {
2442  $MMfields[] = $theColConf['foreign_table'] . '.' . $f;
2443  }
2444  }
2445  $MMfield = implode(',', $MMfields);
2446  }
2448  $dbGroup = GeneralUtility::makeInstance(RelationHandler::class);
2449  $dbGroup->start(
2450  $value,
2451  $theColConf['foreign_table'],
2452  $theColConf['MM'],
2453  $uid,
2454  $table,
2455  $theColConf
2456  );
2457  $selectUids = $dbGroup->tableArray[$theColConf['foreign_table']];
2458  if (!empty($selectUids) && is_array($selectUids)) {
2459  $queryBuilder = static::getQueryBuilderForTable($theColConf['foreign_table']);
2460  $queryBuilder->getRestrictions()
2461  ->removeAll()
2462  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
2463 
2464  $result = $queryBuilder
2465  ->select('uid', $MMfield)
2466  ->from($theColConf['foreign_table'])
2467  ->where(
2468  $queryBuilder->expr()->in(
2469  'uid',
2470  $queryBuilder->createNamedParameter(
2471  $selectUids,
2472  Connection::PARAM_INT_ARRAY
2473  )
2474  )
2475  )
2476  ->execute();
2477 
2478  $mmlA = [];
2479  while ($MMrow = $result->fetch()) {
2480  // Keep sorting of $selectUids
2481  $selectedUid = array_search($MMrow['uid'], $selectUids);
2482  $mmlA[$selectedUid] = $MMrow['uid'];
2483  if (!$noRecordLookup) {
2484  $mmlA[$selectedUid] = static::getRecordTitle(
2485  $theColConf['foreign_table'],
2486  $MMrow,
2487  false,
2488  $forceResult
2489  );
2490  }
2491  }
2492 
2493  if (!empty($mmlA)) {
2494  ksort($mmlA);
2495  $l = implode('; ', $mmlA);
2496  } else {
2497  $l = 'N/A';
2498  }
2499  } else {
2500  $l = 'N/A';
2501  }
2502  } else {
2503  $l = 'N/A';
2504  }
2505  } else {
2506  $finalValues = [];
2507  $relationTableName = $theColConf['allowed'];
2508  $explodedValues = GeneralUtility::trimExplode(',', $value, true);
2509 
2510  foreach ($explodedValues as $explodedValue) {
2511  if (MathUtility::canBeInterpretedAsInteger($explodedValue)) {
2512  $relationTableNameForField = $relationTableName;
2513  } else {
2514  list($relationTableNameForField, $explodedValue) = self::splitTable_Uid($explodedValue);
2515  }
2516 
2517  $relationRecord = static::getRecordWSOL($relationTableNameForField, $explodedValue);
2518  $finalValues[] = static::getRecordTitle($relationTableNameForField, $relationRecord);
2519  }
2520  $l = implode(', ', $finalValues);
2521  }
2522  } else {
2523  $l = implode(', ', GeneralUtility::trimExplode(',', $value, true));
2524  }
2525  break;
2526  case 'check':
2527  if (!is_array($theColConf['items']) || count($theColConf['items']) === 1) {
2528  $l = $value ? $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_common.xlf:yes') : $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_common.xlf:no');
2529  } else {
2530  $lA = [];
2531  foreach ($theColConf['items'] as $key => $val) {
2532  if ($value & pow(2, $key)) {
2533  $lA[] = $lang->sL($val[0]);
2534  }
2535  }
2536  $l = implode(', ', $lA);
2537  }
2538  break;
2539  case 'input':
2540  // Hide value 0 for dates, but show it for everything else
2541  if (isset($value)) {
2542  if (GeneralUtility::inList($theColConf['eval'], 'date')) {
2543  // Handle native date field
2544  if (isset($theColConf['dbType']) && $theColConf['dbType'] === 'date') {
2545  $dateTimeFormats = QueryHelper::getDateTimeFormats();
2546  $emptyValue = $dateTimeFormats['date']['empty'];
2547  $value = $value !== $emptyValue ? strtotime($value) : 0;
2548  }
2549  if (!empty($value)) {
2550  $ageSuffix = '';
2551  $dateColumnConfiguration = $GLOBALS['TCA'][$table]['columns'][$col]['config'];
2552  $ageDisplayKey = 'disableAgeDisplay';
2553 
2554  // generate age suffix as long as not explicitly suppressed
2555  if (!isset($dateColumnConfiguration[$ageDisplayKey])
2556  // non typesafe comparison on intention
2557  || $dateColumnConfiguration[$ageDisplayKey] == false
2558  ) {
2559  $ageSuffix = ' (' . ($GLOBALS['EXEC_TIME'] - $value > 0 ? '-' : '')
2560  . self::calcAge(
2561  abs(($GLOBALS['EXEC_TIME'] - $value)),
2562  $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.minutesHoursDaysYears')
2563  )
2564  . ')';
2565  }
2566 
2567  $l = self::date($value) . $ageSuffix;
2568  }
2569  } elseif (GeneralUtility::inList($theColConf['eval'], 'time')) {
2570  if (!empty($value)) {
2571  $l = self::time($value, false);
2572  }
2573  } elseif (GeneralUtility::inList($theColConf['eval'], 'timesec')) {
2574  if (!empty($value)) {
2575  $l = self::time($value);
2576  }
2577  } elseif (GeneralUtility::inList($theColConf['eval'], 'datetime')) {
2578  // Handle native date/time field
2579  if (isset($theColConf['dbType']) && $theColConf['dbType'] === 'datetime') {
2580  $dateTimeFormats = QueryHelper::getDateTimeFormats();
2581  $emptyValue = $dateTimeFormats['datetime']['empty'];
2582  $value = $value !== $emptyValue ? strtotime($value) : 0;
2583  }
2584  if (!empty($value)) {
2585  $l = self::datetime($value);
2586  }
2587  } else {
2588  $l = $value;
2589  }
2590  }
2591  break;
2592  case 'flex':
2593  $l = strip_tags($value);
2594  break;
2595  default:
2596  if ($defaultPassthrough) {
2597  $l = $value;
2598  } elseif ($theColConf['MM']) {
2599  $l = 'N/A';
2600  } elseif ($value) {
2601  $l = GeneralUtility::fixed_lgd_cs(strip_tags($value), 200);
2602  }
2603  }
2604  // If this field is a password field, then hide the password by changing it to a random number of asterisk (*)
2605  if (stristr($theColConf['eval'], 'password')) {
2606  $l = '';
2607  $randomNumber = rand(5, 12);
2608  for ($i = 0; $i < $randomNumber; $i++) {
2609  $l .= '*';
2610  }
2611  }
2612  /*****************
2613  *HOOK: post-processing the human readable output from a record
2614  ****************/
2615  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['postProcessValue'])) {
2616  // Create NULL-reference
2617  $null = null;
2618  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['postProcessValue'] as $_funcRef) {
2619  $params = [
2620  'value' => $l,
2621  'colConf' => $theColConf
2622  ];
2623  $l = GeneralUtility::callUserFunction($_funcRef, $params, $null);
2624  }
2625  }
2626  if ($fixed_lgd_chars) {
2627  return GeneralUtility::fixed_lgd_cs($l, $fixed_lgd_chars);
2628  } else {
2629  return $l;
2630  }
2631  }
2632 
2646  public static function getProcessedValueExtra(
2647  $table,
2648  $fN,
2649  $fV,
2650  $fixed_lgd_chars = 0,
2651  $uid = 0,
2652  $forceResult = true,
2653  $pid = 0
2654  ) {
2655  $fVnew = self::getProcessedValue($table, $fN, $fV, $fixed_lgd_chars, 1, 0, $uid, $forceResult, $pid);
2656  if (!isset($fVnew)) {
2657  if (is_array($GLOBALS['TCA'][$table])) {
2658  if ($fN == $GLOBALS['TCA'][$table]['ctrl']['tstamp'] || $fN == $GLOBALS['TCA'][$table]['ctrl']['crdate']) {
2659  $fVnew = self::datetime($fV);
2660  } elseif ($fN == 'pid') {
2661  // Fetches the path with no regard to the users permissions to select pages.
2662  $fVnew = self::getRecordPath($fV, '1=1', 20);
2663  } else {
2664  $fVnew = $fV;
2665  }
2666  }
2667  }
2668  return $fVnew;
2669  }
2670 
2681  public static function getCommonSelectFields($table, $prefix = '', $fields = [])
2682  {
2683  $fields[] = $prefix . 'uid';
2684  if (isset($GLOBALS['TCA'][$table]['ctrl']['label']) && $GLOBALS['TCA'][$table]['ctrl']['label'] != '') {
2685  $fields[] = $prefix . $GLOBALS['TCA'][$table]['ctrl']['label'];
2686  }
2687  if ($GLOBALS['TCA'][$table]['ctrl']['label_alt']) {
2688  $secondFields = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['ctrl']['label_alt'], true);
2689  foreach ($secondFields as $fieldN) {
2690  $fields[] = $prefix . $fieldN;
2691  }
2692  }
2693  if (static::isTableWorkspaceEnabled($table)) {
2694  $fields[] = $prefix . 't3ver_id';
2695  $fields[] = $prefix . 't3ver_state';
2696  $fields[] = $prefix . 't3ver_wsid';
2697  $fields[] = $prefix . 't3ver_count';
2698  }
2699  if ($GLOBALS['TCA'][$table]['ctrl']['selicon_field']) {
2700  $fields[] = $prefix . $GLOBALS['TCA'][$table]['ctrl']['selicon_field'];
2701  }
2702  if ($GLOBALS['TCA'][$table]['ctrl']['typeicon_column']) {
2703  $fields[] = $prefix . $GLOBALS['TCA'][$table]['ctrl']['typeicon_column'];
2704  }
2705  if (is_array($GLOBALS['TCA'][$table]['ctrl']['enablecolumns'])) {
2706  if ($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled']) {
2707  $fields[] = $prefix . $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'];
2708  }
2709  if ($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['starttime']) {
2710  $fields[] = $prefix . $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['starttime'];
2711  }
2712  if ($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['endtime']) {
2713  $fields[] = $prefix . $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['endtime'];
2714  }
2715  if ($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['fe_group']) {
2716  $fields[] = $prefix . $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['fe_group'];
2717  }
2718  }
2719  return implode(',', array_unique($fields));
2720  }
2721 
2735  public static function makeConfigForm($configArray, $defaults, $dataPrefix)
2736  {
2738  $params = $defaults;
2739  $lines = [];
2740  if (is_array($configArray)) {
2741  foreach ($configArray as $fname => $config) {
2742  if (is_array($config)) {
2743  $lines[$fname] = '<strong>' . htmlspecialchars($config[1]) . '</strong><br />';
2744  $lines[$fname] .= $config[2] . '<br />';
2745  switch ($config[0]) {
2746  case 'string':
2747 
2748  case 'short':
2749  $formEl = '<input type="text" name="' . $dataPrefix . '[' . $fname . ']" value="' . $params[$fname] . '"' . static::getDocumentTemplate()->formWidth(($config[0] == 'short' ? 24 : 48)) . ' />';
2750  break;
2751  case 'check':
2752  $formEl = '<input type="hidden" name="' . $dataPrefix . '[' . $fname . ']" value="0" /><input type="checkbox" name="' . $dataPrefix . '[' . $fname . ']" value="1"' . ($params[$fname] ? ' checked="checked"' : '') . ' />';
2753  break;
2754  case 'comment':
2755  $formEl = '';
2756  break;
2757  case 'select':
2758  $opt = [];
2759  foreach ($config[3] as $k => $v) {
2760  $opt[] = '<option value="' . htmlspecialchars($k) . '"' . ($params[$fname] == $k ? ' selected="selected"' : '') . '>' . htmlspecialchars($v) . '</option>';
2761  }
2762  $formEl = '<select name="' . $dataPrefix . '[' . $fname . ']">'
2763  . implode('', $opt) . '</select>';
2764  break;
2765  default:
2766  $formEl = '<strong>Should not happen. Bug in config.</strong>';
2767  }
2768  $lines[$fname] .= $formEl;
2769  $lines[$fname] .= '<br /><br />';
2770  } else {
2771  $lines[$fname] = '<hr />';
2772  if ($config) {
2773  $lines[$fname] .= '<strong>' . strtoupper(htmlspecialchars($config)) . '</strong><br />';
2774  }
2775  if ($config) {
2776  $lines[$fname] .= '<br />';
2777  }
2778  }
2779  }
2780  }
2781  $out = implode('', $lines);
2782  $out .= '<input class="btn btn-default" type="submit" name="submit" value="Update configuration" />';
2783  return $out;
2784  }
2785 
2786  /*******************************************
2787  *
2788  * Backend Modules API functions
2789  *
2790  *******************************************/
2791 
2799  public static function helpTextArray($table, $field)
2800  {
2801  if (!isset($GLOBALS['TCA_DESCR'][$table]['columns'])) {
2802  static::getLanguageService()->loadSingleTableDescription($table);
2803  }
2804  $output = [
2805  'description' => null,
2806  'title' => null,
2807  'moreInfo' => false
2808  ];
2809  if (is_array($GLOBALS['TCA_DESCR'][$table]) && is_array($GLOBALS['TCA_DESCR'][$table]['columns'][$field])) {
2810  $data = $GLOBALS['TCA_DESCR'][$table]['columns'][$field];
2811  // Add alternative title, if defined
2812  if ($data['alttitle']) {
2813  $output['title'] = $data['alttitle'];
2814  }
2815  // If we have more information to show and access to the cshmanual
2816  if (($data['image_descr'] || $data['seeAlso'] || $data['details'] || $data['syntax'])
2817  && static::getBackendUserAuthentication()->check('modules', 'help_CshmanualCshmanual')
2818  ) {
2819  $output['moreInfo'] = true;
2820  }
2821  // Add description
2822  if ($data['description']) {
2823  $output['description'] = $data['description'];
2824  }
2825  }
2826  return $output;
2827  }
2828 
2837  public static function helpText($table, $field)
2838  {
2839  $helpTextArray = self::helpTextArray($table, $field);
2840  $output = '';
2841  $arrow = '';
2842  // Put header before the rest of the text
2843  if ($helpTextArray['title'] !== null) {
2844  $output .= '<h2>' . $helpTextArray['title'] . '</h2>';
2845  }
2846  // Add see also arrow if we have more info
2847  if ($helpTextArray['moreInfo']) {
2849  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
2850  $arrow = $iconFactory->getIcon('actions-view-go-forward', Icon::SIZE_SMALL)->render();
2851  }
2852  // Wrap description and arrow in p tag
2853  if ($helpTextArray['description'] !== null || $arrow) {
2854  $output .= '<p class="t3-help-short">' . nl2br(htmlspecialchars($helpTextArray['description'])) . $arrow . '</p>';
2855  }
2856  return $output;
2857  }
2858 
2871  public static function wrapInHelp($table, $field, $text = '', array $overloadHelpText = [])
2872  {
2873  if (!ExtensionManagementUtility::isLoaded('context_help')) {
2874  return $text;
2875  }
2876 
2877  // Initialize some variables
2878  $helpText = '';
2879  $abbrClassAdd = '';
2880  $hasHelpTextOverload = !empty($overloadHelpText);
2881  // Get the help text that should be shown on hover
2882  if (!$hasHelpTextOverload) {
2883  $helpText = self::helpText($table, $field);
2884  }
2885  // If there's a help text or some overload information, proceed with preparing an output
2886  // @todo: right now this is a hard dependency on csh manual, as the whole help system should be moved to
2887  // the extension. The core provides an API for adding help and rendering help, but the rendering
2888  // should be up to the extension itself
2889  if ((!empty($helpText) || $hasHelpTextOverload) && ExtensionManagementUtility::isLoaded('cshmanual')) {
2890  // If no text was given, just use the regular help icon
2891  if ($text == '') {
2893  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
2894  $text = $iconFactory->getIcon('actions-system-help-open', Icon::SIZE_SMALL)->render();
2895  $abbrClassAdd = '-icon';
2896  }
2897  $text = '<abbr class="t3-help-teaser' . $abbrClassAdd . '">' . $text . '</abbr>';
2898  $wrappedText = '<span class="t3-help-link" href="#" data-table="' . $table . '" data-field="' . $field . '"';
2899  // The overload array may provide a title and a description
2900  // If either one is defined, add them to the "data" attributes
2901  if ($hasHelpTextOverload) {
2902  if (isset($overloadHelpText['title'])) {
2903  $wrappedText .= ' data-title="' . htmlspecialchars($overloadHelpText['title']) . '"';
2904  }
2905  if (isset($overloadHelpText['description'])) {
2906  $wrappedText .= ' data-description="' . htmlspecialchars($overloadHelpText['description']) . '"';
2907  }
2908  }
2909  $wrappedText .= '>' . $text . '</span>';
2910  return $wrappedText;
2911  }
2912  return $text;
2913  }
2914 
2925  public static function cshItem($table, $field, $_ = '', $wrap = '')
2926  {
2927  static::getLanguageService()->loadSingleTableDescription($table);
2928  if (is_array($GLOBALS['TCA_DESCR'][$table])
2929  && is_array($GLOBALS['TCA_DESCR'][$table]['columns'][$field])
2930  ) {
2931  // Creating short description
2932  $output = self::wrapInHelp($table, $field);
2933  if ($output && $wrap) {
2934  $wrParts = explode('|', $wrap);
2935  $output = $wrParts[0] . $output . $wrParts[1];
2936  }
2937  return $output;
2938  }
2939  return '';
2940  }
2941 
2952  public static function editOnClick($params, $_ = '', $requestUri = '')
2953  {
2954  if ($requestUri == -1) {
2955  $returnUrl = 'T3_THIS_LOCATION';
2956  } else {
2957  $returnUrl = GeneralUtility::quoteJSvalue(rawurlencode($requestUri ?: GeneralUtility::getIndpEnv('REQUEST_URI')));
2958  }
2959  return 'window.location.href=' . GeneralUtility::quoteJSvalue(self::getModuleUrl('record_edit') . $params . '&returnUrl=') . '+' . $returnUrl . '; return false;';
2960  }
2961 
2976  public static function viewOnClick(
2977  $pageUid,
2978  $backPath = '',
2979  $rootLine = null,
2980  $anchorSection = '',
2981  $alternativeUrl = '',
2982  $additionalGetVars = '',
2983  $switchFocus = true
2984  ) {
2985  $viewScript = '/index.php?id=';
2986  if ($alternativeUrl) {
2987  $viewScript = $alternativeUrl;
2988  }
2989 
2990  if (
2991  isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['viewOnClickClass'])
2992  && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['viewOnClickClass'])
2993  ) {
2994  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['viewOnClickClass'] as $funcRef) {
2995  $hookObj = GeneralUtility::getUserObj($funcRef);
2996  if (method_exists($hookObj, 'preProcess')) {
2997  $hookObj->preProcess(
2998  $pageUid,
2999  $backPath,
3000  $rootLine,
3001  $anchorSection,
3002  $viewScript,
3003  $additionalGetVars,
3004  $switchFocus
3005  );
3006  }
3007  }
3008  }
3009 
3010  if ($alternativeUrl) {
3011  $previewUrl = $viewScript;
3012  } else {
3013  $previewUrl = self::createPreviewUrl($pageUid, $rootLine, $anchorSection, $additionalGetVars, $viewScript);
3014  }
3015 
3016  if (
3017  isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['viewOnClickClass'])
3018  && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['viewOnClickClass'])
3019  ) {
3020  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['viewOnClickClass'] as $className) {
3021  $hookObj = GeneralUtility::makeInstance($className);
3022  if (method_exists($hookObj, 'postProcess')) {
3023  $previewUrl = $hookObj->postProcess(
3024  $previewUrl,
3025  $pageUid,
3026  $rootLine,
3027  $anchorSection,
3028  $viewScript,
3029  $additionalGetVars,
3030  $switchFocus
3031  );
3032  }
3033  }
3034  }
3035 
3036  $onclickCode = 'var previewWin = window.open(' . GeneralUtility::quoteJSvalue($previewUrl) . ',\'newTYPO3frontendWindow\');' . ($switchFocus ? 'previewWin.focus();' : '');
3037  return $onclickCode;
3038  }
3039 
3066  public static function wrapClickMenuOnIcon(
3067  $content,
3068  $table,
3069  $uid = 0,
3070  $listFrame = true,
3071  $addParams = '',
3072  $enDisItems = '',
3073  $returnTagParameters = false
3074  ) {
3075  $tagParameters = [
3076  'class' => 't3-js-clickmenutrigger',
3077  'data-table' => $table,
3078  'data-uid' => (int)$uid !== 0 ? (int)$uid : '',
3079  'data-listframe' => $listFrame,
3080  'data-iteminfo' => str_replace('+', '%2B', $enDisItems),
3081  'data-parameters' => $addParams,
3082  ];
3083 
3084  if ($returnTagParameters) {
3085  return $tagParameters;
3086  }
3087  return '<a href="#" ' . GeneralUtility::implodeAttributes($tagParameters, true) . '>' . $content . '</a>';
3088  }
3089 
3097  public static function getLinkToDataHandlerAction($parameters, $redirectUrl = '')
3098  {
3099  $urlParameters = [
3100  'prErr' => 1,
3101  'uPT' => 1,
3102  'vC' => static::getBackendUserAuthentication()->veriCode()
3103  ];
3104  $url = self::getModuleUrl('tce_db', $urlParameters) . $parameters . '&redirect=';
3105  if ((int)$redirectUrl === -1) {
3106  $url = GeneralUtility::quoteJSvalue($url) . '+T3_THIS_LOCATION';
3107  } else {
3108  $url .= rawurlencode($redirectUrl ?: GeneralUtility::getIndpEnv('REQUEST_URI'));
3109  }
3110  return $url;
3111  }
3112 
3124  protected static function createPreviewUrl($pageUid, $rootLine, $anchorSection, $additionalGetVars, $viewScript)
3125  {
3126  // Look if a fixed preview language should be added:
3127  $beUser = static::getBackendUserAuthentication();
3128  $viewLanguageOrder = $beUser->getTSConfigVal('options.view.languageOrder');
3129 
3130  if ((string)$viewLanguageOrder !== '') {
3131  $suffix = '';
3132  // Find allowed languages (if none, all are allowed!)
3133  $allowedLanguages = null;
3134  if (!$beUser->user['admin'] && $beUser->groupData['allowed_languages'] !== '') {
3135  $allowedLanguages = array_flip(explode(',', $beUser->groupData['allowed_languages']));
3136  }
3137  // Traverse the view order, match first occurrence:
3138  $languageOrder = GeneralUtility::intExplode(',', $viewLanguageOrder);
3139  foreach ($languageOrder as $langUid) {
3140  if (is_array($allowedLanguages) && !empty($allowedLanguages)) {
3141  // Choose if set.
3142  if (isset($allowedLanguages[$langUid])) {
3143  $suffix = '&L=' . $langUid;
3144  break;
3145  }
3146  } else {
3147  // All allowed since no lang. are listed.
3148  $suffix = '&L=' . $langUid;
3149  break;
3150  }
3151  }
3152  // Add it
3153  $additionalGetVars .= $suffix;
3154  }
3155 
3156  // Check a mount point needs to be previewed
3157  $sys_page = GeneralUtility::makeInstance(\TYPO3\CMS\Frontend\Page\PageRepository::class);
3158  $sys_page->init(false);
3159  $mountPointInfo = $sys_page->getMountPointInfo($pageUid);
3160 
3161  if ($mountPointInfo && $mountPointInfo['overlay']) {
3162  $pageUid = $mountPointInfo['mount_pid'];
3163  $additionalGetVars .= '&MP=' . $mountPointInfo['MPvar'];
3164  }
3165  $viewDomain = self::getViewDomain($pageUid, $rootLine);
3166 
3167  return $viewDomain . $viewScript . $pageUid . $additionalGetVars . $anchorSection;
3168  }
3169 
3178  public static function getViewDomain($pageId, $rootLine = null)
3179  {
3180  $domain = rtrim(GeneralUtility::getIndpEnv('TYPO3_SITE_URL'), '/');
3181  if (!is_array($rootLine)) {
3182  $rootLine = self::BEgetRootLine($pageId);
3183  }
3184  // Checks alternate domains
3185  if (!empty($rootLine)) {
3186  $urlParts = parse_url($domain);
3188  $sysPage = GeneralUtility::makeInstance(\TYPO3\CMS\Frontend\Page\PageRepository::class);
3189  $page = (array)$sysPage->getPage($pageId);
3190  $protocol = 'http';
3191  if ($page['url_scheme'] == HttpUtility::SCHEME_HTTPS || $page['url_scheme'] == 0 && GeneralUtility::getIndpEnv('TYPO3_SSL')) {
3192  $protocol = 'https';
3193  }
3194  $previewDomainConfig = static::getBackendUserAuthentication()->getTSConfig(
3195  'TCEMAIN.previewDomain',
3196  self::getPagesTSconfig($pageId)
3197  );
3198  if ($previewDomainConfig['value']) {
3199  if (strpos($previewDomainConfig['value'], '://') !== false) {
3200  list($protocol, $domainName) = explode('://', $previewDomainConfig['value']);
3201  } else {
3202  $domainName = $previewDomainConfig['value'];
3203  }
3204  } else {
3205  $domainName = self::firstDomainRecord($rootLine);
3206  }
3207  if ($domainName) {
3208  $domain = $domainName;
3209  } else {
3210  $domainRecord = self::getDomainStartPage($urlParts['host'], $urlParts['path']);
3211  $domain = $domainRecord['domainName'];
3212  }
3213  if ($domain) {
3214  $domain = $protocol . '://' . $domain;
3215  } else {
3216  $domain = rtrim(GeneralUtility::getIndpEnv('TYPO3_SITE_URL'), '/');
3217  }
3218  // Append port number if lockSSLPort is not the standard port 443
3219  $portNumber = (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['lockSSLPort'];
3220  if ($portNumber > 0 && $portNumber !== 443 && $portNumber < 65536 && $protocol === 'https') {
3221  $domain .= ':' . strval($portNumber);
3222  }
3223  }
3224  return $domain;
3225  }
3226 
3235  public static function getModTSconfig($id, $TSref)
3236  {
3237  $beUser = static::getBackendUserAuthentication();
3238  $pageTS_modOptions = $beUser->getTSConfig($TSref, static::getPagesTSconfig($id));
3239  $BE_USER_modOptions = $beUser->getTSConfig($TSref);
3240  if (is_null($BE_USER_modOptions['value'])) {
3241  unset($BE_USER_modOptions['value']);
3242  }
3243  ArrayUtility::mergeRecursiveWithOverrule($pageTS_modOptions, $BE_USER_modOptions);
3244  return $pageTS_modOptions;
3245  }
3246 
3260  public static function getFuncMenu(
3261  $mainParams,
3262  $elementName,
3263  $currentValue,
3264  $menuItems,
3265  $script = '',
3266  $addParams = ''
3267  ) {
3268  if (!is_array($menuItems) || count($menuItems) <= 1) {
3269  return '';
3270  }
3271  $scriptUrl = self::buildScriptUrl($mainParams, $addParams, $script);
3272  $options = [];
3273  foreach ($menuItems as $value => $label) {
3274  $options[] = '<option value="'
3275  . htmlspecialchars($value) . '"'
3276  . ((string)$currentValue === (string)$value ? ' selected="selected"' : '') . '>'
3277  . htmlspecialchars($label, ENT_COMPAT, 'UTF-8', false) . '</option>';
3278  }
3279  if (!empty($options)) {
3280  $onChange = 'jumpToUrl(' . GeneralUtility::quoteJSvalue($scriptUrl . '&' . $elementName . '=') . '+this.options[this.selectedIndex].value,this);';
3281  return '
3282 
3283  <!-- Function Menu of module -->
3284  <select name="' . $elementName . '" onchange="' . htmlspecialchars($onChange) . '">
3285  ' . implode('
3286  ', $options) . '
3287  </select>
3288  ';
3289  }
3290  return '';
3291  }
3292 
3307  public static function getDropdownMenu(
3308  $mainParams,
3309  $elementName,
3310  $currentValue,
3311  $menuItems,
3312  $script = '',
3313  $addParams = ''
3314  ) {
3315  if (!is_array($menuItems) || count($menuItems) <= 1) {
3316  return '';
3317  }
3318  $scriptUrl = self::buildScriptUrl($mainParams, $addParams, $script);
3319  $options = [];
3320  foreach ($menuItems as $value => $label) {
3321  $options[] = '<option value="'
3322  . htmlspecialchars($value) . '"'
3323  . ((string)$currentValue === (string)$value ? ' selected="selected"' : '') . '>'
3324  . htmlspecialchars($label, ENT_COMPAT, 'UTF-8', false) . '</option>';
3325  }
3326  if (!empty($options)) {
3327  $onChange = 'jumpToUrl(' . GeneralUtility::quoteJSvalue($scriptUrl . '&' . $elementName . '=') . '+this.options[this.selectedIndex].value,this);';
3328  return '
3329  <div class="form-group">
3330  <!-- Function Menu of module -->
3331  <select class="form-control input-sm" name="' . htmlspecialchars($elementName) . '" onchange="' . htmlspecialchars($onChange) . '">
3332  ' . implode(LF, $options) . '
3333  </select>
3334  </div>
3335  ';
3336  }
3337  return '';
3338  }
3339 
3353  public static function getFuncCheck(
3354  $mainParams,
3355  $elementName,
3356  $currentValue,
3357  $script = '',
3358  $addParams = '',
3359  $tagParams = ''
3360  ) {
3361  $scriptUrl = self::buildScriptUrl($mainParams, $addParams, $script);
3362  $onClick = 'jumpToUrl(' . GeneralUtility::quoteJSvalue($scriptUrl . '&' . $elementName . '=') . '+(this.checked?1:0),this);';
3363 
3364  return
3365  '<input' .
3366  ' type="checkbox"' .
3367  ' class="checkbox"' .
3368  ' name="' . $elementName . '"' .
3369  ($currentValue ? ' checked="checked"' : '') .
3370  ' onclick="' . htmlspecialchars($onClick) . '"' .
3371  ($tagParams ? ' ' . $tagParams : '') .
3372  ' value="1"' .
3373  ' />';
3374  }
3375 
3389  public static function getFuncInput(
3390  $mainParams,
3391  $elementName,
3392  $currentValue,
3393  $size = 10,
3394  $script = '',
3395  $addParams = ''
3396  ) {
3397  $scriptUrl = self::buildScriptUrl($mainParams, $addParams, $script);
3398  $onChange = 'jumpToUrl(' . GeneralUtility::quoteJSvalue($scriptUrl . '&' . $elementName . '=') . '+escape(this.value),this);';
3399  return '<input type="text"' . static::getDocumentTemplate()->formWidth($size) . ' name="' . $elementName . '" value="' . htmlspecialchars($currentValue) . '" onchange="' . htmlspecialchars($onChange) . '" />';
3400  }
3401 
3410  protected static function buildScriptUrl($mainParams, $addParams, $script = '')
3411  {
3412  if (!is_array($mainParams)) {
3413  $mainParams = ['id' => $mainParams];
3414  }
3415  if (!$script) {
3416  $script = basename(PATH_thisScript);
3417  }
3418 
3419  if (GeneralUtility::_GP('route')) {
3420  $router = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\Router::class);
3421  $route = $router->match(GeneralUtility::_GP('route'));
3422  $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
3423  $scriptUrl = (string)$uriBuilder->buildUriFromRoute($route->getOption('_identifier'));
3424  $scriptUrl .= $addParams;
3425  } elseif ($script === 'index.php' && GeneralUtility::_GET('M')) {
3426  $scriptUrl = self::getModuleUrl(GeneralUtility::_GET('M'), $mainParams) . $addParams;
3427  } else {
3428  $scriptUrl = $script . '?' . GeneralUtility::implodeArrayForUrl('', $mainParams) . $addParams;
3429  }
3430 
3431  return $scriptUrl;
3432  }
3433 
3443  public static function unsetMenuItems($modTSconfig, $itemArray, $TSref)
3444  {
3445  // Getting TS-config options for this module for the Backend User:
3446  $conf = static::getBackendUserAuthentication()->getTSConfig($TSref, $modTSconfig);
3447  if (is_array($conf['properties'])) {
3448  foreach ($conf['properties'] as $key => $val) {
3449  if (!$val) {
3450  unset($itemArray[$key]);
3451  }
3452  }
3453  }
3454  return $itemArray;
3455  }
3456 
3466  public static function setUpdateSignal($set = '', $params = '')
3467  {
3468  $beUser = static::getBackendUserAuthentication();
3469  $modData = $beUser->getModuleData(
3470  \TYPO3\CMS\Backend\Utility\BackendUtility::class . '::getUpdateSignal',
3471  'ses'
3472  );
3473  if ($set) {
3474  $modData[$set] = [
3475  'set' => $set,
3476  'parameter' => $params
3477  ];
3478  } else {
3479  // clear the module data
3480  $modData = [];
3481  }
3482  $beUser->pushModuleData(\TYPO3\CMS\Backend\Utility\BackendUtility::class . '::getUpdateSignal', $modData);
3483  }
3484 
3493  public static function getUpdateSignalCode()
3494  {
3495  $signals = [];
3496  $modData = static::getBackendUserAuthentication()->getModuleData(
3497  \TYPO3\CMS\Backend\Utility\BackendUtility::class . '::getUpdateSignal',
3498  'ses'
3499  );
3500  if (empty($modData)) {
3501  return '';
3502  }
3503  // Hook: Allows to let TYPO3 execute your JS code
3504  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['updateSignalHook'])) {
3505  $updateSignals = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['updateSignalHook'];
3506  } else {
3507  $updateSignals = [];
3508  }
3509  // Loop through all setUpdateSignals and get the JS code
3510  foreach ($modData as $set => $val) {
3511  if (isset($updateSignals[$set])) {
3512  $params = ['set' => $set, 'parameter' => $val['parameter'], 'JScode' => ''];
3513  $ref = null;
3514  GeneralUtility::callUserFunction($updateSignals[$set], $params, $ref);
3515  $signals[] = $params['JScode'];
3516  } else {
3517  switch ($set) {
3518  case 'updatePageTree':
3519  $signals[] = '
3520  if (top && top.TYPO3.Backend && top.TYPO3.Backend.NavigationContainer.PageTree) {
3521  top.TYPO3.Backend.NavigationContainer.PageTree.refreshTree();
3522  }
3523  ';
3524  break;
3525  case 'updateFolderTree':
3526  $signals[] = '
3527  if (top && top.nav_frame && top.nav_frame.location) {
3528  top.nav_frame.location.reload(true);
3529  }';
3530  break;
3531  case 'updateModuleMenu':
3532  $signals[] = '
3533  if (top && top.TYPO3.ModuleMenu && top.TYPO3.ModuleMenu.App) {
3534  top.TYPO3.ModuleMenu.App.refreshMenu();
3535  }';
3536  }
3537  }
3538  }
3539  $content = implode(LF, $signals);
3540  // For backwards compatibility, should be replaced
3541  self::setUpdateSignal();
3542  return $content;
3543  }
3544 
3559  public static function getModuleData(
3560  $MOD_MENU,
3561  $CHANGED_SETTINGS,
3562  $modName,
3563  $type = '',
3564  $dontValidateList = '',
3565  $setDefaultList = ''
3566  ) {
3567  if ($modName && is_string($modName)) {
3568  // Getting stored user-data from this module:
3569  $beUser = static::getBackendUserAuthentication();
3570  $settings = $beUser->getModuleData($modName, $type);
3571  $changed = 0;
3572  if (!is_array($settings)) {
3573  $changed = 1;
3574  $settings = [];
3575  }
3576  if (is_array($MOD_MENU)) {
3577  foreach ($MOD_MENU as $key => $var) {
3578  // If a global var is set before entering here. eg if submitted, then it's substituting the current value the array.
3579  if (is_array($CHANGED_SETTINGS) && isset($CHANGED_SETTINGS[$key])) {
3580  if (is_array($CHANGED_SETTINGS[$key])) {
3581  $serializedSettings = serialize($CHANGED_SETTINGS[$key]);
3582  if ((string)$settings[$key] !== $serializedSettings) {
3583  $settings[$key] = $serializedSettings;
3584  $changed = 1;
3585  }
3586  } else {
3587  if ((string)$settings[$key] !== (string)$CHANGED_SETTINGS[$key]) {
3588  $settings[$key] = $CHANGED_SETTINGS[$key];
3589  $changed = 1;
3590  }
3591  }
3592  }
3593  // If the $var is an array, which denotes the existence of a menu, we check if the value is permitted
3594  if (is_array($var) && (!$dontValidateList || !GeneralUtility::inList($dontValidateList, $key))) {
3595  // If the setting is an array or not present in the menu-array, MOD_MENU, then the default value is inserted.
3596  if (is_array($settings[$key]) || !isset($MOD_MENU[$key][$settings[$key]])) {
3597  $settings[$key] = (string)key($var);
3598  $changed = 1;
3599  }
3600  }
3601  // Sets default values (only strings/checkboxes, not menus)
3602  if ($setDefaultList && !is_array($var)) {
3603  if (GeneralUtility::inList($setDefaultList, $key) && !isset($settings[$key])) {
3604  $settings[$key] = (string)$var;
3605  }
3606  }
3607  }
3608  } else {
3609  die('No menu!');
3610  }
3611  if ($changed) {
3612  $beUser->pushModuleData($modName, $settings);
3613  }
3614  return $settings;
3615  } else {
3616  die('Wrong module name: "' . $modName . '"');
3617  }
3618  }
3619 
3627  public static function getModuleUrl($moduleName, $urlParameters = [])
3628  {
3630  $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
3631  try {
3632  $uri = $uriBuilder->buildUriFromRoute($moduleName, $urlParameters);
3633  } catch (\TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException $e) {
3634  // no route registered, use the fallback logic to check for a module
3635  $uri = $uriBuilder->buildUriFromModule($moduleName, $urlParameters);
3636  }
3637  return (string)$uri;
3638  }
3639 
3652  public static function getAjaxUrl($ajaxIdentifier, array $urlParameters = [])
3653  {
3656  $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
3657  try {
3658  $routeIdentifier = 'ajax_' . $ajaxIdentifier;
3659  $uri = $uriBuilder->buildUriFromRoute($routeIdentifier, $urlParameters);
3660  } catch (\TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException $e) {
3661  // no route registered, use the fallback logic to check for a module
3662  $uri = $uriBuilder->buildUriFromAjaxId($ajaxIdentifier, $urlParameters);
3663  }
3664  return (string)$uri;
3665  }
3666 
3676  public static function getListViewLink($urlParameters = [], $linkTitle = '', $linkText = '')
3677  {
3680  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
3681  return '<a href="'
3682  . htmlspecialchars(self::getModuleUrl('web_list', $urlParameters)) . '" title="'
3683  . htmlspecialchars($linkTitle) . '">'
3684  . $iconFactory->getIcon('actions-system-list-open', Icon::SIZE_SMALL)->render()
3685  . htmlspecialchars($linkText) . '</a>';
3686  }
3687 
3688  /*******************************************
3689  *
3690  * Core
3691  *
3692  *******************************************/
3703  public static function lockRecords($table = '', $uid = 0, $pid = 0)
3704  {
3705  $beUser = static::getBackendUserAuthentication();
3706  if (isset($beUser->user['uid'])) {
3707  $userId = (int)$beUser->user['uid'];
3708  if ($table && $uid) {
3709  $fieldsValues = [
3710  'userid' => $userId,
3711  'feuserid' => 0,
3712  'tstamp' => $GLOBALS['EXEC_TIME'],
3713  'record_table' => $table,
3714  'record_uid' => $uid,
3715  'username' => $beUser->user['username'],
3716  'record_pid' => $pid
3717  ];
3718  GeneralUtility::makeInstance(ConnectionPool::class)
3719  ->getConnectionForTable('sys_lockedrecords')
3720  ->insert(
3721  'sys_lockedrecords',
3722  $fieldsValues
3723  );
3724  } else {
3725  GeneralUtility::makeInstance(ConnectionPool::class)
3726  ->getConnectionForTable('sys_lockedrecords')
3727  ->delete(
3728  'sys_lockedrecords',
3729  ['userid' => (int)$userId]
3730  );
3731  }
3732  }
3733  }
3734 
3747  public static function isRecordLocked($table, $uid)
3748  {
3749  if (!is_array($GLOBALS['LOCKED_RECORDS'])) {
3750  $GLOBALS['LOCKED_RECORDS'] = [];
3751 
3752  $queryBuilder = static::getQueryBuilderForTable('sys_lockedrecords');
3753  $result = $queryBuilder
3754  ->select('*')
3755  ->from('sys_lockedrecords')
3756  ->where(
3757  $queryBuilder->expr()->neq(
3758  'sys_lockedrecords.userid',
3759  $queryBuilder->createNamedParameter(
3760  static::getBackendUserAuthentication()->user['uid'],
3761  \PDO::PARAM_INT
3762  )
3763  ),
3764  $queryBuilder->expr()->gt(
3765  'sys_lockedrecords.tstamp',
3766  $queryBuilder->createNamedParameter(
3767  ($GLOBALS['EXEC_TIME'] - 2 * 3600),
3768  \PDO::PARAM_INT
3769  )
3770  )
3771  )
3772  ->execute();
3773 
3774  while ($row = $result->fetch()) {
3775  // Get the type of the user that locked this record:
3776  if ($row['userid']) {
3777  $userTypeLabel = 'beUser';
3778  } elseif ($row['feuserid']) {
3779  $userTypeLabel = 'feUser';
3780  } else {
3781  $userTypeLabel = 'user';
3782  }
3783  $lang = static::getLanguageService();
3784  $userType = $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.' . $userTypeLabel);
3785  // Get the username (if available):
3786  if ($row['username']) {
3787  $userName = $row['username'];
3788  } else {
3789  $userName = $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.unknownUser');
3790  }
3791  $GLOBALS['LOCKED_RECORDS'][$row['record_table'] . ':' . $row['record_uid']] = $row;
3792  $GLOBALS['LOCKED_RECORDS'][$row['record_table'] . ':' . $row['record_uid']]['msg'] = sprintf(
3793  $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.lockedRecordUser'),
3794  $userType,
3795  $userName,
3796  self::calcAge(
3797  $GLOBALS['EXEC_TIME'] - $row['tstamp'],
3798  $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.minutesHoursDaysYears')
3799  )
3800  );
3801  if ($row['record_pid'] && !isset($GLOBALS['LOCKED_RECORDS'][$row['record_table'] . ':' . $row['record_pid']])) {
3802  $GLOBALS['LOCKED_RECORDS']['pages:' . $row['record_pid']]['msg'] = sprintf(
3803  $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.lockedRecordUser_content'),
3804  $userType,
3805  $userName,
3806  self::calcAge(
3807  $GLOBALS['EXEC_TIME'] - $row['tstamp'],
3808  $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.minutesHoursDaysYears')
3809  )
3810  );
3811  }
3812  }
3813  }
3814  return $GLOBALS['LOCKED_RECORDS'][$table . ':' . $uid];
3815  }
3816 
3825  public static function getTCEFORM_TSconfig($table, $row)
3826  {
3827  self::fixVersioningPid($table, $row);
3828  $res = [];
3829  // Get main config for the table
3830  list($TScID, $cPid) = self::getTSCpid($table, $row['uid'], $row['pid']);
3831  if ($TScID >= 0) {
3832  $tempConf = static::getBackendUserAuthentication()->getTSConfig(
3833  'TCEFORM.' . $table,
3834  self::getPagesTSconfig($TScID)
3835  );
3836  if (is_array($tempConf['properties'])) {
3837  $typeVal = self::getTCAtypeValue($table, $row);
3838  foreach ($tempConf['properties'] as $key => $val) {
3839  if (is_array($val)) {
3840  $fieldN = substr($key, 0, -1);
3841  $res[$fieldN] = $val;
3842  unset($res[$fieldN]['types.']);
3843  if ((string)$typeVal !== '' && is_array($val['types.'][$typeVal . '.'])) {
3844  ArrayUtility::mergeRecursiveWithOverrule($res[$fieldN], $val['types.'][$typeVal . '.']);
3845  }
3846  }
3847  }
3848  }
3849  }
3850  $res['_CURRENT_PID'] = $cPid;
3851  $res['_THIS_UID'] = $row['uid'];
3852  // So the row will be passed to foreign_table_where_query()
3853  $res['_THIS_ROW'] = $row;
3854  return $res;
3855  }
3856 
3870  public static function getTSconfig_pidValue($table, $uid, $pid)
3871  {
3872  // If pid is an integer this takes precedence in our lookup.
3874  $thePidValue = (int)$pid;
3875  // If ref to another record, look that record up.
3876  if ($thePidValue < 0) {
3877  $pidRec = self::getRecord($table, abs($thePidValue), 'pid');
3878  $thePidValue = is_array($pidRec) ? $pidRec['pid'] : -2;
3879  }
3880  } else {
3881  // Try to fetch the record pid from uid. If the uid is 'NEW...' then this will of course return nothing
3882  $rr = self::getRecord($table, $uid);
3883  $thePidValue = null;
3884  if (is_array($rr)) {
3885  // First check if the pid is -1 which means it is a workspaced element. Get the "real" record:
3886  if ($rr['pid'] == '-1') {
3887  $rr = self::getRecord($table, $rr['t3ver_oid'], 'pid');
3888  if (is_array($rr)) {
3889  $thePidValue = $rr['pid'];
3890  }
3891  } else {
3892  // Returning the "pid" of the record
3893  $thePidValue = $rr['pid'];
3894  }
3895  }
3896  if (!$thePidValue) {
3897  // Returns -1 if the record with this pid was not found.
3898  $thePidValue = -1;
3899  }
3900  }
3901  return $thePidValue;
3902  }
3903 
3913  public static function getPidForModTSconfig($table, $uid, $pid)
3914  {
3915  return $table === 'pages' && MathUtility::canBeInterpretedAsInteger($uid) ? $uid : $pid;
3916  }
3917 
3930  public static function getTSCpidCached($table, $uid, $pid)
3931  {
3932  // A local first level cache
3933  static $firstLevelCache;
3934 
3935  if (!is_array($firstLevelCache)) {
3936  $firstLevelCache = [];
3937  }
3938 
3939  $key = $table . ':' . $uid . ':' . $pid;
3940  if (!isset($firstLevelCache[$key])) {
3941  $firstLevelCache[$key] = static::getTSCpid($table, $uid, $pid);
3942  }
3943  return $firstLevelCache[$key];
3944  }
3945 
3957  public static function getTSCpid($table, $uid, $pid)
3958  {
3959  // If pid is negative (referring to another record) the pid of the other record is fetched and returned.
3960  $cPid = self::getTSconfig_pidValue($table, $uid, $pid);
3961  // $TScID is the id of $table = pages, else it's the pid of the record.
3962  $TScID = self::getPidForModTSconfig($table, $uid, $cPid);
3963  return [$TScID, $cPid];
3964  }
3965 
3972  public static function firstDomainRecord($rootLine)
3973  {
3974  $queryBuilder = static::getQueryBuilderForTable('sys_domain');
3975  $constraint = $queryBuilder->expr()->andX(
3976  $queryBuilder->expr()->eq(
3977  'redirectTo',
3978  $queryBuilder->createNamedParameter('', \PDO::PARAM_STR)
3979  ),
3980  $queryBuilder->expr()->eq(
3981  'hidden',
3982  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
3983  )
3984  );
3985  foreach ($rootLine as $row) {
3986  $dRec = self::getRecordsByField(
3987  'sys_domain',
3988  'pid',
3989  $row['uid'],
3990  (string)$constraint,
3991  '',
3992  'sorting',
3993  '',
3994  true,
3995  $queryBuilder
3996  );
3997  if (is_array($dRec)) {
3998  $dRecord = reset($dRec);
3999  return rtrim($dRecord['domainName'], '/');
4000  }
4001  }
4002  return null;
4003  }
4004 
4012  public static function getDomainStartPage($domain, $path = '')
4013  {
4014  $domain = explode(':', $domain);
4015  $domain = strtolower(preg_replace('/\\.$/', '', $domain[0]));
4016  // Path is calculated.
4017  $path = trim(preg_replace('/\\/[^\\/]*$/', '', $path));
4018  // Stuff
4019  $domain .= $path;
4020 
4021  $queryBuilder = static::getQueryBuilderForTable('sys_domain');
4022  $queryBuilder->getRestrictions()
4023  ->removeAll()
4024  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
4025 
4026  $result = $queryBuilder
4027  ->select('sys_domain.*')
4028  ->from('sys_domain')
4029  ->from('pages')
4030  ->where(
4031  $queryBuilder->expr()->eq(
4032  'sys_domain.pid',
4033  $queryBuilder->quoteIdentifier('pages.uid')
4034  ),
4035  $queryBuilder->expr()->orX(
4036  $queryBuilder->expr()->eq(
4037  'sys_domain.domainName',
4038  $queryBuilder->createNamedParameter($domain, \PDO::PARAM_STR)
4039  ),
4040  $queryBuilder->expr()->eq(
4041  'sys_domain.domainName',
4042  $queryBuilder->createNamedParameter($domain . '/', \PDO::PARAM_STR)
4043  )
4044  )
4045 
4046  )
4047  ->execute()
4048  ->fetch();
4049 
4050  return $result;
4051  }
4052 
4064  public static function RTEsetup($RTEprop, $table, $field, $type = '')
4065  {
4066  $thisConfig = is_array($RTEprop['default.']) ? $RTEprop['default.'] : [];
4067  $thisFieldConf = $RTEprop['config.'][$table . '.'][$field . '.'];
4068  if (is_array($thisFieldConf)) {
4069  unset($thisFieldConf['types.']);
4070  ArrayUtility::mergeRecursiveWithOverrule($thisConfig, $thisFieldConf);
4071  }
4072  if ($type && is_array($RTEprop['config.'][$table . '.'][$field . '.']['types.'][$type . '.'])) {
4074  $thisConfig,
4075  $RTEprop['config.'][$table . '.'][$field . '.']['types.'][$type . '.']
4076  );
4077  }
4078  return $thisConfig;
4079  }
4080 
4088  public static function &softRefParserObj($spKey)
4089  {
4090  // If no softRef parser object has been set previously, try to create it:
4091  if (!isset($GLOBALS['T3_VAR']['softRefParser'][$spKey])) {
4092  // Set the object string to blank by default:
4093  $GLOBALS['T3_VAR']['softRefParser'][$spKey] = '';
4094  // Now, try to create parser object:
4095  $objRef = null;
4096  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['softRefParser'][$spKey])) {
4097  $objRef = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['softRefParser'][$spKey];
4098  if ($objRef) {
4099  $softRefParserObj = GeneralUtility::getUserObj($objRef);
4100  if (is_object($softRefParserObj)) {
4101  $GLOBALS['T3_VAR']['softRefParser'][$spKey] = $softRefParserObj;
4102  }
4103  }
4104  }
4105  }
4106  // Return RTE object (if any!)
4107  return $GLOBALS['T3_VAR']['softRefParser'][$spKey];
4108  }
4109 
4115  protected static function getRuntimeCache()
4116  {
4117  return GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_runtime');
4118  }
4119 
4127  public static function explodeSoftRefParserList($parserList)
4128  {
4129  // Return immediately if list is blank:
4130  if ((string)$parserList === '') {
4131  return false;
4132  }
4133 
4134  $runtimeCache = self::getRuntimeCache();
4135  $cacheId = 'backend-softRefList-' . md5($parserList);
4136  $parserListCache = $runtimeCache->get($cacheId);
4137  if ($parserListCache !== false) {
4138  return $parserListCache;
4139  }
4140 
4141  // Otherwise parse the list:
4142  $keyList = GeneralUtility::trimExplode(',', $parserList, true);
4143  $output = [];
4144  foreach ($keyList as $val) {
4145  $reg = [];
4146  if (preg_match('/^([[:alnum:]_-]+)\\[(.*)\\]$/', $val, $reg)) {
4147  $output[$reg[1]] = GeneralUtility::trimExplode(';', $reg[2], true);
4148  } else {
4149  $output[$val] = '';
4150  }
4151  }
4152  $runtimeCache->set($cacheId, $output);
4153  return $output;
4154  }
4155 
4162  public static function isModuleSetInTBE_MODULES($modName)
4163  {
4164  $loaded = [];
4165  foreach ($GLOBALS['TBE_MODULES'] as $mkey => $list) {
4166  $loaded[$mkey] = 1;
4167  if (!is_array($list) && trim($list)) {
4168  $subList = GeneralUtility::trimExplode(',', $list, true);
4169  foreach ($subList as $skey) {
4170  $loaded[$mkey . '_' . $skey] = 1;
4171  }
4172  }
4173  }
4174  return $modName && isset($loaded[$modName]);
4175  }
4176 
4186  public static function referenceCount($table, $ref, $msg = '', $count = null)
4187  {
4188  if ($count === null) {
4189 
4190  // Build base query
4191  $queryBuilder = static::getQueryBuilderForTable('sys_refindex');
4192  $queryBuilder
4193  ->count('*')
4194  ->from('sys_refindex')
4195  ->where(
4196  $queryBuilder->expr()->eq('ref_table', $queryBuilder->createNamedParameter($table, \PDO::PARAM_STR)),
4197  $queryBuilder->expr()->eq('deleted', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT))
4198  );
4199 
4200  // Look up the path:
4201  if ($table == '_FILE') {
4202  if (!GeneralUtility::isFirstPartOfStr($ref, PATH_site)) {
4203  return '';
4204  }
4205 
4206  $ref = PathUtility::stripPathSitePrefix($ref);
4207  $queryBuilder->andWhere(
4208  $queryBuilder->expr()->eq('ref_string', $queryBuilder->createNamedParameter($ref, \PDO::PARAM_STR))
4209  );
4210  } else {
4211  $queryBuilder->andWhere(
4212  $queryBuilder->expr()->eq('ref_uid', $queryBuilder->createNamedParameter($ref, \PDO::PARAM_INT))
4213  );
4214  if ($table === 'sys_file') {
4215  $queryBuilder->andWhere($queryBuilder->expr()->neq('tablename', $queryBuilder->quote('sys_file_metadata')));
4216  }
4217  }
4218 
4219  $count = $queryBuilder->execute()->fetchColumn(0);
4220  }
4221 
4222  if ($count) {
4223  return $msg ? sprintf($msg, $count) : $count;
4224  } else {
4225  return $msg ? '' : 0;
4226  }
4227  }
4228 
4237  public static function translationCount($table, $ref, $msg = '')
4238  {
4239  $count = null;
4240  if ($table !== 'pages'
4241  && $GLOBALS['TCA'][$table]['ctrl']['languageField']
4242  && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']
4243  && $table !== 'pages_language_overlay'
4244  ) {
4245  $queryBuilder = static::getQueryBuilderForTable($table);
4246  $queryBuilder->getRestrictions()
4247  ->removeAll()
4248  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
4249 
4250  $count = (int)$queryBuilder
4251  ->count('*')
4252  ->from($table)
4253  ->where(
4254  $queryBuilder->expr()->eq(
4255  $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'],
4256  $queryBuilder->createNamedParameter($ref, \PDO::PARAM_INT)
4257  ),
4258  $queryBuilder->expr()->neq(
4259  $GLOBALS['TCA'][$table]['ctrl']['languageField'],
4260  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
4261  )
4262  )
4263  ->execute()
4264  ->fetchColumn(0);
4265  }
4266 
4267  if ($count && $msg) {
4268  return sprintf($msg, $count);
4269  }
4270 
4271  if ($count) {
4272  return $msg ? sprintf($msg, $count) : $count;
4273  } else {
4274  return $msg ? '' : 0;
4275  }
4276  }
4277 
4278  /*******************************************
4279  *
4280  * Workspaces / Versioning
4281  *
4282  *******************************************/
4294  public static function selectVersionsOfRecord(
4295  $table,
4296  $uid,
4297  $fields = '*',
4298  $workspace = 0,
4299  $includeDeletedRecords = false,
4300  $row = null
4301  ) {
4302  $realPid = 0;
4303  $outputRows = [];
4304  if ($GLOBALS['TCA'][$table] && static::isTableWorkspaceEnabled($table)) {
4305  if (is_array($row) && !$includeDeletedRecords) {
4306  $row['_CURRENT_VERSION'] = true;
4307  $realPid = $row['pid'];
4308  $outputRows[] = $row;
4309  } else {
4310  // Select UID version:
4311  $row = self::getRecord($table, $uid, $fields, '', !$includeDeletedRecords);
4312  // Add rows to output array:
4313  if ($row) {
4314  $row['_CURRENT_VERSION'] = true;
4315  $realPid = $row['pid'];
4316  $outputRows[] = $row;
4317  }
4318  }
4319 
4320  $queryBuilder = static::getQueryBuilderForTable($table);
4321  $queryBuilder->getRestrictions()->removeAll();
4322 
4323  // build fields to select
4324  $queryBuilder->select(...GeneralUtility::trimExplode(',', $fields));
4325 
4326  $queryBuilder
4327  ->from($table)
4328  ->where(
4329  $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)),
4330  $queryBuilder->expr()->neq('uid', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)),
4331  $queryBuilder->expr()->eq('t3ver_oid', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT))
4332  )
4333  ->orderBy('t3ver_id', 'DESC');
4334 
4335  if (!$includeDeletedRecords) {
4336  $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
4337  }
4338 
4339  if ($workspace === 0) {
4340  // Only in Live WS
4341  $queryBuilder->andWhere(
4342  $queryBuilder->expr()->eq(
4343  't3ver_wsid',
4344  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
4345  )
4346  );
4347  } elseif ($workspace !== null) {
4348  // In Live WS and Workspace with given ID
4349  $queryBuilder->andWhere(
4350  $queryBuilder->expr()->in(
4351  't3ver_wsid',
4352  $queryBuilder->createNamedParameter([0, (int)$workspace], Connection::PARAM_INT_ARRAY)
4353  )
4354  );
4355  }
4356 
4357  $rows = $queryBuilder->execute()->fetchAll();
4358 
4359  // Add rows to output array:
4360  if (is_array($rows)) {
4361  $outputRows = array_merge($outputRows, $rows);
4362  }
4363  // Set real-pid:
4364  foreach ($outputRows as $idx => $oRow) {
4365  $outputRows[$idx]['_REAL_PID'] = $realPid;
4366  }
4367  return $outputRows;
4368  }
4369  return null;
4370  }
4371 
4390  public static function fixVersioningPid($table, &$rr, $ignoreWorkspaceMatch = false)
4391  {
4392  if (!ExtensionManagementUtility::isLoaded('version')) {
4393  return;
4394  }
4395  // Check that the input record is an offline version from a table that supports versioning:
4396  if (is_array($rr) && $rr['pid'] == -1 && static::isTableWorkspaceEnabled($table)) {
4397  // Check values for t3ver_oid and t3ver_wsid:
4398  if (isset($rr['t3ver_oid']) && isset($rr['t3ver_wsid'])) {
4399  // If "t3ver_oid" is already a field, just set this:
4400  $oid = $rr['t3ver_oid'];
4401  $wsid = $rr['t3ver_wsid'];
4402  } else {
4403  $oid = 0;
4404  $wsid = 0;
4405  // Otherwise we have to expect "uid" to be in the record and look up based on this:
4406  $newPidRec = self::getRecord($table, $rr['uid'], 't3ver_oid,t3ver_wsid');
4407  if (is_array($newPidRec)) {
4408  $oid = $newPidRec['t3ver_oid'];
4409  $wsid = $newPidRec['t3ver_wsid'];
4410  }
4411  }
4412  // If ID of current online version is found, look up the PID value of that:
4413  if ($oid
4414  && ($ignoreWorkspaceMatch || (int)$wsid === (int)static::getBackendUserAuthentication()->workspace)
4415  ) {
4416  $oidRec = self::getRecord($table, $oid, 'pid');
4417  if (is_array($oidRec)) {
4418  $rr['_ORIG_pid'] = $rr['pid'];
4419  $rr['pid'] = $oidRec['pid'];
4420  }
4421  // Use target PID in case of move pointer
4422  if (
4423  !isset($rr['t3ver_state'])
4424  || VersionState::cast($rr['t3ver_state'])->equals(VersionState::MOVE_POINTER)
4425  ) {
4426  $movePlaceholder = self::getMovePlaceholder($table, $oid, 'pid');
4427  if ($movePlaceholder) {
4428  $rr['_ORIG_pid'] = $rr['pid'];
4429  $rr['pid'] = $movePlaceholder['pid'];
4430  }
4431  }
4432  }
4433  }
4434  }
4435 
4453  public static function workspaceOL($table, &$row, $wsid = -99, $unsetMovePointers = false)
4454  {
4455  if (!ExtensionManagementUtility::isLoaded('version')) {
4456  return;
4457  }
4458  // If this is FALSE the placeholder is shown raw in the backend.
4459  // I don't know if this move can be useful for users to toggle. Technically it can help debugging.
4460  $previewMovePlaceholders = true;
4461  // Initialize workspace ID
4462  if ($wsid == -99) {
4463  $wsid = static::getBackendUserAuthentication()->workspace;
4464  }
4465  // Check if workspace is different from zero and record is set:
4466  if ($wsid !== 0 && is_array($row)) {
4467  // Check if input record is a move-placeholder and if so, find the pointed-to live record:
4468  $movePldSwap = null;
4469  $orig_uid = 0;
4470  $orig_pid = 0;
4471  if ($previewMovePlaceholders) {
4472  $orig_uid = $row['uid'];
4473  $orig_pid = $row['pid'];
4474  $movePldSwap = self::movePlhOL($table, $row);
4475  }
4476  $wsAlt = self::getWorkspaceVersionOfRecord($wsid, $table, $row['uid'], implode(',', array_keys($row)));
4477  // If version was found, swap the default record with that one.
4478  if (is_array($wsAlt)) {
4479  // Check if this is in move-state:
4480  if ($previewMovePlaceholders && !$movePldSwap && static::isTableWorkspaceEnabled($table) && $unsetMovePointers) {
4481  // Only for WS ver 2... (moving)
4482  // If t3ver_state is not found, then find it... (but we like best if it is here...)
4483  if (!isset($wsAlt['t3ver_state'])) {
4484  $stateRec = self::getRecord($table, $wsAlt['uid'], 't3ver_state');
4485  $versionState = VersionState::cast($stateRec['t3ver_state']);
4486  } else {
4487  $versionState = VersionState::cast($wsAlt['t3ver_state']);
4488  }
4489  if ($versionState->equals(VersionState::MOVE_POINTER)) {
4490  // @todo Same problem as frontend in versionOL(). See TODO point there.
4491  $row = false;
4492  return;
4493  }
4494  }
4495  // Always correct PID from -1 to what it should be
4496  if (isset($wsAlt['pid'])) {
4497  // Keep the old (-1) - indicates it was a version.
4498  $wsAlt['_ORIG_pid'] = $wsAlt['pid'];
4499  // Set in the online versions PID.
4500  $wsAlt['pid'] = $row['pid'];
4501  }
4502  // For versions of single elements or page+content, swap UID and PID
4503  $wsAlt['_ORIG_uid'] = $wsAlt['uid'];
4504  $wsAlt['uid'] = $row['uid'];
4505  // Backend css class:
4506  $wsAlt['_CSSCLASS'] = 'ver-element';
4507  // Changing input record to the workspace version alternative:
4508  $row = $wsAlt;
4509  }
4510  // If the original record was a move placeholder, the uid and pid of that is preserved here:
4511  if ($movePldSwap) {
4512  $row['_MOVE_PLH'] = true;
4513  $row['_MOVE_PLH_uid'] = $orig_uid;
4514  $row['_MOVE_PLH_pid'] = $orig_pid;
4515  // For display; To make the icon right for the placeholder vs. the original
4516  $row['t3ver_state'] = (string)new VersionState(VersionState::MOVE_PLACEHOLDER);
4517  }
4518  }
4519  }
4520 
4530  public static function movePlhOL($table, &$row)
4531  {
4532  if (static::isTableWorkspaceEnabled($table)) {
4533  // If t3ver_move_id or t3ver_state is not found, then find it... (but we like best if it is here...)
4534  if (!isset($row['t3ver_move_id']) || !isset($row['t3ver_state'])) {
4535  $moveIDRec = self::getRecord($table, $row['uid'], 't3ver_move_id, t3ver_state');
4536  $moveID = $moveIDRec['t3ver_move_id'];
4537  $versionState = VersionState::cast($moveIDRec['t3ver_state']);
4538  } else {
4539  $moveID = $row['t3ver_move_id'];
4540  $versionState = VersionState::cast($row['t3ver_state']);
4541  }
4542  // Find pointed-to record.
4543  if ($versionState->equals(VersionState::MOVE_PLACEHOLDER) && $moveID) {
4544  if ($origRow = self::getRecord($table, $moveID, implode(',', array_keys($row)))) {
4545  $row = $origRow;
4546  return true;
4547  }
4548  }
4549  }
4550  return false;
4551  }
4552 
4562  public static function getWorkspaceVersionOfRecord($workspace, $table, $uid, $fields = '*')
4563  {
4564  if (ExtensionManagementUtility::isLoaded('version')) {
4565  if ($workspace !== 0 && $GLOBALS['TCA'][$table] && self::isTableWorkspaceEnabled($table)) {
4566 
4567  // Select workspace version of record:
4568  $queryBuilder = static::getQueryBuilderForTable($table);
4569  $queryBuilder->getRestrictions()
4570  ->removeAll()
4571  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
4572 
4573  // build fields to select
4574  $queryBuilder->select(...GeneralUtility::trimExplode(',', $fields));
4575 
4576  $row = $queryBuilder
4577  ->from($table)
4578  ->where(
4579  $queryBuilder->expr()->eq(
4580  'pid',
4581  $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)
4582  ),
4583  $queryBuilder->expr()->eq(
4584  't3ver_oid',
4585  $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
4586  ),
4587  $queryBuilder->expr()->eq(
4588  't3ver_wsid',
4589  $queryBuilder->createNamedParameter($workspace, \PDO::PARAM_INT)
4590  )
4591  )
4592  ->execute()
4593  ->fetch();
4594 
4595  return $row;
4596  }
4597  }
4598  return false;
4599  }
4600 
4609  public static function getLiveVersionOfRecord($table, $uid, $fields = '*')
4610  {
4611  $liveVersionId = self::getLiveVersionIdOfRecord($table, $uid);
4612  if ($liveVersionId !== null) {
4613  return self::getRecord($table, $liveVersionId, $fields);
4614  }
4615  return null;
4616  }
4617 
4625  public static function getLiveVersionIdOfRecord($table, $uid)
4626  {
4627  $liveVersionId = null;
4628  if (self::isTableWorkspaceEnabled($table)) {
4629  $currentRecord = self::getRecord($table, $uid, 'pid,t3ver_oid');
4630  if (is_array($currentRecord) && $currentRecord['pid'] == -1) {
4631  $liveVersionId = $currentRecord['t3ver_oid'];
4632  }
4633  }
4634  return $liveVersionId;
4635  }
4636 
4644  public static function versioningPlaceholderClause($table)
4645  {
4646  if (static::isTableWorkspaceEnabled($table)) {
4647  $currentWorkspace = (int)static::getBackendUserAuthentication()->workspace;
4648  return ' AND (' . $table . '.t3ver_state <= ' . new VersionState(VersionState::DEFAULT_STATE) . ' OR ' . $table . '.t3ver_wsid = ' . $currentWorkspace . ')';
4649  }
4650  return '';
4651  }
4652 
4660  public static function getWorkspaceWhereClause($table, $workspaceId = null)
4661  {
4662  $whereClause = '';
4663  if (self::isTableWorkspaceEnabled($table)) {
4664  if (is_null($workspaceId)) {
4665  $workspaceId = static::getBackendUserAuthentication()->workspace;
4666  }
4667  $workspaceId = (int)$workspaceId;
4668  $pidOperator = $workspaceId === 0 ? '!=' : '=';
4669  $whereClause = ' AND ' . $table . '.t3ver_wsid=' . $workspaceId . ' AND ' . $table . '.pid' . $pidOperator . '-1';
4670  }
4671  return $whereClause;
4672  }
4673 
4681  public static function wsMapId($table, $uid)
4682  {
4683  $wsRec = self::getWorkspaceVersionOfRecord(
4684  static::getBackendUserAuthentication()->workspace,
4685  $table,
4686  $uid,
4687  'uid'
4688  );
4689  return is_array($wsRec) ? $wsRec['uid'] : $uid;
4690  }
4691 
4701  public static function getMovePlaceholder($table, $uid, $fields = '*', $workspace = null)
4702  {
4703  if ($workspace === null) {
4704  $workspace = static::getBackendUserAuthentication()->workspace;
4705  }
4706  if ((int)$workspace !== 0 && $GLOBALS['TCA'][$table] && static::isTableWorkspaceEnabled($table)) {
4707  // Select workspace version of record:
4708  $queryBuilder = static::getQueryBuilderForTable($table);
4709  $queryBuilder->getRestrictions()
4710  ->removeAll()
4711  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
4712 
4713  $row = $queryBuilder
4714  ->select(...GeneralUtility::trimExplode(',', $fields, true))
4715  ->from($table)
4716  ->where(
4717  $queryBuilder->expr()->neq(
4718  'pid',
4719  $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)
4720  ),
4721  $queryBuilder->expr()->eq(
4722  't3ver_state',
4723  $queryBuilder->createNamedParameter(
4725  \PDO::PARAM_INT
4726  )
4727  ),
4728  $queryBuilder->expr()->eq(
4729  't3ver_move_id',
4730  $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
4731  ),
4732  $queryBuilder->expr()->eq(
4733  't3ver_wsid',
4734  $queryBuilder->createNamedParameter($workspace, \PDO::PARAM_INT)
4735  )
4736  )
4737  ->execute()
4738  ->fetch();
4739 
4740  return $row ?: false;
4741  }
4742  return false;
4743  }
4744 
4745  /*******************************************
4746  *
4747  * Miscellaneous
4748  *
4749  *******************************************/
4760  public static function TYPO3_copyRightNotice()
4761  {
4762  // Copyright Notice
4763  $loginCopyrightWarrantyProvider = strip_tags(trim($GLOBALS['TYPO3_CONF_VARS']['SYS']['loginCopyrightWarrantyProvider']));
4764  $loginCopyrightWarrantyURL = strip_tags(trim($GLOBALS['TYPO3_CONF_VARS']['SYS']['loginCopyrightWarrantyURL']));
4765 
4766  $lang = static::getLanguageService();
4767 
4768  if (strlen($loginCopyrightWarrantyProvider) >= 2 && strlen($loginCopyrightWarrantyURL) >= 10) {
4769  $warrantyNote = sprintf(
4770  $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_login.xlf:warranty.by'),
4771  htmlspecialchars($loginCopyrightWarrantyProvider),
4772  '<a href="' . htmlspecialchars($loginCopyrightWarrantyURL) . '" target="_blank">',
4773  '</a>'
4774  );
4775  } else {
4776  $warrantyNote = sprintf(
4777  $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_login.xlf:no.warranty'),
4778  '<a href="' . TYPO3_URL_LICENSE . '" target="_blank">',
4779  '</a>'
4780  );
4781  }
4782  $cNotice = '<a href="' . TYPO3_URL_GENERAL . '" target="_blank">' .
4783  $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_login.xlf:typo3.cms') . '</a>. ' .
4784  $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_login.xlf:copyright') . ' &copy; '
4785  . htmlspecialchars(TYPO3_copyright_year) . ' Kasper Sk&aring;rh&oslash;j. ' .
4786  $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_login.xlf:extension.copyright') . ' ' .
4787  sprintf(
4788  $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_login.xlf:details.link'),
4789  ('<a href="' . TYPO3_URL_GENERAL . '" target="_blank">' . TYPO3_URL_GENERAL . '</a>')
4790  ) . ' ' .
4791  strip_tags($warrantyNote, '<a>') . ' ' .
4792  sprintf(
4793  $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_login.xlf:free.software'),
4794  ('<a href="' . TYPO3_URL_LICENSE . '" target="_blank">'),
4795  '</a> ')
4796  . $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_login.xlf:keep.notice');
4797  return $cNotice;
4798  }
4799 
4807  public static function ADMCMD_previewCmds($pageInfo)
4808  {
4809  $simUser = '';
4810  $simTime = '';
4811  if ($pageInfo['fe_group'] > 0) {
4812  $simUser = '&ADMCMD_simUser=' . $pageInfo['fe_group'];
4813  } elseif ((int)$pageInfo['fe_group'] === -2) {
4814  // -2 means "show at any login". We simulate first available fe_group.
4816  $sysPage = GeneralUtility::makeInstance(PageRepository::class);
4817  $activeFeGroupRow = self::getRecordRaw(
4818  'fe_groups',
4819  '1=1' . $sysPage->enableFields('fe_groups'),
4820  'uid'
4821  );
4822  if (!empty($activeFeGroupRow)) {
4823  $simUser = '&ADMCMD_simUser=' . $activeFeGroupRow['uid'];
4824  }
4825  }
4826  if ($pageInfo['starttime'] > $GLOBALS['EXEC_TIME']) {
4827  $simTime = '&ADMCMD_simTime=' . $pageInfo['starttime'];
4828  }
4829  if ($pageInfo['endtime'] < $GLOBALS['EXEC_TIME'] && $pageInfo['endtime'] != 0) {
4830  $simTime = '&ADMCMD_simTime=' . ($pageInfo['endtime'] - 1);
4831  }
4832  return $simUser . $simTime;
4833  }
4834 
4844  public static function processParams($params)
4845  {
4847  $paramArr = [];
4848  $lines = explode(LF, $params);
4849  foreach ($lines as $val) {
4850  $val = trim($val);
4851  if ($val) {
4852  $pair = explode('=', $val, 2);
4853  $paramArr[trim($pair[0])] = trim($pair[1]);
4854  }
4855  }
4856  return $paramArr;
4857  }
4858 
4865  public static function getBackendScript($interface = '')
4866  {
4867  if (!$interface) {
4868  $interface = static::getBackendUserAuthentication()->uc['interfaceSetup'];
4869  }
4870  switch ($interface) {
4871  case 'frontend':
4872  $script = '../.';
4873  break;
4874  case 'backend':
4875  default:
4876  $script = self::getModuleUrl('main');
4877  }
4878  return $script;
4879  }
4880 
4887  public static function isTableWorkspaceEnabled($table)
4888  {
4889  return !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS']);
4890  }
4891 
4899  public static function getTcaFieldConfiguration($table, $field)
4900  {
4901  $configuration = [];
4902  if (isset($GLOBALS['TCA'][$table]['columns'][$field]['config'])) {
4903  $configuration = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
4904  }
4905  return $configuration;
4906  }
4907 
4916  public static function isWebMountRestrictionIgnored($table)
4917  {
4918  return !empty($GLOBALS['TCA'][$table]['ctrl']['security']['ignoreWebMountRestriction']);
4919  }
4920 
4929  public static function isRootLevelRestrictionIgnored($table)
4930  {
4931  return !empty($GLOBALS['TCA'][$table]['ctrl']['security']['ignoreRootLevelRestriction']);
4932  }
4933 
4941  public static function shortcutExists($url)
4942  {
4943  $queryBuilder = static::getQueryBuilderForTable('sys_be_shortcuts');
4944  $queryBuilder->getRestrictions()->removeAll();
4945 
4946  $count = $queryBuilder
4947  ->count('uid')
4948  ->from('sys_be_shortcuts')
4949  ->where(
4950  $queryBuilder->expr()->eq(
4951  'userid',
4952  $queryBuilder->createNamedParameter(
4953  self::getBackendUserAuthentication()->user['uid'],
4954  \PDO::PARAM_INT
4955  )
4956  ),
4957  $queryBuilder->expr()->eq('url', $queryBuilder->createNamedParameter($url, \PDO::PARAM_STR))
4958  )
4959  ->execute()
4960  ->fetchColumn(0);
4961 
4962  return (bool)$count;
4963  }
4964 
4970  protected static function getSignalSlotDispatcher()
4971  {
4972  return GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class);
4973  }
4974 
4984  protected static function emitGetPagesTSconfigPreIncludeSignal(
4985  array $TSdataArray,
4986  $id,
4987  array $rootLine,
4988  $returnPartArray
4989  ) {
4990  $signalArguments = static::getSignalSlotDispatcher()->dispatch(
4991  __CLASS__,
4992  'getPagesTSconfigPreInclude',
4993  [$TSdataArray, $id, $rootLine, $returnPartArray]
4994  );
4995  return $signalArguments[0];
4996  }
4997 
5002  protected static function getConnectionForTable($table)
5003  {
5004  return GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table);
5005  }
5006 
5011  protected static function getQueryBuilderForTable($table)
5012  {
5013  return GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
5014  }
5015 
5019  protected static function getLanguageService()
5020  {
5021  return $GLOBALS['LANG'];
5022  }
5023 
5027  protected static function getBackendUserAuthentication()
5028  {
5029  return $GLOBALS['BE_USER'];
5030  }
5031 
5035  protected static function getDocumentTemplate()
5036  {
5037  return $GLOBALS['TBE_TEMPLATE'];
5038  }
5039 }
static getLabelsFromItemsList($table, $column, $keyList, array $columnTsConfig=[])
static getRecordPath($uid, $clause, $titleLimit, $fullTitleLimit=0)
static getRecordWSOL($table, $uid, $fields= '*', $where= '', $useDeleteClause=true, $unsetMovePointers=false)
static getLabelFromItemListMerged($pageId, $table, $column, $key)
static isFirstPartOfStr($str, $partStr)
static calcAge($seconds, $labels= 'min|hrs|days|yrs|min|hour|day|year')
static createPreviewUrl($pageUid, $rootLine, $anchorSection, $additionalGetVars, $viewScript)
static getProcessedValueExtra($table, $fN, $fV, $fixed_lgd_chars=0, $uid=0, $forceResult=true, $pid=0)
static xml2array($string, $NSprefix= '', $reportDocTag=false)
static blindGroupNames($groups, $groupArray, $excludeBlindedFlag=false)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
static sortArraysByKey(array $arrays, $key, $ascending=true)
static getDropdownMenu($mainParams, $elementName, $currentValue, $menuItems, $script= '', $addParams= '')
static makeConfigForm($configArray, $defaults, $dataPrefix)
static getRecordLocalization($table, $uid, $language, $andWhereClause= '')
static getMovePlaceholder($table, $uid, $fields= '*', $workspace=null)
static implodeArrayForUrl($name, array $theArray, $str= '', $skipBlank=false, $rawurlencodeParamName=false)
static getFlexFormDS($conf, $row, $table, $fieldName= '', $WSOL=true, $newRecordPidValue=0)
static getRecordTitle($table, $row, $prep=false, $forceResult=true)
static getLinkToDataHandlerAction($parameters, $redirectUrl= '')
static getModuleData($MOD_MENU, $CHANGED_SETTINGS, $modName, $type= '', $dontValidateList= '', $setDefaultList= '')
static getRecordsSortedByTitle(array $fields, $table, $titleField, $where= '')
static getSQLselectableList($in_list, $tablename, $default_tablename)
static viewOnClick($pageUid, $backPath= '', $rootLine=null, $anchorSection= '', $alternativeUrl= '', $additionalGetVars= '', $switchFocus=true)
static getFuncMenu($mainParams, $elementName, $currentValue, $menuItems, $script= '', $addParams= '')
static workspaceOL($table, &$row, $wsid=-99, $unsetMovePointers=false)
static selectVersionsOfRecord($table, $uid, $fields= '*', $workspace=0, $includeDeletedRecords=false, $row=null)
static titleAttribForPages($row, $perms_clause= '', $includeAttrib=true)
static BEgetRootLine($uid, $clause= '', $workspaceOL=false)
static getTCAtypes($table, $rec, $useFieldNameAsKey=false)
static getRecord($table, $uid, $fields= '*', $where= '', $useDeleteClause=true)
static getCommonSelectFields($table, $prefix= '', $fields=[])
static uniqueList($in_list, $secondParameter=null)
static getRecordsByField($theTable, $theField, $theValue, $whereClause= '', $groupBy= '', $orderBy= '', $limit= '', $useDeleteClause=true, $queryBuilder=null)
static implodeAttributes(array $arr, $xhtmlSafe=false, $dontOmitBlankAttribs=false)
static getFuncInput($mainParams, $elementName, $currentValue, $size=10, $script= '', $addParams= '')
static getPagesTSconfig($id, $rootLine=null, $returnPartArray=false)
static getFuncCheck($mainParams, $elementName, $currentValue, $script= '', $addParams= '', $tagParams= '')
static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static RTEsetup($RTEprop, $table, $field, $type= '')
static makeInstance($className,...$constructorArguments)
static getUserNames($fields= 'username, usergroup, usergroup_cached_list, uid', $where= '')
static buildScriptUrl($mainParams, $addParams, $script= '')
static stripLogicalOperatorPrefix(string $constraint)
static wrapClickMenuOnIcon($content, $table, $uid=0, $listFrame=true, $addParams= '', $enDisItems= '', $returnTagParameters=false)
static referenceCount($table, $ref, $msg= '', $count=null)
static blindUserNames($usernames, $groupArray, $excludeBlindedFlag=false)
static getFileAbsFileName($filename, $_=null, $_2=null)
static getWorkspaceVersionOfRecord($workspace, $table, $uid, $fields= '*')
static emitGetPagesTSconfigPreIncludeSignal(array $TSdataArray, $id, array $rootLine, $returnPartArray)
static intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
static callUserFunction($funcName, &$params, &$ref, $_= '', $errorMode=0)
static fixVersioningPid($table, &$rr, $ignoreWorkspaceMatch=false)