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