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