TYPO3  7.6
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 
28 use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
44 
54 {
61  protected static $tcaTableTypeConfigurationCache = array();
62 
63  /*******************************************
64  *
65  * SQL-related, selecting records, searching
66  *
67  *******************************************/
82  public static function deleteClause($table, $tableAlias = '')
83  {
84  if ($GLOBALS['TCA'][$table]['ctrl']['delete']) {
85  return ' AND ' . ($tableAlias ?: $table) . '.' . $GLOBALS['TCA'][$table]['ctrl']['delete'] . '=0';
86  } else {
87  return '';
88  }
89  }
90 
105  public static function getRecord($table, $uid, $fields = '*', $where = '', $useDeleteClause = true)
106  {
107  // Ensure we have a valid uid (not 0 and not NEWxxxx) and a valid TCA
108  if ((int)$uid && !empty($GLOBALS['TCA'][$table])) {
109  $where = 'uid=' . (int)$uid . ($useDeleteClause ? self::deleteClause($table) : '') . $where;
110  $row = static::getDatabaseConnection()->exec_SELECTgetSingleRow($fields, $table, $where);
111  if ($row) {
112  return $row;
113  }
114  }
115  return null;
116  }
117 
129  public static function getRecordWSOL($table, $uid, $fields = '*', $where = '', $useDeleteClause = true, $unsetMovePointers = false)
130  {
131  if ($fields !== '*') {
132  $internalFields = GeneralUtility::uniqueList($fields . ',uid,pid');
133  $row = self::getRecord($table, $uid, $internalFields, $where, $useDeleteClause);
134  self::workspaceOL($table, $row, -99, $unsetMovePointers);
135  if (is_array($row)) {
136  foreach ($row as $key => $_) {
137  if (!GeneralUtility::inList($fields, $key) && $key[0] !== '_') {
138  unset($row[$key]);
139  }
140  }
141  }
142  } else {
143  $row = self::getRecord($table, $uid, $fields, $where, $useDeleteClause);
144  self::workspaceOL($table, $row, -99, $unsetMovePointers);
145  }
146  return $row;
147  }
148 
161  public static function getRecordRaw($table, $where = '', $fields = '*')
162  {
163  $row = false;
164  $db = static::getDatabaseConnection();
165  if (false !== ($res = $db->exec_SELECTquery($fields, $table, $where, '', '', '1'))) {
166  $row = $db->sql_fetch_assoc($res);
167  $db->sql_free_result($res);
168  }
169  return $row;
170  }
171 
187  public static function getRecordsByField($theTable, $theField, $theValue, $whereClause = '', $groupBy = '', $orderBy = '', $limit = '', $useDeleteClause = true)
188  {
189  if (is_array($GLOBALS['TCA'][$theTable])) {
190  $db = static::getDatabaseConnection();
191  $res = $db->exec_SELECTquery(
192  '*',
193  $theTable,
194  $theField . '=' . $db->fullQuoteStr($theValue, $theTable) .
195  ($useDeleteClause ? self::deleteClause($theTable) . ' ' : '') .
196  self::versioningPlaceholderClause($theTable) . ' ' .
197  $whereClause,
198  $groupBy,
199  $orderBy,
200  $limit
201  );
202  $rows = array();
203  while ($row = $db->sql_fetch_assoc($res)) {
204  $rows[] = $row;
205  }
206  $db->sql_free_result($res);
207  if (!empty($rows)) {
208  return $rows;
209  }
210  }
211  return null;
212  }
213 
221  public static function splitTable_Uid($str)
222  {
223  list($uid, $table) = explode('_', strrev($str), 2);
224  return array(strrev($table), strrev($uid));
225  }
226 
236  public static function getSQLselectableList($in_list, $tablename, $default_tablename)
237  {
238  $list = array();
239  if ((string)trim($in_list) != '') {
240  $tempItemArray = explode(',', trim($in_list));
241  foreach ($tempItemArray as $key => $val) {
242  $val = strrev($val);
243  $parts = explode('_', $val, 2);
244  if ((string)trim($parts[0]) != '') {
245  $theID = (int)strrev($parts[0]);
246  $theTable = trim($parts[1]) ? strrev(trim($parts[1])) : $default_tablename;
247  if ($theTable == $tablename) {
248  $list[] = $theID;
249  }
250  }
251  }
252  }
253  return implode(',', $list);
254  }
255 
266  public static function BEenableFields($table, $inv = false)
267  {
268  $ctrl = $GLOBALS['TCA'][$table]['ctrl'];
269  $query = array();
270  $invQuery = array();
271  if (is_array($ctrl)) {
272  if (is_array($ctrl['enablecolumns'])) {
273  if ($ctrl['enablecolumns']['disabled']) {
274  $field = $table . '.' . $ctrl['enablecolumns']['disabled'];
275  $query[] = $field . '=0';
276  $invQuery[] = $field . '<>0';
277  }
278  if ($ctrl['enablecolumns']['starttime']) {
279  $field = $table . '.' . $ctrl['enablecolumns']['starttime'];
280  $query[] = '(' . $field . '<=' . $GLOBALS['SIM_ACCESS_TIME'] . ')';
281  $invQuery[] = '(' . $field . '<>0 AND ' . $field . '>' . $GLOBALS['SIM_ACCESS_TIME'] . ')';
282  }
283  if ($ctrl['enablecolumns']['endtime']) {
284  $field = $table . '.' . $ctrl['enablecolumns']['endtime'];
285  $query[] = '(' . $field . '=0 OR ' . $field . '>' . $GLOBALS['SIM_ACCESS_TIME'] . ')';
286  $invQuery[] = '(' . $field . '<>0 AND ' . $field . '<=' . $GLOBALS['SIM_ACCESS_TIME'] . ')';
287  }
288  }
289  }
290  $outQ = $inv ? '(' . implode(' OR ', $invQuery) . ')' : implode(' AND ', $query);
291  return $outQ ? ' AND ' . $outQ : '';
292  }
293 
303  public static function getRecordLocalization($table, $uid, $language, $andWhereClause = '')
304  {
305  $recordLocalization = false;
306 
307  // Check if translations are stored in other table
308  if (isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable'])) {
309  $table = $GLOBALS['TCA'][$table]['ctrl']['transForeignTable'];
310  }
311 
312  if (self::isTableLocalizable($table)) {
313  $tcaCtrl = $GLOBALS['TCA'][$table]['ctrl'];
314  $recordLocalization = self::getRecordsByField($table, $tcaCtrl['transOrigPointerField'], $uid, 'AND ' . $tcaCtrl['languageField'] . '=' . (int)$language . ($andWhereClause ? ' ' . $andWhereClause : ''), '', '', '1');
315  }
316  return $recordLocalization;
317  }
318 
319  /*******************************************
320  *
321  * Page tree, TCA related
322  *
323  *******************************************/
334  public static function BEgetRootLine($uid, $clause = '', $workspaceOL = false)
335  {
336  static $BEgetRootLine_cache = array();
337  $output = array();
338  $pid = $uid;
339  $ident = $pid . '-' . $clause . '-' . $workspaceOL;
340  if (is_array($BEgetRootLine_cache[$ident])) {
341  $output = $BEgetRootLine_cache[$ident];
342  } else {
343  $loopCheck = 100;
344  $theRowArray = array();
345  while ($uid != 0 && $loopCheck) {
346  $loopCheck--;
347  $row = self::getPageForRootline($uid, $clause, $workspaceOL);
348  if (is_array($row)) {
349  $uid = $row['pid'];
350  $theRowArray[] = $row;
351  } else {
352  break;
353  }
354  }
355  if ($uid == 0) {
356  $theRowArray[] = array('uid' => 0, 'title' => '');
357  }
358  $c = count($theRowArray);
359  foreach ($theRowArray as $val) {
360  $c--;
361  $output[$c] = array(
362  'uid' => $val['uid'],
363  'pid' => $val['pid'],
364  'title' => $val['title'],
365  'doktype' => $val['doktype'],
366  'tsconfig_includes' => $val['tsconfig_includes'],
367  'TSconfig' => $val['TSconfig'],
368  'is_siteroot' => $val['is_siteroot'],
369  't3ver_oid' => $val['t3ver_oid'],
370  't3ver_wsid' => $val['t3ver_wsid'],
371  't3ver_state' => $val['t3ver_state'],
372  't3ver_stage' => $val['t3ver_stage'],
373  'backend_layout_next_level' => $val['backend_layout_next_level']
374  );
375  if (isset($val['_ORIG_pid'])) {
376  $output[$c]['_ORIG_pid'] = $val['_ORIG_pid'];
377  }
378  }
379  $BEgetRootLine_cache[$ident] = $output;
380  }
381  return $output;
382  }
383 
393  protected static function getPageForRootline($uid, $clause, $workspaceOL)
394  {
395  static $getPageForRootline_cache = array();
396  $ident = $uid . '-' . $clause . '-' . $workspaceOL;
397  if (is_array($getPageForRootline_cache[$ident])) {
398  $row = $getPageForRootline_cache[$ident];
399  } else {
400  $db = static::getDatabaseConnection();
401  $res = $db->exec_SELECTquery('pid,uid,title,doktype,tsconfig_includes,TSconfig,is_siteroot,t3ver_oid,t3ver_wsid,t3ver_state,t3ver_stage,backend_layout_next_level', 'pages', 'uid=' . (int)$uid . ' ' . self::deleteClause('pages') . ' ' . $clause);
402  $row = $db->sql_fetch_assoc($res);
403  if ($row) {
404  $newLocation = false;
405  if ($workspaceOL) {
406  self::workspaceOL('pages', $row);
407  $newLocation = self::getMovePlaceholder('pages', $row['uid'], 'pid');
408  }
409  if (is_array($row)) {
410  if ($newLocation !== false) {
411  $row['pid'] = $newLocation['pid'];
412  } else {
413  self::fixVersioningPid('pages', $row);
414  }
415  $getPageForRootline_cache[$ident] = $row;
416  }
417  }
418  $db->sql_free_result($res);
419  }
420  return $row;
421  }
422 
430  public static function openPageTree($pid, $clearExpansion)
431  {
432  $beUser = static::getBackendUserAuthentication();
433  // Get current expansion data:
434  if ($clearExpansion) {
435  $expandedPages = array();
436  } else {
437  $expandedPages = unserialize($beUser->uc['browseTrees']['browsePages']);
438  }
439  // Get rootline:
440  $rL = self::BEgetRootLine($pid);
441  // First, find out what mount index to use (if more than one DB mount exists):
442  $mountIndex = 0;
443  $mountKeys = array_flip($beUser->returnWebmounts());
444  foreach ($rL as $rLDat) {
445  if (isset($mountKeys[$rLDat['uid']])) {
446  $mountIndex = $mountKeys[$rLDat['uid']];
447  break;
448  }
449  }
450  // Traverse rootline and open paths:
451  foreach ($rL as $rLDat) {
452  $expandedPages[$mountIndex][$rLDat['uid']] = 1;
453  }
454  // Write back:
455  $beUser->uc['browseTrees']['browsePages'] = serialize($expandedPages);
456  $beUser->writeUC();
457  }
458 
470  public static function getRecordPath($uid, $clause, $titleLimit, $fullTitleLimit = 0)
471  {
472  if (!$titleLimit) {
473  $titleLimit = 1000;
474  }
475  $output = $fullOutput = '/';
476  $clause = trim($clause);
477  if ($clause !== '' && substr($clause, 0, 3) !== 'AND') {
478  $clause = 'AND ' . $clause;
479  }
480  $data = self::BEgetRootLine($uid, $clause);
481  foreach ($data as $record) {
482  if ($record['uid'] === 0) {
483  continue;
484  }
485  $output = '/' . GeneralUtility::fixed_lgd_cs(strip_tags($record['title']), $titleLimit) . $output;
486  if ($fullTitleLimit) {
487  $fullOutput = '/' . GeneralUtility::fixed_lgd_cs(strip_tags($record['title']), $fullTitleLimit) . $fullOutput;
488  }
489  }
490  if ($fullTitleLimit) {
491  return array($output, $fullOutput);
492  } else {
493  return $output;
494  }
495  }
496 
504  public static function getExcludeFields()
505  {
507  $finalExcludeArray = array();
508 
509  // Fetch translations for table names
510  $tableToTranslation = array();
511  $lang = static::getLanguageService();
512  // All TCA keys
513  foreach ($GLOBALS['TCA'] as $table => $conf) {
514  $tableToTranslation[$table] = $lang->sl($conf['ctrl']['title']);
515  }
516  // Sort by translations
517  asort($tableToTranslation);
518  foreach ($tableToTranslation as $table => $translatedTable) {
519  $excludeArrayTable = array();
520 
521  // All field names configured and not restricted to admins
522  if (is_array($GLOBALS['TCA'][$table]['columns'])
523  && empty($GLOBALS['TCA'][$table]['ctrl']['adminOnly'])
524  && (empty($GLOBALS['TCA'][$table]['ctrl']['rootLevel']) || !empty($GLOBALS['TCA'][$table]['ctrl']['security']['ignoreRootLevelRestriction']))
525  ) {
526  foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $_) {
527  if ($GLOBALS['TCA'][$table]['columns'][$field]['exclude']) {
528  // Get human readable names of fields
529  $translatedField = $lang->sl($GLOBALS['TCA'][$table]['columns'][$field]['label']);
530  // Add entry
531  $excludeArrayTable[] = array($translatedTable . ': ' . $translatedField, $table . ':' . $field);
532  }
533  }
534  }
535  // All FlexForm fields
536  $flexFormArray = static::getRegisteredFlexForms($table);
537  foreach ($flexFormArray as $tableField => $flexForms) {
538  // Prefix for field label, e.g. "Plugin Options:"
539  $labelPrefix = '';
540  if (!empty($GLOBALS['TCA'][$table]['columns'][$tableField]['label'])) {
541  $labelPrefix = $lang->sl($GLOBALS['TCA'][$table]['columns'][$tableField]['label']);
542  }
543  // Get all sheets and title
544  foreach ($flexForms as $extIdent => $extConf) {
545  $extTitle = $lang->sl($extConf['title']);
546  // Get all fields in sheet
547  foreach ($extConf['ds']['sheets'] as $sheetName => $sheet) {
548  if (empty($sheet['ROOT']['el']) || !is_array($sheet['ROOT']['el'])) {
549  continue;
550  }
551  foreach ($sheet['ROOT']['el'] as $fieldName => $field) {
552  // Use only fields that have exclude flag set
553  if (empty($field['TCEforms']['exclude'])) {
554  continue;
555  }
556  $fieldLabel = !empty($field['TCEforms']['label']) ? $lang->sl($field['TCEforms']['label']) : $fieldName;
557  $fieldIdent = $table . ':' . $tableField . ';' . $extIdent . ';' . $sheetName . ';' . $fieldName;
558  $excludeArrayTable[] = array(trim($labelPrefix . ' ' . $extTitle, ': ') . ': ' . $fieldLabel, $fieldIdent);
559  }
560  }
561  }
562  }
563  // Sort fields by the translated value
564  if (!empty($excludeArrayTable)) {
565  usort($excludeArrayTable, function (array $array1, array $array2) {
566  $array1 = reset($array1);
567  $array2 = reset($array2);
568  if (is_string($array1) && is_string($array2)) {
569  return strcasecmp($array1, $array2);
570  }
571  return 0;
572  });
573  $finalExcludeArray = array_merge($finalExcludeArray, $excludeArrayTable);
574  }
575  }
576 
577  return $finalExcludeArray;
578  }
579 
587  public static function getExplicitAuthFieldValues()
588  {
590  // Initialize:
591  $lang = static::getLanguageService();
592  $adLabel = array(
593  'ALLOW' => $lang->sl('LLL:EXT:lang/locallang_core.xlf:labels.allow'),
594  'DENY' => $lang->sl('LLL:EXT:lang/locallang_core.xlf:labels.deny')
595  );
596  // All TCA keys:
597  $allowDenyOptions = array();
598  foreach ($GLOBALS['TCA'] as $table => $_) {
599  // All field names configured:
600  if (is_array($GLOBALS['TCA'][$table]['columns'])) {
601  foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $_) {
602  $fCfg = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
603  if ($fCfg['type'] == 'select' && $fCfg['authMode']) {
604  // Check for items:
605  if (is_array($fCfg['items'])) {
606  // Get Human Readable names of fields and table:
607  $allowDenyOptions[$table . ':' . $field]['tableFieldLabel'] =
608  $lang->sl($GLOBALS['TCA'][$table]['ctrl']['title']) . ': '
609  . $lang->sl($GLOBALS['TCA'][$table]['columns'][$field]['label']);
610  // Check for items:
611  foreach ($fCfg['items'] as $iVal) {
612  // Values '' is not controlled by this setting.
613  if ((string)$iVal[1] !== '') {
614  // Find iMode
615  $iMode = '';
616  switch ((string)$fCfg['authMode']) {
617  case 'explicitAllow':
618  $iMode = 'ALLOW';
619  break;
620  case 'explicitDeny':
621  $iMode = 'DENY';
622  break;
623  case 'individual':
624  if ($iVal[4] === 'EXPL_ALLOW') {
625  $iMode = 'ALLOW';
626  } elseif ($iVal[4] === 'EXPL_DENY') {
627  $iMode = 'DENY';
628  }
629  break;
630  }
631  // Set iMode
632  if ($iMode) {
633  $allowDenyOptions[$table . ':' . $field]['items'][$iVal[1]] = array($iMode, $lang->sl($iVal[0]), $adLabel[$iMode]);
634  }
635  }
636  }
637  }
638  }
639  }
640  }
641  }
642  return $allowDenyOptions;
643  }
644 
655  public static function getSystemLanguages()
656  {
659  $translationConfigurationProvider = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider::class);
660  $languages = $translationConfigurationProvider->getSystemLanguages();
661  $sysLanguages = array();
662  foreach ($languages as $language) {
663  if ($language['uid'] !== -1) {
664  $sysLanguages[] = array(
665  0 => htmlspecialchars($language['title']) . ' [' . $language['uid'] . ']',
666  1 => $language['uid'],
667  2 => $language['flagIcon']
668  );
669  }
670  }
671  return $sysLanguages;
672  }
673 
681  public static function getOriginalTranslationTable($table)
682  {
683  if (!empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable'])) {
684  $table = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable'];
685  }
686 
687  return $table;
688  }
689 
696  public static function isTableLocalizable($table)
697  {
698  $isLocalizable = false;
699  if (isset($GLOBALS['TCA'][$table]['ctrl']) && is_array($GLOBALS['TCA'][$table]['ctrl'])) {
700  $tcaCtrl = $GLOBALS['TCA'][$table]['ctrl'];
701  $isLocalizable = isset($tcaCtrl['languageField']) && $tcaCtrl['languageField'] && isset($tcaCtrl['transOrigPointerField']) && $tcaCtrl['transOrigPointerField'];
702  }
703  return $isLocalizable;
704  }
705 
715  public static function getInlineLocalizationMode($table, $fieldOrConfig)
716  {
717  $localizationMode = false;
718  $config = null;
719  if (is_array($fieldOrConfig) && !empty($fieldOrConfig)) {
720  $config = $fieldOrConfig;
721  } elseif (is_string($fieldOrConfig) && isset($GLOBALS['TCA'][$table]['columns'][$fieldOrConfig]['config'])) {
722  $config = $GLOBALS['TCA'][$table]['columns'][$fieldOrConfig]['config'];
723  }
724  if (is_array($config) && isset($config['type']) && $config['type'] === 'inline' && self::isTableLocalizable($table)) {
725  $localizationMode = isset($config['behaviour']['localizationMode']) && $config['behaviour']['localizationMode']
726  ? $config['behaviour']['localizationMode']
727  : 'select';
728  // The mode 'select' is not possible when child table is not localizable at all:
729  if ($localizationMode === 'select' && !self::isTableLocalizable($config['foreign_table'])) {
730  $localizationMode = false;
731  }
732  }
733  return $localizationMode;
734  }
735 
745  public static function readPageAccess($id, $perms_clause)
746  {
747  if ((string)$id !== '') {
748  $id = (int)$id;
749  if (!$id) {
750  if (static::getBackendUserAuthentication()->isAdmin()) {
751  $path = '/';
752  $pageinfo['_thePath'] = $path;
753  return $pageinfo;
754  }
755  } else {
756  $pageinfo = self::getRecord('pages', $id, '*', $perms_clause ? ' AND ' . $perms_clause : '');
757  if ($pageinfo['uid'] && static::getBackendUserAuthentication()->isInWebMount($id, $perms_clause)) {
758  self::workspaceOL('pages', $pageinfo);
759  if (is_array($pageinfo)) {
760  self::fixVersioningPid('pages', $pageinfo);
761  list($pageinfo['_thePath'], $pageinfo['_thePathFull']) = self::getRecordPath((int)$pageinfo['uid'], $perms_clause, 15, 1000);
762  return $pageinfo;
763  }
764  }
765  }
766  }
767  return false;
768  }
769 
778  public static function getTCAtypes($table, $rec, $useFieldNameAsKey = false)
779  {
780  if ($GLOBALS['TCA'][$table]) {
781  // Get type value:
782  $fieldValue = self::getTCAtypeValue($table, $rec);
783  $cacheIdentifier = $table . '-type-' . $fieldValue . '-fnk-' . $useFieldNameAsKey;
784 
785  // Fetch from first-level-cache if available
786  if (isset(self::$tcaTableTypeConfigurationCache[$cacheIdentifier])) {
787  return self::$tcaTableTypeConfigurationCache[$cacheIdentifier];
788  }
789 
790  // Get typesConf
791  $typesConf = $GLOBALS['TCA'][$table]['types'][$fieldValue];
792  // Get fields list and traverse it
793  $fieldList = explode(',', $typesConf['showitem']);
794 
795  // Add subtype fields e.g. for a valid RTE transformation
796  // The RTE runs the DB -> RTE transformation only, if the RTE field is part of the getTCAtypes array
797  if (isset($typesConf['subtype_value_field'])) {
798  $subType = $rec[$typesConf['subtype_value_field']];
799  if (isset($typesConf['subtypes_addlist'][$subType])) {
800  $subFields = GeneralUtility::trimExplode(',', $typesConf['subtypes_addlist'][$subType], true);
801  $fieldList = array_merge($fieldList, $subFields);
802  }
803  }
804 
805  $altFieldList = array();
806  // Traverse fields in types config and parse the configuration into a nice array:
807  foreach ($fieldList as $k => $v) {
808  list($pFieldName, $pAltTitle, $pPalette) = GeneralUtility::trimExplode(';', $v);
809  $defaultExtras = '';
810  if (!empty($typesConf['columnsOverrides'][$pFieldName]['defaultExtras'])) {
811  // Use defaultExtras from columnsOverrides if given
812  $defaultExtras = $typesConf['columnsOverrides'][$pFieldName]['defaultExtras'];
813  } elseif (!empty($GLOBALS['TCA'][$table]['columns'][$pFieldName]['defaultExtras'])) {
814  // Use defaultExtras from columns if given
815  $defaultExtras = $GLOBALS['TCA'][$table]['columns'][$pFieldName]['defaultExtras'];
816  }
817  $specConfParts = self::getSpecConfParts($defaultExtras);
818  $fieldList[$k] = array(
819  'field' => $pFieldName,
820  'title' => $pAltTitle,
821  'palette' => $pPalette,
822  'spec' => $specConfParts,
823  'origString' => $v
824  );
825  if ($useFieldNameAsKey) {
826  $altFieldList[$fieldList[$k]['field']] = $fieldList[$k];
827  }
828  }
829  if ($useFieldNameAsKey) {
830  $fieldList = $altFieldList;
831  }
832 
833  // Add to first-level-cache
834  self::$tcaTableTypeConfigurationCache[$cacheIdentifier] = $fieldList;
835 
836  // Return array:
837  return $fieldList;
838  }
839  return null;
840  }
841 
859  public static function getTCAtypeValue($table, $row)
860  {
861  $typeNum = 0;
862  if ($GLOBALS['TCA'][$table]) {
863  $field = $GLOBALS['TCA'][$table]['ctrl']['type'];
864  if (strpos($field, ':') !== false) {
865  list($pointerField, $foreignTableTypeField) = explode(':', $field);
866  // Get field value from database if field is not in the $row array
867  if (!isset($row[$pointerField])) {
868  $localRow = self::getRecord($table, $row['uid'], $pointerField);
869  $foreignUid = $localRow[$pointerField];
870  } else {
871  $foreignUid = $row[$pointerField];
872  }
873  if ($foreignUid) {
874  $fieldConfig = $GLOBALS['TCA'][$table]['columns'][$pointerField]['config'];
875  $relationType = $fieldConfig['type'];
876  if ($relationType === 'select') {
877  $foreignTable = $fieldConfig['foreign_table'];
878  } elseif ($relationType === 'group') {
879  $allowedTables = explode(',', $fieldConfig['allowed']);
880  $foreignTable = $allowedTables[0];
881  } else {
882  throw new \RuntimeException('TCA foreign field pointer fields are only allowed to be used with group or select field types.', 1325862240);
883  }
884  $foreignRow = self::getRecord($foreignTable, $foreignUid, $foreignTableTypeField);
885  if ($foreignRow[$foreignTableTypeField]) {
886  $typeNum = $foreignRow[$foreignTableTypeField];
887  }
888  }
889  } else {
890  $typeNum = $row[$field];
891  }
892  // If that value is an empty string, set it to "0" (zero)
893  if (empty($typeNum)) {
894  $typeNum = 0;
895  }
896  }
897  // If current typeNum doesn't exist, set it to 0 (or to 1 for historical reasons, if 0 doesn't exist)
898  if (!$GLOBALS['TCA'][$table]['types'][$typeNum]) {
899  $typeNum = $GLOBALS['TCA'][$table]['types']['0'] ? 0 : 1;
900  }
901  // Force to string. Necessary for eg '-1' to be recognized as a type value.
902  $typeNum = (string)$typeNum;
903  return $typeNum;
904  }
905 
916  public static function getSpecConfParts($defaultExtrasString, $_ = '')
917  {
918  if (!empty($_)) {
919  GeneralUtility::deprecationLog('Second parameter of BackendUtility::getSpecConfParts() is deprecated. Will be removed with TYPO3 CMS 8');
920  // Prepend old parameter, can be overwritten by casual defaultExtras string, then.
921  $defaultExtrasString = $_ . ':' . $defaultExtrasString;
922  }
923  $specConfParts = GeneralUtility::trimExplode(':', $defaultExtrasString, true);
924  $reg = array();
925  if (!empty($specConfParts)) {
926  foreach ($specConfParts as $k2 => $v2) {
927  unset($specConfParts[$k2]);
928  if (preg_match('/(.*)\\[(.*)\\]/', $v2, $reg)) {
929  $specConfParts[trim($reg[1])] = array(
930  'parameters' => GeneralUtility::trimExplode('|', $reg[2], true)
931  );
932  } else {
933  $specConfParts[trim($v2)] = 1;
934  }
935  }
936  } else {
937  $specConfParts = array();
938  }
939  return $specConfParts;
940  }
941 
949  public static function getSpecConfParametersFromArray($pArr)
950  {
951  $out = array();
952  if (is_array($pArr)) {
953  foreach ($pArr as $k => $v) {
954  $parts = explode('=', $v, 2);
955  if (count($parts) === 2) {
956  $out[trim($parts[0])] = trim($parts[1]);
957  } else {
958  $out[$k] = $v;
959  }
960  }
961  }
962  return $out;
963  }
964 
993  public static function getFlexFormDS($conf, $row, $table, $fieldName = '', $WSOL = true, $newRecordPidValue = 0)
994  {
995  // Get pointer field etc from TCA-config:
996  $ds_pointerField = $conf['ds_pointerField'];
997  $ds_array = $conf['ds'];
998  $ds_tableField = $conf['ds_tableField'];
999  $ds_searchParentField = $conf['ds_pointerField_searchParent'];
1000  // If there is a data source array, that takes precedence
1001  if (is_array($ds_array)) {
1002  // If a pointer field is set, take the value from that field in the $row array and use as key.
1003  if ($ds_pointerField) {
1004  // Up to two pointer fields can be specified in a comma separated list.
1005  $pointerFields = GeneralUtility::trimExplode(',', $ds_pointerField);
1006  // If we have two pointer fields, the array keys should contain both field values separated by comma. The asterisk "*" catches all values. For backwards compatibility, it's also possible to specify only the value of the first defined ds_pointerField.
1007  if (count($pointerFields) === 2) {
1008  if ($ds_array[$row[$pointerFields[0]] . ',' . $row[$pointerFields[1]]]) {
1009  // Check if we have a DS for the combination of both pointer fields values
1010  $srcPointer = $row[$pointerFields[0]] . ',' . $row[$pointerFields[1]];
1011  } elseif ($ds_array[$row[$pointerFields[1]] . ',*']) {
1012  // Check if we have a DS for the value of the first pointer field suffixed with ",*"
1013  $srcPointer = $row[$pointerFields[1]] . ',*';
1014  } elseif ($ds_array['*,' . $row[$pointerFields[1]]]) {
1015  // Check if we have a DS for the value of the second pointer field prefixed with "*,"
1016  $srcPointer = '*,' . $row[$pointerFields[1]];
1017  } elseif ($ds_array[$row[$pointerFields[0]]]) {
1018  // Check if we have a DS for just the value of the first pointer field (mainly for backwards compatibility)
1019  $srcPointer = $row[$pointerFields[0]];
1020  } else {
1021  $srcPointer = null;
1022  }
1023  } else {
1024  $srcPointer = $row[$pointerFields[0]];
1025  }
1026  $srcPointer = $srcPointer !== null && isset($ds_array[$srcPointer]) ? $srcPointer : 'default';
1027  } else {
1028  $srcPointer = 'default';
1029  }
1030  // 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.
1031  if (substr($ds_array[$srcPointer], 0, 5) == 'FILE:') {
1032  $file = GeneralUtility::getFileAbsFileName(substr($ds_array[$srcPointer], 5));
1033  if ($file && @is_file($file)) {
1034  $dataStructArray = GeneralUtility::xml2array(GeneralUtility::getUrl($file));
1035  } else {
1036  $dataStructArray = 'The file "' . substr($ds_array[$srcPointer], 5) . '" in ds-array key "' . $srcPointer . '" was not found ("' . $file . '")';
1037  }
1038  } else {
1039  $dataStructArray = GeneralUtility::xml2array($ds_array[$srcPointer]);
1040  }
1041  } elseif ($ds_pointerField) {
1042  // If pointer field AND possibly a table/field is set:
1043  // Value of field pointed to:
1044  $srcPointer = $row[$ds_pointerField];
1045  // Searching recursively back if 'ds_pointerField_searchParent' is defined (typ. a page rootline, or maybe a tree-table):
1046  if ($ds_searchParentField && !$srcPointer) {
1047  $rr = self::getRecord($table, $row['uid'], 'uid,' . $ds_searchParentField);
1048  // Get the "pid" field - we cannot know that it is in the input record! ###NOTE_A###
1049  if ($WSOL) {
1050  self::workspaceOL($table, $rr);
1051  self::fixVersioningPid($table, $rr, true);
1052  }
1053  $db = static::getDatabaseConnection();
1054  $uidAcc = array();
1055  // Used to avoid looping, if any should happen.
1056  $subFieldPointer = $conf['ds_pointerField_searchParent_subField'];
1057  while (!$srcPointer) {
1058  $res = $db->exec_SELECTquery('uid,' . $ds_pointerField . ',' . $ds_searchParentField . ($subFieldPointer ? ',' . $subFieldPointer : ''), $table, 'uid=' . (int)($newRecordPidValue ?: $rr[$ds_searchParentField]) . self::deleteClause($table));
1059  $newRecordPidValue = 0;
1060  $rr = $db->sql_fetch_assoc($res);
1061  $db->sql_free_result($res);
1062  // Break if no result from SQL db or if looping...
1063  if (!is_array($rr) || isset($uidAcc[$rr['uid']])) {
1064  break;
1065  }
1066  $uidAcc[$rr['uid']] = 1;
1067  if ($WSOL) {
1068  self::workspaceOL($table, $rr);
1069  self::fixVersioningPid($table, $rr, true);
1070  }
1071  $srcPointer = $subFieldPointer && $rr[$subFieldPointer] ? $rr[$subFieldPointer] : $rr[$ds_pointerField];
1072  }
1073  }
1074  // If there is a srcPointer value:
1075  if ($srcPointer) {
1076  if (MathUtility::canBeInterpretedAsInteger($srcPointer)) {
1077  // If integer, then its a record we will look up:
1078  list($tName, $fName) = explode(':', $ds_tableField, 2);
1079  if ($tName && $fName && is_array($GLOBALS['TCA'][$tName])) {
1080  $dataStructRec = self::getRecord($tName, $srcPointer);
1081  if ($WSOL) {
1082  self::workspaceOL($tName, $dataStructRec);
1083  }
1084  if (strpos($dataStructRec[$fName], '<') === false) {
1085  if (is_file(PATH_site . $dataStructRec[$fName])) {
1086  // The value is a pointer to a file
1087  $dataStructArray = GeneralUtility::xml2array(GeneralUtility::getUrl(PATH_site . $dataStructRec[$fName]));
1088  } else {
1089  $dataStructArray = sprintf('File \'%s\' was not found', $dataStructRec[$fName]);
1090  }
1091  } else {
1092  // No file pointer, handle as being XML (default behaviour)
1093  $dataStructArray = GeneralUtility::xml2array($dataStructRec[$fName]);
1094  }
1095  } else {
1096  $dataStructArray = 'No tablename (' . $tName . ') or fieldname (' . $fName . ') was found an valid!';
1097  }
1098  } else {
1099  // Otherwise expect it to be a file:
1100  $file = GeneralUtility::getFileAbsFileName($srcPointer);
1101  if ($file && @is_file($file)) {
1102  $dataStructArray = GeneralUtility::xml2array(GeneralUtility::getUrl($file));
1103  } else {
1104  // Error message.
1105  $dataStructArray = 'The file "' . $srcPointer . '" was not found ("' . $file . '")';
1106  }
1107  }
1108  } else {
1109  // Error message.
1110  $dataStructArray = 'No source value in fieldname "' . $ds_pointerField . '"';
1111  }
1112  } else {
1113  $dataStructArray = 'No proper configuration!';
1114  }
1115  // Hook for post-processing the Flexform DS. Introduces the possibility to configure Flexforms via TSConfig
1116  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['getFlexFormDSClass'])) {
1117  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['getFlexFormDSClass'] as $classRef) {
1118  $hookObj = GeneralUtility::getUserObj($classRef);
1119  if (method_exists($hookObj, 'getFlexFormDS_postProcessDS')) {
1120  $hookObj->getFlexFormDS_postProcessDS($dataStructArray, $conf, $row, $table, $fieldName);
1121  }
1122  }
1123  }
1124  return $dataStructArray;
1125  }
1126 
1135  public static function getRegisteredFlexForms($table = 'tt_content')
1136  {
1138  if (empty($table) || empty($GLOBALS['TCA'][$table]['columns'])) {
1139  return array();
1140  }
1141  $flexForms = array();
1142  foreach ($GLOBALS['TCA'][$table]['columns'] as $tableField => $fieldConf) {
1143  if (!empty($fieldConf['config']['type']) && !empty($fieldConf['config']['ds']) && $fieldConf['config']['type'] == 'flex') {
1144  $flexForms[$tableField] = array();
1145  unset($fieldConf['config']['ds']['default']);
1146  // Get pointer fields
1147  $pointerFields = !empty($fieldConf['config']['ds_pointerField']) ? $fieldConf['config']['ds_pointerField'] : 'list_type,CType';
1148  $pointerFields = GeneralUtility::trimExplode(',', $pointerFields);
1149  // Get FlexForms
1150  foreach ($fieldConf['config']['ds'] as $flexFormKey => $dataStruct) {
1151  // Get extension identifier (uses second value if it's not empty, "list" or "*", else first one)
1152  $identFields = GeneralUtility::trimExplode(',', $flexFormKey);
1153  $extIdent = $identFields[0];
1154  if (!empty($identFields[1]) && $identFields[1] != 'list' && $identFields[1] != '*') {
1155  $extIdent = $identFields[1];
1156  }
1157  // Load external file references
1158  if (!is_array($dataStruct)) {
1159  $file = GeneralUtility::getFileAbsFileName(str_ireplace('FILE:', '', $dataStruct));
1160  if ($file && @is_file($file)) {
1161  $dataStruct = GeneralUtility::getUrl($file);
1162  }
1163  $dataStruct = GeneralUtility::xml2array($dataStruct);
1164  if (!is_array($dataStruct)) {
1165  continue;
1166  }
1167  }
1168  // Get flexform content
1169  $dataStruct = GeneralUtility::resolveAllSheetsInDS($dataStruct);
1170  if (empty($dataStruct['sheets']) || !is_array($dataStruct['sheets'])) {
1171  continue;
1172  }
1173  // Use DS pointer to get extension title from TCA
1174  $title = $extIdent;
1175  $keyFields = GeneralUtility::trimExplode(',', $flexFormKey);
1176  foreach ($pointerFields as $pointerKey => $pointerName) {
1177  if (empty($keyFields[$pointerKey]) || $keyFields[$pointerKey] == '*' || $keyFields[$pointerKey] == 'list') {
1178  continue;
1179  }
1180  if (!empty($GLOBALS['TCA'][$table]['columns'][$pointerName]['config']['items'])) {
1181  $items = $GLOBALS['TCA'][$table]['columns'][$pointerName]['config']['items'];
1182  if (!is_array($items)) {
1183  continue;
1184  }
1185  foreach ($items as $itemConf) {
1186  if (!empty($itemConf[0]) && !empty($itemConf[1]) && $itemConf[1] == $keyFields[$pointerKey]) {
1187  $title = $itemConf[0];
1188  break 2;
1189  }
1190  }
1191  }
1192  }
1193  $flexForms[$tableField][$extIdent] = array(
1194  'title' => $title,
1195  'ds' => $dataStruct
1196  );
1197  }
1198  }
1199  }
1200  return $flexForms;
1201  }
1202 
1203  /*******************************************
1204  *
1205  * Caching related
1206  *
1207  *******************************************/
1219  public static function storeHash($hash, $data, $ident)
1220  {
1222  $cacheManager = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class);
1223  $cacheManager->getCache('cache_hash')->set($hash, $data, array('ident_' . $ident), 0);
1224  }
1225 
1235  public static function getHash($hash)
1236  {
1238  $cacheManager = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class);
1239  $cacheEntry = $cacheManager->getCache('cache_hash')->get($hash);
1240  $hashContent = null;
1241  if ($cacheEntry) {
1242  $hashContent = $cacheEntry;
1243  }
1244  return $hashContent;
1245  }
1246 
1247  /*******************************************
1248  *
1249  * TypoScript related
1250  *
1251  *******************************************/
1261  public static function getPagesTSconfig($id, $rootLine = null, $returnPartArray = false)
1262  {
1263  static $pagesTSconfig_cacheReference = array();
1264  static $combinedTSconfig_cache = array();
1265 
1266  $id = (int)$id;
1267  if ($returnPartArray === false
1268  && $rootLine === null
1269  && isset($pagesTSconfig_cacheReference[$id])
1270  ) {
1271  return $combinedTSconfig_cache[$pagesTSconfig_cacheReference[$id]];
1272  } else {
1273  $TSconfig = array();
1274  if (!is_array($rootLine)) {
1275  $useCacheForCurrentPageId = true;
1276  $rootLine = self::BEgetRootLine($id, '', true);
1277  } else {
1278  $useCacheForCurrentPageId = false;
1279  }
1280 
1281  // Order correctly
1282  ksort($rootLine);
1283  $TSdataArray = array();
1284  // Setting default configuration
1285  $TSdataArray['defaultPageTSconfig'] = $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPageTSconfig'];
1286  foreach ($rootLine as $k => $v) {
1287  if (trim($v['tsconfig_includes'])) {
1288  $includeTsConfigFileList = GeneralUtility::trimExplode(',', $v['tsconfig_includes'], true);
1289  // Traversing list
1290  foreach ($includeTsConfigFileList as $key => $includeTsConfigFile) {
1291  if (StringUtility::beginsWith($includeTsConfigFile, 'EXT:')) {
1292  list($includeTsConfigFileExtensionKey, $includeTsConfigFilename) = explode(
1293  '/',
1294  substr($includeTsConfigFile, 4),
1295  2
1296  );
1297  if (
1298  (string)$includeTsConfigFileExtensionKey !== ''
1299  && ExtensionManagementUtility::isLoaded($includeTsConfigFileExtensionKey)
1300  && (string)$includeTsConfigFilename !== ''
1301  ) {
1302  $includeTsConfigFileAndPath = ExtensionManagementUtility::extPath($includeTsConfigFileExtensionKey) .
1303  $includeTsConfigFilename;
1304  if (file_exists($includeTsConfigFileAndPath)) {
1305  $TSdataArray['uid_' . $v['uid'] . '_static_' . $key] = GeneralUtility::getUrl($includeTsConfigFileAndPath);
1306  }
1307  }
1308  }
1309  }
1310  }
1311  $TSdataArray['uid_' . $v['uid']] = $v['TSconfig'];
1312  }
1313  $TSdataArray = static::emitGetPagesTSconfigPreIncludeSignal($TSdataArray, $id, $rootLine, $returnPartArray);
1314  $TSdataArray = TypoScriptParser::checkIncludeLines_array($TSdataArray);
1315  if ($returnPartArray) {
1316  return $TSdataArray;
1317  }
1318  // Parsing the page TS-Config
1319  $pageTS = implode(LF . '[GLOBAL]' . LF, $TSdataArray);
1320  /* @var $parseObj \TYPO3\CMS\Backend\Configuration\TsConfigParser */
1321  $parseObj = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Configuration\TsConfigParser::class);
1322  $res = $parseObj->parseTSconfig($pageTS, 'PAGES', $id, $rootLine);
1323  if ($res) {
1324  $TSconfig = $res['TSconfig'];
1325  }
1326  $cacheHash = $res['hash'];
1327  // Get User TSconfig overlay
1328  $userTSconfig = static::getBackendUserAuthentication()->userTS['page.'];
1329  if (is_array($userTSconfig)) {
1330  ArrayUtility::mergeRecursiveWithOverrule($TSconfig, $userTSconfig);
1331  $cacheHash .= '_user' . $GLOBALS['BE_USER']->user['uid'];
1332  }
1333 
1334  if ($useCacheForCurrentPageId) {
1335  if (!isset($combinedTSconfig_cache[$cacheHash])) {
1336  $combinedTSconfig_cache[$cacheHash] = $TSconfig;
1337  }
1338  $pagesTSconfig_cacheReference[$id] = $cacheHash;
1339  }
1340  }
1341  return $TSconfig;
1342  }
1343 
1352  public static function implodeTSParams($p, $k = '')
1353  {
1355  $implodeParams = array();
1356  if (is_array($p)) {
1357  foreach ($p as $kb => $val) {
1358  if (is_array($val)) {
1359  $implodeParams = array_merge($implodeParams, self::implodeTSParams($val, $k . $kb));
1360  } else {
1361  $implodeParams[$k . $kb] = $val;
1362  }
1363  }
1364  }
1365  return $implodeParams;
1366  }
1367 
1368  /*******************************************
1369  *
1370  * Users / Groups related
1371  *
1372  *******************************************/
1381  public static function getUserNames($fields = 'username,usergroup,usergroup_cached_list,uid', $where = '')
1382  {
1383  return self::getRecordsSortedByTitle(
1384  GeneralUtility::trimExplode(',', $fields, true),
1385  'be_users',
1386  'username',
1387  'AND pid=0 ' . $where
1388  );
1389  }
1390 
1398  public static function getGroupNames($fields = 'title,uid', $where = '')
1399  {
1400  return self::getRecordsSortedByTitle(
1401  GeneralUtility::trimExplode(',', $fields, true),
1402  'be_groups',
1403  'title',
1404  'AND pid=0 ' . $where
1405  );
1406  }
1407 
1419  protected static function getRecordsSortedByTitle(array $fields, $table, $titleField, $where = '')
1420  {
1421  $fieldsIndex = array_flip($fields);
1422  // Make sure the titleField is amongst the fields when getting sorted
1423  $fieldsIndex[$titleField] = 1;
1424 
1425  $result = array();
1426  $db = static::getDatabaseConnection();
1427  $res = $db->exec_SELECTquery('*', $table, '1=1 ' . $where . self::deleteClause($table));
1428  while ($record = $db->sql_fetch_assoc($res)) {
1429  // store the uid, because it might be unset if it's not among the requested $fields
1430  $recordId = $record['uid'];
1431  $record[$titleField] = self::getRecordTitle($table, $record);
1432 
1433  // include only the requested fields in the result
1434  $result[$recordId] = array_intersect_key($record, $fieldsIndex);
1435  }
1436  $db->sql_free_result($res);
1437 
1438  // sort records by $sortField. This is not done in the query because the title might have been overwritten by
1439  // self::getRecordTitle();
1440  return ArrayUtility::sortArraysByKey($result, $titleField);
1441  }
1442 
1450  public static function getListGroupNames($fields = 'title, uid')
1451  {
1452  $beUser = static::getBackendUserAuthentication();
1453  $exQ = ' AND hide_in_lists=0';
1454  if (!$beUser->isAdmin()) {
1455  $exQ .= ' AND uid IN (' . ($beUser->user['usergroup_cached_list'] ?: 0) . ')';
1456  }
1457  return self::getGroupNames($fields, $exQ);
1458  }
1459 
1470  public static function blindUserNames($usernames, $groupArray, $excludeBlindedFlag = false)
1471  {
1472  if (is_array($usernames) && is_array($groupArray)) {
1473  foreach ($usernames as $uid => $row) {
1474  $userN = $uid;
1475  $set = 0;
1476  if ($row['uid'] != static::getBackendUserAuthentication()->user['uid']) {
1477  foreach ($groupArray as $v) {
1478  if ($v && GeneralUtility::inList($row['usergroup_cached_list'], $v)) {
1479  $userN = $row['username'];
1480  $set = 1;
1481  }
1482  }
1483  } else {
1484  $userN = $row['username'];
1485  $set = 1;
1486  }
1487  $usernames[$uid]['username'] = $userN;
1488  if ($excludeBlindedFlag && !$set) {
1489  unset($usernames[$uid]);
1490  }
1491  }
1492  }
1493  return $usernames;
1494  }
1495 
1504  public static function blindGroupNames($groups, $groupArray, $excludeBlindedFlag = false)
1505  {
1506  if (is_array($groups) && is_array($groupArray)) {
1507  foreach ($groups as $uid => $row) {
1508  $groupN = $uid;
1509  $set = 0;
1510  if (ArrayUtility::inArray($groupArray, $uid)) {
1511  $groupN = $row['title'];
1512  $set = 1;
1513  }
1514  $groups[$uid]['title'] = $groupN;
1515  if ($excludeBlindedFlag && !$set) {
1516  unset($groups[$uid]);
1517  }
1518  }
1519  }
1520  return $groups;
1521  }
1522 
1523  /*******************************************
1524  *
1525  * Output related
1526  *
1527  *******************************************/
1534  public static function daysUntil($tstamp)
1535  {
1536  $delta_t = $tstamp - $GLOBALS['EXEC_TIME'];
1537  return ceil($delta_t / (3600 * 24));
1538  }
1539 
1546  public static function date($tstamp)
1547  {
1548  return date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], (int)$tstamp);
1549  }
1550 
1557  public static function datetime($value)
1558  {
1559  return date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'], $value);
1560  }
1561 
1570  public static function time($value, $withSeconds = true)
1571  {
1572  $hh = floor($value / 3600);
1573  $min = floor(($value - $hh * 3600) / 60);
1574  $sec = $value - $hh * 3600 - $min * 60;
1575  $l = sprintf('%02d', $hh) . ':' . sprintf('%02d', $min);
1576  if ($withSeconds) {
1577  $l .= ':' . sprintf('%02d', $sec);
1578  }
1579  return $l;
1580  }
1581 
1589  public static function calcAge($seconds, $labels = ' min| hrs| days| yrs| min| hour| day| year')
1590  {
1591  $labelArr = explode('|', $labels);
1592  $absSeconds = abs($seconds);
1593  $sign = $seconds < 0 ? -1 : 1;
1594  if ($absSeconds < 3600) {
1595  $val = round($absSeconds / 60);
1596  $seconds = $sign * $val . ($val == 1 ? $labelArr[4] : $labelArr[0]);
1597  } elseif ($absSeconds < 24 * 3600) {
1598  $val = round($absSeconds / 3600);
1599  $seconds = $sign * $val . ($val == 1 ? $labelArr[5] : $labelArr[1]);
1600  } elseif ($absSeconds < 365 * 24 * 3600) {
1601  $val = round($absSeconds / (24 * 3600));
1602  $seconds = $sign * $val . ($val == 1 ? $labelArr[6] : $labelArr[2]);
1603  } else {
1604  $val = round($absSeconds / (365 * 24 * 3600));
1605  $seconds = $sign * $val . ($val == 1 ? $labelArr[7] : $labelArr[3]);
1606  }
1607  return $seconds;
1608  }
1609 
1619  public static function dateTimeAge($tstamp, $prefix = 1, $date = '')
1620  {
1621  if (!$tstamp) {
1622  return '';
1623  }
1624  $label = static::getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.minutesHoursDaysYears');
1625  $age = ' (' . self::calcAge($prefix * ($GLOBALS['EXEC_TIME'] - $tstamp), $label) . ')';
1626  return $date === 'date' ? self::date($tstamp) : self::datetime($tstamp) . $age;
1627  }
1628 
1635  public static function titleAltAttrib($content)
1636  {
1637  $out = '';
1638  $out .= ' alt="' . htmlspecialchars($content) . '"';
1639  $out .= ' title="' . htmlspecialchars($content) . '"';
1640  return $out;
1641  }
1642 
1652  public static function resolveFileReferences($tableName, $fieldName, $element, $workspaceId = null)
1653  {
1654  if (empty($GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config'])) {
1655  return null;
1656  }
1657  $configuration = $GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config'];
1658  if (empty($configuration['type']) || $configuration['type'] !== 'inline'
1659  || empty($configuration['foreign_table']) || $configuration['foreign_table'] !== 'sys_file_reference') {
1660  return null;
1661  }
1662 
1663  $fileReferences = array();
1665  $relationHandler = GeneralUtility::makeInstance(RelationHandler::class);
1666  if ($workspaceId !== null) {
1667  $relationHandler->setWorkspaceId($workspaceId);
1668  }
1669  $relationHandler->start($element[$fieldName], $configuration['foreign_table'], $configuration['MM'], $element['uid'], $tableName, $configuration);
1670  $relationHandler->processDeletePlaceholder();
1671  $referenceUids = $relationHandler->tableArray[$configuration['foreign_table']];
1672 
1673  foreach ($referenceUids as $referenceUid) {
1674  try {
1675  $fileReference = ResourceFactory::getInstance()->getFileReferenceObject($referenceUid, array(), ($workspaceId === 0));
1676  $fileReferences[$fileReference->getUid()] = $fileReference;
1677  } catch (\TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException $e) {
1682  } catch (\InvalidArgumentException $e) {
1687  $logMessage = $e->getMessage() . ' (table: "' . $tableName . '", fieldName: "' . $fieldName . '", referenceUid: ' . $referenceUid . ')';
1688  GeneralUtility::sysLog($logMessage, 'core', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1689  }
1690  }
1691 
1692  return $fileReferences;
1693  }
1694 
1712  public static function thumbCode($row, $table, $field, $backPath = '', $thumbScript = '', $uploaddir = null, $abs = 0, $tparams = '', $size = '', $linkInfoPopup = true)
1713  {
1714  // Check and parse the size parameter
1715  $size = trim($size);
1716  $sizeParts = array(64, 64);
1717  if ($size) {
1718  $sizeParts = explode('x', $size . 'x' . $size);
1719  }
1720  $thumbData = '';
1721  $fileReferences = static::resolveFileReferences($table, $field, $row);
1722  // FAL references
1723  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
1724  if ($fileReferences !== null) {
1725  foreach ($fileReferences as $fileReferenceObject) {
1726  $fileObject = $fileReferenceObject->getOriginalFile();
1727 
1728  if ($fileObject->isMissing()) {
1729  $flashMessage = \TYPO3\CMS\Core\Resource\Utility\BackendUtility::getFlashMessageForMissingFile($fileObject);
1730  $thumbData .= $flashMessage->render();
1731  continue;
1732  }
1733 
1734  // Preview web image or media elements
1735  if (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'] . ',' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['mediafile_ext'], $fileReferenceObject->getExtension())) {
1736  $processedImage = $fileObject->process(ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, array(
1737  'width' => $sizeParts[0],
1738  'height' => $sizeParts[1] . 'c',
1739  'crop' => $fileReferenceObject->getProperty('crop')
1740  ));
1741  $imageUrl = $processedImage->getPublicUrl(true);
1742  $imgTag = '<img src="' . $imageUrl . '" ' .
1743  'width="' . $processedImage->getProperty('width') . '" ' .
1744  'height="' . $processedImage->getProperty('height') . '" ' .
1745  'alt="' . htmlspecialchars($fileReferenceObject->getName()) . '" />';
1746  } else {
1747  // Icon
1748  $imgTag = '<span title="' . htmlspecialchars($fileObject->getName()) . '">' . $iconFactory->getIconForResource($fileObject, Icon::SIZE_SMALL)->render() . '</span>';
1749  }
1750  if ($linkInfoPopup) {
1751  $onClick = 'top.launchView(\'_FILE\',\'' . (int)$fileObject->getUid() . '\',' . GeneralUtility::quoteJSvalue($backPath) . '); return false;';
1752  $thumbData .= '<a href="#" onclick="' . htmlspecialchars($onClick) . '">' . $imgTag . '</a> ';
1753  } else {
1754  $thumbData .= $imgTag;
1755  }
1756  }
1757  } else {
1758  // Find uploaddir automatically
1759  if (is_null($uploaddir)) {
1760  $uploaddir = $GLOBALS['TCA'][$table]['columns'][$field]['config']['uploadfolder'];
1761  }
1762  $uploaddir = rtrim($uploaddir, '/');
1763  // Traverse files:
1764  $thumbs = GeneralUtility::trimExplode(',', $row[$field], true);
1765  $thumbData = '';
1766  foreach ($thumbs as $theFile) {
1767  if ($theFile) {
1768  $fileName = trim($uploaddir . '/' . $theFile, '/');
1769  try {
1771  $fileObject = ResourceFactory::getInstance()->retrieveFileOrFolderObject($fileName);
1772  if ($fileObject->isMissing()) {
1773  $flashMessage = \TYPO3\CMS\Core\Resource\Utility\BackendUtility::getFlashMessageForMissingFile($fileObject);
1774  $thumbData .= $flashMessage->render();
1775  continue;
1776  }
1777  } catch (ResourceDoesNotExistException $exception) {
1779  $flashMessage = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessage::class,
1780  htmlspecialchars($exception->getMessage()),
1781  static::getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:warning.file_missing', true),
1782  FlashMessage::ERROR
1783  );
1784  $thumbData .= $flashMessage->render();
1785  continue;
1786  }
1787 
1788  $fileExtension = $fileObject->getExtension();
1789  if ($fileExtension == 'ttf' || GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileExtension)) {
1790  $imageUrl = $fileObject->process(ProcessedFile::CONTEXT_IMAGEPREVIEW, array(
1791  'width' => $sizeParts[0],
1792  'height' => $sizeParts[1]
1793  ))->getPublicUrl(true);
1794  $image = '<img src="' . htmlspecialchars($imageUrl) . '" hspace="2" border="0" title="' . htmlspecialchars($fileObject->getName()) . '"' . $tparams . ' alt="" />';
1795  if ($linkInfoPopup) {
1796  $onClick = 'top.launchView(\'_FILE\', ' . GeneralUtility::quoteJSvalue($fileName) . ',\'\',' . GeneralUtility::quoteJSvalue($backPath) . ');return false;';
1797  $thumbData .= '<a href="#" onclick="' . htmlspecialchars($onClick) . '">' . $image . '</a> ';
1798  } else {
1799  $thumbData .= $image;
1800  }
1801  } else {
1802  // Gets the icon
1803  $fileIcon = '<span title="' . htmlspecialchars($fileObject->getName()) . '">' . $iconFactory->getIconForResource($fileObject, Icon::SIZE_SMALL)->render() . '</span>';
1804  if ($linkInfoPopup) {
1805  $onClick = 'top.launchView(\'_FILE\', ' . GeneralUtility::quoteJSvalue($fileName) . ',\'\',' . GeneralUtility::quoteJSvalue($backPath) . '); return false;';
1806  $thumbData .= '<a href="#" onclick="' . htmlspecialchars($onClick) . '">' . $fileIcon . '</a> ';
1807  } else {
1808  $thumbData .= $fileIcon;
1809  }
1810  }
1811  }
1812  }
1813  }
1814  return $thumbData;
1815  }
1816 
1827  public static function getThumbNail($thumbScript, $theFile, $tparams = '', $size = '')
1828  {
1829  GeneralUtility::logDeprecatedFunction();
1830  $size = trim($size);
1831  $check = basename($theFile) . ':' . filemtime($theFile) . ':' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'];
1832  $params = '&file=' . rawurlencode($theFile);
1833  $params .= $size ? '&size=' . $size : '';
1834  $params .= '&md5sum=' . md5($check);
1835  $url = $thumbScript . '?' . $params;
1836  $th = '<img src="' . htmlspecialchars($url) . '" title="' . trim(basename($theFile)) . '"' . ($tparams ? ' ' . $tparams : '') . ' alt="" />';
1837  return $th;
1838  }
1839 
1848  public static function titleAttribForPages($row, $perms_clause = '', $includeAttrib = true)
1849  {
1850  $lang = static::getLanguageService();
1851  $parts = array();
1852  $parts[] = 'id=' . $row['uid'];
1853  if ($row['alias']) {
1854  $parts[] = $lang->sL($GLOBALS['TCA']['pages']['columns']['alias']['label']) . ' ' . $row['alias'];
1855  }
1856  if ($row['pid'] < 0) {
1857  $parts[] = 'v#1.' . $row['t3ver_id'];
1858  }
1859  switch (VersionState::cast($row['t3ver_state'])) {
1860  case new VersionState(VersionState::NEW_PLACEHOLDER):
1861  $parts[] = 'PLH WSID#' . $row['t3ver_wsid'];
1862  break;
1863  case new VersionState(VersionState::DELETE_PLACEHOLDER):
1864  $parts[] = 'Deleted element!';
1865  break;
1866  case new VersionState(VersionState::MOVE_PLACEHOLDER):
1867  $parts[] = 'NEW LOCATION (PLH) WSID#' . $row['t3ver_wsid'];
1868  break;
1869  case new VersionState(VersionState::MOVE_POINTER):
1870  $parts[] = 'OLD LOCATION (PNT) WSID#' . $row['t3ver_wsid'];
1871  break;
1872  case new VersionState(VersionState::NEW_PLACEHOLDER_VERSION):
1873  $parts[] = 'New element!';
1874  break;
1875  }
1876  if ($row['doktype'] == PageRepository::DOKTYPE_LINK) {
1877  $parts[] = $lang->sL($GLOBALS['TCA']['pages']['columns']['url']['label']) . ' ' . $row['url'];
1878  } elseif ($row['doktype'] == PageRepository::DOKTYPE_SHORTCUT) {
1879  if ($perms_clause) {
1880  $label = self::getRecordPath((int)$row['shortcut'], $perms_clause, 20);
1881  } else {
1882  $row['shortcut'] = (int)$row['shortcut'];
1883  $lRec = self::getRecordWSOL('pages', $row['shortcut'], 'title');
1884  $label = $lRec['title'] . ' (id=' . $row['shortcut'] . ')';
1885  }
1886  if ($row['shortcut_mode'] != PageRepository::SHORTCUT_MODE_NONE) {
1887  $label .= ', ' . $lang->sL($GLOBALS['TCA']['pages']['columns']['shortcut_mode']['label']) . ' ' . $lang->sL(self::getLabelFromItemlist('pages', 'shortcut_mode', $row['shortcut_mode']));
1888  }
1889  $parts[] = $lang->sL($GLOBALS['TCA']['pages']['columns']['shortcut']['label']) . ' ' . $label;
1890  } elseif ($row['doktype'] == PageRepository::DOKTYPE_MOUNTPOINT) {
1891  if ($perms_clause) {
1892  $label = self::getRecordPath((int)$row['mount_pid'], $perms_clause, 20);
1893  } else {
1894  $lRec = self::getRecordWSOL('pages', (int)$row['mount_pid'], 'title');
1895  $label = $lRec['title'] . ' (id=' . $row['mount_pid'] . ')';
1896  }
1897  $parts[] = $lang->sL($GLOBALS['TCA']['pages']['columns']['mount_pid']['label']) . ' ' . $label;
1898  if ($row['mount_pid_ol']) {
1899  $parts[] = $lang->sL($GLOBALS['TCA']['pages']['columns']['mount_pid_ol']['label']);
1900  }
1901  }
1902  if ($row['nav_hide']) {
1903  $parts[] = rtrim($lang->sL($GLOBALS['TCA']['pages']['columns']['nav_hide']['label']), ':');
1904  }
1905  if ($row['hidden']) {
1906  $parts[] = $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.hidden');
1907  }
1908  if ($row['starttime']) {
1909  $parts[] = $lang->sL($GLOBALS['TCA']['pages']['columns']['starttime']['label']) . ' ' . self::dateTimeAge($row['starttime'], -1, 'date');
1910  }
1911  if ($row['endtime']) {
1912  $parts[] = $lang->sL($GLOBALS['TCA']['pages']['columns']['endtime']['label']) . ' ' . self::dateTimeAge($row['endtime'], -1, 'date');
1913  }
1914  if ($row['fe_group']) {
1915  $fe_groups = array();
1916  foreach (GeneralUtility::intExplode(',', $row['fe_group']) as $fe_group) {
1917  if ($fe_group < 0) {
1918  $fe_groups[] = $lang->sL(self::getLabelFromItemlist('pages', 'fe_group', $fe_group));
1919  } else {
1920  $lRec = self::getRecordWSOL('fe_groups', $fe_group, 'title');
1921  $fe_groups[] = $lRec['title'];
1922  }
1923  }
1924  $label = implode(', ', $fe_groups);
1925  $parts[] = $lang->sL($GLOBALS['TCA']['pages']['columns']['fe_group']['label']) . ' ' . $label;
1926  }
1927  $out = htmlspecialchars(implode(' - ', $parts));
1928  return $includeAttrib ? 'title="' . $out . '"' : $out;
1929  }
1930 
1938  public static function getRecordToolTip(array $row, $table = 'pages')
1939  {
1940  $toolTipText = self::getRecordIconAltText($row, $table);
1941  $toolTipCode = 'data-toggle="tooltip" data-title=" ' . str_replace(' - ', '<br>', $toolTipText) . '" data-html="true" data-placement="right"';
1942  return $toolTipCode;
1943  }
1944 
1954  public static function getRecordIconAltText($row, $table = 'pages')
1955  {
1956  if ($table == 'pages') {
1957  $out = self::titleAttribForPages($row, '', 0);
1958  } else {
1959  $out = !empty(trim($GLOBALS['TCA'][$table]['ctrl']['descriptionColumn'])) ? $row[$GLOBALS['TCA'][$table]['ctrl']['descriptionColumn']] . ' ' : '';
1960  $ctrl = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns'];
1961  // Uid is added
1962  $out .= 'id=' . $row['uid'];
1963  if ($table == 'pages' && $row['alias']) {
1964  $out .= ' / ' . $row['alias'];
1965  }
1966  if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS'] && $row['pid'] < 0) {
1967  $out .= ' - v#1.' . $row['t3ver_id'];
1968  }
1969  if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
1970  switch (VersionState::cast($row['t3ver_state'])) {
1971  case new VersionState(VersionState::NEW_PLACEHOLDER):
1972  $out .= ' - PLH WSID#' . $row['t3ver_wsid'];
1973  break;
1974  case new VersionState(VersionState::DELETE_PLACEHOLDER):
1975  $out .= ' - Deleted element!';
1976  break;
1977  case new VersionState(VersionState::MOVE_PLACEHOLDER):
1978  $out .= ' - NEW LOCATION (PLH) WSID#' . $row['t3ver_wsid'];
1979  break;
1980  case new VersionState(VersionState::MOVE_POINTER):
1981  $out .= ' - OLD LOCATION (PNT) WSID#' . $row['t3ver_wsid'];
1982  break;
1983  case new VersionState(VersionState::NEW_PLACEHOLDER_VERSION):
1984  $out .= ' - New element!';
1985  break;
1986  }
1987  }
1988  // Hidden
1989  $lang = static::getLanguageService();
1990  if ($ctrl['disabled']) {
1991  $out .= $row[$ctrl['disabled']] ? ' - ' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.hidden') : '';
1992  }
1993  if ($ctrl['starttime']) {
1994  if ($row[$ctrl['starttime']] > $GLOBALS['EXEC_TIME']) {
1995  $out .= ' - ' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.starttime') . ':' . self::date($row[$ctrl['starttime']]) . ' (' . self::daysUntil($row[$ctrl['starttime']]) . ' ' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.days') . ')';
1996  }
1997  }
1998  if ($row[$ctrl['endtime']]) {
1999  $out .= ' - ' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.endtime') . ': ' . self::date($row[$ctrl['endtime']]) . ' (' . self::daysUntil($row[$ctrl['endtime']]) . ' ' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.days') . ')';
2000  }
2001  }
2002  return htmlspecialchars($out);
2003  }
2004 
2013  public static function getLabelFromItemlist($table, $col, $key)
2014  {
2015  // Check, if there is an "items" array:
2016  if (is_array($GLOBALS['TCA'][$table]) && is_array($GLOBALS['TCA'][$table]['columns'][$col]) && is_array($GLOBALS['TCA'][$table]['columns'][$col]['config']['items'])) {
2017  // Traverse the items-array...
2018  foreach ($GLOBALS['TCA'][$table]['columns'][$col]['config']['items'] as $v) {
2019  // ... and return the first found label where the value was equal to $key
2020  if ((string)$v[1] === (string)$key) {
2021  return $v[0];
2022  }
2023  }
2024  }
2025  return '';
2026  }
2027 
2037  public static function getLabelFromItemListMerged($pageId, $table, $column, $key)
2038  {
2039  $pageTsConfig = static::getPagesTSconfig($pageId);
2040  $label = '';
2041  if (is_array($pageTsConfig['TCEFORM.']) && is_array($pageTsConfig['TCEFORM.'][$table . '.']) && is_array($pageTsConfig['TCEFORM.'][$table . '.'][$column . '.'])) {
2042  if (is_array($pageTsConfig['TCEFORM.'][$table . '.'][$column . '.']['addItems.']) && isset($pageTsConfig['TCEFORM.'][$table . '.'][$column . '.']['addItems.'][$key])) {
2043  $label = $pageTsConfig['TCEFORM.'][$table . '.'][$column . '.']['addItems.'][$key];
2044  } elseif (is_array($pageTsConfig['TCEFORM.'][$table . '.'][$column . '.']['altLabels.']) && isset($pageTsConfig['TCEFORM.'][$table . '.'][$column . '.']['altLabels.'][$key])) {
2045  $label = $pageTsConfig['TCEFORM.'][$table . '.'][$column . '.']['altLabels.'][$key];
2046  }
2047  }
2048  if (empty($label)) {
2049  $tcaValue = self::getLabelFromItemlist($table, $column, $key);
2050  if (!empty($tcaValue)) {
2051  $label = $tcaValue;
2052  }
2053  }
2054  return $label;
2055  }
2056 
2067  public static function getLabelsFromItemsList($table, $column, $keyList, array $columnTsConfig = array())
2068  {
2069  // Check if there is an "items" array
2070  if (
2071  !isset($GLOBALS['TCA'][$table]['columns'][$column]['config']['items'])
2072  || !is_array($GLOBALS['TCA'][$table]['columns'][$column]['config']['items'])
2073  || $keyList === ''
2074  ) {
2075  return '';
2076  }
2077 
2078  $keys = GeneralUtility::trimExplode(',', $keyList, true);
2079  $labels = array();
2080  // Loop on all selected values
2081  foreach ($keys as $key) {
2082  $label = null;
2083  if ($columnTsConfig) {
2084  // Check if label has been defined or redefined via pageTsConfig
2085  if (isset($columnTsConfig['addItems.'][$key])) {
2086  $label = $columnTsConfig['addItems.'][$key];
2087  } elseif (isset($columnTsConfig['altLabels.'][$key])) {
2088  $label = $columnTsConfig['altLabels.'][$key];
2089  }
2090  }
2091  if ($label === null) {
2092  // Otherwise lookup the label in TCA items list
2093  foreach ($GLOBALS['TCA'][$table]['columns'][$column]['config']['items'] as $itemConfiguration) {
2094  list($currentLabel, $currentKey) = $itemConfiguration;
2095  if ((string)$key === (string)$currentKey) {
2096  $label = $currentLabel;
2097  break;
2098  }
2099  }
2100  }
2101  if ($label !== null) {
2102  $labels[] = static::getLanguageService()->sL($label);
2103  }
2104  }
2105  return implode(', ', $labels);
2106  }
2107 
2117  public static function getItemLabel($table, $col, $printAllWrap = '')
2118  {
2119  // Check if column exists
2120  if (is_array($GLOBALS['TCA'][$table]) && is_array($GLOBALS['TCA'][$table]['columns'][$col])) {
2121  return $GLOBALS['TCA'][$table]['columns'][$col]['label'];
2122  }
2123  if ($printAllWrap) {
2124  GeneralUtility::deprecationLog('The third parameter of getItemLabel() is deprecated with TYPO3 CMS 6.2 and will be removed two versions later.');
2125  $parts = explode('|', $printAllWrap);
2126  return $parts[0] . $col . $parts[1];
2127  }
2128 
2129  return null;
2130  }
2131 
2140  protected static function replaceL10nModeFields($table, array $row)
2141  {
2142  $originalUidField = isset($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'])
2143  ? $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']
2144  : '';
2145  if (empty($row[$originalUidField])) {
2146  return $row;
2147  }
2148 
2149  $originalTable = self::getOriginalTranslationTable($table);
2150  $originalRow = self::getRecord($originalTable, $row[$originalUidField]);
2151  foreach ($row as $field => $_) {
2152  $l10n_mode = isset($GLOBALS['TCA'][$originalTable]['columns'][$field]['l10n_mode'])
2153  ? $GLOBALS['TCA'][$originalTable]['columns'][$field]['l10n_mode']
2154  : '';
2155  if ($l10n_mode === 'exclude' || ($l10n_mode === 'mergeIfNotBlank' && trim($row[$field]) === '')) {
2156  $row[$field] = $originalRow[$field];
2157  }
2158  }
2159  return $row;
2160  }
2161 
2172  public static function getRecordTitle($table, $row, $prep = false, $forceResult = true)
2173  {
2174  $recordTitle = '';
2175  if (is_array($GLOBALS['TCA'][$table])) {
2176  // If configured, call userFunc
2177  if ($GLOBALS['TCA'][$table]['ctrl']['label_userFunc']) {
2178  $params['table'] = $table;
2179  $params['row'] = $row;
2180  $params['title'] = '';
2181  $params['options'] = isset($GLOBALS['TCA'][$table]['ctrl']['label_userFunc_options']) ? $GLOBALS['TCA'][$table]['ctrl']['label_userFunc_options'] : array();
2182 
2183  // Create NULL-reference
2184  $null = null;
2185  GeneralUtility::callUserFunction($GLOBALS['TCA'][$table]['ctrl']['label_userFunc'], $params, $null);
2186  $recordTitle = $params['title'];
2187  } else {
2188  if (is_array($row)) {
2189  $row = self::replaceL10nModeFields($table, $row);
2190  }
2191 
2192  // No userFunc: Build label
2193  $recordTitle = self::getProcessedValue($table, $GLOBALS['TCA'][$table]['ctrl']['label'], $row[$GLOBALS['TCA'][$table]['ctrl']['label']], 0, 0, false, $row['uid'], $forceResult);
2194  if ($GLOBALS['TCA'][$table]['ctrl']['label_alt'] && ($GLOBALS['TCA'][$table]['ctrl']['label_alt_force'] || (string)$recordTitle === '')) {
2195  $altFields = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['ctrl']['label_alt'], true);
2196  $tA = array();
2197  if (!empty($recordTitle)) {
2198  $tA[] = $recordTitle;
2199  }
2200  foreach ($altFields as $fN) {
2201  $recordTitle = trim(strip_tags($row[$fN]));
2202  if ((string)$recordTitle !== '') {
2203  $recordTitle = self::getProcessedValue($table, $fN, $recordTitle, 0, 0, false, $row['uid']);
2204  if (!$GLOBALS['TCA'][$table]['ctrl']['label_alt_force']) {
2205  break;
2206  }
2207  $tA[] = $recordTitle;
2208  }
2209  }
2210  if ($GLOBALS['TCA'][$table]['ctrl']['label_alt_force']) {
2211  $recordTitle = implode(', ', $tA);
2212  }
2213  }
2214  }
2215  // If the current result is empty, set it to '[No title]' (localized) and prepare for output if requested
2216  if ($prep || $forceResult) {
2217  if ($prep) {
2218  $recordTitle = self::getRecordTitlePrep($recordTitle);
2219  }
2220  if (trim($recordTitle) === '') {
2221  $recordTitle = self::getNoRecordTitle($prep);
2222  }
2223  }
2224  }
2225 
2226  return $recordTitle;
2227  }
2228 
2237  public static function getRecordTitlePrep($title, $titleLength = 0)
2238  {
2239  // If $titleLength is not a valid positive integer, use BE_USER->uc['titleLen']:
2240  if (!$titleLength || !MathUtility::canBeInterpretedAsInteger($titleLength) || $titleLength < 0) {
2241  $titleLength = static::getBackendUserAuthentication()->uc['titleLen'];
2242  }
2243  $titleOrig = htmlspecialchars($title);
2244  $title = htmlspecialchars(GeneralUtility::fixed_lgd_cs($title, $titleLength));
2245  // If title was cropped, offer a tooltip:
2246  if ($titleOrig != $title) {
2247  $title = '<span title="' . $titleOrig . '">' . $title . '</span>';
2248  }
2249  return $title;
2250  }
2251 
2258  public static function getNoRecordTitle($prep = false)
2259  {
2260  $noTitle = '[' . static::getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.no_title', true) . ']';
2261  if ($prep) {
2262  $noTitle = '<em>' . $noTitle . '</em>';
2263  }
2264  return $noTitle;
2265  }
2266 
2285  public static function getProcessedValue($table, $col, $value, $fixed_lgd_chars = 0, $defaultPassthrough = false, $noRecordLookup = false, $uid = 0, $forceResult = true, $pid = 0)
2286  {
2287  if ($col === 'uid') {
2288  // uid is not in TCA-array
2289  return $value;
2290  }
2291  // Check if table and field is configured
2292  if (!is_array($GLOBALS['TCA'][$table]) || !is_array($GLOBALS['TCA'][$table]['columns'][$col])) {
2293  return null;
2294  }
2295  // Depending on the fields configuration, make a meaningful output value.
2296  $theColConf = $GLOBALS['TCA'][$table]['columns'][$col]['config'];
2297  /*****************
2298  *HOOK: pre-processing the human readable output from a record
2299  ****************/
2300  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['preProcessValue'])) {
2301  // Create NULL-reference
2302  $null = null;
2303  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['preProcessValue'] as $_funcRef) {
2304  GeneralUtility::callUserFunction($_funcRef, $theColConf, $null);
2305  }
2306  }
2307  $l = '';
2308  $db = static::getDatabaseConnection();
2309  $lang = static::getLanguageService();
2310  switch ((string)$theColConf['type']) {
2311  case 'radio':
2312  $l = self::getLabelFromItemlist($table, $col, $value);
2313  $l = $lang->sL($l);
2314  break;
2315  case 'inline':
2316  case 'select':
2317  if ($theColConf['MM']) {
2318  if ($uid) {
2319  // Display the title of MM related records in lists
2320  if ($noRecordLookup) {
2321  $MMfield = $theColConf['foreign_table'] . '.uid';
2322  } else {
2323  $MMfields = array($theColConf['foreign_table'] . '.' . $GLOBALS['TCA'][$theColConf['foreign_table']]['ctrl']['label']);
2324  foreach (GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$theColConf['foreign_table']]['ctrl']['label_alt'], true) as $f) {
2325  $MMfields[] = $theColConf['foreign_table'] . '.' . $f;
2326  }
2327  $MMfield = join(',', $MMfields);
2328  }
2330  $dbGroup = GeneralUtility::makeInstance(RelationHandler::class);
2331  $dbGroup->start($value, $theColConf['foreign_table'], $theColConf['MM'], $uid, $table, $theColConf);
2332  $selectUids = $dbGroup->tableArray[$theColConf['foreign_table']];
2333  if (is_array($selectUids) && !empty($selectUids)) {
2334  $MMres = $db->exec_SELECTquery('uid, ' . $MMfield, $theColConf['foreign_table'], 'uid IN (' . implode(',', $selectUids) . ')' . self::deleteClause($theColConf['foreign_table']));
2335  $mmlA = array();
2336  while ($MMrow = $db->sql_fetch_assoc($MMres)) {
2337  // Keep sorting of $selectUids
2338  $mmlA[array_search($MMrow['uid'], $selectUids)] = $noRecordLookup ?
2339  $MMrow['uid'] :
2340  static::getRecordTitle($theColConf['foreign_table'], $MMrow, false, $forceResult);
2341  }
2342  $db->sql_free_result($MMres);
2343  if (!empty($mmlA)) {
2344  ksort($mmlA);
2345  $l = implode('; ', $mmlA);
2346  } else {
2347  $l = 'N/A';
2348  }
2349  } else {
2350  $l = 'N/A';
2351  }
2352  } else {
2353  $l = 'N/A';
2354  }
2355  } else {
2356  $columnTsConfig = array();
2357  if ($pid) {
2358  $pageTsConfig = self::getPagesTSconfig($pid);
2359  if (isset($pageTsConfig['TCEFORM.'][$table . '.'][$col . '.']) && is_array($pageTsConfig['TCEFORM.'][$table . '.'][$col . '.'])) {
2360  $columnTsConfig = $pageTsConfig['TCEFORM.'][$table . '.'][$col . '.'];
2361  }
2362  }
2363  $l = self::getLabelsFromItemsList($table, $col, $value, $columnTsConfig);
2364  if ($theColConf['foreign_table'] && !$l && $GLOBALS['TCA'][$theColConf['foreign_table']]) {
2365  if ($noRecordLookup) {
2366  $l = $value;
2367  } else {
2368  $rParts = array();
2369  if ($uid && isset($theColConf['foreign_field']) && $theColConf['foreign_field'] !== '') {
2370  $whereClause = '';
2371  if (!empty($theColConf['foreign_table_field'])) {
2372  $whereClause .= ' AND ' . $theColConf['foreign_table_field'] . ' = ' . static::getDatabaseConnection()->fullQuoteStr($table, $theColConf['foreign_table']);
2373  }
2374  // Add additional where clause if foreign_match_fields are defined
2375  $foreignMatchFields = is_array($theColConf['foreign_match_fields']) ? $theColConf['foreign_match_fields'] : array();
2376  foreach ($foreignMatchFields as $matchField => $matchValue) {
2377  $whereClause .= ' AND ' . $matchField . '=' . static::getDatabaseConnection()->fullQuoteStr($matchValue, $theColConf['foreign_table']);
2378  }
2379  $records = self::getRecordsByField($theColConf['foreign_table'], $theColConf['foreign_field'], $uid, $whereClause);
2380  if (!empty($records)) {
2381  foreach ($records as $record) {
2382  $rParts[] = $record['uid'];
2383  }
2384  }
2385  }
2386  if (empty($rParts)) {
2387  $rParts = GeneralUtility::trimExplode(',', $value, true);
2388  }
2389  $lA = array();
2390  foreach ($rParts as $rVal) {
2391  $rVal = (int)$rVal;
2392  $r = self::getRecordWSOL($theColConf['foreign_table'], $rVal);
2393  if (is_array($r)) {
2394  $lA[] = $lang->sL($theColConf['foreign_table_prefix']) . self::getRecordTitle($theColConf['foreign_table'], $r, false, $forceResult);
2395  } else {
2396  $lA[] = $rVal ? '[' . $rVal . '!]' : '';
2397  }
2398  }
2399  $l = implode(', ', $lA);
2400  }
2401  }
2402  if (empty($l) && !empty($value)) {
2403  // Use plain database value when label is empty
2404  $l = $value;
2405  }
2406  }
2407  break;
2408  case 'group':
2409  // resolve the titles for DB records
2410  if ($theColConf['internal_type'] === 'db') {
2411  if ($theColConf['MM']) {
2412  if ($uid) {
2413  // Display the title of MM related records in lists
2414  if ($noRecordLookup) {
2415  $MMfield = $theColConf['foreign_table'] . '.uid';
2416  } else {
2417  $MMfields = array($theColConf['foreign_table'] . '.' . $GLOBALS['TCA'][$theColConf['foreign_table']]['ctrl']['label']);
2418  $altLabelFields = explode(',', $GLOBALS['TCA'][$theColConf['foreign_table']]['ctrl']['label_alt']);
2419  foreach ($altLabelFields as $f) {
2420  $f = trim($f);
2421  if ($f !== '') {
2422  $MMfields[] = $theColConf['foreign_table'] . '.' . $f;
2423  }
2424  }
2425  $MMfield = join(',', $MMfields);
2426  }
2428  $dbGroup = GeneralUtility::makeInstance(RelationHandler::class);
2429  $dbGroup->start($value, $theColConf['foreign_table'], $theColConf['MM'], $uid, $table, $theColConf);
2430  $selectUids = $dbGroup->tableArray[$theColConf['foreign_table']];
2431  if (!empty($selectUids) && is_array($selectUids)) {
2432  $MMres = $db->exec_SELECTquery(
2433  'uid, ' . $MMfield,
2434  $theColConf['foreign_table'],
2435  'uid IN (' . implode(',', $selectUids) . ')' . static::deleteClause($theColConf['foreign_table'])
2436  );
2437  $mmlA = array();
2438  while ($MMrow = $db->sql_fetch_assoc($MMres)) {
2439  // Keep sorting of $selectUids
2440  $mmlA[array_search($MMrow['uid'], $selectUids)] = $noRecordLookup
2441  ? $MMrow['uid']
2442  : static::getRecordTitle($theColConf['foreign_table'], $MMrow, false, $forceResult);
2443  }
2444  $db->sql_free_result($MMres);
2445  if (!empty($mmlA)) {
2446  ksort($mmlA);
2447  $l = implode('; ', $mmlA);
2448  } else {
2449  $l = 'N/A';
2450  }
2451  } else {
2452  $l = 'N/A';
2453  }
2454  } else {
2455  $l = 'N/A';
2456  }
2457  } else {
2458  $finalValues = array();
2459  $relationTableName = $theColConf['allowed'];
2460  $explodedValues = GeneralUtility::trimExplode(',', $value, true);
2461 
2462  foreach ($explodedValues as $explodedValue) {
2463  if (MathUtility::canBeInterpretedAsInteger($explodedValue)) {
2464  $relationTableNameForField = $relationTableName;
2465  } else {
2466  list($relationTableNameForField, $explodedValue) = self::splitTable_Uid($explodedValue);
2467  }
2468 
2469  $relationRecord = static::getRecordWSOL($relationTableNameForField, $explodedValue);
2470  $finalValues[] = static::getRecordTitle($relationTableNameForField, $relationRecord);
2471  }
2472  $l = implode(', ', $finalValues);
2473  }
2474  } else {
2475  $l = implode(', ', GeneralUtility::trimExplode(',', $value, true));
2476  }
2477  break;
2478  case 'check':
2479  if (!is_array($theColConf['items']) || count($theColConf['items']) === 1) {
2480  $l = $value ? $lang->sL('LLL:EXT:lang/locallang_common.xlf:yes') : $lang->sL('LLL:EXT:lang/locallang_common.xlf:no');
2481  } else {
2482  $lA = array();
2483  foreach ($theColConf['items'] as $key => $val) {
2484  if ($value & pow(2, $key)) {
2485  $lA[] = $lang->sL($val[0]);
2486  }
2487  }
2488  $l = implode(', ', $lA);
2489  }
2490  break;
2491  case 'input':
2492  // Hide value 0 for dates, but show it for everything else
2493  if (isset($value)) {
2494  if (GeneralUtility::inList($theColConf['eval'], 'date')) {
2495  // Handle native date field
2496  if (isset($theColConf['dbType']) && $theColConf['dbType'] === 'date') {
2497  $dateTimeFormats = $db->getDateTimeFormats($table);
2498  $emptyValue = $dateTimeFormats['date']['empty'];
2499  $value = $value !== $emptyValue ? strtotime($value) : 0;
2500  }
2501  if (!empty($value)) {
2502  $ageSuffix = '';
2503  $dateColumnConfiguration = $GLOBALS['TCA'][$table]['columns'][$col]['config'];
2504  $ageDisplayKey = 'disableAgeDisplay';
2505 
2506  // generate age suffix as long as not explicitly suppressed
2507  if (!isset($dateColumnConfiguration[$ageDisplayKey])
2508  // non typesafe comparison on intention
2509  || $dateColumnConfiguration[$ageDisplayKey] == false) {
2510  $ageSuffix = ' (' . ($GLOBALS['EXEC_TIME'] - $value > 0 ? '-' : '') . self::calcAge(abs(($GLOBALS['EXEC_TIME'] - $value)), $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.minutesHoursDaysYears')) . ')';
2511  }
2512 
2513  $l = self::date($value) . $ageSuffix;
2514  }
2515  } elseif (GeneralUtility::inList($theColConf['eval'], 'time')) {
2516  if (!empty($value)) {
2517  $l = self::time($value, false);
2518  }
2519  } elseif (GeneralUtility::inList($theColConf['eval'], 'timesec')) {
2520  if (!empty($value)) {
2521  $l = self::time($value);
2522  }
2523  } elseif (GeneralUtility::inList($theColConf['eval'], 'datetime')) {
2524  // Handle native date/time field
2525  if (isset($theColConf['dbType']) && $theColConf['dbType'] === 'datetime') {
2526  $dateTimeFormats = $db->getDateTimeFormats($table);
2527  $emptyValue = $dateTimeFormats['datetime']['empty'];
2528  $value = $value !== $emptyValue ? strtotime($value) : 0;
2529  }
2530  if (!empty($value)) {
2531  $l = self::datetime($value);
2532  }
2533  } else {
2534  $l = $value;
2535  }
2536  }
2537  break;
2538  case 'flex':
2539  $l = strip_tags($value);
2540  break;
2541  default:
2542  if ($defaultPassthrough) {
2543  $l = $value;
2544  } elseif ($theColConf['MM']) {
2545  $l = 'N/A';
2546  } elseif ($value) {
2547  $l = GeneralUtility::fixed_lgd_cs(strip_tags($value), 200);
2548  }
2549  }
2550  // If this field is a password field, then hide the password by changing it to a random number of asterisk (*)
2551  if (stristr($theColConf['eval'], 'password')) {
2552  $l = '';
2553  $randomNumber = rand(5, 12);
2554  for ($i = 0; $i < $randomNumber; $i++) {
2555  $l .= '*';
2556  }
2557  }
2558  /*****************
2559  *HOOK: post-processing the human readable output from a record
2560  ****************/
2561  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['postProcessValue'])) {
2562  // Create NULL-reference
2563  $null = null;
2564  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['postProcessValue'] as $_funcRef) {
2565  $params = array(
2566  'value' => $l,
2567  'colConf' => $theColConf
2568  );
2569  $l = GeneralUtility::callUserFunction($_funcRef, $params, $null);
2570  }
2571  }
2572  if ($fixed_lgd_chars) {
2573  return GeneralUtility::fixed_lgd_cs($l, $fixed_lgd_chars);
2574  } else {
2575  return $l;
2576  }
2577  }
2578 
2592  public static function getProcessedValueExtra($table, $fN, $fV, $fixed_lgd_chars = 0, $uid = 0, $forceResult = true, $pid = 0)
2593  {
2594  $fVnew = self::getProcessedValue($table, $fN, $fV, $fixed_lgd_chars, 1, 0, $uid, $forceResult, $pid);
2595  if (!isset($fVnew)) {
2596  if (is_array($GLOBALS['TCA'][$table])) {
2597  if ($fN == $GLOBALS['TCA'][$table]['ctrl']['tstamp'] || $fN == $GLOBALS['TCA'][$table]['ctrl']['crdate']) {
2598  $fVnew = self::datetime($fV);
2599  } elseif ($fN == 'pid') {
2600  // Fetches the path with no regard to the users permissions to select pages.
2601  $fVnew = self::getRecordPath($fV, '1=1', 20);
2602  } else {
2603  $fVnew = $fV;
2604  }
2605  }
2606  }
2607  return $fVnew;
2608  }
2609 
2620  public static function getCommonSelectFields($table, $prefix = '', $fields = array())
2621  {
2622  $fields[] = $prefix . 'uid';
2623  if (isset($GLOBALS['TCA'][$table]['ctrl']['label']) && $GLOBALS['TCA'][$table]['ctrl']['label'] != '') {
2624  $fields[] = $prefix . $GLOBALS['TCA'][$table]['ctrl']['label'];
2625  }
2626  if ($GLOBALS['TCA'][$table]['ctrl']['label_alt']) {
2627  $secondFields = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['ctrl']['label_alt'], true);
2628  foreach ($secondFields as $fieldN) {
2629  $fields[] = $prefix . $fieldN;
2630  }
2631  }
2632  if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
2633  $fields[] = $prefix . 't3ver_id';
2634  $fields[] = $prefix . 't3ver_state';
2635  $fields[] = $prefix . 't3ver_wsid';
2636  $fields[] = $prefix . 't3ver_count';
2637  }
2638  if ($GLOBALS['TCA'][$table]['ctrl']['selicon_field']) {
2639  $fields[] = $prefix . $GLOBALS['TCA'][$table]['ctrl']['selicon_field'];
2640  }
2641  if ($GLOBALS['TCA'][$table]['ctrl']['typeicon_column']) {
2642  $fields[] = $prefix . $GLOBALS['TCA'][$table]['ctrl']['typeicon_column'];
2643  }
2644  if (is_array($GLOBALS['TCA'][$table]['ctrl']['enablecolumns'])) {
2645  if ($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled']) {
2646  $fields[] = $prefix . $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'];
2647  }
2648  if ($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['starttime']) {
2649  $fields[] = $prefix . $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['starttime'];
2650  }
2651  if ($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['endtime']) {
2652  $fields[] = $prefix . $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['endtime'];
2653  }
2654  if ($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['fe_group']) {
2655  $fields[] = $prefix . $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['fe_group'];
2656  }
2657  }
2658  return implode(',', array_unique($fields));
2659  }
2660 
2673  public static function makeConfigForm($configArray, $defaults, $dataPrefix)
2674  {
2675  $params = $defaults;
2676  $lines = array();
2677  if (is_array($configArray)) {
2678  foreach ($configArray as $fname => $config) {
2679  if (is_array($config)) {
2680  $lines[$fname] = '<strong>' . htmlspecialchars($config[1]) . '</strong><br />';
2681  $lines[$fname] .= $config[2] . '<br />';
2682  switch ($config[0]) {
2683  case 'string':
2684 
2685  case 'short':
2686  $formEl = '<input type="text" name="' . $dataPrefix . '[' . $fname . ']" value="' . $params[$fname] . '"' . static::getDocumentTemplate()->formWidth(($config[0] == 'short' ? 24 : 48)) . ' />';
2687  break;
2688  case 'check':
2689  $formEl = '<input type="hidden" name="' . $dataPrefix . '[' . $fname . ']" value="0" /><input type="checkbox" name="' . $dataPrefix . '[' . $fname . ']" value="1"' . ($params[$fname] ? ' checked="checked"' : '') . ' />';
2690  break;
2691  case 'comment':
2692  $formEl = '';
2693  break;
2694  case 'select':
2695  $opt = array();
2696  foreach ($config[3] as $k => $v) {
2697  $opt[] = '<option value="' . htmlspecialchars($k) . '"' . ($params[$fname] == $k ? ' selected="selected"' : '') . '>' . htmlspecialchars($v) . '</option>';
2698  }
2699  $formEl = '<select name="' . $dataPrefix . '[' . $fname . ']">' . implode('', $opt) . '</select>';
2700  break;
2701  default:
2702  $formEl = '<strong>Should not happen. Bug in config.</strong>';
2703  }
2704  $lines[$fname] .= $formEl;
2705  $lines[$fname] .= '<br /><br />';
2706  } else {
2707  $lines[$fname] = '<hr />';
2708  if ($config) {
2709  $lines[$fname] .= '<strong>' . strtoupper(htmlspecialchars($config)) . '</strong><br />';
2710  }
2711  if ($config) {
2712  $lines[$fname] .= '<br />';
2713  }
2714  }
2715  }
2716  }
2717  $out = implode('', $lines);
2718  $out .= '<input class="btn btn-default" type="submit" name="submit" value="Update configuration" />';
2719  return $out;
2720  }
2721 
2722  /*******************************************
2723  *
2724  * Backend Modules API functions
2725  *
2726  *******************************************/
2743  public static function helpTextIcon($table, $field, $_ = '', $force = false)
2744  {
2745  GeneralUtility::logDeprecatedFunction();
2746  if (is_array($GLOBALS['TCA_DESCR'][$table]) && is_array($GLOBALS['TCA_DESCR'][$table]['columns'][$field])) {
2747  return self::wrapInHelp($table, $field);
2748  }
2749  return '';
2750  }
2751 
2759  public static function helpTextArray($table, $field)
2760  {
2761  if (!isset($GLOBALS['TCA_DESCR'][$table]['columns'])) {
2762  static::getLanguageService()->loadSingleTableDescription($table);
2763  }
2764  $output = array(
2765  'description' => null,
2766  'title' => null,
2767  'moreInfo' => false
2768  );
2769  if (is_array($GLOBALS['TCA_DESCR'][$table]) && is_array($GLOBALS['TCA_DESCR'][$table]['columns'][$field])) {
2770  $data = $GLOBALS['TCA_DESCR'][$table]['columns'][$field];
2771  // Add alternative title, if defined
2772  if ($data['alttitle']) {
2773  $output['title'] = $data['alttitle'];
2774  }
2775  // If we have more information to show
2776  if ($data['image_descr'] || $data['seeAlso'] || $data['details'] || $data['syntax']) {
2777  $output['moreInfo'] = true;
2778  }
2779  // Add description
2780  if ($data['description']) {
2781  $output['description'] = $data['description'];
2782  }
2783  }
2784  return $output;
2785  }
2786 
2795  public static function helpText($table, $field)
2796  {
2797  $helpTextArray = self::helpTextArray($table, $field);
2798  $output = '';
2799  $arrow = '';
2800  // Put header before the rest of the text
2801  if ($helpTextArray['title'] !== null) {
2802  $output .= '<h2 class="t3-row-header">' . $helpTextArray['title'] . '</h2>';
2803  }
2804  // Add see also arrow if we have more info
2805  if ($helpTextArray['moreInfo']) {
2807  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
2808  $arrow = $iconFactory->getIcon('actions-view-go-forward', Icon::SIZE_SMALL)->render();
2809  }
2810  // Wrap description and arrow in p tag
2811  if ($helpTextArray['description'] !== null || $arrow) {
2812  $output .= '<p class="t3-help-short">' . nl2br(htmlspecialchars($helpTextArray['description'])) . $arrow . '</p>';
2813  }
2814  return $output;
2815  }
2816 
2829  public static function wrapInHelp($table, $field, $text = '', array $overloadHelpText = array())
2830  {
2831  // Initialize some variables
2832  $helpText = '';
2833  $abbrClassAdd = '';
2834  $wrappedText = $text;
2835  $hasHelpTextOverload = !empty($overloadHelpText);
2836  // Get the help text that should be shown on hover
2837  if (!$hasHelpTextOverload) {
2838  $helpText = self::helpText($table, $field);
2839  }
2840  // If there's a help text or some overload information, proceed with preparing an output
2841  // @todo: right now this is a hard dependency on csh manual, as the whole help system should be moved to
2842  // the extension. The core provides an API for adding help and rendering help, but the rendering
2843  // should be up to the extension itself
2844  if ((!empty($helpText) || $hasHelpTextOverload) && ExtensionManagementUtility::isLoaded('cshmanual')) {
2845  // If no text was given, just use the regular help icon
2846  if ($text == '') {
2848  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
2849  $text = $iconFactory->getIcon('actions-system-help-open', Icon::SIZE_SMALL)->render();
2850  $abbrClassAdd = '-icon';
2851  }
2852  $text = '<abbr class="t3-help-teaser' . $abbrClassAdd . '">' . $text . '</abbr>';
2853  $wrappedText = '<span class="t3-help-link" href="#" data-table="' . $table . '" data-field="' . $field . '"';
2854  // The overload array may provide a title and a description
2855  // If either one is defined, add them to the "data" attributes
2856  if ($hasHelpTextOverload) {
2857  if (isset($overloadHelpText['title'])) {
2858  $wrappedText .= ' data-title="' . htmlspecialchars($overloadHelpText['title']) . '"';
2859  }
2860  if (isset($overloadHelpText['description'])) {
2861  $wrappedText .= ' data-description="' . htmlspecialchars($overloadHelpText['description']) . '"';
2862  }
2863  }
2864  $wrappedText .= '>' . $text . '</span>';
2865  return $wrappedText;
2866  }
2867  return $text;
2868  }
2869 
2881  public static function cshItem($table, $field, $_ = '', $wrap = '')
2882  {
2883  static::getLanguageService()->loadSingleTableDescription($table);
2884  if (is_array($GLOBALS['TCA_DESCR'][$table])
2885  && is_array($GLOBALS['TCA_DESCR'][$table]['columns'][$field])) {
2886  // Creating short description
2887  $output = self::wrapInHelp($table, $field);
2888  if ($output && $wrap) {
2889  $wrParts = explode('|', $wrap);
2890  $output = $wrParts[0] . $output . $wrParts[1];
2891  }
2892  return $output;
2893  }
2894  return '';
2895  }
2896 
2907  public static function editOnClick($params, $_ = '', $requestUri = '')
2908  {
2909  if ($requestUri == -1) {
2910  $returnUrl = 'T3_THIS_LOCATION';
2911  } else {
2912  $returnUrl = GeneralUtility::quoteJSvalue(rawurlencode($requestUri ?: GeneralUtility::getIndpEnv('REQUEST_URI')));
2913  }
2914  return 'window.location.href=' . GeneralUtility::quoteJSvalue(self::getModuleUrl('record_edit') . $params . '&returnUrl=') . '+' . $returnUrl . '; return false;';
2915  }
2916 
2931  public static function viewOnClick($pageUid, $backPath = '', $rootLine = null, $anchorSection = '', $alternativeUrl = '', $additionalGetVars = '', $switchFocus = true)
2932  {
2933  $viewScript = '/index.php?id=';
2934  if ($alternativeUrl) {
2935  $viewScript = $alternativeUrl;
2936  }
2937 
2938  if (
2939  isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['viewOnClickClass'])
2940  && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['viewOnClickClass'])
2941  ) {
2942  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['viewOnClickClass'] as $funcRef) {
2943  $hookObj = GeneralUtility::getUserObj($funcRef);
2944  if (method_exists($hookObj, 'preProcess')) {
2945  $hookObj->preProcess($pageUid, $backPath, $rootLine, $anchorSection, $viewScript, $additionalGetVars, $switchFocus);
2946  }
2947  }
2948  }
2949 
2950  if ($alternativeUrl) {
2951  $previewUrl = $viewScript;
2952  } else {
2953  $previewUrl = self::createPreviewUrl($pageUid, $rootLine, $anchorSection, $additionalGetVars, $viewScript);
2954  }
2955 
2956  $onclickCode = 'var previewWin = window.open(' . GeneralUtility::quoteJSvalue($previewUrl) . ',\'newTYPO3frontendWindow\');' . ($switchFocus ? 'previewWin.focus();' : '');
2957  return $onclickCode;
2958  }
2959 
2986  public static function wrapClickMenuOnIcon(
2987  $content,
2988  $table,
2989  $uid = 0,
2990  $listFrame = true,
2991  $addParams = '',
2992  $enDisItems = '',
2993  $returnTagParameters = false
2994  ) {
2995  $tagParameters = array(
2996  'class' => 't3-js-clickmenutrigger',
2997  'data-table' => $table,
2998  'data-uid' => (int)$uid !== 0 ? (int)$uid : '',
2999  'data-listframe' => $listFrame,
3000  'data-iteminfo' => str_replace('+', '%2B', $enDisItems),
3001  'data-parameters' => $addParams,
3002  );
3003 
3004  if ($returnTagParameters) {
3005  return $tagParameters;
3006  }
3007  return '<a href="#" ' . GeneralUtility::implodeAttributes($tagParameters, true) . '>' . $content . '</a>';
3008  }
3009 
3017  public static function getLinkToDataHandlerAction($parameters, $redirectUrl = '')
3018  {
3019  $urlParameters = [
3020  'prErr' => 1,
3021  'uPT' => 1,
3022  'vC' => static::getBackendUserAuthentication()->veriCode()
3023  ];
3024  $url = BackendUtility::getModuleUrl('tce_db', $urlParameters) . $parameters . '&redirect=';
3025  if ((int)$redirectUrl === -1) {
3026  $url = GeneralUtility::quoteJSvalue($url) . '+T3_THIS_LOCATION';
3027  } else {
3028  $url .= rawurlencode($redirectUrl ?: GeneralUtility::getIndpEnv('REQUEST_URI'));
3029  }
3030  return $url;
3031  }
3032 
3044  protected static function createPreviewUrl($pageUid, $rootLine, $anchorSection, $additionalGetVars, $viewScript)
3045  {
3046  // Look if a fixed preview language should be added:
3047  $beUser = static::getBackendUserAuthentication();
3048  $viewLanguageOrder = $beUser->getTSConfigVal('options.view.languageOrder');
3049 
3050  if ((string)$viewLanguageOrder !== '') {
3051  $suffix = '';
3052  // Find allowed languages (if none, all are allowed!)
3053  $allowedLanguages = null;
3054  if (!$beUser->user['admin'] && $beUser->groupData['allowed_languages'] !== '') {
3055  $allowedLanguages = array_flip(explode(',', $beUser->groupData['allowed_languages']));
3056  }
3057  // Traverse the view order, match first occurrence:
3058  $languageOrder = GeneralUtility::intExplode(',', $viewLanguageOrder);
3059  foreach ($languageOrder as $langUid) {
3060  if (is_array($allowedLanguages) && !empty($allowedLanguages)) {
3061  // Choose if set.
3062  if (isset($allowedLanguages[$langUid])) {
3063  $suffix = '&L=' . $langUid;
3064  break;
3065  }
3066  } else {
3067  // All allowed since no lang. are listed.
3068  $suffix = '&L=' . $langUid;
3069  break;
3070  }
3071  }
3072  // Add it
3073  $additionalGetVars .= $suffix;
3074  }
3075 
3076  // Check a mount point needs to be previewed
3077  $sys_page = GeneralUtility::makeInstance(\TYPO3\CMS\Frontend\Page\PageRepository::class);
3078  $sys_page->init(false);
3079  $mountPointInfo = $sys_page->getMountPointInfo($pageUid);
3080 
3081  if ($mountPointInfo && $mountPointInfo['overlay']) {
3082  $pageUid = $mountPointInfo['mount_pid'];
3083  $additionalGetVars .= '&MP=' . $mountPointInfo['MPvar'];
3084  }
3085  $viewDomain = self::getViewDomain($pageUid, $rootLine);
3086 
3087  return $viewDomain . $viewScript . $pageUid . $additionalGetVars . $anchorSection;
3088  }
3089 
3098  public static function getViewDomain($pageId, $rootLine = null)
3099  {
3100  $domain = rtrim(GeneralUtility::getIndpEnv('TYPO3_SITE_URL'), '/');
3101  if (!is_array($rootLine)) {
3102  $rootLine = self::BEgetRootLine($pageId);
3103  }
3104  // Checks alternate domains
3105  if (!empty($rootLine)) {
3106  $urlParts = parse_url($domain);
3108  $sysPage = GeneralUtility::makeInstance(\TYPO3\CMS\Frontend\Page\PageRepository::class);
3109  $page = (array)$sysPage->getPage($pageId);
3110  $protocol = 'http';
3111  if ($page['url_scheme'] == HttpUtility::SCHEME_HTTPS || $page['url_scheme'] == 0 && GeneralUtility::getIndpEnv('TYPO3_SSL')) {
3112  $protocol = 'https';
3113  }
3114  $previewDomainConfig = static::getBackendUserAuthentication()->getTSConfig('TCEMAIN.previewDomain', self::getPagesTSconfig($pageId));
3115  if ($previewDomainConfig['value']) {
3116  if (strpos($previewDomainConfig['value'], '://') !== false) {
3117  list($protocol, $domainName) = explode('://', $previewDomainConfig['value']);
3118  } else {
3119  $domainName = $previewDomainConfig['value'];
3120  }
3121  } else {
3122  $domainName = self::firstDomainRecord($rootLine);
3123  }
3124  if ($domainName) {
3125  $domain = $domainName;
3126  } else {
3127  $domainRecord = self::getDomainStartPage($urlParts['host'], $urlParts['path']);
3128  $domain = $domainRecord['domainName'];
3129  }
3130  if ($domain) {
3131  $domain = $protocol . '://' . $domain;
3132  } else {
3133  $domain = rtrim(GeneralUtility::getIndpEnv('TYPO3_SITE_URL'), '/');
3134  }
3135  // Append port number if lockSSLPort is not the standard port 443
3136  $portNumber = (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['lockSSLPort'];
3137  if ($portNumber > 0 && $portNumber !== 443 && $portNumber < 65536 && $protocol === 'https') {
3138  $domain .= ':' . strval($portNumber);
3139  }
3140  }
3141  return $domain;
3142  }
3143 
3152  public static function getModTSconfig($id, $TSref)
3153  {
3154  $beUser = static::getBackendUserAuthentication();
3155  $pageTS_modOptions = $beUser->getTSConfig($TSref, static::getPagesTSconfig($id));
3156  $BE_USER_modOptions = $beUser->getTSConfig($TSref);
3157  if (is_null($BE_USER_modOptions['value'])) {
3158  unset($BE_USER_modOptions['value']);
3159  }
3160  ArrayUtility::mergeRecursiveWithOverrule($pageTS_modOptions, $BE_USER_modOptions);
3161  return $pageTS_modOptions;
3162  }
3163 
3177  public static function getFuncMenu($mainParams, $elementName, $currentValue, $menuItems, $script = '', $addParams = '')
3178  {
3179  if (!is_array($menuItems) || count($menuItems) <= 1) {
3180  return '';
3181  }
3182  $scriptUrl = self::buildScriptUrl($mainParams, $addParams, $script);
3183  $options = array();
3184  foreach ($menuItems as $value => $label) {
3185  $options[] = '<option value="' . htmlspecialchars($value) . '"' . ((string)$currentValue === (string)$value ? ' selected="selected"' : '') . '>' . htmlspecialchars($label, ENT_COMPAT, 'UTF-8', false) . '</option>';
3186  }
3187  if (!empty($options)) {
3188  $onChange = 'jumpToUrl(' . GeneralUtility::quoteJSvalue($scriptUrl . '&' . $elementName . '=') . '+this.options[this.selectedIndex].value,this);';
3189  return '
3190 
3191  <!-- Function Menu of module -->
3192  <select name="' . $elementName . '" onchange="' . htmlspecialchars($onChange) . '">
3193  ' . implode('
3194  ', $options) . '
3195  </select>
3196  ';
3197  }
3198  return '';
3199  }
3200 
3215  public static function getDropdownMenu($mainParams, $elementName, $currentValue, $menuItems, $script = '', $addParams = '')
3216  {
3217  if (!is_array($menuItems) || count($menuItems) <= 1) {
3218  return '';
3219  }
3220  $scriptUrl = self::buildScriptUrl($mainParams, $addParams, $script);
3221  $options = array();
3222  foreach ($menuItems as $value => $label) {
3223  $options[] = '<option value="' . htmlspecialchars($value) . '"' . ((string)$currentValue === (string)$value ? ' selected="selected"' : '') . '>' . htmlspecialchars($label, ENT_COMPAT, 'UTF-8', false) . '</option>';
3224  }
3225  if (!empty($options)) {
3226  $onChange = 'jumpToUrl(' . GeneralUtility::quoteJSvalue($scriptUrl . '&' . $elementName . '=') . '+this.options[this.selectedIndex].value,this);';
3227  return '
3228  <div class="form-group">
3229  <!-- Function Menu of module -->
3230  <select class="form-control input-sm" name="' . htmlspecialchars($elementName) . '" onchange="' . htmlspecialchars($onChange) . '">
3231  ' . implode(LF, $options) . '
3232  </select>
3233  </div>
3234  ';
3235  }
3236  return '';
3237  }
3238 
3252  public static function getFuncCheck($mainParams, $elementName, $currentValue, $script = '', $addParams = '', $tagParams = '')
3253  {
3254  $scriptUrl = self::buildScriptUrl($mainParams, $addParams, $script);
3255  $onClick = 'jumpToUrl(' . GeneralUtility::quoteJSvalue($scriptUrl . '&' . $elementName . '=') . '+(this.checked?1:0),this);';
3256 
3257  return
3258  '<input' .
3259  ' type="checkbox"' .
3260  ' class="checkbox"' .
3261  ' name="' . $elementName . '"' .
3262  ($currentValue ? ' checked="checked"' : '') .
3263  ' onclick="' . htmlspecialchars($onClick) . '"' .
3264  ($tagParams ? ' ' . $tagParams : '') .
3265  ' value="1"' .
3266  ' />';
3267  }
3268 
3282  public static function getFuncInput($mainParams, $elementName, $currentValue, $size = 10, $script = '', $addParams = '')
3283  {
3284  $scriptUrl = self::buildScriptUrl($mainParams, $addParams, $script);
3285  $onChange = 'jumpToUrl(' . GeneralUtility::quoteJSvalue($scriptUrl . '&' . $elementName . '=') . '+escape(this.value),this);';
3286  return '<input type="text"' . static::getDocumentTemplate()->formWidth($size) . ' name="' . $elementName . '" value="' . htmlspecialchars($currentValue) . '" onchange="' . htmlspecialchars($onChange) . '" />';
3287  }
3288 
3297  protected static function buildScriptUrl($mainParams, $addParams, $script = '')
3298  {
3299  if (!is_array($mainParams)) {
3300  $mainParams = array('id' => $mainParams);
3301  }
3302  if (!$script) {
3303  $script = basename(PATH_thisScript);
3304  }
3305 
3306  if (GeneralUtility::_GP('route')) {
3307  $router = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\Router::class);
3308  $route = $router->match(GeneralUtility::_GP('route'));
3309  $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
3310  $scriptUrl = (string)$uriBuilder->buildUriFromRoute($route->getOption('_identifier'));
3311  $scriptUrl .= $addParams;
3312  } elseif ($script === 'index.php' && GeneralUtility::_GET('M')) {
3313  $scriptUrl = self::getModuleUrl(GeneralUtility::_GET('M'), $mainParams) . $addParams;
3314  } else {
3315  $scriptUrl = $script . '?' . GeneralUtility::implodeArrayForUrl('', $mainParams) . $addParams;
3316  }
3317 
3318  return $scriptUrl;
3319  }
3320 
3330  public static function unsetMenuItems($modTSconfig, $itemArray, $TSref)
3331  {
3332  // Getting TS-config options for this module for the Backend User:
3333  $conf = static::getBackendUserAuthentication()->getTSConfig($TSref, $modTSconfig);
3334  if (is_array($conf['properties'])) {
3335  foreach ($conf['properties'] as $key => $val) {
3336  if (!$val) {
3337  unset($itemArray[$key]);
3338  }
3339  }
3340  }
3341  return $itemArray;
3342  }
3343 
3353  public static function setUpdateSignal($set = '', $params = '')
3354  {
3355  $beUser = static::getBackendUserAuthentication();
3356  $modData = $beUser->getModuleData(\TYPO3\CMS\Backend\Utility\BackendUtility::class . '::getUpdateSignal', 'ses');
3357  if ($set) {
3358  $modData[$set] = array(
3359  'set' => $set,
3360  'parameter' => $params
3361  );
3362  } else {
3363  // clear the module data
3364  $modData = array();
3365  }
3366  $beUser->pushModuleData(\TYPO3\CMS\Backend\Utility\BackendUtility::class . '::getUpdateSignal', $modData);
3367  }
3368 
3377  public static function getUpdateSignalCode()
3378  {
3379  $signals = array();
3380  $modData = static::getBackendUserAuthentication()->getModuleData(\TYPO3\CMS\Backend\Utility\BackendUtility::class . '::getUpdateSignal', 'ses');
3381  if (empty($modData)) {
3382  return '';
3383  }
3384  // Hook: Allows to let TYPO3 execute your JS code
3385  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['updateSignalHook'])) {
3386  $updateSignals = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['updateSignalHook'];
3387  } else {
3388  $updateSignals = array();
3389  }
3390  // Loop through all setUpdateSignals and get the JS code
3391  foreach ($modData as $set => $val) {
3392  if (isset($updateSignals[$set])) {
3393  $params = array('set' => $set, 'parameter' => $val['parameter'], 'JScode' => '');
3394  $ref = null;
3395  GeneralUtility::callUserFunction($updateSignals[$set], $params, $ref);
3396  $signals[] = $params['JScode'];
3397  } else {
3398  switch ($set) {
3399  case 'updatePageTree':
3400  $signals[] = '
3401  if (top && top.TYPO3.Backend.NavigationContainer.PageTree) {
3402  top.TYPO3.Backend.NavigationContainer.PageTree.refreshTree();
3403  }
3404  ';
3405  break;
3406  case 'updateFolderTree':
3407  $signals[] = '
3408  if (top && top.TYPO3.Backend.NavigationIframe) {
3409  top.TYPO3.Backend.NavigationIframe.refresh();
3410  }';
3411  break;
3412  case 'updateModuleMenu':
3413  $signals[] = '
3414  if (top && top.TYPO3.ModuleMenu.App) {
3415  top.TYPO3.ModuleMenu.App.refreshMenu();
3416  }';
3417  }
3418  }
3419  }
3420  $content = implode(LF, $signals);
3421  // For backwards compatibility, should be replaced
3422  self::setUpdateSignal();
3423  return $content;
3424  }
3425 
3440  public static function getModuleData($MOD_MENU, $CHANGED_SETTINGS, $modName, $type = '', $dontValidateList = '', $setDefaultList = '')
3441  {
3442  if ($modName && is_string($modName)) {
3443  // Getting stored user-data from this module:
3444  $beUser = static::getBackendUserAuthentication();
3445  $settings = $beUser->getModuleData($modName, $type);
3446  $changed = 0;
3447  if (!is_array($settings)) {
3448  $changed = 1;
3449  $settings = array();
3450  }
3451  if (is_array($MOD_MENU)) {
3452  foreach ($MOD_MENU as $key => $var) {
3453  // If a global var is set before entering here. eg if submitted, then it's substituting the current value the array.
3454  if (is_array($CHANGED_SETTINGS) && isset($CHANGED_SETTINGS[$key])) {
3455  if (is_array($CHANGED_SETTINGS[$key])) {
3456  $serializedSettings = serialize($CHANGED_SETTINGS[$key]);
3457  if ((string)$settings[$key] !== $serializedSettings) {
3458  $settings[$key] = $serializedSettings;
3459  $changed = 1;
3460  }
3461  } else {
3462  if ((string)$settings[$key] !== (string)$CHANGED_SETTINGS[$key]) {
3463  $settings[$key] = $CHANGED_SETTINGS[$key];
3464  $changed = 1;
3465  }
3466  }
3467  }
3468  // If the $var is an array, which denotes the existence of a menu, we check if the value is permitted
3469  if (is_array($var) && (!$dontValidateList || !GeneralUtility::inList($dontValidateList, $key))) {
3470  // If the setting is an array or not present in the menu-array, MOD_MENU, then the default value is inserted.
3471  if (is_array($settings[$key]) || !isset($MOD_MENU[$key][$settings[$key]])) {
3472  $settings[$key] = (string)key($var);
3473  $changed = 1;
3474  }
3475  }
3476  // Sets default values (only strings/checkboxes, not menus)
3477  if ($setDefaultList && !is_array($var)) {
3478  if (GeneralUtility::inList($setDefaultList, $key) && !isset($settings[$key])) {
3479  $settings[$key] = (string)$var;
3480  }
3481  }
3482  }
3483  } else {
3484  die('No menu!');
3485  }
3486  if ($changed) {
3487  $beUser->pushModuleData($modName, $settings);
3488  }
3489  return $settings;
3490  } else {
3491  die('Wrong module name: "' . $modName . '"');
3492  }
3493  }
3494 
3504  public static function getModuleUrl($moduleName, $urlParameters = array(), $backPathOverride = false, $returnAbsoluteUrl = false)
3505  {
3507  $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
3508  try {
3509  $uri = $uriBuilder->buildUriFromRoute($moduleName, $urlParameters, $returnAbsoluteUrl ? UriBuilder::ABSOLUTE_URL : UriBuilder::ABSOLUTE_PATH);
3510  } catch (\TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException $e) {
3511  // no route registered, use the fallback logic to check for a module
3512  $uri = $uriBuilder->buildUriFromModule($moduleName, $urlParameters, $returnAbsoluteUrl ? UriBuilder::ABSOLUTE_URL : UriBuilder::ABSOLUTE_PATH);
3513  }
3514  return (string)$uri;
3515  }
3516 
3530  public static function getAjaxUrl($ajaxIdentifier, array $urlParameters = array(), $backPathOverride = false, $returnAbsoluteUrl = false)
3531  {
3533  $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
3534  try {
3535  $routeIdentifier = 'ajax_' . $ajaxIdentifier;
3536  $uri = $uriBuilder->buildUriFromRoute($routeIdentifier, $urlParameters, $returnAbsoluteUrl ? UriBuilder::ABSOLUTE_URL : UriBuilder::ABSOLUTE_PATH);
3537  } catch (\TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException $e) {
3538  // no route registered, use the fallback logic to check for a module
3539  $uri = $uriBuilder->buildUriFromAjaxId($ajaxIdentifier, $urlParameters, $returnAbsoluteUrl ? UriBuilder::ABSOLUTE_URL : UriBuilder::ABSOLUTE_PATH);
3540  }
3541  return (string)$uri;
3542  }
3543 
3552  public static function getListViewLink($urlParameters = array(), $linkTitle = '', $linkText = '')
3553  {
3555  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
3556  return '<a href="' . htmlspecialchars(self::getModuleUrl('web_list', $urlParameters)) . '" title="' . htmlspecialchars($linkTitle) . '">' . $iconFactory->getIcon('actions-system-list-open', Icon::SIZE_SMALL)->render() . htmlspecialchars($linkText) . '</a>';
3557  }
3558 
3568  public static function getUrlToken($formName = 'securityToken', $tokenName = 'formToken')
3569  {
3570  GeneralUtility::logDeprecatedFunction();
3571  $formProtection = FormProtectionFactory::get();
3572  return '&' . $tokenName . '=' . $formProtection->generateToken($formName);
3573  }
3574 
3575  /*******************************************
3576  *
3577  * Core
3578  *
3579  *******************************************/
3590  public static function lockRecords($table = '', $uid = 0, $pid = 0)
3591  {
3592  $beUser = static::getBackendUserAuthentication();
3593  if (isset($beUser->user['uid'])) {
3594  $user_id = (int)$beUser->user['uid'];
3595  if ($table && $uid) {
3596  $fields_values = array(
3597  'userid' => $user_id,
3598  'feuserid' => 0,
3599  'tstamp' => $GLOBALS['EXEC_TIME'],
3600  'record_table' => $table,
3601  'record_uid' => $uid,
3602  'username' => $beUser->user['username'],
3603  'record_pid' => $pid
3604  );
3605  static::getDatabaseConnection()->exec_INSERTquery('sys_lockedrecords', $fields_values);
3606  } else {
3607  static::getDatabaseConnection()->exec_DELETEquery('sys_lockedrecords', 'userid=' . (int)$user_id);
3608  }
3609  }
3610  }
3611 
3624  public static function isRecordLocked($table, $uid)
3625  {
3626  if (!is_array($GLOBALS['LOCKED_RECORDS'])) {
3627  $GLOBALS['LOCKED_RECORDS'] = array();
3628  $db = static::getDatabaseConnection();
3629  $res = $db->exec_SELECTquery(
3630  '*',
3631  'sys_lockedrecords',
3632  'sys_lockedrecords.userid<>' . (int)static::getBackendUserAuthentication()->user['uid']
3633  . ' AND sys_lockedrecords.tstamp > ' . ($GLOBALS['EXEC_TIME'] - 2 * 3600)
3634  );
3635  while ($row = $db->sql_fetch_assoc($res)) {
3636  // Get the type of the user that locked this record:
3637  if ($row['userid']) {
3638  $userTypeLabel = 'beUser';
3639  } elseif ($row['feuserid']) {
3640  $userTypeLabel = 'feUser';
3641  } else {
3642  $userTypeLabel = 'user';
3643  }
3644  $lang = static::getLanguageService();
3645  $userType = $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.' . $userTypeLabel);
3646  // Get the username (if available):
3647  if ($row['username']) {
3648  $userName = $row['username'];
3649  } else {
3650  $userName = $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.unknownUser');
3651  }
3652  $GLOBALS['LOCKED_RECORDS'][$row['record_table'] . ':' . $row['record_uid']] = $row;
3653  $GLOBALS['LOCKED_RECORDS'][$row['record_table'] . ':' . $row['record_uid']]['msg'] = sprintf(
3654  $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.lockedRecordUser'),
3655  $userType,
3656  $userName,
3657  self::calcAge($GLOBALS['EXEC_TIME'] - $row['tstamp'], $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.minutesHoursDaysYears'))
3658  );
3659  if ($row['record_pid'] && !isset($GLOBALS['LOCKED_RECORDS'][($row['record_table'] . ':' . $row['record_pid'])])) {
3660  $GLOBALS['LOCKED_RECORDS']['pages:' . $row['record_pid']]['msg'] = sprintf(
3661  $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.lockedRecordUser_content'),
3662  $userType,
3663  $userName,
3664  self::calcAge($GLOBALS['EXEC_TIME'] - $row['tstamp'], $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.minutesHoursDaysYears'))
3665  );
3666  }
3667  }
3668  $db->sql_free_result($res);
3669  }
3670  return $GLOBALS['LOCKED_RECORDS'][$table . ':' . $uid];
3671  }
3672 
3683  public static function exec_foreign_table_where_query($fieldConfig, $field = '', $TSconfig = array())
3684  {
3685  GeneralUtility::logDeprecatedFunction();
3686  $foreign_table = $fieldConfig['config']['foreign_table'];
3687  $rootLevel = $GLOBALS['TCA'][$foreign_table]['ctrl']['rootLevel'];
3688  $fTWHERE = $fieldConfig['config']['foreign_table_where'];
3689  $fTWHERE = static::replaceMarkersInWhereClause($fTWHERE, $foreign_table, $field, $TSconfig);
3690  $db = static::getDatabaseConnection();
3691  $wgolParts = $db->splitGroupOrderLimit($fTWHERE);
3692  // rootLevel = -1 means that elements can be on the rootlevel OR on any page (pid!=-1)
3693  // rootLevel = 0 means that elements are not allowed on root level
3694  // rootLevel = 1 means that elements are only on the root level (pid=0)
3695  if ($rootLevel == 1 || $rootLevel == -1) {
3696  $pidWhere = $foreign_table . '.pid' . (($rootLevel == -1) ? '<>-1' : '=0');
3697  $queryParts = array(
3698  'SELECT' => self::getCommonSelectFields($foreign_table, $foreign_table . '.'),
3699  'FROM' => $foreign_table,
3700  'WHERE' => $pidWhere . ' ' . self::deleteClause($foreign_table) . ' ' . $wgolParts['WHERE'],
3701  'GROUPBY' => $wgolParts['GROUPBY'],
3702  'ORDERBY' => $wgolParts['ORDERBY'],
3703  'LIMIT' => $wgolParts['LIMIT']
3704  );
3705  } else {
3706  $pageClause = static::getBackendUserAuthentication()->getPagePermsClause(1);
3707  if ($foreign_table != 'pages') {
3708  $queryParts = array(
3709  'SELECT' => self::getCommonSelectFields($foreign_table, $foreign_table . '.'),
3710  'FROM' => $foreign_table . ', pages',
3711  'WHERE' => 'pages.uid=' . $foreign_table . '.pid
3712  AND pages.deleted=0 ' . self::deleteClause($foreign_table) . ' AND ' . $pageClause . ' ' . $wgolParts['WHERE'],
3713  'GROUPBY' => $wgolParts['GROUPBY'],
3714  'ORDERBY' => $wgolParts['ORDERBY'],
3715  'LIMIT' => $wgolParts['LIMIT']
3716  );
3717  } else {
3718  $queryParts = array(
3719  'SELECT' => self::getCommonSelectFields($foreign_table, $foreign_table . '.'),
3720  'FROM' => 'pages',
3721  'WHERE' => 'pages.deleted=0
3722  AND ' . $pageClause . ' ' . $wgolParts['WHERE'],
3723  'GROUPBY' => $wgolParts['GROUPBY'],
3724  'ORDERBY' => $wgolParts['ORDERBY'],
3725  'LIMIT' => $wgolParts['LIMIT']
3726  );
3727  }
3728  }
3729  return $db->exec_SELECT_queryArray($queryParts);
3730  }
3731 
3751  public static function replaceMarkersInWhereClause($whereClause, $table, $field = '', $tsConfig = array())
3752  {
3753  GeneralUtility::logDeprecatedFunction();
3754  $db = static::getDatabaseConnection();
3755  if (strstr($whereClause, '###REC_FIELD_')) {
3756  $whereClauseParts = explode('###REC_FIELD_', $whereClause);
3757  foreach ($whereClauseParts as $key => $value) {
3758  if ($key) {
3759  $whereClauseSubarts = explode('###', $value, 2);
3760  if (substr($whereClauseParts[0], -1) === '\'' && $whereClauseSubarts[1][0] === '\'') {
3761  $whereClauseParts[$key] = $db->quoteStr($tsConfig['_THIS_ROW'][$whereClauseSubarts[0]], $table) . $whereClauseSubarts[1];
3762  } else {
3763  $whereClauseParts[$key] = $db->fullQuoteStr($tsConfig['_THIS_ROW'][$whereClauseSubarts[0]], $table) . $whereClauseSubarts[1];
3764  }
3765  }
3766  }
3767  $whereClause = implode('', $whereClauseParts);
3768  }
3769  return str_replace(
3770  array(
3771  '###CURRENT_PID###',
3772  '###THIS_UID###',
3773  '###STORAGE_PID###',
3774  '###SITEROOT###',
3775  '###PAGE_TSCONFIG_ID###',
3776  '###PAGE_TSCONFIG_IDLIST###',
3777  '###PAGE_TSCONFIG_STR###'
3778  ),
3779  array(
3780  (int)$tsConfig['_CURRENT_PID'],
3781  (int)$tsConfig['_THIS_UID'],
3782  (int)$tsConfig['_STORAGE_PID'],
3783  (int)$tsConfig['_SITEROOT'],
3784  (int)$tsConfig[$field]['PAGE_TSCONFIG_ID'],
3785  $db->cleanIntList($tsConfig[$field]['PAGE_TSCONFIG_IDLIST']),
3786  $db->quoteStr($tsConfig[$field]['PAGE_TSCONFIG_STR'], $table)
3787  ),
3788  $whereClause
3789  );
3790  }
3791 
3800  public static function getTCEFORM_TSconfig($table, $row)
3801  {
3802  self::fixVersioningPid($table, $row);
3803  $res = array();
3804  $typeVal = self::getTCAtypeValue($table, $row);
3805  // Get main config for the table
3806  list($TScID, $cPid) = self::getTSCpid($table, $row['uid'], $row['pid']);
3807  if ($TScID >= 0) {
3808  $tempConf = static::getBackendUserAuthentication()->getTSConfig('TCEFORM.' . $table, self::getPagesTSconfig($TScID));
3809  if (is_array($tempConf['properties'])) {
3810  foreach ($tempConf['properties'] as $key => $val) {
3811  if (is_array($val)) {
3812  $fieldN = substr($key, 0, -1);
3813  $res[$fieldN] = $val;
3814  unset($res[$fieldN]['types.']);
3815  if ((string)$typeVal !== '' && is_array($val['types.'][$typeVal . '.'])) {
3816  ArrayUtility::mergeRecursiveWithOverrule($res[$fieldN], $val['types.'][$typeVal . '.']);
3817  }
3818  }
3819  }
3820  }
3821  }
3822  $res['_CURRENT_PID'] = $cPid;
3823  $res['_THIS_UID'] = $row['uid'];
3824  // So the row will be passed to foreign_table_where_query()
3825  $res['_THIS_ROW'] = $row;
3826  $rootLine = self::BEgetRootLine($TScID, '', true);
3827  foreach ($rootLine as $rC) {
3828  if (!$res['_STORAGE_PID']) {
3829  $res['_STORAGE_PID'] = (int)$rC['storage_pid'];
3830  }
3831  if (!$res['_SITEROOT']) {
3832  $res['_SITEROOT'] = $rC['is_siteroot'] ? (int)$rC['uid'] : 0;
3833  }
3834  }
3835  return $res;
3836  }
3837 
3851  public static function getTSconfig_pidValue($table, $uid, $pid)
3852  {
3853  // If pid is an integer this takes precedence in our lookup.
3854  if (MathUtility::canBeInterpretedAsInteger($pid)) {
3855  $thePidValue = (int)$pid;
3856  // If ref to another record, look that record up.
3857  if ($thePidValue < 0) {
3858  $pidRec = self::getRecord($table, abs($thePidValue), 'pid');
3859  $thePidValue = is_array($pidRec) ? $pidRec['pid'] : -2;
3860  }
3861  } else {
3862  // Try to fetch the record pid from uid. If the uid is 'NEW...' then this will of course return nothing
3863  $rr = self::getRecord($table, $uid);
3864  $thePidValue = null;
3865  if (is_array($rr)) {
3866  // First check if the pid is -1 which means it is a workspaced element. Get the "real" record:
3867  if ($rr['pid'] == '-1') {
3868  $rr = self::getRecord($table, $rr['t3ver_oid'], 'pid');
3869  if (is_array($rr)) {
3870  $thePidValue = $rr['pid'];
3871  }
3872  } else {
3873  // Returning the "pid" of the record
3874  $thePidValue = $rr['pid'];
3875  }
3876  }
3877  if (!$thePidValue) {
3878  // Returns -1 if the record with this pid was not found.
3879  $thePidValue = -1;
3880  }
3881  }
3882  return $thePidValue;
3883  }
3884 
3894  public static function getPidForModTSconfig($table, $uid, $pid)
3895  {
3896  return $table === 'pages' && MathUtility::canBeInterpretedAsInteger($uid) ? $uid : $pid;
3897  }
3898 
3911  public static function getTSCpidCached($table, $uid, $pid)
3912  {
3913  // A local first level cache
3914  static $firstLevelCache;
3915 
3916  if (!is_array($firstLevelCache)) {
3917  $firstLevelCache = array();
3918  }
3919 
3920  $key = $table . ':' . $uid . ':' . $pid;
3921  if (!isset($firstLevelCache[$key])) {
3922  $firstLevelCache[$key] = static::getTSCpid($table, $uid, $pid);
3923  }
3924  return $firstLevelCache[$key];
3925  }
3926 
3938  public static function getTSCpid($table, $uid, $pid)
3939  {
3940  // If pid is negative (referring to another record) the pid of the other record is fetched and returned.
3941  $cPid = self::getTSconfig_pidValue($table, $uid, $pid);
3942  // $TScID is the id of $table = pages, else it's the pid of the record.
3943  $TScID = self::getPidForModTSconfig($table, $uid, $cPid);
3944  return array($TScID, $cPid);
3945  }
3946 
3953  public static function firstDomainRecord($rootLine)
3954  {
3955  foreach ($rootLine as $row) {
3956  $dRec = self::getRecordsByField('sys_domain', 'pid', $row['uid'], ' AND redirectTo=\'\' AND hidden=0', '', 'sorting');
3957  if (is_array($dRec)) {
3958  $dRecord = reset($dRec);
3959  return rtrim($dRecord['domainName'], '/');
3960  }
3961  }
3962  return null;
3963  }
3964 
3972  public static function getDomainStartPage($domain, $path = '')
3973  {
3974  $domain = explode(':', $domain);
3975  $domain = strtolower(preg_replace('/\\.$/', '', $domain[0]));
3976  // Path is calculated.
3977  $path = trim(preg_replace('/\\/[^\\/]*$/', '', $path));
3978  // Stuff
3979  $domain .= $path;
3980  $db = static::getDatabaseConnection();
3981  $res = $db->exec_SELECTquery('sys_domain.*', 'pages,sys_domain', '
3982  pages.uid=sys_domain.pid
3983  AND sys_domain.hidden=0
3984  AND (sys_domain.domainName=' . $db->fullQuoteStr($domain, 'sys_domain') . ' OR sys_domain.domainName='
3985  . $db->fullQuoteStr(($domain . '/'), 'sys_domain') . ')' . self::deleteClause('pages'), '', '', '1');
3986  $result = $db->sql_fetch_assoc($res);
3987  $db->sql_free_result($res);
3988  return $result;
3989  }
3990 
4002  public static function RTEsetup($RTEprop, $table, $field, $type = '')
4003  {
4004  $thisConfig = is_array($RTEprop['default.']) ? $RTEprop['default.'] : array();
4005  $thisFieldConf = $RTEprop['config.'][$table . '.'][$field . '.'];
4006  if (is_array($thisFieldConf)) {
4007  unset($thisFieldConf['types.']);
4008  ArrayUtility::mergeRecursiveWithOverrule($thisConfig, $thisFieldConf);
4009  }
4010  if ($type && is_array($RTEprop['config.'][$table . '.'][$field . '.']['types.'][$type . '.'])) {
4011  ArrayUtility::mergeRecursiveWithOverrule($thisConfig, $RTEprop['config.'][$table . '.'][$field . '.']['types.'][$type . '.']);
4012  }
4013  return $thisConfig;
4014  }
4015 
4023  public static function RTEgetObj()
4024  {
4025  GeneralUtility::logDeprecatedFunction();
4026  // If no RTE object has been set previously, try to create it:
4027  if (!isset($GLOBALS['T3_VAR']['RTEobj'])) {
4028  // Set the object string to blank by default:
4029  $GLOBALS['T3_VAR']['RTEobj'] = array();
4030  // Traverse registered RTEs:
4031  if (is_array($GLOBALS['TYPO3_CONF_VARS']['BE']['RTE_reg'])) {
4032  foreach ($GLOBALS['TYPO3_CONF_VARS']['BE']['RTE_reg'] as $rteObjCfg) {
4033  $rteObj = GeneralUtility::getUserObj($rteObjCfg['objRef']);
4034  if (is_object($rteObj)) {
4035  if ($rteObj->isAvailable()) {
4036  $GLOBALS['T3_VAR']['RTEobj'] = $rteObj;
4037  break;
4038  } else {
4039  $GLOBALS['T3_VAR']['RTEobj'] = array_merge($GLOBALS['T3_VAR']['RTEobj'], $rteObj->errorLog);
4040  }
4041  }
4042  }
4043  }
4044  if (empty($GLOBALS['T3_VAR']['RTEobj'])) {
4045  $GLOBALS['T3_VAR']['RTEobj'][] = 'No RTEs configured at all';
4046  }
4047  }
4048  // Return RTE object (if any!)
4049  return $GLOBALS['T3_VAR']['RTEobj'];
4050  }
4051 
4059  public static function &softRefParserObj($spKey)
4060  {
4061  // If no softRef parser object has been set previously, try to create it:
4062  if (!isset($GLOBALS['T3_VAR']['softRefParser'][$spKey])) {
4063  // Set the object string to blank by default:
4064  $GLOBALS['T3_VAR']['softRefParser'][$spKey] = '';
4065  // Now, try to create parser object:
4066  $objRef = null;
4067  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['softRefParser'][$spKey])) {
4068  $objRef = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['softRefParser'][$spKey];
4069  } elseif (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['softRefParser_GL'][$spKey])) {
4070  GeneralUtility::deprecationLog('The hook softRefParser_GL (used with parser key "'
4071  . $spKey . '") is deprecated since TYPO3 CMS 7 and will be removed in TYPO3 CMS 8');
4072  $objRef = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['softRefParser_GL'][$spKey];
4073  }
4074  if ($objRef) {
4075  $softRefParserObj = GeneralUtility::getUserObj($objRef);
4076  if (is_object($softRefParserObj)) {
4077  $GLOBALS['T3_VAR']['softRefParser'][$spKey] = $softRefParserObj;
4078  }
4079  }
4080  }
4081  // Return RTE object (if any!)
4082  return $GLOBALS['T3_VAR']['softRefParser'][$spKey];
4083  }
4084 
4090  protected static function getRuntimeCache()
4091  {
4092  return GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_runtime');
4093  }
4094 
4101  public static function explodeSoftRefParserList($parserList)
4102  {
4103  $runtimeCache = self::getRuntimeCache();
4104  $cacheId = 'backend-softRefList-' . md5($parserList);
4105  if ($runtimeCache->has($cacheId)) {
4106  return $runtimeCache->get($cacheId);
4107  }
4108 
4109  // Looking for global parsers:
4110  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['softRefParser_GL']) && !empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['softRefParser_GL'])) {
4111  GeneralUtility::deprecationLog('The hook softRefParser_GL is deprecated since TYPO3 CMS 7 and will be removed in TYPO3 CMS 8');
4112  $parserList = implode(',', array_keys($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['softRefParser_GL'])) . ',' . $parserList;
4113  }
4114 
4115  // Return immediately if list is blank:
4116  if ($parserList === '') {
4117  $runtimeCache->set($cacheId, false);
4118  return false;
4119  }
4120 
4121  // Otherwise parse the list:
4122  $keyList = GeneralUtility::trimExplode(',', $parserList, true);
4123  $output = array();
4124  foreach ($keyList as $val) {
4125  $reg = array();
4126  if (preg_match('/^([[:alnum:]_-]+)\\[(.*)\\]$/', $val, $reg)) {
4127  $output[$reg[1]] = GeneralUtility::trimExplode(';', $reg[2], true);
4128  } else {
4129  $output[$val] = '';
4130  }
4131  }
4132  $runtimeCache->set($cacheId, $output);
4133  return $output;
4134  }
4135 
4142  public static function isModuleSetInTBE_MODULES($modName)
4143  {
4144  $loaded = array();
4145  foreach ($GLOBALS['TBE_MODULES'] as $mkey => $list) {
4146  $loaded[$mkey] = 1;
4147  if (!is_array($list) && trim($list)) {
4148  $subList = GeneralUtility::trimExplode(',', $list, true);
4149  foreach ($subList as $skey) {
4150  $loaded[$mkey . '_' . $skey] = 1;
4151  }
4152  }
4153  }
4154  return $modName && isset($loaded[$modName]);
4155  }
4156 
4166  public static function referenceCount($table, $ref, $msg = '', $count = null)
4167  {
4168  if ($count === null) {
4169  $db = static::getDatabaseConnection();
4170  // Look up the path:
4171  if ($table == '_FILE') {
4172  if (GeneralUtility::isFirstPartOfStr($ref, PATH_site)) {
4173  $ref = PathUtility::stripPathSitePrefix($ref);
4174  $condition = 'ref_string=' . $db->fullQuoteStr($ref, 'sys_refindex');
4175  } else {
4176  return '';
4177  }
4178  } else {
4179  $condition = 'ref_uid=' . (int)$ref;
4180  }
4181  $count = $db->exec_SELECTcountRows('*', 'sys_refindex', 'ref_table=' . $db->fullQuoteStr($table, 'sys_refindex') . ' AND ' . $condition . ' AND deleted=0');
4182  }
4183  return $count ? ($msg ? sprintf($msg, $count) : $count) : '';
4184  }
4185 
4194  public static function translationCount($table, $ref, $msg = '')
4195  {
4196  $count = null;
4197  if (empty($GLOBALS['TCA'][$table]['ctrl']['transForeignTable']) && $GLOBALS['TCA'][$table]['ctrl']['languageField'] && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] && !$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable']) {
4198  $where = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] . '=' . (int)$ref . ' AND ' . $GLOBALS['TCA'][$table]['ctrl']['languageField'] . '<>0';
4199  if (!empty($GLOBALS['TCA'][$table]['ctrl']['delete'])) {
4200  $where .= ' AND ' . $GLOBALS['TCA'][$table]['ctrl']['delete'] . '=0';
4201  }
4202  $count = static::getDatabaseConnection()->exec_SELECTcountRows('*', $table, $where);
4203  }
4204  return $count ? ($msg ? sprintf($msg, $count) : $count) : '';
4205  }
4206 
4207  /*******************************************
4208  *
4209  * Workspaces / Versioning
4210  *
4211  *******************************************/
4223  public static function selectVersionsOfRecord($table, $uid, $fields = '*', $workspace = 0, $includeDeletedRecords = false, $row = null)
4224  {
4225  $realPid = 0;
4226  $outputRows = array();
4227  if ($GLOBALS['TCA'][$table] && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
4228  if (is_array($row) && !$includeDeletedRecords) {
4229  $row['_CURRENT_VERSION'] = true;
4230  $realPid = $row['pid'];
4231  $outputRows[] = $row;
4232  } else {
4233  // Select UID version:
4234  $row = BackendUtility::getRecord($table, $uid, $fields, '', !$includeDeletedRecords);
4235  // Add rows to output array:
4236  if ($row) {
4237  $row['_CURRENT_VERSION'] = true;
4238  $realPid = $row['pid'];
4239  $outputRows[] = $row;
4240  }
4241  }
4242  $workspaceSqlPart = '';
4243  if ($workspace === 0) {
4244  // Only in Live WS
4245  $workspaceSqlPart = ' AND t3ver_wsid=0';
4246  } elseif ($workspace !== null) {
4247  // In Live WS and Workspace with given ID
4248  $workspaceSqlPart = ' AND t3ver_wsid IN (0,' . (int)$workspace . ')';
4249  }
4250  // Select all offline versions of record:
4251  $rows = static::getDatabaseConnection()->exec_SELECTgetRows(
4252  $fields,
4253  $table,
4254  'pid=-1 AND uid<>' . (int)$uid . ' AND t3ver_oid=' . (int)$uid
4255  . $workspaceSqlPart
4256  . ($includeDeletedRecords ? '' : self::deleteClause($table)),
4257  '',
4258  't3ver_id DESC'
4259  );
4260  // Add rows to output array:
4261  if (is_array($rows)) {
4262  $outputRows = array_merge($outputRows, $rows);
4263  }
4264  // Set real-pid:
4265  foreach ($outputRows as $idx => $oRow) {
4266  $outputRows[$idx]['_REAL_PID'] = $realPid;
4267  }
4268  return $outputRows;
4269  }
4270  return null;
4271  }
4272 
4291  public static function fixVersioningPid($table, &$rr, $ignoreWorkspaceMatch = false)
4292  {
4293  if (!ExtensionManagementUtility::isLoaded('version')) {
4294  return;
4295  }
4296  // Check that the input record is an offline version from a table that supports versioning:
4297  if (is_array($rr) && $rr['pid'] == -1 && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
4298  // Check values for t3ver_oid and t3ver_wsid:
4299  if (isset($rr['t3ver_oid']) && isset($rr['t3ver_wsid'])) {
4300  // If "t3ver_oid" is already a field, just set this:
4301  $oid = $rr['t3ver_oid'];
4302  $wsid = $rr['t3ver_wsid'];
4303  } else {
4304  $oid = 0;
4305  $wsid = 0;
4306  // Otherwise we have to expect "uid" to be in the record and look up based on this:
4307  $newPidRec = self::getRecord($table, $rr['uid'], 't3ver_oid,t3ver_wsid');
4308  if (is_array($newPidRec)) {
4309  $oid = $newPidRec['t3ver_oid'];
4310  $wsid = $newPidRec['t3ver_wsid'];
4311  }
4312  }
4313  // If ID of current online version is found, look up the PID value of that:
4314  if ($oid && ($ignoreWorkspaceMatch || (int)$wsid === (int)static::getBackendUserAuthentication()->workspace)) {
4315  $oidRec = self::getRecord($table, $oid, 'pid');
4316  if (is_array($oidRec)) {
4317  $rr['_ORIG_pid'] = $rr['pid'];
4318  $rr['pid'] = $oidRec['pid'];
4319  }
4320  // Use target PID in case of move pointer
4321  if (
4322  !isset($rr['t3ver_state'])
4323  || VersionState::cast($rr['t3ver_state'])->equals(VersionState::MOVE_POINTER)
4324  ) {
4325  $movePlaceholder = self::getMovePlaceholder($table, $oid, 'pid');
4326  if ($movePlaceholder) {
4327  $rr['_ORIG_pid'] = $rr['pid'];
4328  $rr['pid'] = $movePlaceholder['pid'];
4329  }
4330  }
4331  }
4332  }
4333  }
4334 
4352  public static function workspaceOL($table, &$row, $wsid = -99, $unsetMovePointers = false)
4353  {
4354  if (!ExtensionManagementUtility::isLoaded('version')) {
4355  return;
4356  }
4357  // If this is FALSE the placeholder is shown raw in the backend.
4358  // I don't know if this move can be useful for users to toggle. Technically it can help debugging.
4359  $previewMovePlaceholders = true;
4360  // Initialize workspace ID
4361  if ($wsid == -99) {
4362  $wsid = static::getBackendUserAuthentication()->workspace;
4363  }
4364  // Check if workspace is different from zero and record is set:
4365  if ($wsid !== 0 && is_array($row)) {
4366  // Check if input record is a move-placeholder and if so, find the pointed-to live record:
4367  $movePldSwap = null;
4368  $orig_uid = 0;
4369  $orig_pid = 0;
4370  if ($previewMovePlaceholders) {
4371  $orig_uid = $row['uid'];
4372  $orig_pid = $row['pid'];
4373  $movePldSwap = self::movePlhOL($table, $row);
4374  }
4375  $wsAlt = self::getWorkspaceVersionOfRecord($wsid, $table, $row['uid'], implode(',', array_keys($row)));
4376  // If version was found, swap the default record with that one.
4377  if (is_array($wsAlt)) {
4378  // Check if this is in move-state:
4379  if ($previewMovePlaceholders && !$movePldSwap && $GLOBALS['TCA'][$table]['ctrl']['versioningWS'] && $unsetMovePointers) {
4380  // Only for WS ver 2... (moving)
4381  // If t3ver_state is not found, then find it... (but we like best if it is here...)
4382  if (!isset($wsAlt['t3ver_state'])) {
4383  $stateRec = self::getRecord($table, $wsAlt['uid'], 't3ver_state');
4384  $versionState = VersionState::cast($stateRec['t3ver_state']);
4385  } else {
4386  $versionState = VersionState::cast($wsAlt['t3ver_state']);
4387  }
4388  if ($versionState->equals(VersionState::MOVE_POINTER)) {
4389  // @todo Same problem as frontend in versionOL(). See TODO point there.
4390  $row = false;
4391  return;
4392  }
4393  }
4394  // Always correct PID from -1 to what it should be
4395  if (isset($wsAlt['pid'])) {
4396  // Keep the old (-1) - indicates it was a version.
4397  $wsAlt['_ORIG_pid'] = $wsAlt['pid'];
4398  // Set in the online versions PID.
4399  $wsAlt['pid'] = $row['pid'];
4400  }
4401  // For versions of single elements or page+content, swap UID and PID
4402  $wsAlt['_ORIG_uid'] = $wsAlt['uid'];
4403  $wsAlt['uid'] = $row['uid'];
4404  // Backend css class:
4405  $wsAlt['_CSSCLASS'] = 'ver-element';
4406  // Changing input record to the workspace version alternative:
4407  $row = $wsAlt;
4408  }
4409  // If the original record was a move placeholder, the uid and pid of that is preserved here:
4410  if ($movePldSwap) {
4411  $row['_MOVE_PLH'] = true;
4412  $row['_MOVE_PLH_uid'] = $orig_uid;
4413  $row['_MOVE_PLH_pid'] = $orig_pid;
4414  // For display; To make the icon right for the placeholder vs. the original
4415  $row['t3ver_state'] = (string)new VersionState(VersionState::MOVE_PLACEHOLDER);
4416  }
4417  }
4418  }
4419 
4429  public static function movePlhOL($table, &$row)
4430  {
4431  if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
4432  // If t3ver_move_id or t3ver_state is not found, then find it... (but we like best if it is here...)
4433  if (!isset($row['t3ver_move_id']) || !isset($row['t3ver_state'])) {
4434  $moveIDRec = self::getRecord($table, $row['uid'], 't3ver_move_id, t3ver_state');
4435  $moveID = $moveIDRec['t3ver_move_id'];
4436  $versionState = VersionState::cast($moveIDRec['t3ver_state']);
4437  } else {
4438  $moveID = $row['t3ver_move_id'];
4439  $versionState = VersionState::cast($row['t3ver_state']);
4440  }
4441  // Find pointed-to record.
4442  if ($versionState->equals(VersionState::MOVE_PLACEHOLDER) && $moveID) {
4443  if ($origRow = self::getRecord($table, $moveID, implode(',', array_keys($row)))) {
4444  $row = $origRow;
4445  return true;
4446  }
4447  }
4448  }
4449  return false;
4450  }
4451 
4461  public static function getWorkspaceVersionOfRecord($workspace, $table, $uid, $fields = '*')
4462  {
4463  if (ExtensionManagementUtility::isLoaded('version')) {
4464  if ($workspace !== 0 && $GLOBALS['TCA'][$table] && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
4465  // Select workspace version of record:
4466  $row = static::getDatabaseConnection()->exec_SELECTgetSingleRow($fields, $table, 'pid=-1 AND ' . 't3ver_oid=' . (int)$uid . ' AND ' . 't3ver_wsid=' . (int)$workspace . self::deleteClause($table));
4467  if (is_array($row)) {
4468  return $row;
4469  }
4470  }
4471  }
4472  return false;
4473  }
4474 
4483  public static function getLiveVersionOfRecord($table, $uid, $fields = '*')
4484  {
4485  $liveVersionId = self::getLiveVersionIdOfRecord($table, $uid);
4486  if (is_null($liveVersionId) === false) {
4487  return self::getRecord($table, $liveVersionId, $fields);
4488  }
4489  return null;
4490  }
4491 
4499  public static function getLiveVersionIdOfRecord($table, $uid)
4500  {
4501  $liveVersionId = null;
4502  if (self::isTableWorkspaceEnabled($table)) {
4503  $currentRecord = self::getRecord($table, $uid, 'pid,t3ver_oid');
4504  if (is_array($currentRecord) && $currentRecord['pid'] == -1) {
4505  $liveVersionId = $currentRecord['t3ver_oid'];
4506  }
4507  }
4508  return $liveVersionId;
4509  }
4510 
4518  public static function versioningPlaceholderClause($table)
4519  {
4520  if ($GLOBALS['TCA'][$table] && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
4521  $currentWorkspace = (int)static::getBackendUserAuthentication()->workspace;
4522  return ' AND (' . $table . '.t3ver_state <= ' . new VersionState(VersionState::DEFAULT_STATE) . ' OR ' . $table . '.t3ver_wsid = ' . $currentWorkspace . ')';
4523  }
4524  return '';
4525  }
4526 
4534  public static function getWorkspaceWhereClause($table, $workspaceId = null)
4535  {
4536  $whereClause = '';
4537  if (self::isTableWorkspaceEnabled($table)) {
4538  if (is_null($workspaceId)) {
4539  $workspaceId = static::getBackendUserAuthentication()->workspace;
4540  }
4541  $workspaceId = (int)$workspaceId;
4542  $pidOperator = $workspaceId === 0 ? '!=' : '=';
4543  $whereClause = ' AND ' . $table . '.t3ver_wsid=' . $workspaceId . ' AND ' . $table . '.pid' . $pidOperator . '-1';
4544  }
4545  return $whereClause;
4546  }
4547 
4556  public static function countVersionsOfRecordsOnPage($workspace, $pageId)
4557  {
4558  GeneralUtility::logDeprecatedFunction();
4559  if ((int)$workspace === 0) {
4560  return array();
4561  }
4562  $output = array();
4563  foreach ($GLOBALS['TCA'] as $tableName => $cfg) {
4564  if ($tableName != 'pages' && $cfg['ctrl']['versioningWS']) {
4565  $movePointer = new VersionState(VersionState::MOVE_POINTER);
4566  $joinStatement = '(A.t3ver_oid=B.uid AND A.t3ver_state<>' . $movePointer
4567  . ' OR A.t3ver_oid=B.t3ver_move_id AND A.t3ver_state=' . $movePointer . ')';
4568 
4569  // Select all records from this table in the database from the workspace
4570  // This joins the online version with the offline version as tables A and B
4571  $output[$tableName] = static::getDatabaseConnection()->exec_SELECTgetRows(
4572  'B.uid as live_uid, A.uid as offline_uid',
4573  $tableName . ' A,' . $tableName . ' B',
4574  'A.pid=-1' . ' AND B.pid=' . (int)$pageId
4575  . ' AND A.t3ver_wsid=' . (int)$workspace . ' AND ' . $joinStatement
4576  . self::deleteClause($tableName, 'A') . self::deleteClause($tableName, 'B')
4577  );
4578  if (!is_array($output[$tableName]) || empty($output[$tableName])) {
4579  unset($output[$tableName]);
4580  }
4581  }
4582  }
4583  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['countVersionsOfRecordsOnPage'])) {
4584  $reference = null;
4585  $parameters = array(
4586  'workspace' => 'workspace',
4587  'pageId' => $pageId,
4588  'versions' => &$output,
4589  );
4590  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['countVersionsOfRecordsOnPage'] as $hookFunction) {
4591  GeneralUtility::callUserFunction($hookFunction, $parameters, $reference);
4592  }
4593  }
4594  return $output;
4595  }
4596 
4604  public static function wsMapId($table, $uid)
4605  {
4606  $wsRec = self::getWorkspaceVersionOfRecord(static::getBackendUserAuthentication()->workspace, $table, $uid, 'uid');
4607  return is_array($wsRec) ? $wsRec['uid'] : $uid;
4608  }
4609 
4619  public static function getMovePlaceholder($table, $uid, $fields = '*', $workspace = null)
4620  {
4621  if ($workspace === null) {
4622  $workspace = static::getBackendUserAuthentication()->workspace;
4623  }
4624  if ((int)$workspace !== 0 && $GLOBALS['TCA'][$table] && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
4625  // Select workspace version of record:
4626  $row = static::getDatabaseConnection()->exec_SELECTgetSingleRow(
4627  $fields,
4628  $table,
4629  'pid<>-1 AND t3ver_state=' . new VersionState(VersionState::MOVE_PLACEHOLDER) . ' AND t3ver_move_id='
4630  . (int)$uid . ' AND t3ver_wsid=' . (int)$workspace . self::deleteClause($table)
4631  );
4632  if (is_array($row)) {
4633  return $row;
4634  }
4635  }
4636  return false;
4637  }
4638 
4639  /*******************************************
4640  *
4641  * Miscellaneous
4642  *
4643  *******************************************/
4654  public static function TYPO3_copyRightNotice()
4655  {
4656  // Copyright Notice
4657  $loginCopyrightWarrantyProvider = strip_tags(trim($GLOBALS['TYPO3_CONF_VARS']['SYS']['loginCopyrightWarrantyProvider']));
4658  $loginCopyrightWarrantyURL = strip_tags(trim($GLOBALS['TYPO3_CONF_VARS']['SYS']['loginCopyrightWarrantyURL']));
4659 
4660  $lang = static::getLanguageService();
4661 
4662  if (strlen($loginCopyrightWarrantyProvider) >= 2 && strlen($loginCopyrightWarrantyURL) >= 10) {
4663  $warrantyNote = sprintf($lang->sL('LLL:EXT:lang/locallang_login.xlf:warranty.by'), htmlspecialchars($loginCopyrightWarrantyProvider), '<a href="' . htmlspecialchars($loginCopyrightWarrantyURL) . '" target="_blank">', '</a>');
4664  } else {
4665  $warrantyNote = sprintf($lang->sL('LLL:EXT:lang/locallang_login.xlf:no.warranty'), '<a href="' . TYPO3_URL_LICENSE . '" target="_blank">', '</a>');
4666  }
4667  $cNotice = '<a href="' . TYPO3_URL_GENERAL . '" target="_blank">' .
4668  $lang->sL('LLL:EXT:lang/locallang_login.xlf:typo3.cms') . '</a>. ' .
4669  $lang->sL('LLL:EXT:lang/locallang_login.xlf:copyright') . ' &copy; ' . htmlspecialchars(TYPO3_copyright_year) . ' Kasper Sk&aring;rh&oslash;j. ' .
4670  $lang->sL('LLL:EXT:lang/locallang_login.xlf:extension.copyright') . ' ' .
4671  sprintf($lang->sL('LLL:EXT:lang/locallang_login.xlf:details.link'), ('<a href="' . TYPO3_URL_GENERAL . '" target="_blank">' . TYPO3_URL_GENERAL . '</a>')) . ' ' .
4672  strip_tags($warrantyNote, '<a>') . ' ' .
4673  sprintf($lang->sL('LLL:EXT:lang/locallang_login.xlf:free.software'), ('<a href="' . TYPO3_URL_LICENSE . '" target="_blank">'), '</a> ') .
4674  $lang->sL('LLL:EXT:lang/locallang_login.xlf:keep.notice');
4675  return $cNotice;
4676  }
4677 
4685  public static function getPathType_web_nonweb($path)
4686  {
4687  GeneralUtility::logDeprecatedFunction();
4688  return GeneralUtility::isFirstPartOfStr($path, GeneralUtility::getIndpEnv('TYPO3_DOCUMENT_ROOT')) ? 'web' : '';
4689  }
4690 
4698  public static function ADMCMD_previewCmds($pageInfo)
4699  {
4700  $simUser = '';
4701  $simTime = '';
4702  if ($pageInfo['fe_group'] > 0) {
4703  $simUser = '&ADMCMD_simUser=' . $pageInfo['fe_group'];
4704  } elseif ((int)$pageInfo['fe_group'] === -2) {
4705  // -2 means "show at any login". We simulate first available fe_group.
4707  $sysPage = GeneralUtility::makeInstance(PageRepository::class);
4708  $activeFeGroupRow = BackendUtility::getRecordRaw('fe_groups', '1=1' . $sysPage->enableFields('fe_groups'), 'uid');
4709  if (!empty($activeFeGroupRow)) {
4710  $simUser = '&ADMCMD_simUser=' . $activeFeGroupRow['uid'];
4711  }
4712  }
4713  if ($pageInfo['starttime'] > $GLOBALS['EXEC_TIME']) {
4714  $simTime = '&ADMCMD_simTime=' . $pageInfo['starttime'];
4715  }
4716  if ($pageInfo['endtime'] < $GLOBALS['EXEC_TIME'] && $pageInfo['endtime'] != 0) {
4717  $simTime = '&ADMCMD_simTime=' . ($pageInfo['endtime'] - 1);
4718  }
4719  return $simUser . $simTime;
4720  }
4721 
4730  public static function processParams($params)
4731  {
4732  $paramArr = array();
4733  $lines = explode(LF, $params);
4734  foreach ($lines as $val) {
4735  $val = trim($val);
4736  if ($val) {
4737  $pair = explode('=', $val, 2);
4738  $paramArr[trim($pair[0])] = trim($pair[1]);
4739  }
4740  }
4741  return $paramArr;
4742  }
4743 
4750  public static function getBackendScript($interface = '')
4751  {
4752  if (!$interface) {
4753  $interface = static::getBackendUserAuthentication()->uc['interfaceSetup'];
4754  }
4755  switch ($interface) {
4756  case 'frontend':
4757  $script = '../.';
4758  break;
4759  case 'backend':
4760  default:
4761  $script = self::getModuleUrl('main');
4762  }
4763  return $script;
4764  }
4765 
4772  public static function isTableWorkspaceEnabled($table)
4773  {
4774  return !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS']);
4775  }
4776 
4786  public static function isTableMovePlaceholderAware($table)
4787  {
4788  return self::isTableWorkspaceEnabled($table);
4789  }
4790 
4798  public static function getTcaFieldConfiguration($table, $field)
4799  {
4800  $configuration = array();
4801  if (isset($GLOBALS['TCA'][$table]['columns'][$field]['config'])) {
4802  $configuration = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
4803  }
4804  return $configuration;
4805  }
4806 
4815  public static function isWebMountRestrictionIgnored($table)
4816  {
4817  return !empty($GLOBALS['TCA'][$table]['ctrl']['security']['ignoreWebMountRestriction']);
4818  }
4819 
4828  public static function isRootLevelRestrictionIgnored($table)
4829  {
4830  return !empty($GLOBALS['TCA'][$table]['ctrl']['security']['ignoreRootLevelRestriction']);
4831  }
4832 
4840  public static function shortcutExists($url)
4841  {
4842  $statement = self::getDatabaseConnection()->prepare_SELECTquery(
4843  'uid',
4844  'sys_be_shortcuts',
4845  'userid = :userid AND url = :url'
4846  );
4847 
4848  $statement->bindValues([
4849  ':userid' => self::getBackendUserAuthentication()->user['uid'],
4850  ':url' => $url
4851  ]
4852  );
4853 
4854  $statement->execute();
4855  $rows = $statement->fetch(PreparedStatement::FETCH_ASSOC);
4856  $statement->free();
4857 
4858  return !empty($rows);
4859  }
4860 
4866  protected static function getSignalSlotDispatcher()
4867  {
4868  return GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class);
4869  }
4870 
4880  protected static function emitGetPagesTSconfigPreIncludeSignal(array $TSdataArray, $id, array $rootLine, $returnPartArray)
4881  {
4882  $signalArguments = static::getSignalSlotDispatcher()->dispatch(__CLASS__, 'getPagesTSconfigPreInclude', array($TSdataArray, $id, $rootLine, $returnPartArray));
4883  return $signalArguments[0];
4884  }
4885 
4889  protected static function getDatabaseConnection()
4890  {
4891  return $GLOBALS['TYPO3_DB'];
4892  }
4893 
4897  protected static function getLanguageService()
4898  {
4899  return $GLOBALS['LANG'];
4900  }
4901 
4905  protected static function getBackendUserAuthentication()
4906  {
4907  return $GLOBALS['BE_USER'];
4908  }
4909 
4913  protected static function getDocumentTemplate()
4914  {
4915  return $GLOBALS['TBE_TEMPLATE'];
4916  }
4917 }