[TASK] Remove unneeded parenthesis on array-access
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / TypoScript / ExtendedTemplateService.php
1 <?php
2 namespace TYPO3\CMS\Core\TypoScript;
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 TYPO3\CMS\Backend\Template\DocumentTemplate;
18 use TYPO3\CMS\Backend\Utility\BackendUtility;
19 use TYPO3\CMS\Core\Exception;
20 use TYPO3\CMS\Core\Imaging\Icon;
21 use TYPO3\CMS\Core\Imaging\IconFactory;
22 use TYPO3\CMS\Core\Utility\GeneralUtility;
23 use TYPO3\CMS\Core\Utility\MathUtility;
24 use TYPO3\CMS\Core\Utility\PathUtility;
25 use TYPO3\CMS\Dbal\Database\DatabaseConnection;
26 use TYPO3\CMS\Frontend\Configuration\TypoScript\ConditionMatching\ConditionMatcher;
27 use TYPO3\CMS\Lang\LanguageService;
28
29 /**
30 * TSParser extension class to TemplateService
31 * Contains functions for the TS module in TYPO3 backend
32 */
33 class ExtendedTemplateService extends TemplateService
34 {
35 /**
36 * This string is used to indicate the point in a template from where the editable constants are listed.
37 * Any vars before this point (if it exists though) is regarded as default values.
38 *
39 * @var string
40 */
41 public $edit_divider = '###MOD_TS:EDITABLE_CONSTANTS###';
42
43 /**
44 * @var string
45 */
46 public $HTMLcolorList = 'aqua,beige,black,blue,brown,fuchsia,gold,gray,green,lime,maroon,navy,olive,orange,purple,red,silver,tan,teal,turquoise,yellow,white';
47
48 /**
49 * @var array
50 */
51 public $categories = array(
52 'basic' => array(),
53 // Constants of superior importance for the template-layout. This is dimensions, imagefiles and enabling of various features. The most basic constants, which you would almost always want to configure.
54 'menu' => array(),
55 // Menu setup. This includes fontfiles, sizes, background images. Depending on the menutype.
56 'content' => array(),
57 // All constants related to the display of pagecontent elements
58 'page' => array(),
59 // General configuration like metatags, link targets
60 'advanced' => array(),
61 // Advanced functions, which are used very seldomly.
62 'all' => array()
63 );
64
65 /**
66 * Translated categories
67 *
68 * @var array
69 */
70 protected $categoryLabels = array();
71
72 /**
73 * This will be filled with the available categories of the current template.
74 *
75 * @var array
76 */
77 public $subCategories = array(
78 // Standard categories:
79 'enable' => array('Enable features', 'a'),
80 'dims' => array('Dimensions, widths, heights, pixels', 'b'),
81 'file' => array('Files', 'c'),
82 'typo' => array('Typography', 'd'),
83 'color' => array('Colors', 'e'),
84 'links' => array('Links and targets', 'f'),
85 'language' => array('Language specific constants', 'g'),
86 // subcategories based on the default content elements
87 'cheader' => array('Content: \'Header\'', 'ma'),
88 'cheader_g' => array('Content: \'Header\', Graphical', 'ma'),
89 'ctext' => array('Content: \'Text\'', 'mb'),
90 'cimage' => array('Content: \'Image\'', 'md'),
91 'ctextmedia' => array('Content: \'Textmedia\'', 'ml'),
92 'cbullets' => array('Content: \'Bullet list\'', 'me'),
93 'ctable' => array('Content: \'Table\'', 'mf'),
94 'cuploads' => array('Content: \'Filelinks\'', 'mg'),
95 'cmultimedia' => array('Content: \'Multimedia\'', 'mh'),
96 'cmedia' => array('Content: \'Media\'', 'mr'),
97 'cmailform' => array('Content: \'Form\'', 'mi'),
98 'csearch' => array('Content: \'Search\'', 'mj'),
99 'clogin' => array('Content: \'Login\'', 'mk'),
100 'cmenu' => array('Content: \'Menu/Sitemap\'', 'mm'),
101 'cshortcut' => array('Content: \'Insert records\'', 'mn'),
102 'clist' => array('Content: \'List of records\'', 'mo'),
103 'chtml' => array('Content: \'HTML\'', 'mq')
104 );
105
106 /**
107 * @var bool
108 */
109 public $backend_info = true;
110
111 /**
112 * Tsconstanteditor
113 *
114 * @var int
115 */
116 public $ext_inBrace = 0;
117
118 /**
119 * Tsbrowser
120 *
121 * @var array
122 */
123 public $tsbrowser_searchKeys = array();
124
125 /**
126 * @var array
127 */
128 public $tsbrowser_depthKeys = array();
129
130 /**
131 * @var string
132 */
133 public $constantMode = '';
134
135 /**
136 * @var string
137 */
138 public $regexMode = '';
139
140 /**
141 * @var string
142 */
143 public $fixedLgd = '';
144
145 /**
146 * @var int
147 */
148 public $ext_lineNumberOffset = 0;
149
150 /**
151 * @var string
152 */
153 public $ext_localGfxPrefix = '';
154
155 /**
156 * @var string
157 */
158 public $ext_localWebGfxPrefix = '';
159
160 /**
161 * @var int
162 */
163 public $ext_expandAllNotes = 0;
164
165 /**
166 * @var int
167 */
168 public $ext_noPMicons = 0;
169
170 /**
171 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
172 *
173 * @var int
174 */
175 public $ext_noSpecialCharsOnLabels = 0;
176
177 /**
178 * @var array
179 */
180 public $ext_listOfTemplatesArr = array();
181
182 /**
183 * @var string
184 */
185 public $ext_lineNumberOffset_mode = '';
186
187 /**
188 * Don't change
189 *
190 * @var int
191 */
192 public $ext_dontCheckIssetValues = 0;
193
194 /**
195 * @var int
196 */
197 public $ext_printAll = 0;
198
199 /**
200 * @var string
201 */
202 public $ext_CEformName = 'forms[0]';
203
204 /**
205 * @var bool
206 */
207 public $doNotSortCategoriesBeforeMakingForm = false;
208
209 /**
210 * Ts analyzer
211 *
212 * @var array
213 */
214 public $templateTitles = array();
215
216 /**
217 * @var array|NULL
218 */
219 protected $lnToScript = null;
220
221 /**
222 * @var array
223 */
224 public $clearList_const_temp;
225
226 /**
227 * @var array
228 */
229 public $clearList_setup_temp;
230
231 /**
232 * @var string
233 */
234 protected $Cmarker = '';
235
236 /**
237 * @var string
238 */
239 public $bType = '';
240
241 /**
242 * @var bool
243 */
244 public $linkObjects = false;
245
246 /**
247 * @var array
248 */
249 public $helpConfig = array();
250
251 /**
252 * @var bool
253 */
254 public $changed = false;
255
256 /**
257 * @var int[]
258 */
259 protected $objReg = array();
260
261 /**
262 * @var array
263 */
264 public $raw = array();
265
266 /**
267 * @var int
268 */
269 public $rawP = 0;
270
271 /**
272 * @var string
273 */
274 public $lastComment = '';
275
276 /**
277 * Substitute constant
278 *
279 * @param string $all
280 * @return string
281 */
282 public function substituteConstants($all)
283 {
284 $this->Cmarker = substr(md5(uniqid('', true)), 0, 6);
285 return preg_replace_callback('/\\{\\$(.[^}]+)\\}/', array($this, 'substituteConstantsCallBack'), $all);
286 }
287
288 /**
289 * Call back method for preg_replace_callback in substituteConstants
290 *
291 * @param array $matches Regular expression matches
292 * @return string Replacement
293 * @see substituteConstants()
294 */
295 public function substituteConstantsCallBack($matches)
296 {
297 switch ($this->constantMode) {
298 case 'const':
299 $ret_val = isset($this->flatSetup[$matches[1]]) && !is_array($this->flatSetup[$matches[1]]) ? '##' . $this->Cmarker . '_B##' . $matches[0] . '##' . $this->Cmarker . '_E##' : $matches[0];
300 break;
301 case 'subst':
302 $ret_val = isset($this->flatSetup[$matches[1]]) && !is_array($this->flatSetup[$matches[1]]) ? '##' . $this->Cmarker . '_B##' . $this->flatSetup[$matches[1]] . '##' . $this->Cmarker . '_E##' : $matches[0];
303 break;
304 case 'untouched':
305 $ret_val = $matches[0];
306 break;
307 default:
308 $ret_val = isset($this->flatSetup[$matches[1]]) && !is_array($this->flatSetup[$matches[1]]) ? $this->flatSetup[$matches[1]] : $matches[0];
309 }
310 return $ret_val;
311 }
312
313 /**
314 * Subsitute markers
315 *
316 * @param string $all
317 * @return string
318 */
319 public function substituteCMarkers($all)
320 {
321 switch ($this->constantMode) {
322 case 'const':
323 case 'subst':
324 $all = str_replace(
325 array('##' . $this->Cmarker . '_B##', '##' . $this->Cmarker . '_E##'),
326 array('<strong style="color: green;">', '</strong>'),
327 $all
328 );
329 break;
330 default:
331 }
332 return $all;
333 }
334
335 /**
336 * Parse constants with respect to the constant-editor in this module.
337 * In particular comments in the code are registered and the edit_divider is taken into account.
338 *
339 * @return array
340 */
341 public function generateConfig_constants()
342 {
343 // These vars are also set lateron...
344 $this->setup['sitetitle'] = $this->sitetitle;
345 // Parse constants
346 $constants = GeneralUtility::makeInstance(Parser\TypoScriptParser::class);
347 // Register comments!
348 $constants->regComments = 1;
349 $constants->setup = $this->mergeConstantsFromPageTSconfig(array());
350 /** @var ConditionMatcher $matchObj */
351 $matchObj = GeneralUtility::makeInstance(ConditionMatcher::class);
352 // Matches ALL conditions in TypoScript
353 $matchObj->setSimulateMatchResult(true);
354 $c = 0;
355 $cc = count($this->constants);
356 $defaultConstants = array();
357 foreach ($this->constants as $str) {
358 $c++;
359 if ($c == $cc) {
360 if (strstr($str, $this->edit_divider)) {
361 $parts = explode($this->edit_divider, $str, 2);
362 $str = $parts[1];
363 $constants->parse($parts[0], $matchObj);
364 }
365 $this->flatSetup = array();
366 $this->flattenSetup($constants->setup, '', '');
367 $defaultConstants = $this->flatSetup;
368 }
369 $constants->parse($str, $matchObj);
370 }
371 $this->flatSetup = array();
372 $this->flattenSetup($constants->setup, '', '');
373 $this->setup['constants'] = $constants->setup;
374 return $this->ext_compareFlatSetups($defaultConstants);
375 }
376
377 /**
378 * @param array $theSetup
379 * @param string $theKey
380 * @return array
381 */
382 public function ext_getSetup($theSetup, $theKey)
383 {
384 $parts = explode('.', $theKey, 2);
385 if ((string)$parts[0] !== '' && is_array($theSetup[$parts[0] . '.'])) {
386 if (trim($parts[1]) !== '') {
387 return $this->ext_getSetup($theSetup[$parts[0] . '.'], trim($parts[1]));
388 } else {
389 return array($theSetup[$parts[0] . '.'], $theSetup[$parts[0]]);
390 }
391 } else {
392 if (trim($theKey) !== '') {
393 return array(array(), $theSetup[$theKey]);
394 } else {
395 return array($theSetup, '');
396 }
397 }
398 }
399
400 /**
401 * Get object tree
402 *
403 * @param array $arr
404 * @param string $depth_in
405 * @param string $depthData
406 * @param string $parentType (unused)
407 * @param string $parentValue (unused)
408 * @param string $alphaSort sorts the array keys / tree by alphabet when set to 1
409 * @return array
410 */
411 public function ext_getObjTree($arr, $depth_in, $depthData, $parentType = '', $parentValue = '', $alphaSort = '0')
412 {
413 $HTML = '';
414 $a = 0;
415 if ($alphaSort == '1') {
416 ksort($arr);
417 }
418 $keyArr_num = array();
419 $keyArr_alpha = array();
420 foreach ($arr as $key => $value) {
421 // Don't do anything with comments / linenumber registrations...
422 if (substr($key, -2) != '..') {
423 $key = preg_replace('/\\.$/', '', $key);
424 if (substr($key, -1) != '.') {
425 if (MathUtility::canBeInterpretedAsInteger($key)) {
426 $keyArr_num[$key] = $arr[$key];
427 } else {
428 $keyArr_alpha[$key] = $arr[$key];
429 }
430 }
431 }
432 }
433 ksort($keyArr_num);
434 $keyArr = $keyArr_num + $keyArr_alpha;
435 $c = count($keyArr);
436 if ($depth_in) {
437 $depth_in = $depth_in . '.';
438 }
439 foreach ($keyArr as $key => $value) {
440 $a++;
441 $depth = $depth_in . $key;
442 // This excludes all constants starting with '_' from being shown.
443 if ($this->bType !== 'const' || $depth[0] !== '_') {
444 $goto = substr(md5($depth), 0, 6);
445 $deeper = is_array($arr[$key . '.']) && ($this->tsbrowser_depthKeys[$depth] || $this->ext_expandAllNotes) ? 1 : 0;
446 $LN = $a == $c ? 'blank' : 'line';
447 $BTM = $a == $c ? 'bottom' : '';
448 $PM = is_array($arr[$key . '.']) && !$this->ext_noPMicons ? ($deeper ? 'minus' : 'plus') : 'join';
449 $HTML .= $depthData . '<li>';
450 if ($PM !== 'join') {
451 $urlParameters = array(
452 'id' => $GLOBALS['SOBE']->id,
453 'tsbr[' . $depth . ']' => $deeper ? 0 : 1
454 );
455 if (GeneralUtility::_GP('breakPointLN')) {
456 $urlParameters['breakPointLN'] = GeneralUtility::_GP('breakPointLN');
457 }
458 $aHref = BackendUtility::getModuleUrl('web_ts', $urlParameters) . '#' . $goto;
459 $HTML .= '<a class="list-tree-control' . ($PM === 'minus' ? ' list-tree-control-open' : ' list-tree-control-closed') . '" name="' . $goto . '" href="' . htmlspecialchars($aHref) . '"><i class="fa"></i></a>';
460 }
461 $label = $key;
462 // Read only...
463 if (GeneralUtility::inList('types,resources,sitetitle', $depth) && $this->bType == 'setup') {
464 $label = '<span style="color: #666666;">' . $label . '</span>';
465 } else {
466 if ($this->linkObjects) {
467 $urlParameters = array(
468 'id' => $GLOBALS['SOBE']->id,
469 'sObj' => $depth
470 );
471 if (GeneralUtility::_GP('breakPointLN')) {
472 $urlParameters['breakPointLN'] = GeneralUtility::_GP('breakPointLN');
473 }
474 $aHref = BackendUtility::getModuleUrl('web_ts', $urlParameters);
475 if ($this->bType != 'const') {
476 $ln = is_array($arr[$key . '.ln..']) ? 'Defined in: ' . $this->lineNumberToScript($arr[$key . '.ln..']) : 'N/A';
477 } else {
478 $ln = '';
479 }
480 if ($this->tsbrowser_searchKeys[$depth] & 4) {
481 $label = '<strong class="text-danger">' . $label . '</strong>';
482 }
483 // The key has matched the search string
484 $label = '<a href="' . htmlspecialchars($aHref) . '" title="' . htmlspecialchars($ln) . '">' . $label . '</a>';
485 }
486 }
487 $HTML .= '
488 <span class="list-tree-group">
489 <span class="list-tree-label">[' . $label . ']</span>';
490 if (isset($arr[$key])) {
491 $theValue = $arr[$key];
492 if ($this->fixedLgd) {
493 $imgBlocks = ceil(1 + strlen($depthData) / 77);
494 $lgdChars = 68 - ceil(strlen(('[' . $key . ']')) * 0.8) - $imgBlocks * 3;
495 $theValue = $this->ext_fixed_lgd($theValue, $lgdChars);
496 }
497 // The value has matched the search string
498 if ($this->tsbrowser_searchKeys[$depth] & 2) {
499 $HTML .= ' = <span class="list-tree-value text-danger">' . htmlspecialchars($theValue) . '</span>';
500 } else {
501 $HTML .= ' = <span class="list-tree-value">' . htmlspecialchars($theValue) . '</span>';
502 }
503 if ($this->ext_regComments && isset($arr[$key . '..'])) {
504 $comment = $arr[$key . '..'];
505 // Skip INCLUDE_TYPOSCRIPT comments, they are almost useless
506 if (!preg_match('/### <INCLUDE_TYPOSCRIPT:.*/', $comment)) {
507 // Remove linebreaks, replace with ' '
508 $comment = preg_replace('/[\\r\\n]/', ' ', $comment);
509 // Remove # and * if more than twice in a row
510 $comment = preg_replace('/[#\\*]{2,}/', '', $comment);
511 // Replace leading # (just if it exists) and add it again. Result: Every comment should be prefixed by a '#'.
512 $comment = preg_replace('/^[#\\*\\s]+/', '# ', $comment);
513 // Masking HTML Tags: Replace < with &lt; and > with &gt;
514 $comment = htmlspecialchars($comment);
515 $HTML .= ' <i class="text-muted">' . trim($comment) . '</i>';
516 }
517 }
518 }
519 $HTML .= '</span>';
520 if ($deeper) {
521 $HTML .= $this->ext_getObjTree($arr[$key . '.'], $depth, $depthData, '', $arr[$key], $alphaSort);
522 }
523 }
524 }
525 if ($HTML !== '') {
526 $HTML = '<ul class="list-tree text-monospace">' . $HTML . '</ul>';
527 }
528
529 return $HTML;
530 }
531
532 /**
533 * Find the originating template name for an array of line numbers (TypoScript setup only!)
534 * Given an array of linenumbers the method will try to find the corresponding template where this line originated
535 * The linenumber indicates the *last* lineNumber that is part of the template
536 *
537 * lineNumbers are in sync with the calculated lineNumbers '.ln..' in TypoScriptParser
538 *
539 * @param array $lnArr Array with linenumbers (might have some extra symbols, for example for unsetting) to be processed
540 * @return array The same array where each entry has been prepended by the template title if available
541 */
542 public function lineNumberToScript(array $lnArr)
543 {
544 // On the first call, construct the lnToScript array.
545 if (!is_array($this->lnToScript)) {
546 $this->lnToScript = array();
547
548 // aggregatedTotalLineCount
549 $c = 0;
550 foreach ($this->hierarchyInfo as $templateNumber => $info) {
551 // hierarchyInfo has the number of lines in configLines, but unfortunatly this value
552 // was calculated *before* processing of any INCLUDE instructions
553 // for some yet unknown reason we have to add an extra +2 offset
554 $linecountAfterIncludeProcessing = substr_count($this->config[$templateNumber], LF) + 2;
555 $c += $linecountAfterIncludeProcessing;
556 $this->lnToScript[$c] = $info['title'];
557 }
558 }
559
560 foreach ($lnArr as $k => $ln) {
561 foreach ($this->lnToScript as $endLn => $title) {
562 if ($endLn >= (int)$ln) {
563 $lnArr[$k] = '"' . $title . '", ' . $ln;
564 break;
565 }
566 }
567 }
568
569 return implode('; ', $lnArr);
570 }
571
572 /**
573 * @param array $theValue
574 * @return array
575 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8 - use htmlspecialchars() directly
576 */
577 public function makeHtmlspecialchars($theValue)
578 {
579 GeneralUtility::logDeprecatedFunction();
580 return $this->ext_noSpecialCharsOnLabels ? $theValue : htmlspecialchars($theValue);
581 }
582
583 /**
584 * @param array $arr
585 * @param string $depth_in
586 * @param string $searchString
587 * @param array $keyArray
588 * @return array
589 * @throws Exception
590 */
591 public function ext_getSearchKeys($arr, $depth_in, $searchString, $keyArray)
592 {
593 $keyArr = array();
594 foreach ($arr as $key => $value) {
595 $key = preg_replace('/\\.$/', '', $key);
596 if (substr($key, -1) != '.') {
597 $keyArr[$key] = 1;
598 }
599 }
600 if ($depth_in) {
601 $depth_in = $depth_in . '.';
602 }
603 $searchPattern = '';
604 if ($this->regexMode) {
605 $searchPattern = '/' . addcslashes($searchString, '/') . '/';
606 $matchResult = @preg_match($searchPattern, '');
607 if ($matchResult === false) {
608 throw new Exception(sprintf('Error evaluating regular expression "%s".', $searchPattern), 1446559458);
609 }
610 }
611 foreach ($keyArr as $key => $value) {
612 $depth = $depth_in . $key;
613 $deeper = is_array($arr[$key . '.']);
614 if ($this->regexMode) {
615 // The value has matched
616 if (preg_match($searchPattern, $arr[$key])) {
617 $this->tsbrowser_searchKeys[$depth] += 2;
618 }
619 // The key has matched
620 if (preg_match($searchPattern, $key)) {
621 $this->tsbrowser_searchKeys[$depth] += 4;
622 }
623 // Just open this subtree if the parent key has matched the search
624 if (preg_match($searchPattern, $depth_in)) {
625 $this->tsbrowser_searchKeys[$depth] = 1;
626 }
627 } else {
628 // The value has matched
629 if (stristr($arr[$key], $searchString)) {
630 $this->tsbrowser_searchKeys[$depth] += 2;
631 }
632 // The key has matches
633 if (stristr($key, $searchString)) {
634 $this->tsbrowser_searchKeys[$depth] += 4;
635 }
636 // Just open this subtree if the parent key has matched the search
637 if (stristr($depth_in, $searchString)) {
638 $this->tsbrowser_searchKeys[$depth] = 1;
639 }
640 }
641 if ($deeper) {
642 $cS = count($this->tsbrowser_searchKeys);
643 $keyArray = $this->ext_getSearchKeys($arr[$key . '.'], $depth, $searchString, $keyArray);
644 if ($cS != count($this->tsbrowser_searchKeys)) {
645 $keyArray[$depth] = 1;
646 }
647 }
648 }
649 return $keyArray;
650 }
651
652 /**
653 * @param int $pid
654 * @return int
655 */
656 public function ext_getRootlineNumber($pid)
657 {
658 if ($pid) {
659 foreach ($this->getRootLine() as $key => $val) {
660 if ((int)$val['uid'] === (int)$pid) {
661 return (int)$key;
662 }
663 }
664 }
665 return -1;
666 }
667
668 /**
669 * @param array $arr
670 * @param string $depthData
671 * @param array $keyArray
672 * @param int $first
673 * @return array
674 */
675 public function ext_getTemplateHierarchyArr($arr, $depthData, $keyArray, $first = 0)
676 {
677 $keyArr = array();
678 foreach ($arr as $key => $value) {
679 $key = preg_replace('/\\.$/', '', $key);
680 if (substr($key, -1) != '.') {
681 $keyArr[$key] = 1;
682 }
683 }
684 $a = 0;
685 $c = count($keyArr);
686 static $i = 0;
687 /** @var IconFactory $iconFactory */
688 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
689 foreach ($keyArr as $key => $value) {
690 $HTML = '';
691 $a++;
692 $deeper = is_array($arr[$key . '.']);
693 $row = $arr[$key];
694 $LN = $a == $c ? 'blank' : 'line';
695 $BTM = $a == $c ? 'top' : '';
696 $HTML .= $depthData;
697 $alttext = '[' . $row['templateID'] . ']';
698 $alttext .= $row['pid'] ? ' - ' . BackendUtility::getRecordPath($row['pid'], $GLOBALS['SOBE']->perms_clause, 20) : '';
699 $icon = substr($row['templateID'], 0, 3) === 'sys'
700 ? '<span title="' . htmlspecialchars($alttext) . '">' . $iconFactory->getIconForRecord('sys_template', $row, Icon::SIZE_SMALL)->render() . '</span>'
701 : '<span title="' . htmlspecialchars($alttext) . '">' . $iconFactory->getIcon('mimetypes-x-content-template-static', Icon::SIZE_SMALL)->render() . '</span>';
702 if (in_array($row['templateID'], $this->clearList_const) || in_array($row['templateID'], $this->clearList_setup)) {
703 $urlParameters = array(
704 'id' => $GLOBALS['SOBE']->id,
705 'template' => $row['templateID']
706 );
707 $aHref = BackendUtility::getModuleUrl('web_ts', $urlParameters);
708 $A_B = '<a href="' . htmlspecialchars($aHref) . '">';
709 $A_E = '</a>';
710 if (GeneralUtility::_GP('template') == $row['templateID']) {
711 $A_B = '<strong>' . $A_B;
712 $A_E .= '</strong>';
713 }
714 } else {
715 $A_B = '';
716 $A_E = '';
717 }
718 $HTML .= ($first ? '' : '<span class="treeline-icon treeline-icon-join' . $BTM . '"></span>') . $icon . $A_B
719 . htmlspecialchars(GeneralUtility::fixed_lgd_cs($row['title'], $GLOBALS['BE_USER']->uc['titleLen']))
720 . $A_E . '&nbsp;&nbsp;';
721 $RL = $this->ext_getRootlineNumber($row['pid']);
722 $statusCheckedIcon = $iconFactory->getIcon('status-status-checked', Icon::SIZE_SMALL)->render();
723 $keyArray[] = '<tr class="' . ($i++ % 2 == 0 ? 'bgColor4' : 'bgColor6') . '">
724 <td nowrap="nowrap">' . $HTML . '</td>
725 <td align="center">' . ($row['root'] ? $statusCheckedIcon : '') . '&nbsp;&nbsp;</td>
726 <td align="center">' . ($row['clConf'] ? $statusCheckedIcon : '') . '&nbsp;&nbsp;' . '</td>
727 <td align="center">' . ($row['clConst'] ? $statusCheckedIcon : '') . '&nbsp;&nbsp;' . '</td>
728 <td align="center">' . ($row['pid'] ?: '') . '</td>
729 <td align="center">' . ($RL >= 0 ? $RL : '') . '</td>
730 <td>' . ($row['next'] ? '&nbsp;' . $row['next'] . '&nbsp;&nbsp;' : '') . '</td>
731 </tr>';
732 if ($deeper) {
733 $keyArray = $this->ext_getTemplateHierarchyArr($arr[$key . '.'], $depthData . ($first ? '' : '<span class="treeline-icon treeline-icon-' . $LN . '"></span>'), $keyArray);
734 }
735 }
736 return $keyArray;
737 }
738
739 /**
740 * Processes the flat array from TemplateService->hierarchyInfo
741 * and turns it into a hierachical array to show dependencies (used by TemplateAnalyzer)
742 *
743 * @param array $depthDataArr (empty array on external call)
744 * @param int &$pointer Element number (1! to count()) of $this->hierarchyInfo that should be processed.
745 * @return array Processed hierachyInfo.
746 */
747 public function ext_process_hierarchyInfo(array $depthDataArr, &$pointer)
748 {
749 $parent = $this->hierarchyInfo[$pointer - 1]['templateParent'];
750 while ($pointer > 0 && $this->hierarchyInfo[$pointer - 1]['templateParent'] == $parent) {
751 $pointer--;
752 $row = $this->hierarchyInfo[$pointer];
753 $depthDataArr[$row['templateID']] = $row;
754 $depthDataArr[$row['templateID']]['bgcolor_setup'] = isset($this->clearList_setup_temp[$row['templateID']]) ? ' class="bgColor5"' : '';
755 $depthDataArr[$row['templateID']]['bgcolor_const'] = isset($this->clearList_const_temp[$row['templateID']]) ? ' class="bgColor5"' : '';
756 unset($this->clearList_setup_temp[$row['templateID']]);
757 unset($this->clearList_const_temp[$row['templateID']]);
758 $this->templateTitles[$row['templateID']] = $row['title'];
759 if ($row['templateID'] == $this->hierarchyInfo[$pointer - 1]['templateParent']) {
760 $depthDataArr[$row['templateID'] . '.'] = $this->ext_process_hierarchyInfo(array(), $pointer);
761 }
762 }
763 return $depthDataArr;
764 }
765
766 /**
767 * Get formatted HTML output for TypoScript either with Syntaxhiglighting or in plain mode
768 *
769 * @param array $config Array with simple strings of typoscript code.
770 * @param bool $lineNumbers Prepend linNumbers to each line.
771 * @param bool $comments Enable including comments in output.
772 * @param bool $crop Enable cropping of long lines.
773 * @param bool $syntaxHL Enrich output with syntaxhighlighting.
774 * @param int $syntaxHLBlockmode
775 * @return string
776 */
777 public function ext_outputTS(
778 array $config, $lineNumbers = false, $comments = false, $crop = false, $syntaxHL = false, $syntaxHLBlockmode = 0
779 ) {
780 $all = '';
781 foreach ($config as $str) {
782 $all .= '[GLOBAL]' . LF . $str;
783 }
784 if ($syntaxHL) {
785 $tsparser = GeneralUtility::makeInstance(Parser\TypoScriptParser::class);
786 $tsparser->lineNumberOffset = $this->ext_lineNumberOffset + 1;
787 $tsparser->parentObject = $this;
788 return $tsparser->doSyntaxHighlight($all, $lineNumbers ? array($this->ext_lineNumberOffset + 1) : '', $syntaxHLBlockmode);
789 } else {
790 return $this->ext_formatTS($all, $lineNumbers, $comments, $crop);
791 }
792 }
793
794 /**
795 * Returns a new string of max. $chars length
796 * If the string is longer, it will be truncated and prepended with '...'
797 * $chars must be an integer of at least 4
798 *
799 * @param string $string
800 * @param int $chars
801 * @return string
802 */
803 public function ext_fixed_lgd($string, $chars)
804 {
805 if ($chars >= 4) {
806 if (strlen($string) > $chars) {
807 if (strlen($string) > 24 && substr($string, 0, 12) == '##' . $this->Cmarker . '_B##') {
808 return '##' . $this->Cmarker . '_B##' . GeneralUtility::fixed_lgd_cs(substr($string, 12, -12), ($chars - 3))
809 . '##' . $this->Cmarker . '_E##';
810 } else {
811 return GeneralUtility::fixed_lgd_cs($string, $chars - 3);
812 }
813 }
814 }
815 return $string;
816 }
817
818 /**
819 * @param int $lineNumber Line Number
820 * @param array $str
821 * @return string
822 */
823 public function ext_lnBreakPointWrap($lineNumber, $str)
824 {
825 return '<a href="#" id="line-' . $lineNumber . '" onClick="return brPoint(' . $lineNumber . ','
826 . ($this->ext_lineNumberOffset_mode == 'setup' ? 1 : 0) . ');">' . $str . '</a>';
827 }
828
829 /**
830 * @param string $input
831 * @param bool $ln
832 * @param bool $comments
833 * @param bool $crop
834 * @return string
835 */
836 public function ext_formatTS($input, $ln, $comments = true, $crop = false)
837 {
838 $cArr = explode(LF, $input);
839 $n = ceil(log10(count($cArr) + $this->ext_lineNumberOffset));
840 $lineNum = '';
841 foreach ($cArr as $k => $v) {
842 $lln = $k + $this->ext_lineNumberOffset + 1;
843 if ($ln) {
844 $lineNum = $this->ext_lnBreakPointWrap($lln, str_replace(' ', '&nbsp;', sprintf(('% ' . $n . 'd'), $lln))) . ': ';
845 }
846 $v = htmlspecialchars($v);
847 if ($crop) {
848 $v = $this->ext_fixed_lgd($v, $ln ? 71 : 77);
849 }
850 $cArr[$k] = $lineNum . str_replace(' ', '&nbsp;', $v);
851 $firstChar = substr(trim($v), 0, 1);
852 if ($firstChar == '[') {
853 $cArr[$k] = '<strong class="text-success">' . $cArr[$k] . '</strong>';
854 } elseif ($firstChar == '/' || $firstChar == '#') {
855 if ($comments) {
856 $cArr[$k] = '<span class="text-muted">' . $cArr[$k] . '</span>';
857 } else {
858 unset($cArr[$k]);
859 }
860 }
861 }
862 $output = implode($cArr, '<br />') . '<br />';
863 return $output;
864 }
865
866 /**
867 * @param int $id
868 * @param int $template_uid
869 * @return array|NULL Returns the template record or NULL if none was found
870 */
871 public function ext_getFirstTemplate($id, $template_uid = 0)
872 {
873 // Query is taken from the runThroughTemplates($theRootLine) function in the parent class.
874 if ((int)$id) {
875 $addC = $template_uid ? ' AND uid=' . (int)$template_uid : '';
876 $where = 'pid=' . (int)$id . $addC . ' ' . $this->whereClause;
877 $res = $this->getDatabaseConnection()->exec_SELECTquery('*', 'sys_template', $where, '', 'sorting', '1');
878 $row = $this->getDatabaseConnection()->sql_fetch_assoc($res);
879 BackendUtility::workspaceOL('sys_template', $row);
880 $this->getDatabaseConnection()->sql_free_result($res);
881 // Returns the template row if found.
882 return $row;
883 }
884 return null;
885 }
886
887 /**
888 * @param int $id
889 * @return array[] Array of template records
890 */
891 public function ext_getAllTemplates($id)
892 {
893 if (!$id) {
894 return array();
895 }
896
897 // Query is taken from the runThroughTemplates($theRootLine) function in the parent class.
898 $res = $this->getDatabaseConnection()->exec_SELECTquery('*', 'sys_template', 'pid=' . (int)$id . ' ' . $this->whereClause, '', 'sorting');
899
900 $outRes = array();
901 while ($row = $this->getDatabaseConnection()->sql_fetch_assoc($res)) {
902 BackendUtility::workspaceOL('sys_template', $row);
903 if (is_array($row)) {
904 $outRes[] = $row;
905 }
906 }
907 $this->getDatabaseConnection()->sql_free_result($res);
908
909 return $outRes;
910 }
911
912 /**
913 * This function compares the flattened constants (default and all).
914 * Returns an array with the constants from the whole template which may be edited by the module.
915 *
916 * @param array $default
917 * @return array
918 */
919 public function ext_compareFlatSetups($default)
920 {
921 $editableComments = array();
922 foreach ($this->flatSetup as $const => $value) {
923 if (substr($const, -2) === '..' || !isset($this->flatSetup[$const . '..'])) {
924 continue;
925 }
926 $comment = trim($this->flatSetup[$const . '..']);
927 $c_arr = explode(LF, $comment);
928 foreach ($c_arr as $k => $v) {
929 $line = trim(preg_replace('/^[#\\/]*/', '', $v));
930 if (!$line) {
931 continue;
932 }
933 $parts = explode(';', $line);
934 foreach ($parts as $par) {
935 if (strstr($par, '=')) {
936 $keyValPair = explode('=', $par, 2);
937 switch (trim(strtolower($keyValPair[0]))) {
938 case 'type':
939 // Type:
940 $editableComments[$const]['type'] = trim($keyValPair[1]);
941 break;
942 case 'cat':
943 // List of categories.
944 $catSplit = explode('/', strtolower($keyValPair[1]));
945 $catSplit[0] = trim($catSplit[0]);
946 if (isset($this->categoryLabels[$catSplit[0]])) {
947 $catSplit[0] = $this->categoryLabels[$catSplit[0]];
948 }
949 $editableComments[$const]['cat'] = $catSplit[0];
950 // This is the subcategory. Must be a key in $this->subCategories[].
951 // catSplit[2] represents the search-order within the subcat.
952 $catSplit[1] = trim($catSplit[1]);
953 if ($catSplit[1] && isset($this->subCategories[$catSplit[1]])) {
954 $editableComments[$const]['subcat_name'] = $catSplit[1];
955 $editableComments[$const]['subcat'] = $this->subCategories[$catSplit[1]][1]
956 . '/' . $catSplit[1] . '/' . trim($catSplit[2]) . 'z';
957 } else {
958 $editableComments[$const]['subcat'] = 'x' . '/' . trim($catSplit[2]) . 'z';
959 }
960 break;
961 case 'label':
962 // Label
963 $editableComments[$const]['label'] = trim($keyValPair[1]);
964 break;
965 case 'customcategory':
966 // Custom category label
967 $customCategory = explode('=', $keyValPair[1], 2);
968 if (trim($customCategory[0])) {
969 $categoryKey = strtolower($customCategory[0]);
970 $this->categoryLabels[$categoryKey] = $this->getLanguageService()->sL($customCategory[1]);
971 }
972 break;
973 case 'customsubcategory':
974 // Custom subCategory label
975 $customSubcategory = explode('=', $keyValPair[1], 2);
976 if (trim($customSubcategory[0])) {
977 $subCategoryKey = strtolower($customSubcategory[0]);
978 $this->subCategories[$subCategoryKey][0] = $this->getLanguageService()->sL($customSubcategory[1]);
979 }
980 break;
981 }
982 }
983 }
984 }
985 if (isset($editableComments[$const])) {
986 $editableComments[$const]['name'] = $const;
987 $editableComments[$const]['value'] = trim($value);
988 if (isset($default[$const])) {
989 $editableComments[$const]['default_value'] = trim($default[$const]);
990 }
991 }
992 }
993 return $editableComments;
994 }
995
996 /**
997 * @param array $editConstArray
998 * @return void
999 */
1000 public function ext_categorizeEditableConstants($editConstArray)
1001 {
1002 // Runs through the available constants and fills the $this->categories array with pointers and priority-info
1003 foreach ($editConstArray as $constName => $constData) {
1004 if (!$constData['type']) {
1005 $constData['type'] = 'string';
1006 }
1007 $cats = explode(',', $constData['cat']);
1008 // if = only one category, while allows for many. We have agreed on only one category is the most basic way...
1009 foreach ($cats as $theCat) {
1010 $theCat = trim($theCat);
1011 if ($theCat) {
1012 $this->categories[$theCat][$constName] = $constData['subcat'];
1013 }
1014 }
1015 }
1016 }
1017
1018 /**
1019 * @return array
1020 */
1021 public function ext_getCategoryLabelArray()
1022 {
1023 // Returns array used for labels in the menu.
1024 $retArr = array();
1025 foreach ($this->categories as $k => $v) {
1026 if (!empty($v)) {
1027 $retArr[$k] = strtoupper($k) . ' (' . count($v) . ')';
1028 }
1029 }
1030 return $retArr;
1031 }
1032
1033 /**
1034 * @param string $type
1035 * @return array
1036 */
1037 public function ext_getTypeData($type)
1038 {
1039 $retArr = array();
1040 $type = trim($type);
1041 if (!$type) {
1042 $retArr['type'] = 'string';
1043 } else {
1044 $m = strcspn($type, ' [');
1045 $retArr['type'] = strtolower(substr($type, 0, $m));
1046 if (GeneralUtility::inList('int,options,file,boolean,offset,user', $retArr['type'])) {
1047 $p = trim(substr($type, $m));
1048 $reg = array();
1049 preg_match('/\\[(.*)\\]/', $p, $reg);
1050 $p = trim($reg[1]);
1051 if ($p) {
1052 $retArr['paramstr'] = $p;
1053 switch ($retArr['type']) {
1054 case 'int':
1055 if ($retArr['paramstr'][0] === '-') {
1056 $retArr['params'] = GeneralUtility::intExplode('-', substr($retArr['paramstr'], 1));
1057 $retArr['params'][0] = (int)('-' . $retArr['params'][0]);
1058 } else {
1059 $retArr['params'] = GeneralUtility::intExplode('-', $retArr['paramstr']);
1060 }
1061 $retArr['paramstr'] = $retArr['params'][0] . ' - ' . $retArr['params'][1];
1062 break;
1063 case 'options':
1064 $retArr['params'] = explode(',', $retArr['paramstr']);
1065 break;
1066 }
1067 }
1068 }
1069 }
1070 return $retArr;
1071 }
1072
1073 /**
1074 * @param string $category
1075 * @return void
1076 */
1077 public function ext_getTSCE_config($category)
1078 {
1079 $catConf = $this->setup['constants']['TSConstantEditor.'][$category . '.'];
1080 $out = array();
1081 if (is_array($catConf)) {
1082 foreach ($catConf as $key => $val) {
1083 switch ($key) {
1084 case 'image':
1085 $out['imagetag'] = $this->ext_getTSCE_config_image($catConf['image']);
1086 break;
1087 case 'description':
1088 case 'bulletlist':
1089 case 'header':
1090 $out[$key] = $val;
1091 break;
1092 default:
1093 if (MathUtility::canBeInterpretedAsInteger($key)) {
1094 $constRefs = explode(',', $val);
1095 foreach ($constRefs as $const) {
1096 $const = trim($const);
1097 if ($const) {
1098 $out['constants'][$const] .= '<span class="label label-danger">' . $key . '</span>';
1099 }
1100 }
1101 }
1102 }
1103 }
1104 }
1105 $this->helpConfig = $out;
1106 }
1107
1108 /**
1109 * @param string $key
1110 * @return string
1111 * @deprecated since TYPO3 CMS 7, will be removed with TYPO3 CMS 8
1112 */
1113 public function ext_getKeyImage($key)
1114 {
1115 GeneralUtility::logDeprecatedFunction();
1116 return '<span class="label label-danger">' . $key . '</span>';
1117 }
1118
1119 /**
1120 * @param string $imgConf
1121 * @return string
1122 */
1123 public function ext_getTSCE_config_image($imgConf)
1124 {
1125 $iFile = null;
1126 $tFile = null;
1127 if (substr($imgConf, 0, 4) == 'gfx/') {
1128 $iFile = $this->ext_localGfxPrefix . $imgConf;
1129 $tFile = $this->ext_localWebGfxPrefix . $imgConf;
1130 } elseif (substr($imgConf, 0, 4) == 'EXT:') {
1131 $iFile = GeneralUtility::getFileAbsFileName($imgConf);
1132 if ($iFile) {
1133 $f = PathUtility::stripPathSitePrefix($iFile);
1134 $tFile = $GLOBALS['BACK_PATH'] . '../' . $f;
1135 }
1136 }
1137 if ($iFile !== null) {
1138 $imageInfo = @getImagesize($iFile);
1139 return '<img src="' . $tFile . '" ' . $imageInfo[3] . '>';
1140 }
1141 return '';
1142 }
1143
1144 /**
1145 * @param array $params
1146 * @return array
1147 */
1148 public function ext_fNandV($params)
1149 {
1150 $fN = 'data[' . $params['name'] . ']';
1151 $idName = str_replace('.', '-', $params['name']);
1152 $fV = $params['value'];
1153 // Values entered from the constantsedit cannot be constants! 230502; removed \{ and set {
1154 if (preg_match('/^{[\\$][a-zA-Z0-9\\.]*}$/', trim($fV), $reg)) {
1155 $fV = '';
1156 }
1157 $fV = htmlspecialchars($fV);
1158 return array($fN, $fV, $params, $idName);
1159 }
1160
1161 /**
1162 * This functions returns the HTML-code that creates the editor-layout of the module.
1163 *
1164 * @param array $theConstants
1165 * @param string $category
1166 * @return string
1167 */
1168 public function ext_printFields($theConstants, $category)
1169 {
1170 reset($theConstants);
1171 $output = '';
1172 $subcat = '';
1173 if (is_array($this->categories[$category])) {
1174 $help = $this->helpConfig;
1175 if (!$this->doNotSortCategoriesBeforeMakingForm) {
1176 asort($this->categories[$category]);
1177 }
1178 /** @var IconFactory $iconFactory */
1179 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
1180 foreach ($this->categories[$category] as $name => $type) {
1181 $params = $theConstants[$name];
1182 if (is_array($params)) {
1183 if ($subcat != $params['subcat_name']) {
1184 $subcat = $params['subcat_name'];
1185 $subcat_name = $params['subcat_name'] ? $this->subCategories[$params['subcat_name']][0] : 'Others';
1186 $output .= '<h3 class="typo3-tstemplate-ceditor-subcat">' . $subcat_name . '</h3>';
1187 }
1188 $label = $this->getLanguageService()->sL($params['label']);
1189 $label_parts = explode(':', $label, 2);
1190 if (count($label_parts) === 2) {
1191 $head = trim($label_parts[0]);
1192 $body = trim($label_parts[1]);
1193 } else {
1194 $head = trim($label_parts[0]);
1195 $body = '';
1196 }
1197 if (strlen($head) > 35) {
1198 if (!$body) {
1199 $body = $head;
1200 }
1201 $head = GeneralUtility::fixed_lgd_cs($head, 35);
1202 }
1203 $typeDat = $this->ext_getTypeData($params['type']);
1204 $p_field = '';
1205 $raname = substr(md5($params['name']), 0, 10);
1206 $aname = '\'' . $raname . '\'';
1207 list($fN, $fV, $params, $idName) = $this->ext_fNandV($params);
1208 $idName = htmlspecialchars($idName);
1209 $hint = '';
1210 switch ($typeDat['type']) {
1211 case 'int':
1212 case 'int+':
1213 if ($typeDat['paramstr']) {
1214 $hint = ' Range: ' . $typeDat['paramstr'];
1215 } elseif ($typeDat['type'] === 'int+') {
1216 $hint = ' Range: 0 - ';
1217 } else {
1218 $hint = ' (Integer)';
1219 }
1220
1221 $p_field =
1222 '<input class="form-control" id="' . $idName . '" type="text"'
1223 . ' name="' . $fN . '" value="' . $fV . '"' . ' onChange="uFormUrl(' . $aname . ')" />';
1224 break;
1225 case 'color':
1226 $colorNames = explode(',', ',' . $this->HTMLcolorList);
1227 $p_field = '';
1228 foreach ($colorNames as $val) {
1229 $sel = '';
1230 if ($val == strtolower($params['value'])) {
1231 $sel = ' selected';
1232 }
1233 $p_field .= '<option value="' . htmlspecialchars($val) . '"' . $sel . '>' . $val . '</option>';
1234 }
1235 $p_field = '<select class="form-control t3js-color-select" id="select-' . $idName . '" rel="' . $idName . '" name="C' . $fN . '" onChange="uFormUrl(' . $aname . ');"' . $this->getDocumentTemplate()->formWidth(7) . '>' . $p_field . '</select>';
1236 $p_field .= '<input class="form-control t3js-color-input" type="text" id="input-' . $idName . '" rel="' . $idName . '" name="' . $fN . '" value="' . $fV . '"' . $this->getDocumentTemplate()->formWidth(7) . ' onChange="uFormUrl(' . $aname . ')" />';
1237 break;
1238 case 'wrap':
1239 $wArr = explode('|', $fV);
1240 $p_field = '<input class="form-control" type="text" id="' . $idName . '" name="' . $fN . '" value="' . $wArr[0] . '"' . $this->getDocumentTemplate()->formWidth(29) . ' onChange="uFormUrl(' . $aname . ')" />';
1241 $p_field .= ' | ';
1242 $p_field .= '<input class="form-control" type="text" name="W' . $fN . '" value="' . $wArr[1] . '"' . $this->getDocumentTemplate()->formWidth(15) . ' onChange="uFormUrl(' . $aname . ')" />';
1243 break;
1244 case 'offset':
1245 $wArr = explode(',', $fV);
1246 $labels = GeneralUtility::trimExplode(',', $typeDat['paramstr']);
1247 $p_field = ($labels[0] ? $labels[0] : 'x') . ':<input type="text" name="' . $fN . '" value="' . $wArr[0] . '"' . $this->getDocumentTemplate()->formWidth(4) . ' onChange="uFormUrl(' . $aname . ')" />';
1248 $p_field .= ' , ';
1249 $p_field .= ($labels[1] ? $labels[1] : 'y') . ':<input type="text" name="W' . $fN . '" value="' . $wArr[1] . '"' . $this->getDocumentTemplate()->formWidth(4) . ' onChange="uFormUrl(' . $aname . ')" />';
1250 $labelsCount = count($labels);
1251 for ($aa = 2; $aa < $labelsCount; $aa++) {
1252 if ($labels[$aa]) {
1253 $p_field .= ' , ' . $labels[$aa] . ':<input type="text" name="W' . $aa . $fN . '" value="' . $wArr[$aa] . '"' . $this->getDocumentTemplate()->formWidth(4) . ' onChange="uFormUrl(' . $aname . ')" />';
1254 } else {
1255 $p_field .= '<input type="hidden" name="W' . $aa . $fN . '" value="' . $wArr[$aa] . '" />';
1256 }
1257 }
1258 break;
1259 case 'options':
1260 if (is_array($typeDat['params'])) {
1261 $p_field = '';
1262 foreach ($typeDat['params'] as $val) {
1263 $vParts = explode('=', $val, 2);
1264 $label = $vParts[0];
1265 $val = isset($vParts[1]) ? $vParts[1] : $vParts[0];
1266 // option tag:
1267 $sel = '';
1268 if ($val === $params['value']) {
1269 $sel = ' selected';
1270 }
1271 $p_field .= '<option value="' . htmlspecialchars($val) . '"' . $sel . '>' . $this->getLanguageService()->sL($label) . '</option>';
1272 }
1273 $p_field = '<select class="form-control" id="' . $idName . '" name="' . $fN . '" onChange="uFormUrl(' . $aname . ')">' . $p_field . '</select>';
1274 }
1275 break;
1276 case 'boolean':
1277 $sel = $fV ? 'checked' : '';
1278 $p_field =
1279 '<label class="btn btn-default btn-checkbox">'
1280 . '<input type="hidden" name="' . $fN . '" value="0" />'
1281 . '<input id="' . $idName . '" type="checkbox" name="' . $fN . '" value="' . ($typeDat['paramstr'] ? $typeDat['paramstr'] : 1) . '" ' . $sel . ' onClick="uFormUrl(' . $aname . ')" />'
1282 . '<span class="t3-icon fa"></span>'
1283 . '</label>';
1284 break;
1285 case 'comment':
1286 $sel = $fV ? 'checked' : '';
1287 $p_field =
1288 '<label class="btn btn-default btn-checkbox">'
1289 . '<input type="hidden" name="' . $fN . '" value="#" />'
1290 . '<input id="' . $idName . '" type="checkbox" name="' . $fN . '" value="" ' . $sel . ' onClick="uFormUrl(' . $aname . ')" />'
1291 . '<span class="t3-icon fa"></span>'
1292 . '</label>';
1293 break;
1294 case 'file':
1295 // extensionlist
1296 $extList = $typeDat['paramstr'];
1297 if ($extList === 'IMAGE_EXT') {
1298 $extList = $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'];
1299 }
1300 $p_field = '<option value="">(' . $extList . ')</option>';
1301 if (trim($params['value'])) {
1302 $val = $params['value'];
1303 $p_field .= '<option value=""></option>';
1304 $p_field .= '<option value="' . htmlspecialchars($val) . '" selected>' . $val . '</option>';
1305 }
1306 $p_field = '<select class="form-select" id="' . $idName . '" name="' . $fN . '" onChange="uFormUrl(' . $aname . ')">' . $p_field . '</select>';
1307 break;
1308 case 'user':
1309 $userFunction = $typeDat['paramstr'];
1310 $userFunctionParams = array('fieldName' => $fN, 'fieldValue' => $fV);
1311 $p_field = GeneralUtility::callUserFunction($userFunction, $userFunctionParams, $this, '');
1312 break;
1313 case 'small':
1314
1315 default:
1316 $p_field = '<input class="form-control" id="' . $idName . '" type="text" name="' . $fN . '" value="' . $fV . '"'
1317 . ' onChange="uFormUrl(' . $aname . ')" />';
1318 }
1319 // Define default names and IDs
1320 $userTyposcriptID = 'userTS-' . $idName;
1321 $defaultTyposcriptID = 'defaultTS-' . $idName;
1322 $checkboxName = 'check[' . $params['name'] . ']';
1323 $checkboxID = 'check-' . $idName;
1324 // Handle type=color specially
1325 if ($typeDat['type'] === 'color' && substr($params['value'], 0, 2) != '{$') {
1326 $appendedGroupAddon = '<span class="input-group-addon colorbox" id="colorbox-' . $idName . '" class="typo3-tstemplate-ceditor-colorblock" style="background-color:' . $params['value'] . ';"></span>';
1327 } else {
1328 $appendedGroupAddon = '';
1329 }
1330 $userTyposcriptStyle = '';
1331 $deleteIconHTML = '';
1332 $constantCheckbox = '';
1333 $constantDefaultRow = '';
1334 if (!$this->ext_dontCheckIssetValues) {
1335 // Set the default styling options
1336 if (isset($this->objReg[$params['name']])) {
1337 $checkboxValue = 'checked';
1338 $defaultTyposcriptStyle = 'style="display:none;"';
1339 } else {
1340 $checkboxValue = '';
1341 $userTyposcriptStyle = 'style="display:none;"';
1342 $defaultTyposcriptStyle = '';
1343 }
1344 $deleteTitle = $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.deleteTitle', true);
1345 $deleteIcon = $iconFactory->getIcon('actions-edit-undo', Icon::SIZE_SMALL)->render();
1346 $deleteIconHTML =
1347 '<button type="button" class="btn btn-default t3js-toggle" data-toggle="undo" rel="' . $idName . '">'
1348 . '<span title="' . $deleteTitle . '" alt="' . $deleteTitle . '">'
1349 . $deleteIcon
1350 . '</span>'
1351 . '</button>';
1352 $editTitle = $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.editTitle', true);
1353 $editIcon = $iconFactory->getIcon('actions-open', Icon::SIZE_SMALL)->render();
1354 $editIconHTML =
1355 '<button type="button" class="btn btn-default t3js-toggle" data-toggle="edit" rel="' . $idName . '">'
1356 . '<span title="' . $editTitle . '" alt="' . $editTitle . '">'
1357 . $editIcon
1358 . '</span>'
1359 . '</button>';
1360 $constantCheckbox = '<input type="hidden" name="' . $checkboxName . '" id="' . $checkboxID . '" value="' . $checkboxValue . '"/>';
1361 // If there's no default value for the field, use a static label.
1362 if (!$params['default_value']) {
1363 $params['default_value'] = '[Empty]';
1364 }
1365 $constantDefaultRow =
1366 '<div class="input-group defaultTS" id="' . $defaultTyposcriptID . '" ' . $defaultTyposcriptStyle . '>'
1367 . '<span class="input-group-btn">' . $editIconHTML . '</span>'
1368 . '<input class="form-control" type="text" placeholder="' . htmlspecialchars($params['default_value']) . '" readonly>'
1369 . $appendedGroupAddon
1370 . '</div>';
1371 }
1372 $constantEditRow =
1373 '<div class="input-group userTS" id="' . $userTyposcriptID . '" ' . $userTyposcriptStyle . '>'
1374 . '<span class="input-group-btn">' . $deleteIconHTML . '</span>'
1375 . $p_field
1376 . $appendedGroupAddon
1377 . '</div>';
1378 $constantLabel = '<label class="t3js-formengine-label"><span>' . htmlspecialchars($head) . '</span></label>';
1379 $constantName = '<span class="help-block">[' . $params['name'] . ']</span>';
1380 $constantDescription = $body ? '<p class="help-block">' . htmlspecialchars($body) . '</p>' : '';
1381 $constantData = '';
1382 if ($hint !== '') {
1383 $constantData .= '<span class="help-block">' . $hint . '</span>';
1384 }
1385 $constantData .=
1386 $constantCheckbox
1387 . $constantEditRow
1388 . $constantDefaultRow;
1389
1390 $output .=
1391 '<fieldset class="form-section">'
1392 . '<a name="' . $raname . '"></a>' . $help['constants'][$params['name']]
1393 . '<div class="form-group">'
1394 . $constantLabel . $constantName . $constantDescription . $constantData
1395 . '</div>'
1396 . '</fieldset>';
1397 } else {
1398 debug('Error. Constant did not exist. Should not happen.');
1399 }
1400 }
1401 }
1402 return '<div class="tstemplate-constanteditor">' . $output . '</div>';
1403 }
1404
1405 /***************************
1406 *
1407 * Processing input values
1408 *
1409 ***************************/
1410 /**
1411 * @param string $constants
1412 * @return void
1413 */
1414 public function ext_regObjectPositions($constants)
1415 {
1416 // This runs through the lines of the constants-field of the active template and registers the constants-names
1417 // and line positions in an array, $this->objReg
1418 $this->raw = explode(LF, $constants);
1419 $this->rawP = 0;
1420 // Resetting the objReg if the divider is found!!
1421 $this->objReg = array();
1422 $this->ext_regObjects('');
1423 }
1424
1425 /**
1426 * @param string $pre
1427 * @return void
1428 */
1429 public function ext_regObjects($pre)
1430 {
1431 // Works with regObjectPositions. "expands" the names of the TypoScript objects
1432 while (isset($this->raw[$this->rawP])) {
1433 $line = ltrim($this->raw[$this->rawP]);
1434 if (strstr($line, $this->edit_divider)) {
1435 // Resetting the objReg if the divider is found!!
1436 $this->objReg = array();
1437 }
1438 $this->rawP++;
1439 if ($line) {
1440 if ($line[0] === '[') {
1441 } elseif (strcspn($line, '}#/') != 0) {
1442 $varL = strcspn($line, ' {=<');
1443 $var = substr($line, 0, $varL);
1444 $line = ltrim(substr($line, $varL));
1445 switch ($line[0]) {
1446 case '=':
1447 $this->objReg[$pre . $var] = $this->rawP - 1;
1448 break;
1449 case '{':
1450 $this->ext_inBrace++;
1451 $this->ext_regObjects($pre . $var . '.');
1452 break;
1453 }
1454 $this->lastComment = '';
1455 } elseif ($line[0] === '}') {
1456 $this->lastComment = '';
1457 $this->ext_inBrace--;
1458 if ($this->ext_inBrace < 0) {
1459 $this->ext_inBrace = 0;
1460 } else {
1461 break;
1462 }
1463 }
1464 }
1465 }
1466 }
1467
1468 /**
1469 * @param string $key
1470 * @param string $var
1471 * @return void
1472 */
1473 public function ext_putValueInConf($key, $var)
1474 {
1475 // Puts the value $var to the TypoScript value $key in the current lines of the templates.
1476 // If the $key is not found in the template constants field, a new line is inserted in the bottom.
1477 $theValue = ' ' . trim($var);
1478 if (isset($this->objReg[$key])) {
1479 $lineNum = $this->objReg[$key];
1480 $parts = explode('=', $this->raw[$lineNum], 2);
1481 if (count($parts) === 2) {
1482 $parts[1] = $theValue;
1483 }
1484 $this->raw[$lineNum] = implode($parts, '=');
1485 } else {
1486 $this->raw[] = $key . ' =' . $theValue;
1487 }
1488 $this->changed = true;
1489 }
1490
1491 /**
1492 * @param string $key
1493 * @return void
1494 */
1495 public function ext_removeValueInConf($key)
1496 {
1497 // Removes the value in the configuration
1498 if (isset($this->objReg[$key])) {
1499 $lineNum = $this->objReg[$key];
1500 unset($this->raw[$lineNum]);
1501 }
1502 $this->changed = true;
1503 }
1504
1505 /**
1506 * @param array $arr
1507 * @param array $settings
1508 * @return array
1509 */
1510 public function ext_depthKeys($arr, $settings)
1511 {
1512 $tsbrArray = array();
1513 foreach ($arr as $theK => $theV) {
1514 $theKeyParts = explode('.', $theK);
1515 $depth = '';
1516 $c = count($theKeyParts);
1517 $a = 0;
1518 foreach ($theKeyParts as $p) {
1519 $a++;
1520 $depth .= ($depth ? '.' : '') . $p;
1521 $tsbrArray[$depth] = $c == $a ? $theV : 1;
1522 }
1523 }
1524 // Modify settings
1525 foreach ($tsbrArray as $theK => $theV) {
1526 if ($theV) {
1527 $settings[$theK] = 1;
1528 } else {
1529 unset($settings[$theK]);
1530 }
1531 }
1532 return $settings;
1533 }
1534
1535 /**
1536 * Proces input
1537 *
1538 * @param array $http_post_vars
1539 * @param array $http_post_files (not used anymore)
1540 * @param array $theConstants
1541 * @param array $tplRow Not used
1542 * @return void
1543 */
1544 public function ext_procesInput($http_post_vars, $http_post_files, $theConstants, $tplRow)
1545 {
1546 $data = $http_post_vars['data'];
1547 $check = $http_post_vars['check'];
1548 $Wdata = $http_post_vars['Wdata'];
1549 $W2data = $http_post_vars['W2data'];
1550 $W3data = $http_post_vars['W3data'];
1551 $W4data = $http_post_vars['W4data'];
1552 $W5data = $http_post_vars['W5data'];
1553 if (is_array($data)) {
1554 foreach ($data as $key => $var) {
1555 if (isset($theConstants[$key])) {
1556 // If checkbox is set, update the value
1557 if ($this->ext_dontCheckIssetValues || isset($check[$key])) {
1558 // Exploding with linebreak, just to make sure that no multiline input is given!
1559 list($var) = explode(LF, $var);
1560 $typeDat = $this->ext_getTypeData($theConstants[$key]['type']);
1561 switch ($typeDat['type']) {
1562 case 'int':
1563 if ($typeDat['paramstr']) {
1564 $var = MathUtility::forceIntegerInRange($var, $typeDat['params'][0], $typeDat['params'][1]);
1565 } else {
1566 $var = (int)$var;
1567 }
1568 break;
1569 case 'int+':
1570 $var = max(0, (int)$var);
1571 break;
1572 case 'color':
1573 $col = array();
1574 if ($var && !GeneralUtility::inList($this->HTMLcolorList, strtolower($var))) {
1575 $var = preg_replace('/[^A-Fa-f0-9]*/', '', $var);
1576 $useFulHex = strlen($var) > 3;
1577 $col[] = HexDec($var[0]);
1578 $col[] = HexDec($var[1]);
1579 $col[] = HexDec($var[2]);
1580 if ($useFulHex) {
1581 $col[] = HexDec($var[3]);
1582 $col[] = HexDec($var[4]);
1583 $col[] = HexDec($var[5]);
1584 }
1585 $var = substr(('0' . DecHex($col[0])), -1) . substr(('0' . DecHex($col[1])), -1) . substr(('0' . DecHex($col[2])), -1);
1586 if ($useFulHex) {
1587 $var .= substr(('0' . DecHex($col[3])), -1) . substr(('0' . DecHex($col[4])), -1) . substr(('0' . DecHex($col[5])), -1);
1588 }
1589 $var = '#' . strtoupper($var);
1590 }
1591 break;
1592 case 'comment':
1593 if ($var) {
1594 $var = '#';
1595 } else {
1596 $var = '';
1597 }
1598 break;
1599 case 'wrap':
1600 if (isset($Wdata[$key])) {
1601 $var .= '|' . $Wdata[$key];
1602 }
1603 break;
1604 case 'offset':
1605 if (isset($Wdata[$key])) {
1606 $var = (int)$var . ',' . (int)$Wdata[$key];
1607 if (isset($W2data[$key])) {
1608 $var .= ',' . (int)$W2data[$key];
1609 if (isset($W3data[$key])) {
1610 $var .= ',' . (int)$W3data[$key];
1611 if (isset($W4data[$key])) {
1612 $var .= ',' . (int)$W4data[$key];
1613 if (isset($W5data[$key])) {
1614 $var .= ',' . (int)$W5data[$key];
1615 }
1616 }
1617 }
1618 }
1619 }
1620 break;
1621 case 'boolean':
1622 if ($var) {
1623 $var = $typeDat['paramstr'] ? $typeDat['paramstr'] : 1;
1624 }
1625 break;
1626 }
1627 if ($this->ext_printAll || (string)$theConstants[$key]['value'] !== (string)$var) {
1628 // Put value in, if changed.
1629 $this->ext_putValueInConf($key, $var);
1630 }
1631 // Remove the entry because it has been "used"
1632 unset($check[$key]);
1633 } else {
1634 $this->ext_removeValueInConf($key);
1635 }
1636 }
1637 }
1638 }
1639 // Remaining keys in $check indicates fields that are just clicked "on" to be edited.
1640 // Therefore we get the default value and puts that in the template as a start...
1641 if (!$this->ext_dontCheckIssetValues && is_array($check)) {
1642 foreach ($check as $key => $var) {
1643 if (isset($theConstants[$key])) {
1644 $dValue = $theConstants[$key]['default_value'];
1645 $this->ext_putValueInConf($key, $dValue);
1646 }
1647 }
1648 }
1649 }
1650
1651 /**
1652 * @param int $id
1653 * @param string $perms_clause
1654 * @return array
1655 */
1656 public function ext_prevPageWithTemplate($id, $perms_clause)
1657 {
1658 $rootLine = BackendUtility::BEgetRootLine($id, $perms_clause ? ' AND ' . $perms_clause : '');
1659 foreach ($rootLine as $p) {
1660 if ($this->ext_getFirstTemplate($p['uid'])) {
1661 return $p;
1662 }
1663 }
1664 return array();
1665 }
1666
1667 /**
1668 * @return array
1669 */
1670 protected function getRootLine()
1671 {
1672 return isset($GLOBALS['rootLine']) ? $GLOBALS['rootLine'] : array();
1673 }
1674
1675 /**
1676 * @return DatabaseConnection
1677 */
1678 protected function getDatabaseConnection()
1679 {
1680 return $GLOBALS['TYPO3_DB'];
1681 }
1682
1683 /**
1684 * @return LanguageService
1685 */
1686 protected function getLanguageService()
1687 {
1688 return $GLOBALS['LANG'];
1689 }
1690
1691 /**
1692 * @return DocumentTemplate
1693 */
1694 protected function getDocumentTemplate()
1695 {
1696 return $GLOBALS['TBE_TEMPLATE'];
1697 }
1698 }