TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
AbstractPlugin.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Frontend\Plugin;
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 
17 use Doctrine\DBAL\Driver\Statement;
30 
38 {
44  public $cObj;
45 
51  public $prefixId;
52 
59 
65  public $extKey;
66 
74  public $piVars = [
75  'pointer' => '',
76  // Used as a pointer for lists
77  'mode' => '',
78  // List mode
79  'sword' => '',
80  // Search word
81  'sort' => ''
82  ];
83 
91  public $internal = ['res_count' => 0, 'results_at_a_time' => 20, 'maxPages' => 10, 'currentRow' => [], 'currentTable' => ''];
92 
98  public $LOCAL_LANG = [];
99 
108  protected $LOCAL_LANG_UNSET = [];
109 
116  public $LOCAL_LANG_loaded = false;
117 
123  public $LLkey = 'default';
124 
130  public $altLLkey = '';
131 
138  public $LLtestPrefix = '';
139 
146  public $LLtestPrefixAlt = '';
147 
151  public $pi_isOnlyFields = 'mode,pointer';
152 
156  public $pi_alwaysPrev = 0;
157 
161  public $pi_lowerThan = 5;
162 
166  public $pi_moreParams = '';
167 
171  public $pi_listFields = '*';
172 
176  public $pi_autoCacheFields = [];
177 
181  public $pi_autoCacheEn = false;
182 
190  public $pi_USER_INT_obj = false;
191 
198  public $pi_checkCHash = false;
199 
208  public $conf = [];
209 
216 
220  public $pi_tmpPageId = 0;
221 
228 
236 
246  {
247  $this->databaseConnection = $databaseConnection ?: $GLOBALS['TYPO3_DB'];
248  $this->frontendController = $frontendController ?: $GLOBALS['TSFE'];
249  // Setting piVars:
250  if ($this->prefixId) {
251  $this->piVars = GeneralUtility::_GPmerged($this->prefixId);
252  // cHash mode check
253  // IMPORTANT FOR CACHED PLUGINS (USER cObject): As soon as you generate cached plugin output which
254  // depends on parameters (eg. seeing the details of a news item) you MUST check if a cHash value is set.
255  // Background: The function call will check if a cHash parameter was sent with the URL because only if
256  // it was the page may be cached. If no cHash was found the function will simply disable caching to
257  // avoid unpredictable caching behaviour. In any case your plugin can generate the expected output and
258  // the only risk is that the content may not be cached. A missing cHash value is considered a mistake
259  // in the URL resulting from either URL manipulation, "realurl" "grayzones" etc. The problem is rare
260  // (more frequent with "realurl") but when it occurs it is very puzzling!
261  if ($this->pi_checkCHash && !empty($this->piVars)) {
262  $this->frontendController->reqCHash();
263  }
264  }
265  if (!empty($this->frontendController->config['config']['language'])) {
266  $this->LLkey = $this->frontendController->config['config']['language'];
267  if (empty($this->frontendController->config['config']['language_alt'])) {
269  $locales = GeneralUtility::makeInstance(Locales::class);
270  if (in_array($this->LLkey, $locales->getLocales())) {
271  $this->altLLkey = '';
272  foreach ($locales->getLocaleDependencies($this->LLkey) as $language) {
273  $this->altLLkey .= $language . ',';
274  }
275  $this->altLLkey = rtrim($this->altLLkey, ',');
276  }
277  } else {
278  $this->altLLkey = $this->frontendController->config['config']['language_alt'];
279  }
280  }
281  }
282 
290  protected function applyStdWrapRecursive(array $conf, $level = 0)
291  {
292  foreach ($conf as $key => $confNextLevel) {
293  if (strpos($key, '.') !== false) {
294  $key = substr($key, 0, -1);
295 
296  // descend into all non-stdWrap-subelements first
297  foreach ($confNextLevel as $subKey => $subConfNextLevel) {
298  if (is_array($subConfNextLevel) && strpos($subKey, '.') !== false && $subKey !== 'stdWrap.') {
299  $conf[$key . '.'] = $this->applyStdWrapRecursive($confNextLevel, $level + 1);
300  }
301  }
302 
303  // now for stdWrap
304  foreach ($confNextLevel as $subKey => $subConfNextLevel) {
305  if (is_array($subConfNextLevel) && $subKey === 'stdWrap.') {
306  $conf[$key] = $this->cObj->stdWrap($conf[$key], $conf[$key . '.']['stdWrap.']);
307  unset($conf[$key . '.']['stdWrap.']);
308  if (empty($conf[$key . '.'])) {
309  unset($conf[$key . '.']);
310  }
311  }
312  }
313  }
314  }
315  return $conf;
316  }
317 
323  public function pi_setPiVarDefaults()
324  {
325  if (isset($this->conf['_DEFAULT_PI_VARS.']) && is_array($this->conf['_DEFAULT_PI_VARS.'])) {
326  $this->conf['_DEFAULT_PI_VARS.'] = $this->applyStdWrapRecursive($this->conf['_DEFAULT_PI_VARS.']);
327  $tmp = $this->conf['_DEFAULT_PI_VARS.'];
328  ArrayUtility::mergeRecursiveWithOverrule($tmp, is_array($this->piVars) ? $this->piVars : []);
329  $this->piVars = $tmp;
330  }
331  }
332 
333  /***************************
334  *
335  * Link functions
336  *
337  **************************/
352  public function pi_getPageLink($id, $target = '', $urlParameters = [])
353  {
354  return $this->cObj->getTypoLink_URL($id, $urlParameters, $target);
355  }
356 
369  public function pi_linkToPage($str, $id, $target = '', $urlParameters = [])
370  {
371  return $this->cObj->getTypoLink($str, $id, $urlParameters, $target);
372  }
373 
385  public function pi_linkTP($str, $urlParameters = [], $cache = false, $altPageId = 0)
386  {
387  $conf = [];
388  $conf['useCacheHash'] = $this->pi_USER_INT_obj ? 0 : $cache;
389  $conf['no_cache'] = $this->pi_USER_INT_obj ? 0 : !$cache;
390  $conf['parameter'] = $altPageId ? $altPageId : ($this->pi_tmpPageId ? $this->pi_tmpPageId : $this->frontendController->id);
391  $conf['additionalParams'] = $this->conf['parent.']['addParams'] . GeneralUtility::implodeArrayForUrl('', $urlParameters, '', true) . $this->pi_moreParams;
392  return $this->cObj->typoLink($str, $conf);
393  }
394 
408  public function pi_linkTP_keepPIvars($str, $overrulePIvars = [], $cache = false, $clearAnyway = false, $altPageId = 0)
409  {
410  if (is_array($this->piVars) && is_array($overrulePIvars) && !$clearAnyway) {
412  unset($piVars['DATA']);
414  $overrulePIvars = $piVars;
415  if ($this->pi_autoCacheEn) {
416  $cache = $this->pi_autoCache($overrulePIvars);
417  }
418  }
419  return $this->pi_linkTP($str, [$this->prefixId => $overrulePIvars], $cache, $altPageId);
420  }
421 
433  public function pi_linkTP_keepPIvars_url($overrulePIvars = [], $cache = false, $clearAnyway = false, $altPageId = 0)
434  {
435  $this->pi_linkTP_keepPIvars('|', $overrulePIvars, $cache, $clearAnyway, $altPageId);
436  return $this->cObj->lastTypoLinkUrl;
437  }
438 
452  public function pi_list_linkSingle($str, $uid, $cache = false, $mergeArr = [], $urlOnly = false, $altPageId = 0)
453  {
454  if ($this->prefixId) {
455  if ($cache) {
456  $overrulePIvars = $uid ? ['showUid' => $uid] : [];
457  $overrulePIvars = array_merge($overrulePIvars, (array)$mergeArr);
458  $str = $this->pi_linkTP($str, [$this->prefixId => $overrulePIvars], $cache, $altPageId);
459  } else {
460  $overrulePIvars = ['showUid' => $uid ?: ''];
461  $overrulePIvars = array_merge($overrulePIvars, (array)$mergeArr);
462  $str = $this->pi_linkTP_keepPIvars($str, $overrulePIvars, $cache, 0, $altPageId);
463  }
464  // If urlOnly flag, return only URL as it has recently be generated.
465  if ($urlOnly) {
466  $str = $this->cObj->lastTypoLinkUrl;
467  }
468  }
469  return $str;
470  }
471 
480  public function pi_openAtagHrefInJSwindow($str, $winName = '', $winParams = 'width=670,height=500,status=0,menubar=0,scrollbars=1,resizable=1')
481  {
482  if (preg_match('/(.*)(<a[^>]*>)(.*)/i', $str, $match)) {
483  $aTagContent = GeneralUtility::get_tag_attributes($match[2]);
484  $onClick = 'vHWin=window.open('
485  . GeneralUtility::quoteJSvalue($this->frontendController->baseUrlWrap($aTagContent['href'])) . ','
486  . GeneralUtility::quoteJSvalue($winName ?: md5($aTagContent['href'])) . ','
487  . GeneralUtility::quoteJSvalue($winParams) . ');vHWin.focus();return false;';
488  $match[2] = '<a href="#" onclick="' . htmlspecialchars($onClick) . '">';
489  $str = $match[1] . $match[2] . $match[3];
490  }
491  return $str;
492  }
493 
494  /***************************
495  *
496  * Functions for listing, browsing, searching etc.
497  *
498  **************************/
523  public function pi_list_browseresults($showResultCount = 1, $tableParams = '', $wrapArr = [], $pointerName = 'pointer', $hscText = true, $forceOutput = false)
524  {
525  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class]['pi_list_browseresults'])
526  && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class]['pi_list_browseresults'])
527  ) {
528  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class]['pi_list_browseresults'] as $classRef) {
529  $hookObj = GeneralUtility::makeInstance($classRef);
530  if (method_exists($hookObj, 'pi_list_browseresults')) {
531  $pageBrowser = $hookObj->pi_list_browseresults($showResultCount, $tableParams, $wrapArr, $pointerName, $hscText, $forceOutput, $this);
532  if (is_string($pageBrowser) && trim($pageBrowser) !== '') {
533  return $pageBrowser;
534  }
535  }
536  }
537  }
538  // example $wrapArr-array how it could be traversed from an extension
539  /* $wrapArr = array(
540  'browseBoxWrap' => '<div class="browseBoxWrap">|</div>',
541  'showResultsWrap' => '<div class="showResultsWrap">|</div>',
542  'browseLinksWrap' => '<div class="browseLinksWrap">|</div>',
543  'showResultsNumbersWrap' => '<span class="showResultsNumbersWrap">|</span>',
544  'disabledLinkWrap' => '<span class="disabledLinkWrap">|</span>',
545  'inactiveLinkWrap' => '<span class="inactiveLinkWrap">|</span>',
546  'activeLinkWrap' => '<span class="activeLinkWrap">|</span>'
547  );*/
548  // Initializing variables:
549  $pointer = (int)$this->piVars[$pointerName];
550  $count = (int)$this->internal['res_count'];
551  $results_at_a_time = MathUtility::forceIntegerInRange($this->internal['results_at_a_time'], 1, 1000);
552  $totalPages = ceil($count / $results_at_a_time);
553  $maxPages = MathUtility::forceIntegerInRange($this->internal['maxPages'], 1, 100);
555  if (!$forceOutput && $count <= $results_at_a_time) {
556  return '';
557  }
558  // $showResultCount determines how the results of the pagerowser will be shown.
559  // If set to 0: only the result-browser will be shown
560  // 1: (default) the text "Displaying results..." and the result-browser will be shown.
561  // 2: only the text "Displaying results..." will be shown
562  $showResultCount = (int)$showResultCount;
563  // If this is set, two links named "<< First" and "LAST >>" will be shown and point to the very first or last page.
564  $showFirstLast = !empty($this->internal['showFirstLast']);
565  // If this has a value the "previous" button is always visible (will be forced if "showFirstLast" is set)
566  $alwaysPrev = $showFirstLast ? 1 : $this->pi_alwaysPrev;
567  if (isset($this->internal['pagefloat'])) {
568  if (strtoupper($this->internal['pagefloat']) == 'CENTER') {
569  $pagefloat = ceil(($maxPages - 1) / 2);
570  } else {
571  // pagefloat set as integer. 0 = left, value >= $this->internal['maxPages'] = right
572  $pagefloat = MathUtility::forceIntegerInRange($this->internal['pagefloat'], -1, $maxPages - 1);
573  }
574  } else {
575  // pagefloat disabled
576  $pagefloat = -1;
577  }
578  // Default values for "traditional" wrapping with a table. Can be overwritten by vars from $wrapArr
579  $wrapper['disabledLinkWrap'] = '<td nowrap="nowrap"><p>|</p></td>';
580  $wrapper['inactiveLinkWrap'] = '<td nowrap="nowrap"><p>|</p></td>';
581  $wrapper['activeLinkWrap'] = '<td' . $this->pi_classParam('browsebox-SCell') . ' nowrap="nowrap"><p>|</p></td>';
582  $wrapper['browseLinksWrap'] = rtrim('<table ' . $tableParams) . '><tr>|</tr></table>';
583  $wrapper['showResultsWrap'] = '<p>|</p>';
584  $wrapper['browseBoxWrap'] = '
585  <!--
586  List browsing box:
587  -->
588  <div ' . $this->pi_classParam('browsebox') . '>
589  |
590  </div>';
591  // Now overwrite all entries in $wrapper which are also in $wrapArr
592  $wrapper = array_merge($wrapper, $wrapArr);
593  // Show pagebrowser
594  if ($showResultCount != 2) {
595  if ($pagefloat > -1) {
596  $lastPage = min($totalPages, max($pointer + 1 + $pagefloat, $maxPages));
597  $firstPage = max(0, $lastPage - $maxPages);
598  } else {
599  $firstPage = 0;
600  $lastPage = MathUtility::forceIntegerInRange($totalPages, 1, $maxPages);
601  }
602  $links = [];
603  // Make browse-table/links:
604  // Link to first page
605  if ($showFirstLast) {
606  if ($pointer > 0) {
607  $label = $this->pi_getLL('pi_list_browseresults_first', '<< First');
608  $links[] = $this->cObj->wrap($this->pi_linkTP_keepPIvars($hscText ? htmlspecialchars($label) : $label, [$pointerName => null], $pi_isOnlyFields), $wrapper['inactiveLinkWrap']);
609  } else {
610  $label = $this->pi_getLL('pi_list_browseresults_first', '<< First');
611  $links[] = $this->cObj->wrap(hscText ? htmlspecialchars($label) : $label, $wrapper['disabledLinkWrap']);
612  }
613  }
614  // Link to previous page
615  if ($alwaysPrev >= 0) {
616  if ($pointer > 0) {
617  $label = $this->pi_getLL('pi_list_browseresults_prev', '< Previous');
618  $links[] = $this->cObj->wrap($this->pi_linkTP_keepPIvars($hscText ? htmlspecialchars($label) : $label, [$pointerName => ($pointer - 1) ?: ''], $pi_isOnlyFields), $wrapper['inactiveLinkWrap']);
619  } elseif ($alwaysPrev) {
620  $label = $this->pi_getLL('pi_list_browseresults_prev', '< Previous');
621  $links[] = $this->cObj->wrap($hscText ? htmlspecialchars($label) : $label, $wrapper['disabledLinkWrap']);
622  }
623  }
624  // Links to pages
625  for ($a = $firstPage; $a < $lastPage; $a++) {
626  if ($this->internal['showRange']) {
627  $pageText = ($a * $results_at_a_time + 1) . '-' . min($count, ($a + 1) * $results_at_a_time);
628  } else {
629  $label = $this->pi_getLL('pi_list_browseresults_page', 'Page');
630  $pageText = trim($hscText ? htmlspecialchars($label) : $label . ' ' . ($a + 1));
631  }
632  // Current page
633  if ($pointer == $a) {
634  if ($this->internal['dontLinkActivePage']) {
635  $links[] = $this->cObj->wrap($pageText, $wrapper['activeLinkWrap']);
636  } else {
637  $links[] = $this->cObj->wrap($this->pi_linkTP_keepPIvars($pageText, [$pointerName => $a ?: ''], $pi_isOnlyFields), $wrapper['activeLinkWrap']);
638  }
639  } else {
640  $links[] = $this->cObj->wrap($this->pi_linkTP_keepPIvars($pageText, [$pointerName => $a ?: ''], $pi_isOnlyFields), $wrapper['inactiveLinkWrap']);
641  }
642  }
643  if ($pointer < $totalPages - 1 || $showFirstLast) {
644  // Link to next page
645  if ($pointer >= $totalPages - 1) {
646  $label = $this->pi_getLL('pi_list_browseresults_next', 'Next >');
647  $links[] = $this->cObj->wrap($hscText ? htmlspecialchars($label) : $label, $wrapper['disabledLinkWrap']);
648  } else {
649  $label = $this->pi_getLL('pi_list_browseresults_next', 'Next >');
650  $links[] = $this->cObj->wrap($this->pi_linkTP_keepPIvars($hscText ? htmlspecialchars($label) : $label, [$pointerName => $pointer + 1], $pi_isOnlyFields), $wrapper['inactiveLinkWrap']);
651  }
652  }
653  // Link to last page
654  if ($showFirstLast) {
655  if ($pointer < $totalPages - 1) {
656  $label = $this->pi_getLL('pi_list_browseresults_last', 'Last >>');
657  $links[] = $this->cObj->wrap($this->pi_linkTP_keepPIvars($hscText ? htmlspecialchars($label) : $label, [$pointerName => $totalPages - 1], $pi_isOnlyFields), $wrapper['inactiveLinkWrap']);
658  } else {
659  $label = $this->pi_getLL('pi_list_browseresults_last', 'Last >>');
660  $links[] = $this->cObj->wrap($hscText ? htmlspecialchars($label) : $label, $wrapper['disabledLinkWrap']);
661  }
662  }
663  $theLinks = $this->cObj->wrap(implode(LF, $links), $wrapper['browseLinksWrap']);
664  } else {
665  $theLinks = '';
666  }
667  $pR1 = $pointer * $results_at_a_time + 1;
668  $pR2 = $pointer * $results_at_a_time + $results_at_a_time;
669  if ($showResultCount) {
670  if ($wrapper['showResultsNumbersWrap']) {
671  // This will render the resultcount in a more flexible way using markers (new in TYPO3 3.8.0).
672  // The formatting string is expected to hold template markers (see function header). Example: 'Displaying results ###FROM### to ###TO### out of ###OUT_OF###'
673  $markerArray['###FROM###'] = $this->cObj->wrap($this->internal['res_count'] > 0 ? $pR1 : 0, $wrapper['showResultsNumbersWrap']);
674  $markerArray['###TO###'] = $this->cObj->wrap(min($this->internal['res_count'], $pR2), $wrapper['showResultsNumbersWrap']);
675  $markerArray['###OUT_OF###'] = $this->cObj->wrap($this->internal['res_count'], $wrapper['showResultsNumbersWrap']);
676  $markerArray['###FROM_TO###'] = $this->cObj->wrap(($this->internal['res_count'] > 0 ? $pR1 : 0) . ' ' . $this->pi_getLL('pi_list_browseresults_to', 'to') . ' ' . min($this->internal['res_count'], $pR2), $wrapper['showResultsNumbersWrap']);
677  $markerArray['###CURRENT_PAGE###'] = $this->cObj->wrap($pointer + 1, $wrapper['showResultsNumbersWrap']);
678  $markerArray['###TOTAL_PAGES###'] = $this->cObj->wrap($totalPages, $wrapper['showResultsNumbersWrap']);
679  // Substitute markers
680  $resultCountMsg = $this->cObj->substituteMarkerArray($this->pi_getLL('pi_list_browseresults_displays', 'Displaying results ###FROM### to ###TO### out of ###OUT_OF###'), $markerArray);
681  } else {
682  // Render the resultcount in the "traditional" way using sprintf
683  $resultCountMsg = sprintf(str_replace('###SPAN_BEGIN###', '<span' . $this->pi_classParam('browsebox-strong') . '>', $this->pi_getLL('pi_list_browseresults_displays', 'Displaying results ###SPAN_BEGIN###%s to %s</span> out of ###SPAN_BEGIN###%s</span>')), $count > 0 ? $pR1 : 0, min($count, $pR2), $count);
684  }
685  $resultCountMsg = $this->cObj->wrap($resultCountMsg, $wrapper['showResultsWrap']);
686  } else {
687  $resultCountMsg = '';
688  }
689  $sTables = $this->cObj->wrap($resultCountMsg . $theLinks, $wrapper['browseBoxWrap']);
690  return $sTables;
691  }
692 
700  public function pi_list_modeSelector($items = [], $tableParams = '')
701  {
702  $cells = [];
703  foreach ($items as $k => $v) {
704  $cells[] = '
705  <td' . ($this->piVars['mode'] == $k ? $this->pi_classParam('modeSelector-SCell') : '') . '><p>' . $this->pi_linkTP_keepPIvars(htmlspecialchars($v), ['mode' => $k], $this->pi_isOnlyFields($this->pi_isOnlyFields)) . '</p></td>';
706  }
707  $sTables = '
708 
709  <!--
710  Mode selector (menu for list):
711  -->
712  <div' . $this->pi_classParam('modeSelector') . '>
713  <' . rtrim('table ' . $tableParams) . '>
714  <tr>
715  ' . implode('', $cells) . '
716  </tr>
717  </table>
718  </div>';
719  return $sTables;
720  }
721 
734  public function pi_list_makelist($statement, $tableParams = '')
735  {
736  // Make list table header:
737  $tRows = [];
738  $this->internal['currentRow'] = '';
739  $tRows[] = $this->pi_list_header();
740  // Make list table rows
741  $c = 0;
742  while ($this->internal['currentRow'] = $statement->fetch()) {
743  $tRows[] = $this->pi_list_row($c);
744  $c++;
745  }
746  $out = '
747 
748  <!--
749  Record list:
750  -->
751  <div' . $this->pi_classParam('listrow') . '>
752  <' . rtrim('table ' . $tableParams) . '>
753  ' . implode('', $tRows) . '
754  </table>
755  </div>';
756  return $out;
757  }
758 
767  public function pi_list_row($c)
768  {
769  // Dummy
770  return '<tr' . ($c % 2 ? $this->pi_classParam('listrow-odd') : '') . '><td><p>[dummy row]</p></td></tr>';
771  }
772 
780  public function pi_list_header()
781  {
782  return '<tr' . $this->pi_classParam('listrow-header') . '><td><p>[dummy header row]</p></td></tr>';
783  }
784 
785  /***************************
786  *
787  * Stylesheet, CSS
788  *
789  **************************/
796  public function pi_getClassName($class)
797  {
798  return str_replace('_', '-', $this->prefixId) . ($this->prefixId ? '-' : '') . $class;
799  }
800 
810  public function pi_classParam($class, $addClasses = '')
811  {
812  $output = '';
813  $classNames = GeneralUtility::trimExplode(',', $class);
814  foreach ($classNames as $className) {
815  $output .= ' ' . $this->pi_getClassName($className);
816  }
817  $additionalClassNames = GeneralUtility::trimExplode(',', $addClasses);
818  foreach ($additionalClassNames as $additionalClassName) {
819  $output .= ' ' . $additionalClassName;
820  }
821  return ' class="' . trim($output) . '"';
822  }
823 
831  public function pi_wrapInBaseClass($str)
832  {
833  $content = '<div class="' . str_replace('_', '-', $this->prefixId) . '">
834  ' . $str . '
835  </div>
836  ';
837  if (!$this->frontendController->config['config']['disablePrefixComment']) {
838  $content = '
839 
840 
841  <!--
842 
843  BEGIN: Content of extension "' . $this->extKey . '", plugin "' . $this->prefixId . '"
844 
845  -->
846  ' . $content . '
847  <!-- END: Content of extension "' . $this->extKey . '", plugin "' . $this->prefixId . '" -->
848 
849  ';
850  }
851  return $content;
852  }
853 
854  /***************************
855  *
856  * Frontend editing: Edit panel, edit icons
857  *
858  **************************/
869  public function pi_getEditPanel($row = [], $tablename = '', $label = '', $conf = [])
870  {
871  $panel = '';
872  if (!$row || !$tablename) {
873  $row = $this->internal['currentRow'];
874  $tablename = $this->internal['currentTable'];
875  }
876  if ($this->frontendController->beUserLogin) {
877  // Create local cObj if not set:
878  if (!is_object($this->pi_EPtemp_cObj)) {
879  $this->pi_EPtemp_cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
880  $this->pi_EPtemp_cObj->setParent($this->cObj->data, $this->cObj->currentRecord);
881  }
882  // Initialize the cObj object with current row
883  $this->pi_EPtemp_cObj->start($row, $tablename);
884  // Setting TypoScript values in the $conf array. See documentation in TSref for the EDITPANEL cObject.
885  $conf['allow'] = 'edit,new,delete,move,hide';
886  $panel = $this->pi_EPtemp_cObj->cObjGetSingle('EDITPANEL', $conf, 'editpanel');
887  }
888  if ($panel) {
889  if ($label) {
890  return '<!-- BEGIN: EDIT PANEL --><table border="0" cellpadding="0" cellspacing="0" width="100%"><tr><td valign="top">' . $label . '</td><td valign="top" align="right">' . $panel . '</td></tr></table><!-- END: EDIT PANEL -->';
891  } else {
892  return '<!-- BEGIN: EDIT PANEL -->' . $panel . '<!-- END: EDIT PANEL -->';
893  }
894  } else {
895  return $label;
896  }
897  }
898 
912  public function pi_getEditIcon($content, $fields, $title = '', $row = [], $tablename = '', $oConf = [])
913  {
914  if ($this->frontendController->beUserLogin) {
915  if (!$row || !$tablename) {
916  $row = $this->internal['currentRow'];
917  $tablename = $this->internal['currentTable'];
918  }
919  $conf = array_merge([
920  'beforeLastTag' => 1,
921  'iconTitle' => $title
922  ], $oConf);
923  $content = $this->cObj->editIcons($content, $tablename . ':' . $fields, $conf, $tablename . ':' . $row['uid'], $row, '&viewUrl=' . rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI')));
924  }
925  return $content;
926  }
927 
928  /***************************
929  *
930  * Localization, locallang functions
931  *
932  **************************/
942  public function pi_getLL($key, $alternativeLabel = '', $hsc = false)
943  {
944  $word = null;
945  if (!empty($this->LOCAL_LANG[$this->LLkey][$key][0]['target'])
946  || isset($this->LOCAL_LANG_UNSET[$this->LLkey][$key])
947  ) {
948  $word = $this->LOCAL_LANG[$this->LLkey][$key][0]['target'];
949  } elseif ($this->altLLkey) {
950  $alternativeLanguageKeys = GeneralUtility::trimExplode(',', $this->altLLkey, true);
951  $alternativeLanguageKeys = array_reverse($alternativeLanguageKeys);
952  foreach ($alternativeLanguageKeys as $languageKey) {
953  if (!empty($this->LOCAL_LANG[$languageKey][$key][0]['target'])
954  || isset($this->LOCAL_LANG_UNSET[$languageKey][$key])
955  ) {
956  // Alternative language translation for key exists
957  $word = $this->LOCAL_LANG[$languageKey][$key][0]['target'];
958  break;
959  }
960  }
961  }
962  if ($word === null) {
963  if (!empty($this->LOCAL_LANG['default'][$key][0]['target'])
964  || isset($this->LOCAL_LANG_UNSET['default'][$key])
965  ) {
966  // Get default translation (without charset conversion, english)
967  $word = $this->LOCAL_LANG['default'][$key][0]['target'];
968  } else {
969  // Return alternative string or empty
970  $word = isset($this->LLtestPrefixAlt) ? $this->LLtestPrefixAlt . $alternativeLabel : $alternativeLabel;
971  }
972  }
973  $output = isset($this->LLtestPrefix) ? $this->LLtestPrefix . $word : $word;
974  if ($hsc) {
976  'Calling pi_getLL() with argument \'hsc\' has been deprecated.'
977  );
978  $output = htmlspecialchars($output);
979  }
980  return $output;
981  }
982 
994  public function pi_loadLL($languageFilePath = '')
995  {
996  if ($this->LOCAL_LANG_loaded) {
997  return;
998  }
999 
1000  if ($languageFilePath === '' && $this->scriptRelPath) {
1001  $languageFilePath = 'EXT:' . $this->extKey . '/' . dirname($this->scriptRelPath) . '/locallang.xlf';
1002  }
1003  if ($languageFilePath !== '') {
1005  $languageFactory = GeneralUtility::makeInstance(LocalizationFactory::class);
1006  // Read the strings in the required charset (since TYPO3 4.2)
1007  $this->LOCAL_LANG = $languageFactory->getParsedData($languageFilePath, $this->LLkey, 'utf-8');
1008  $alternativeLanguageKeys = GeneralUtility::trimExplode(',', $this->altLLkey, true);
1009  foreach ($alternativeLanguageKeys as $languageKey) {
1010  $tempLL = $languageFactory->getParsedData($languageFilePath, $languageKey);
1011  if ($this->LLkey !== 'default' && isset($tempLL[$languageKey])) {
1012  $this->LOCAL_LANG[$languageKey] = $tempLL[$languageKey];
1013  }
1014  }
1015  // Overlaying labels from TypoScript (including fictitious language keys for non-system languages!):
1016  if (isset($this->conf['_LOCAL_LANG.'])) {
1017  // Clear the "unset memory"
1018  $this->LOCAL_LANG_UNSET = [];
1019  foreach ($this->conf['_LOCAL_LANG.'] as $languageKey => $languageArray) {
1020  // Remove the dot after the language key
1021  $languageKey = substr($languageKey, 0, -1);
1022  // Don't process label if the language is not loaded
1023  if (is_array($languageArray) && isset($this->LOCAL_LANG[$languageKey])) {
1024  foreach ($languageArray as $labelKey => $labelValue) {
1025  if (!is_array($labelValue)) {
1026  $this->LOCAL_LANG[$languageKey][$labelKey][0]['target'] = $labelValue;
1027  if ($labelValue === '') {
1028  $this->LOCAL_LANG_UNSET[$languageKey][$labelKey] = '';
1029  }
1030  }
1031  }
1032  }
1033  }
1034  }
1035  }
1036  $this->LOCAL_LANG_loaded = true;
1037  }
1038 
1039  /***************************
1040  *
1041  * Database, queries
1042  *
1043  **************************/
1058  public function pi_exec_query($table, $count = false, $addWhere = '', $mm_cat = '', $groupBy = '', $orderBy = '', $query = '')
1059  {
1060  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
1061  $queryBuilder->from($table);
1062 
1063  // Begin Query:
1064  if (!$query) {
1065  // This adds WHERE-clauses that ensures deleted, hidden, starttime/endtime/access records are NOT
1066  // selected, if they should not! Almost ALWAYS add this to your queries!
1067  $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
1068 
1069  // Fetches the list of PIDs to select from.
1070  // TypoScript property .pidList is a comma list of pids. If blank, current page id is used.
1071  // TypoScript property .recursive is an int+ which determines how many levels down from the pids in the pid-list subpages should be included in the select.
1072  $pidList = GeneralUtility::intExplode(',', $this->pi_getPidList($this->conf['pidList'], $this->conf['recursive']), true);
1073  if (is_array($mm_cat)) {
1074  $queryBuilder->from($mm_cat['table'])
1075  ->from($mm_cat['mmtable'])
1076  ->where(
1077  $queryBuilder->expr()->eq($table . '.uid', $queryBuilder->quoteIdentifier($mm_cat['mmtable'] . '.uid_local')),
1078  $queryBuilder->expr()->eq($mm_cat['table'] . '.uid', $queryBuilder->quoteIdentifier($mm_cat['mmtable'] . '.uid_foreign')),
1079  $queryBuilder->expr()->in(
1080  $table . '.pid',
1081  $queryBuilder->createNamedParameter($pidList, Connection::PARAM_INT_ARRAY)
1082  )
1083  );
1084  if (strcmp($mm_cat['catUidList'], '')) {
1085  $queryBuilder->andWhere(
1086  $queryBuilder->expr()->in(
1087  $mm_cat['table'] . '.uid',
1088  $queryBuilder->createNamedParameter(
1089  GeneralUtility::intExplode(',', $mm_cat['catUidList'], true),
1090  Connection::PARAM_INT_ARRAY
1091  )
1092  )
1093  );
1094  }
1095  } else {
1096  $queryBuilder->where(
1097  $queryBuilder->expr()->in(
1098  'pid',
1099  $queryBuilder->createNamedParameter($pidList, Connection::PARAM_INT_ARRAY)
1100  )
1101  );
1102  }
1103  } else {
1104  // Restrictions need to be handled by the $query parameter!
1105  $queryBuilder->getRestrictions()->removeAll();
1106 
1107  // Split the "FROM ... WHERE" string so we get the WHERE part and TABLE names separated...:
1108  list($tableListFragment, $whereFragment) = preg_split('/WHERE/i', trim($query), 2);
1109  foreach (QueryHelper::parseTableList($tableListFragment) as $tableNameAndAlias) {
1110  list($tableName, $tableAlias) = $tableNameAndAlias;
1111  $queryBuilder->from($tableName, $tableAlias);
1112  }
1113  $queryBuilder->where(QueryHelper::stripLogicalOperatorPrefix($whereFragment));
1114  }
1115 
1116  // Add '$addWhere'
1117  if ($addWhere) {
1118  $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($addWhere));
1119  }
1120  // Search word:
1121  if ($this->piVars['sword'] && $this->internal['searchFieldList']) {
1122  $queryBuilder->andWhere(
1124  $this->cObj->searchWhere($this->piVars['sword'], $this->internal['searchFieldList'], $table)
1125  )
1126  );
1127  }
1128 
1129  if ($count) {
1130  $queryBuilder->count('*');
1131  } else {
1132  // Add 'SELECT'
1133  $fields = $this->pi_prependFieldsWithTable($table, $this->pi_listFields);
1134  $queryBuilder->select(...GeneralUtility::trimExplode(',', $fields, true));
1135 
1136  // Order by data:
1137  if (!$orderBy && $this->internal['orderBy']) {
1138  if (GeneralUtility::inList($this->internal['orderByList'], $this->internal['orderBy'])) {
1139  $sorting = $this->internal['descFlag'] ? ' DESC' : 'ASC';
1140  $queryBuilder->orderBy($table . '.' . $this->internal['orderBy'], $sorting);
1141  }
1142  } elseif ($orderBy) {
1143  foreach (QueryHelper::parseOrderBy($orderBy) as $fieldNameAndSorting) {
1144  list($fieldName, $sorting) = $fieldNameAndSorting;
1145  $queryBuilder->addOrderBy($fieldName, $sorting);
1146  }
1147  }
1148 
1149  // Limit data:
1150  $pointer = (int)$this->piVars['pointer'];
1151  $results_at_a_time = MathUtility::forceIntegerInRange($this->internal['results_at_a_time'], 1, 1000);
1152  $queryBuilder->setFirstResult($pointer * $results_at_a_time)
1153  ->setMaxResults($results_at_a_time);
1154 
1155  // Grouping
1156  if (!empty($groupBy)) {
1157  $queryBuilder->groupBy(...QueryHelper::parseGroupBy($groupBy));
1158  }
1159  }
1160 
1161  return $queryBuilder->execute();
1162  }
1163 
1173  public function pi_getRecord($table, $uid, $checkPage = false)
1174  {
1175  return $this->frontendController->sys_page->checkRecord($table, $uid, $checkPage);
1176  }
1177 
1185  public function pi_getPidList($pid_list, $recursive = 0)
1186  {
1187  if (!strcmp($pid_list, '')) {
1188  $pid_list = $this->frontendController->id;
1189  }
1190  $recursive = MathUtility::forceIntegerInRange($recursive, 0);
1191  $pid_list_arr = array_unique(GeneralUtility::trimExplode(',', $pid_list, true));
1192  $pid_list = [];
1193  foreach ($pid_list_arr as $val) {
1194  $val = MathUtility::forceIntegerInRange($val, 0);
1195  if ($val) {
1196  $_list = $this->cObj->getTreeList(-1 * $val, $recursive);
1197  if ($_list) {
1198  $pid_list[] = $_list;
1199  }
1200  }
1201  }
1202  return implode(',', $pid_list);
1203  }
1204 
1212  public function pi_prependFieldsWithTable($table, $fieldList)
1213  {
1214  $list = GeneralUtility::trimExplode(',', $fieldList, true);
1215  $return = [];
1216  foreach ($list as $listItem) {
1217  $return[] = $table . '.' . $listItem;
1218  }
1219  return implode(',', $return);
1220  }
1221 
1233  public function pi_getCategoryTableContents($table, $pid, $whereClause = '', $groupBy = '', $orderBy = '', $limit = '')
1234  {
1235  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
1236  $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
1237  $queryBuilder->select('*')
1238  ->from($table)
1239  ->where(
1240  $queryBuilder->expr()->eq(
1241  'pid',
1242  $queryBuilder->createNamedParameter($pid, \PDO::PARAM_INT)
1243  ),
1245  );
1246 
1247  if (!empty($orderBy)) {
1248  foreach (QueryHelper::parseOrderBy($orderBy) as $fieldNameAndSorting) {
1249  list($fieldName, $sorting) = $fieldNameAndSorting;
1250  $queryBuilder->addOrderBy($fieldName, $sorting);
1251  }
1252  }
1253 
1254  if (!empty($groupBy)) {
1255  $queryBuilder->groupBy(...QueryHelper::parseGroupBy($groupBy));
1256  }
1257 
1258  if (!empty($limit)) {
1259  $limitValues = GeneralUtility::intExplode(',', $limit, true);
1260  if (count($limitValues) === 1) {
1261  $queryBuilder->setMaxResults($limitValues[0]);
1262  } else {
1263  $queryBuilder->setFirstResult($limitValues[0])
1264  ->setMaxResults($limitValues[1]);
1265  }
1266  }
1267 
1268  $result = $queryBuilder->execute();
1269  $outArr = [];
1270  while ($row = $result->fetch()) {
1271  $outArr[$row['uid']] = $row;
1272  }
1273  return $outArr;
1274  }
1275 
1276  /***************************
1277  *
1278  * Various
1279  *
1280  **************************/
1289  public function pi_isOnlyFields($fList, $lowerThan = -1)
1290  {
1291  $lowerThan = $lowerThan == -1 ? $this->pi_lowerThan : $lowerThan;
1292  $fList = GeneralUtility::trimExplode(',', $fList, true);
1293  $tempPiVars = $this->piVars;
1294  foreach ($fList as $k) {
1295  if (!MathUtility::canBeInterpretedAsInteger($tempPiVars[$k]) || $tempPiVars[$k] < $lowerThan) {
1296  unset($tempPiVars[$k]);
1297  }
1298  }
1299  if (empty($tempPiVars)) {
1300  //@TODO: How do we deal with this? return TRUE would be the right thing to do here but that might be breaking
1301  return 1;
1302  }
1303  return null;
1304  }
1305 
1315  public function pi_autoCache($inArray)
1316  {
1317  if (is_array($inArray)) {
1318  foreach ($inArray as $fN => $fV) {
1319  if (!strcmp($inArray[$fN], '')) {
1320  unset($inArray[$fN]);
1321  } elseif (is_array($this->pi_autoCacheFields[$fN])) {
1322  if (is_array($this->pi_autoCacheFields[$fN]['range']) && (int)$inArray[$fN] >= (int)$this->pi_autoCacheFields[$fN]['range'][0] && (int)$inArray[$fN] <= (int)$this->pi_autoCacheFields[$fN]['range'][1]) {
1323  unset($inArray[$fN]);
1324  }
1325  if (is_array($this->pi_autoCacheFields[$fN]['list']) && in_array($inArray[$fN], $this->pi_autoCacheFields[$fN]['list'])) {
1326  unset($inArray[$fN]);
1327  }
1328  }
1329  }
1330  }
1331  if (empty($inArray)) {
1332  //@TODO: How do we deal with this? return TRUE would be the right thing to do here but that might be breaking
1333  return 1;
1334  }
1335  return null;
1336  }
1337 
1347  public function pi_RTEcssText($str)
1348  {
1349  $parseFunc = $this->frontendController->tmpl->setup['lib.']['parseFunc_RTE.'];
1350  if (is_array($parseFunc)) {
1351  $str = $this->cObj->parseFunc($str, $parseFunc);
1352  }
1353  return $str;
1354  }
1355 
1356  /*******************************
1357  *
1358  * FlexForms related functions
1359  *
1360  *******************************/
1367  public function pi_initPIflexForm($field = 'pi_flexform')
1368  {
1369  // Converting flexform data into array:
1370  if (!is_array($this->cObj->data[$field]) && $this->cObj->data[$field]) {
1371  $this->cObj->data[$field] = GeneralUtility::xml2array($this->cObj->data[$field]);
1372  if (!is_array($this->cObj->data[$field])) {
1373  $this->cObj->data[$field] = [];
1374  }
1375  }
1376  }
1377 
1388  public function pi_getFFvalue($T3FlexForm_array, $fieldName, $sheet = 'sDEF', $lang = 'lDEF', $value = 'vDEF')
1389  {
1390  $sheetArray = is_array($T3FlexForm_array) ? $T3FlexForm_array['data'][$sheet][$lang] : '';
1391  if (is_array($sheetArray)) {
1392  return $this->pi_getFFvalueFromSheetArray($sheetArray, explode('/', $fieldName), $value);
1393  }
1394  return null;
1395  }
1396 
1407  public function pi_getFFvalueFromSheetArray($sheetArray, $fieldNameArr, $value)
1408  {
1409  $tempArr = $sheetArray;
1410  foreach ($fieldNameArr as $k => $v) {
1412  if (is_array($tempArr)) {
1413  $c = 0;
1414  foreach ($tempArr as $values) {
1415  if ($c == $v) {
1416  $tempArr = $values;
1417  break;
1418  }
1419  $c++;
1420  }
1421  }
1422  } else {
1423  $tempArr = $tempArr[$v];
1424  }
1425  }
1426  return $tempArr[$value];
1427  }
1428 }
pi_linkTP_keepPIvars_url($overrulePIvars=[], $cache=false, $clearAnyway=false, $altPageId=0)
pi_linkTP_keepPIvars($str, $overrulePIvars=[], $cache=false, $clearAnyway=false, $altPageId=0)
pi_getFFvalueFromSheetArray($sheetArray, $fieldNameArr, $value)
pi_getEditPanel($row=[], $tablename= '', $label= '', $conf=[])
pi_linkToPage($str, $id, $target= '', $urlParameters=[])
pi_getEditIcon($content, $fields, $title= '', $row=[], $tablename= '', $oConf=[])
static xml2array($string, $NSprefix= '', $reportDocTag=false)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
pi_linkTP($str, $urlParameters=[], $cache=false, $altPageId=0)
static implodeArrayForUrl($name, array $theArray, $str= '', $skipBlank=false, $rawurlencodeParamName=false)
pi_getFFvalue($T3FlexForm_array, $fieldName, $sheet= 'sDEF', $lang= 'lDEF', $value= 'vDEF')
pi_getCategoryTableContents($table, $pid, $whereClause= '', $groupBy= '', $orderBy= '', $limit= '')
pi_list_modeSelector($items=[], $tableParams= '')
pi_getRecord($table, $uid, $checkPage=false)
pi_getLL($key, $alternativeLabel= '', $hsc=false)
pi_openAtagHrefInJSwindow($str, $winName= '', $winParams= 'width=670, height=500, status=0, menubar=0, scrollbars=1, resizable=1')
pi_getPageLink($id, $target= '', $urlParameters=[])
applyStdWrapRecursive(array $conf, $level=0)
static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static makeInstance($className,...$constructorArguments)
pi_list_makelist($statement, $tableParams= '')
pi_exec_query($table, $count=false, $addWhere= '', $mm_cat= '', $groupBy= '', $orderBy= '', $query= '')
static forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:31
static stripLogicalOperatorPrefix(string $constraint)
pi_list_linkSingle($str, $uid, $cache=false, $mergeArr=[], $urlOnly=false, $altPageId=0)
pi_list_browseresults($showResultCount=1, $tableParams= '', $wrapArr=[], $pointerName= 'pointer', $hscText=true, $forceOutput=false)
static intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)