[TASK] Replace treeline GFX icons with CSS icons
[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 . '<li>';
443 if ($PM !== 'join') {
444 $urlParameters = array(
445 'id' => $GLOBALS['SOBE']->id,
446 'tsbr[' . $depth . ']' => $deeper ? 0 : 1
447 );
448 if (GeneralUtility::_GP('breakPointLN')) {
449 $urlParameters['breakPointLN'] = GeneralUtility::_GP('breakPointLN');
450 }
451 $aHref = BackendUtility::getModuleUrl('web_ts', $urlParameters) . '#' . $goto;
452 $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>';
453 }
454 $label = $key;
455 // Read only...
456 if (GeneralUtility::inList('types,resources,sitetitle', $depth) && $this->bType == 'setup') {
457 $label = '<span style="color: #666666;">' . $label . '</span>';
458 } else {
459 if ($this->linkObjects) {
460 $urlParameters = array(
461 'id' => $GLOBALS['SOBE']->id,
462 'sObj' => $depth
463 );
464 if (GeneralUtility::_GP('breakPointLN')) {
465 $urlParameters['breakPointLN'] = GeneralUtility::_GP('breakPointLN');
466 }
467 $aHref = BackendUtility::getModuleUrl('web_ts', $urlParameters);
468 if ($this->bType != 'const') {
469 $ln = is_array($arr[$key . '.ln..']) ? 'Defined in: ' . $this->lineNumberToScript($arr[($key . '.ln..')]) : 'N/A';
470 } else {
471 $ln = '';
472 }
473 if ($this->tsbrowser_searchKeys[$depth] & 4) {
474 $label = '<strong class="text-danger">' . $label . '</strong>';
475 }
476 // The key has matched the search string
477 $label = '<a href="' . htmlspecialchars($aHref) . '" title="' . htmlspecialchars($ln) . '">' . $label . '</a>';
478 }
479 }
480 $HTML .= '<span class="list-tree-group">[' . $label . ']</span>';
481 if (isset($arr[$key])) {
482 $theValue = $arr[$key];
483 if ($this->fixedLgd) {
484 $imgBlocks = ceil(1 + strlen($depthData) / 77);
485 $lgdChars = 68 - ceil(strlen(('[' . $key . ']')) * 0.8) - $imgBlocks * 3;
486 $theValue = $this->ext_fixed_lgd($theValue, $lgdChars);
487 }
488 // The value has matched the search string
489 if ($this->tsbrowser_searchKeys[$depth] & 2) {
490 $HTML .= '&nbsp;=&nbsp;<strong class="text-danger">' . htmlspecialchars($theValue) . '</strong>';
491 } else {
492 $HTML .= '&nbsp;=&nbsp;<strong>' . htmlspecialchars($theValue) . '</strong>';
493 }
494 if ($this->ext_regComments && isset($arr[$key . '..'])) {
495 $comment = $arr[$key . '..'];
496 // Skip INCLUDE_TYPOSCRIPT comments, they are almost useless
497 if (!preg_match('/### <INCLUDE_TYPOSCRIPT:.*/', $comment)) {
498 // Remove linebreaks, replace with ' '
499 $comment = preg_replace('/[\\r\\n]/', ' ', $comment);
500 // Remove # and * if more than twice in a row
501 $comment = preg_replace('/[#\\*]{2,}/', '', $comment);
502 // Replace leading # (just if it exists) and add it again. Result: Every comment should be prefixed by a '#'.
503 $comment = preg_replace('/^[#\\*\\s]+/', '# ', $comment);
504 // Masking HTML Tags: Replace < with &lt; and > with &gt;
505 $comment = htmlspecialchars($comment);
506 $HTML .= ' <i class="text-muted">' . trim($comment) . '</i>';
507 }
508 }
509 }
510 if ($deeper) {
511 $HTML .= $this->ext_getObjTree($arr[$key . '.'], $depth, $depthData, '', $arr[$key], $alphaSort);
512 }
513 }
514 }
515 if ($HTML !== '') {
516 $HTML = '<ul class="list-tree text-monospace">' . $HTML . '</ul>';
517 }
518
519 return $HTML;
520 }
521
522 /**
523 * Find the originating template name for an array of line numbers (TypoScript setup only!)
524 * Given an array of linenumbers the method will try to find the corresponding template where this line originated
525 * The linenumber indicates the *last* lineNumber that is part of the template
526 *
527 * lineNumbers are in sync with the calculated lineNumbers '.ln..' in TypoScriptParser
528 *
529 * @param array $lnArr Array with linenumbers (might have some extra symbols, for example for unsetting) to be processed
530 * @return array The same array where each entry has been prepended by the template title if available
531 */
532 public function lineNumberToScript(array $lnArr) {
533 // On the first call, construct the lnToScript array.
534 if (!is_array($this->lnToScript)) {
535 $this->lnToScript = array();
536
537 // aggregatedTotalLineCount
538 $c = 0;
539 foreach ($this->hierarchyInfo as $templateNumber => $info) {
540 // hierarchyInfo has the number of lines in configLines, but unfortunatly this value
541 // was calculated *before* processing of any INCLUDE instructions
542 // for some yet unknown reason we have to add an extra +2 offset
543 $linecountAfterIncludeProcessing = substr_count($this->config[$templateNumber], LF) + 2;
544 $c += $linecountAfterIncludeProcessing;
545 $this->lnToScript[$c] = $info['title'];
546 }
547 }
548
549 foreach ($lnArr as $k => $ln) {
550 foreach ($this->lnToScript as $endLn => $title) {
551 if ($endLn >= (int)$ln) {
552 $lnArr[$k] = '"' . $title . '", ' . $ln;
553 break;
554 }
555 }
556 }
557
558 return implode('; ', $lnArr);
559 }
560
561 /**
562 * @param array $theValue
563 * @return array
564 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8 - use htmlspecialchars() directly
565 */
566 public function makeHtmlspecialchars($theValue) {
567 GeneralUtility::logDeprecatedFunction();
568 return $this->ext_noSpecialCharsOnLabels ? $theValue : htmlspecialchars($theValue);
569 }
570
571 /**
572 * @param array $arr
573 * @param string $depth_in
574 * @param string $searchString
575 * @param array $keyArray
576 * @return array
577 */
578 public function ext_getSearchKeys($arr, $depth_in, $searchString, $keyArray) {
579 $keyArr = array();
580 foreach ($arr as $key => $value) {
581 $key = preg_replace('/\\.$/', '', $key);
582 if (substr($key, -1) != '.') {
583 $keyArr[$key] = 1;
584 }
585 }
586 if ($depth_in) {
587 $depth_in = $depth_in . '.';
588 }
589 foreach ($keyArr as $key => $value) {
590 $depth = $depth_in . $key;
591 $deeper = is_array($arr[$key . '.']);
592 if ($this->regexMode) {
593 // The value has matched
594 if (preg_match('/' . $searchString . '/', $arr[$key])) {
595 $this->tsbrowser_searchKeys[$depth] += 2;
596 }
597 // The key has matched
598 if (preg_match('/' . $searchString . '/', $key)) {
599 $this->tsbrowser_searchKeys[$depth] += 4;
600 }
601 // Just open this subtree if the parent key has matched the search
602 if (preg_match('/' . $searchString . '/', $depth_in)) {
603 $this->tsbrowser_searchKeys[$depth] = 1;
604 }
605 } else {
606 // The value has matched
607 if (stristr($arr[$key], $searchString)) {
608 $this->tsbrowser_searchKeys[$depth] += 2;
609 }
610 // The key has matches
611 if (stristr($key, $searchString)) {
612 $this->tsbrowser_searchKeys[$depth] += 4;
613 }
614 // Just open this subtree if the parent key has matched the search
615 if (stristr($depth_in, $searchString)) {
616 $this->tsbrowser_searchKeys[$depth] = 1;
617 }
618 }
619 if ($deeper) {
620 $cS = count($this->tsbrowser_searchKeys);
621 $keyArray = $this->ext_getSearchKeys($arr[$key . '.'], $depth, $searchString, $keyArray);
622 if ($cS != count($this->tsbrowser_searchKeys)) {
623 $keyArray[$depth] = 1;
624 }
625 }
626 }
627 return $keyArray;
628 }
629
630 /**
631 * @param int $pid
632 * @return int
633 */
634 public function ext_getRootlineNumber($pid) {
635 if ($pid) {
636 foreach ($this->getRootLine() as $key => $val) {
637 if ((int)$val['uid'] === (int)$pid) {
638 return (int)$key;
639 }
640 }
641 }
642 return -1;
643 }
644
645 /**
646 * @param array $arr
647 * @param string $depthData
648 * @param array $keyArray
649 * @param int $first
650 * @return array
651 */
652 public function ext_getTemplateHierarchyArr($arr, $depthData, $keyArray, $first = 0) {
653 $keyArr = array();
654 foreach ($arr as $key => $value) {
655 $key = preg_replace('/\\.$/', '', $key);
656 if (substr($key, -1) != '.') {
657 $keyArr[$key] = 1;
658 }
659 }
660 $a = 0;
661 $c = count($keyArr);
662 static $i = 0;
663 foreach ($keyArr as $key => $value) {
664 $HTML = '';
665 $a++;
666 $deeper = is_array($arr[$key . '.']);
667 $row = $arr[$key];
668 $LN = $a == $c ? 'blank' : 'line';
669 $BTM = $a == $c ? 'top' : '';
670 $HTML .= $depthData;
671 $alttext = '[' . $row['templateID'] . ']';
672 $alttext .= $row['pid'] ? ' - ' . BackendUtility::getRecordPath($row['pid'], $GLOBALS['SOBE']->perms_clause, 20) : '';
673 $icon = substr($row['templateID'], 0, 3) === 'sys'
674 ? IconUtility::getSpriteIconForRecord('sys_template', $row, array('title' => $alttext))
675 : IconUtility::getSpriteIcon('mimetypes-x-content-template-static', array('title' => $alttext));
676 if (in_array($row['templateID'], $this->clearList_const) || in_array($row['templateID'], $this->clearList_setup)) {
677 $urlParameters = array(
678 'id' => $GLOBALS['SOBE']->id,
679 'template' => $row['templateID']
680 );
681 $aHref = BackendUtility::getModuleUrl('web_ts', $urlParameters);
682 $A_B = '<a href="' . htmlspecialchars($aHref) . '">';
683 $A_E = '</a>';
684 if (GeneralUtility::_GP('template') == $row['templateID']) {
685 $A_B = '<strong>' . $A_B;
686 $A_E .= '</strong>';
687 }
688 } else {
689 $A_B = '';
690 $A_E = '';
691 }
692 $HTML .= ($first ? '' : '<span class="treeline-icon treeline-icon-join' . $BTM . '"></span>') . $icon . $A_B
693 . htmlspecialchars(GeneralUtility::fixed_lgd_cs($row['title'], $GLOBALS['BE_USER']->uc['titleLen']))
694 . $A_E . '&nbsp;&nbsp;';
695 $RL = $this->ext_getRootlineNumber($row['pid']);
696 $keyArray[] = '<tr class="' . ($i++ % 2 == 0 ? 'bgColor4' : 'bgColor6') . '">
697 <td nowrap="nowrap">' . $HTML . '</td>
698 <td align="center">' . ($row['root'] ? IconUtility::getSpriteIcon('status-status-checked') : '') . '&nbsp;&nbsp;</td>
699 <td align="center">' . ($row['clConf'] ? IconUtility::getSpriteIcon('status-status-checked') : '') . '&nbsp;&nbsp;' . '</td>
700 <td align="center">' . ($row['clConst'] ? IconUtility::getSpriteIcon('status-status-checked') : '') . '&nbsp;&nbsp;' . '</td>
701 <td align="center">' . ($row['pid'] ?: '') . '</td>
702 <td align="center">' . ($RL >= 0 ? $RL : '') . '</td>
703 <td>' . ($row['next'] ? '&nbsp;' . $row['next'] . '&nbsp;&nbsp;' : '') . '</td>
704 </tr>';
705 if ($deeper) {
706 $keyArray = $this->ext_getTemplateHierarchyArr($arr[$key . '.'], $depthData . ($first ? '' : '<span class="treeline-icon treeline-icon-' . $LN . '"></span>'), $keyArray);
707 }
708 }
709 return $keyArray;
710 }
711
712 /**
713 * Processes the flat array from TemplateService->hierarchyInfo
714 * and turns it into a hierachical array to show dependencies (used by TemplateAnalyzer)
715 *
716 * @param array $depthDataArr (empty array on external call)
717 * @param int &$pointer Element number (1! to count()) of $this->hierarchyInfo that should be processed.
718 * @return array Processed hierachyInfo.
719 */
720 public function ext_process_hierarchyInfo(array $depthDataArr, &$pointer) {
721 $parent = $this->hierarchyInfo[$pointer - 1]['templateParent'];
722 while ($pointer > 0 && $this->hierarchyInfo[$pointer - 1]['templateParent'] == $parent) {
723 $pointer--;
724 $row = $this->hierarchyInfo[$pointer];
725 $depthDataArr[$row['templateID']] = $row;
726 $depthDataArr[$row['templateID']]['bgcolor_setup'] = isset($this->clearList_setup_temp[$row['templateID']]) ? ' class="bgColor5"' : '';
727 $depthDataArr[$row['templateID']]['bgcolor_const'] = isset($this->clearList_const_temp[$row['templateID']]) ? ' class="bgColor5"' : '';
728 unset($this->clearList_setup_temp[$row['templateID']]);
729 unset($this->clearList_const_temp[$row['templateID']]);
730 $this->templateTitles[$row['templateID']] = $row['title'];
731 if ($row['templateID'] == $this->hierarchyInfo[$pointer - 1]['templateParent']) {
732 $depthDataArr[$row['templateID'] . '.'] = $this->ext_process_hierarchyInfo(array(), $pointer);
733 }
734 }
735 return $depthDataArr;
736 }
737
738 /**
739 * Get formatted HTML output for TypoScript either with Syntaxhiglighting or in plain mode
740 *
741 * @param array $config Array with simple strings of typoscript code.
742 * @param bool $lineNumbers Prepend linNumbers to each line.
743 * @param bool $comments Enable including comments in output.
744 * @param bool $crop Enable cropping of long lines.
745 * @param bool $syntaxHL Enrich output with syntaxhighlighting.
746 * @param int $syntaxHLBlockmode
747 * @return string
748 */
749 public function ext_outputTS(
750 array $config, $lineNumbers = FALSE, $comments = FALSE, $crop = FALSE, $syntaxHL = FALSE, $syntaxHLBlockmode = 0
751 ) {
752 $all = '';
753 foreach ($config as $str) {
754 $all .= '[GLOBAL]' . LF . $str;
755 }
756 if ($syntaxHL) {
757 $tsparser = GeneralUtility::makeInstance(Parser\TypoScriptParser::class);
758 $tsparser->lineNumberOffset = $this->ext_lineNumberOffset + 1;
759 $tsparser->parentObject = $this;
760 return $tsparser->doSyntaxHighlight($all, $lineNumbers ? array($this->ext_lineNumberOffset + 1) : '', $syntaxHLBlockmode);
761 } else {
762 return $this->ext_formatTS($all, $lineNumbers, $comments, $crop);
763 }
764 }
765
766 /**
767 * Returns a new string of max. $chars length
768 * If the string is longer, it will be truncated and prepended with '...'
769 * $chars must be an integer of at least 4
770 *
771 * @param string $string
772 * @param int $chars
773 * @return string
774 */
775 public function ext_fixed_lgd($string, $chars) {
776 if ($chars >= 4) {
777 if (strlen($string) > $chars) {
778 if (strlen($string) > 24 && substr($string, 0, 12) == '##' . $this->Cmarker . '_B##') {
779 return '##' . $this->Cmarker . '_B##' . GeneralUtility::fixed_lgd_cs(substr($string, 12, -12), ($chars - 3))
780 . '##' . $this->Cmarker . '_E##';
781 } else {
782 return GeneralUtility::fixed_lgd_cs($string, $chars - 3);
783 }
784 }
785 }
786 return $string;
787 }
788
789 /**
790 * @param int $lineNumber Line Number
791 * @param array $str
792 * @return string
793 */
794 public function ext_lnBreakPointWrap($lineNumber, $str) {
795 return '<a href="#" id="line-' . $lineNumber . '" onClick="return brPoint(' . $lineNumber . ','
796 . ($this->ext_lineNumberOffset_mode == 'setup' ? 1 : 0) . ');">' . $str . '</a>';
797 }
798
799 /**
800 * @param string $input
801 * @param bool $ln
802 * @param bool $comments
803 * @param bool $crop
804 * @return string
805 */
806 public function ext_formatTS($input, $ln, $comments = TRUE, $crop = FALSE) {
807 $cArr = explode(LF, $input);
808 $n = ceil(log10(count($cArr) + $this->ext_lineNumberOffset));
809 $lineNum = '';
810 foreach ($cArr as $k => $v) {
811 $lln = $k + $this->ext_lineNumberOffset + 1;
812 if ($ln) {
813 $lineNum = $this->ext_lnBreakPointWrap($lln, str_replace(' ', '&nbsp;', sprintf(('% ' . $n . 'd'), $lln))) . ': ';
814 }
815 $v = htmlspecialchars($v);
816 if ($crop) {
817 $v = $this->ext_fixed_lgd($v, $ln ? 71 : 77);
818 }
819 $cArr[$k] = $lineNum . str_replace(' ', '&nbsp;', $v);
820 $firstChar = substr(trim($v), 0, 1);
821 if ($firstChar == '[') {
822 $cArr[$k] = '<strong class="text-success">' . $cArr[$k] . '</strong>';
823 } elseif ($firstChar == '/' || $firstChar == '#') {
824 if ($comments) {
825 $cArr[$k] = '<span class="text-muted">' . $cArr[$k] . '</span>';
826 } else {
827 unset($cArr[$k]);
828 }
829 }
830 }
831 $output = implode($cArr, '<br />') . '<br />';
832 return $output;
833 }
834
835 /**
836 * @param int $id
837 * @param int $template_uid
838 * @return array|NULL Returns the template record or NULL if none was found
839 */
840 public function ext_getFirstTemplate($id, $template_uid = 0) {
841 // Query is taken from the runThroughTemplates($theRootLine) function in the parent class.
842 if ((int)$id) {
843 $addC = $template_uid ? ' AND uid=' . (int)$template_uid : '';
844 $where = 'pid=' . (int)$id . $addC . ' ' . $this->whereClause;
845 $res = $this->getDatabaseConnection()->exec_SELECTquery('*', 'sys_template', $where, '', 'sorting', '1');
846 $row = $this->getDatabaseConnection()->sql_fetch_assoc($res);
847 BackendUtility::workspaceOL('sys_template', $row);
848 $this->getDatabaseConnection()->sql_free_result($res);
849 // Returns the template row if found.
850 return $row;
851 }
852 return NULL;
853 }
854
855 /**
856 * @param int $id
857 * @return array[] Array of template records
858 */
859 public function ext_getAllTemplates($id) {
860 if (!$id) {
861 return array();
862 }
863
864 // Query is taken from the runThroughTemplates($theRootLine) function in the parent class.
865 $res = $this->getDatabaseConnection()->exec_SELECTquery('*', 'sys_template', 'pid=' . (int)$id . ' ' . $this->whereClause, '', 'sorting');
866
867 $outRes = array();
868 while ($row = $this->getDatabaseConnection()->sql_fetch_assoc($res)) {
869 BackendUtility::workspaceOL('sys_template', $row);
870 if (is_array($row)) {
871 $outRes[] = $row;
872 }
873 }
874 $this->getDatabaseConnection()->sql_free_result($res);
875
876 return $outRes;
877 }
878
879 /**
880 * This function compares the flattened constants (default and all).
881 * Returns an array with the constants from the whole template which may be edited by the module.
882 *
883 * @param array $default
884 * @return array
885 */
886 public function ext_compareFlatSetups($default) {
887 $editableComments = array();
888 foreach ($this->flatSetup as $const => $value) {
889 if (substr($const, -2) === '..' || !isset($this->flatSetup[$const . '..'])) {
890 continue;
891 }
892 $comment = trim($this->flatSetup[$const . '..']);
893 $c_arr = explode(LF, $comment);
894 foreach ($c_arr as $k => $v) {
895 $line = trim(preg_replace('/^[#\\/]*/', '', $v));
896 if (!$line) {
897 continue;
898 }
899 $parts = explode(';', $line);
900 foreach ($parts as $par) {
901 if (strstr($par, '=')) {
902 $keyValPair = explode('=', $par, 2);
903 switch (trim(strtolower($keyValPair[0]))) {
904 case 'type':
905 // Type:
906 $editableComments[$const]['type'] = trim($keyValPair[1]);
907 break;
908 case 'cat':
909 // List of categories.
910 $catSplit = explode('/', strtolower($keyValPair[1]));
911 $catSplit[0] = trim($catSplit[0]);
912 if (isset($this->categoryLabels[$catSplit[0]])) {
913 $catSplit[0] = $this->categoryLabels[$catSplit[0]];
914 }
915 $editableComments[$const]['cat'] = $catSplit[0];
916 // This is the subcategory. Must be a key in $this->subCategories[].
917 // catSplit[2] represents the search-order within the subcat.
918 $catSplit[1] = trim($catSplit[1]);
919 if ($catSplit[1] && isset($this->subCategories[$catSplit[1]])) {
920 $editableComments[$const]['subcat_name'] = $catSplit[1];
921 $editableComments[$const]['subcat'] = $this->subCategories[$catSplit[1]][1]
922 . '/' . $catSplit[1] . '/' . trim($catSplit[2]) . 'z';
923 } else {
924 $editableComments[$const]['subcat'] = 'x' . '/' . trim($catSplit[2]) . 'z';
925 }
926 break;
927 case 'label':
928 // Label
929 $editableComments[$const]['label'] = trim($keyValPair[1]);
930 break;
931 case 'customcategory':
932 // Custom category label
933 $customCategory = explode('=', $keyValPair[1], 2);
934 if (trim($customCategory[0])) {
935 $categoryKey = strtolower($customCategory[0]);
936 $this->categoryLabels[$categoryKey] = $this->getLanguageService()->sL($customCategory[1]);
937 }
938 break;
939 case 'customsubcategory':
940 // Custom subCategory label
941 $customSubcategory = explode('=', $keyValPair[1], 2);
942 if (trim($customSubcategory[0])) {
943 $subCategoryKey = strtolower($customSubcategory[0]);
944 $this->subCategories[$subCategoryKey][0] = $this->getLanguageService()->sL($customSubcategory[1]);
945 }
946 break;
947 }
948 }
949 }
950 }
951 if (isset($editableComments[$const])) {
952 $editableComments[$const]['name'] = $const;
953 $editableComments[$const]['value'] = trim($value);
954 if (isset($default[$const])) {
955 $editableComments[$const]['default_value'] = trim($default[$const]);
956 }
957 }
958 }
959 return $editableComments;
960 }
961
962 /**
963 * @param array $editConstArray
964 * @return void
965 */
966 public function ext_categorizeEditableConstants($editConstArray) {
967 // Runs through the available constants and fills the $this->categories array with pointers and priority-info
968 foreach ($editConstArray as $constName => $constData) {
969 if (!$constData['type']) {
970 $constData['type'] = 'string';
971 }
972 $cats = explode(',', $constData['cat']);
973 // if = only one category, while allows for many. We have agreed on only one category is the most basic way...
974 foreach ($cats as $theCat) {
975 $theCat = trim($theCat);
976 if ($theCat) {
977 $this->categories[$theCat][$constName] = $constData['subcat'];
978 }
979 }
980 }
981 }
982
983 /**
984 * @return array
985 */
986 public function ext_getCategoryLabelArray() {
987 // Returns array used for labels in the menu.
988 $retArr = array();
989 foreach ($this->categories as $k => $v) {
990 if (!empty($v)) {
991 $retArr[$k] = strtoupper($k) . ' (' . count($v) . ')';
992 }
993 }
994 return $retArr;
995 }
996
997 /**
998 * @param string $type
999 * @return array
1000 */
1001 public function ext_getTypeData($type) {
1002 $retArr = array();
1003 $type = trim($type);
1004 if (!$type) {
1005 $retArr['type'] = 'string';
1006 } else {
1007 $m = strcspn($type, ' [');
1008 $retArr['type'] = strtolower(substr($type, 0, $m));
1009 if (GeneralUtility::inList('int,options,file,boolean,offset,user', $retArr['type'])) {
1010 $p = trim(substr($type, $m));
1011 $reg = array();
1012 preg_match('/\\[(.*)\\]/', $p, $reg);
1013 $p = trim($reg[1]);
1014 if ($p) {
1015 $retArr['paramstr'] = $p;
1016 switch ($retArr['type']) {
1017 case 'int':
1018 if ($retArr['paramstr'][0] === '-') {
1019 $retArr['params'] = GeneralUtility::intExplode('-', substr($retArr['paramstr'], 1));
1020 $retArr['params'][0] = (int)('-' . $retArr['params'][0]);
1021 } else {
1022 $retArr['params'] = GeneralUtility::intExplode('-', $retArr['paramstr']);
1023 }
1024 $retArr['paramstr'] = $retArr['params'][0] . ' - ' . $retArr['params'][1];
1025 break;
1026 case 'options':
1027 $retArr['params'] = explode(',', $retArr['paramstr']);
1028 break;
1029 }
1030 }
1031 }
1032 }
1033 return $retArr;
1034 }
1035
1036 /**
1037 * @param string $category
1038 * @return void
1039 */
1040 public function ext_getTSCE_config($category) {
1041 $catConf = $this->setup['constants']['TSConstantEditor.'][$category . '.'];
1042 $out = array();
1043 if (is_array($catConf)) {
1044 foreach ($catConf as $key => $val) {
1045 switch ($key) {
1046 case 'image':
1047 $out['imagetag'] = $this->ext_getTSCE_config_image($catConf['image']);
1048 break;
1049 case 'description':
1050 case 'bulletlist':
1051 case 'header':
1052 $out[$key] = $val;
1053 break;
1054 default:
1055 if (MathUtility::canBeInterpretedAsInteger($key)) {
1056 $constRefs = explode(',', $val);
1057 foreach ($constRefs as $const) {
1058 $const = trim($const);
1059 if ($const) {
1060 $out['constants'][$const] .= '<span class="label label-danger">' . $key . '</span>';
1061 }
1062 }
1063 }
1064 }
1065 }
1066 }
1067 $this->helpConfig = $out;
1068 }
1069
1070 /**
1071 * @param string $key
1072 * @return string
1073 * @deprecated since TYPO3 CMS 7, will be removed with TYPO3 CMS 8
1074 */
1075 public function ext_getKeyImage($key) {
1076 GeneralUtility::logDeprecatedFunction();
1077 return '<span class="label label-danger">' . $key . '</span>';
1078 }
1079
1080 /**
1081 * @param string $imgConf
1082 * @return string
1083 */
1084 public function ext_getTSCE_config_image($imgConf) {
1085 $iFile = NULL;
1086 $tFile = NULL;
1087 if (substr($imgConf, 0, 4) == 'gfx/') {
1088 $iFile = $this->ext_localGfxPrefix . $imgConf;
1089 $tFile = $this->ext_localWebGfxPrefix . $imgConf;
1090 } elseif (substr($imgConf, 0, 4) == 'EXT:') {
1091 $iFile = GeneralUtility::getFileAbsFileName($imgConf);
1092 if ($iFile) {
1093 $f = PathUtility::stripPathSitePrefix($iFile);
1094 $tFile = $GLOBALS['BACK_PATH'] . '../' . $f;
1095 }
1096 }
1097 if ($iFile !== NULL) {
1098 $imageInfo = @getImagesize($iFile);
1099 return '<img src="' . $tFile . '" ' . $imageInfo[3] . '>';
1100 }
1101 return '';
1102 }
1103
1104 /**
1105 * @param array $params
1106 * @return array
1107 */
1108 public function ext_fNandV($params) {
1109 $fN = 'data[' . $params['name'] . ']';
1110 $idName = str_replace('.', '-', $params['name']);
1111 $fV = $params['value'];
1112 // Values entered from the constantsedit cannot be constants! 230502; removed \{ and set {
1113 if (preg_match('/^{[\\$][a-zA-Z0-9\\.]*}$/', trim($fV), $reg)) {
1114 $fV = '';
1115 }
1116 $fV = htmlspecialchars($fV);
1117 return array($fN, $fV, $params, $idName);
1118 }
1119
1120 /**
1121 * This functions returns the HTML-code that creates the editor-layout of the module.
1122 *
1123 * @param array $theConstants
1124 * @param string $category
1125 * @return string
1126 */
1127 public function ext_printFields($theConstants, $category) {
1128 reset($theConstants);
1129 $output = '';
1130 $subcat = '';
1131 if (is_array($this->categories[$category])) {
1132 $help = $this->helpConfig;
1133 if (!$this->doNotSortCategoriesBeforeMakingForm) {
1134 asort($this->categories[$category]);
1135 }
1136 foreach ($this->categories[$category] as $name => $type) {
1137 $params = $theConstants[$name];
1138 if (is_array($params)) {
1139 if ($subcat != $params['subcat_name']) {
1140 $subcat = $params['subcat_name'];
1141 $subcat_name = $params['subcat_name'] ? $this->subCategories[$params['subcat_name']][0] : 'Others';
1142 $output .= '<h3 class="typo3-tstemplate-ceditor-subcat">' . $subcat_name . '</h3>';
1143 }
1144 $label = $this->getLanguageService()->sL($params['label']);
1145 $label_parts = explode(':', $label, 2);
1146 if (count($label_parts) === 2) {
1147 $head = trim($label_parts[0]);
1148 $body = trim($label_parts[1]);
1149 } else {
1150 $head = trim($label_parts[0]);
1151 $body = '';
1152 }
1153 if (strlen($head) > 35) {
1154 if (!$body) {
1155 $body = $head;
1156 }
1157 $head = GeneralUtility::fixed_lgd_cs($head, 35);
1158 }
1159 $typeDat = $this->ext_getTypeData($params['type']);
1160 $p_field = '';
1161 $raname = substr(md5($params['name']), 0, 10);
1162 $aname = '\'' . $raname . '\'';
1163 list($fN, $fV, $params, $idName) = $this->ext_fNandV($params);
1164 switch ($typeDat['type']) {
1165 case 'int':
1166
1167 case 'int+':
1168 $p_field = '<input id="' . $idName . '" type="text" name="' . $fN . '" value="' . $fV . '"'
1169 . $this->getDocumentTemplate()->formWidth(5) . ' onChange="uFormUrl(' . $aname . ')" />';
1170 if ($typeDat['paramstr']) {
1171 $p_field .= ' Range: ' . $typeDat['paramstr'];
1172 } elseif ($typeDat['type'] == 'int+') {
1173 $p_field .= ' Range: 0 - ';
1174 } else {
1175 $p_field .= ' (Integer)';
1176 }
1177 break;
1178 case 'color':
1179 $colorNames = explode(',', ',' . $this->HTMLcolorList);
1180 $p_field = '';
1181 foreach ($colorNames as $val) {
1182 $sel = '';
1183 if ($val == strtolower($params['value'])) {
1184 $sel = ' selected';
1185 }
1186 $p_field .= '<option value="' . htmlspecialchars($val) . '"' . $sel . '>' . $val . '</option>';
1187 }
1188 $p_field = '<select id="select-' . $idName . '" rel="' . $idName . '" name="C' . $fN . '" class="typo3-tstemplate-ceditor-color-select" onChange="uFormUrl(' . $aname . ');">' . $p_field . '</select>';
1189 $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 . ')" />';
1190 break;
1191 case 'wrap':
1192 $wArr = explode('|', $fV);
1193 $p_field = '<input type="text" id="' . $idName . '" name="' . $fN . '" value="' . $wArr[0] . '"' . $this->getDocumentTemplate()->formWidth(29) . ' onChange="uFormUrl(' . $aname . ')" />';
1194 $p_field .= ' | ';
1195 $p_field .= '<input type="text" name="W' . $fN . '" value="' . $wArr[1] . '"' . $this->getDocumentTemplate()->formWidth(15) . ' onChange="uFormUrl(' . $aname . ')" />';
1196 break;
1197 case 'offset':
1198 $wArr = explode(',', $fV);
1199 $labels = GeneralUtility::trimExplode(',', $typeDat['paramstr']);
1200 $p_field = ($labels[0] ? $labels[0] : 'x') . ':<input type="text" name="' . $fN . '" value="' . $wArr[0] . '"' . $this->getDocumentTemplate()->formWidth(4) . ' onChange="uFormUrl(' . $aname . ')" />';
1201 $p_field .= ' , ';
1202 $p_field .= ($labels[1] ? $labels[1] : 'y') . ':<input type="text" name="W' . $fN . '" value="' . $wArr[1] . '"' . $this->getDocumentTemplate()->formWidth(4) . ' onChange="uFormUrl(' . $aname . ')" />';
1203 $labelsCount = count($labels);
1204 for ($aa = 2; $aa < $labelsCount; $aa++) {
1205 if ($labels[$aa]) {
1206 $p_field .= ' , ' . $labels[$aa] . ':<input type="text" name="W' . $aa . $fN . '" value="' . $wArr[$aa] . '"' . $this->getDocumentTemplate()->formWidth(4) . ' onChange="uFormUrl(' . $aname . ')" />';
1207 } else {
1208 $p_field .= '<input type="hidden" name="W' . $aa . $fN . '" value="' . $wArr[$aa] . '" />';
1209 }
1210 }
1211 break;
1212 case 'options':
1213 if (is_array($typeDat['params'])) {
1214 $p_field = '';
1215 foreach ($typeDat['params'] as $val) {
1216 $vParts = explode('=', $val, 2);
1217 $label = $vParts[0];
1218 $val = isset($vParts[1]) ? $vParts[1] : $vParts[0];
1219 // option tag:
1220 $sel = '';
1221 if ($val == $params['value']) {
1222 $sel = ' selected';
1223 }
1224 $p_field .= '<option value="' . htmlspecialchars($val) . '"' . $sel . '>' . $this->getLanguageService()->sL($label) . '</option>';
1225 }
1226 $p_field = '<select id="' . $idName . '" name="' . $fN . '" onChange="uFormUrl(' . $aname . ')">' . $p_field . '</select>';
1227 }
1228 break;
1229 case 'boolean':
1230 $p_field = '<input type="hidden" name="' . $fN . '" value="0" />';
1231 $sel = '';
1232 if ($fV) {
1233 $sel = ' checked';
1234 }
1235 $p_field .= '<input id="' . $idName . '" type="checkbox" name="' . $fN . '" value="' . ($typeDat['paramstr'] ? $typeDat['paramstr'] : 1) . '"' . $sel . ' onClick="uFormUrl(' . $aname . ')" />';
1236 break;
1237 case 'comment':
1238 $p_field = '<input type="hidden" name="' . $fN . '" value="#" />';
1239 $sel = '';
1240 if (!$fV) {
1241 $sel = ' checked';
1242 }
1243 $p_field .= '<input id="' . $idName . '" type="checkbox" name="' . $fN . '" value=""' . $sel . ' onClick="uFormUrl(' . $aname . ')" />';
1244 break;
1245 case 'file':
1246 // extensionlist
1247 $extList = $typeDat['paramstr'];
1248 if ($extList == 'IMAGE_EXT') {
1249 $extList = $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'];
1250 }
1251 $p_field = '<option value="">(' . $extList . ')</option>';
1252 if (trim($params['value'])) {
1253 $val = $params['value'];
1254 $p_field .= '<option value=""></option>';
1255 $p_field .= '<option value="' . htmlspecialchars($val) . '" selected>' . $val . '</option>';
1256 }
1257 $p_field = '<select id="' . $idName . '" name="' . $fN . '" onChange="uFormUrl(' . $aname . ')">' . $p_field . '</select>';
1258 break;
1259 case 'user':
1260 $userFunction = $typeDat['paramstr'];
1261 $userFunctionParams = array('fieldName' => $fN, 'fieldValue' => $fV);
1262 $p_field = GeneralUtility::callUserFunction($userFunction, $userFunctionParams, $this, '');
1263 break;
1264 case 'small':
1265
1266 default:
1267 $fwidth = $typeDat['type'] == 'small' ? 10 : 46;
1268 $p_field = '<input id="' . $idName . '" type="text" name="' . $fN . '" value="' . $fV . '"'
1269 . $this->getDocumentTemplate()->formWidth($fwidth) . ' onChange="uFormUrl(' . $aname . ')" />';
1270 }
1271 // Define default names and IDs
1272 $userTyposcriptID = 'userTS-' . $idName;
1273 $defaultTyposcriptID = 'defaultTS-' . $idName;
1274 $checkboxName = 'check[' . $params['name'] . ']';
1275 $checkboxID = 'check-' . $idName;
1276 // Handle type=color specially
1277 if ($typeDat['type'] == 'color' && substr($params['value'], 0, 2) != '{$') {
1278 $color = '<div id="colorbox-' . $idName . '" class="typo3-tstemplate-ceditor-colorblock" style="background-color:' . $params['value'] . ';">&nbsp;</div>';
1279 } else {
1280 $color = '';
1281 }
1282 $userTyposcriptStyle = '';
1283 $deleteIconHTML = '';
1284 $constantCheckbox = '';
1285 $constantDefaultRow = '';
1286 if (!$this->ext_dontCheckIssetValues) {
1287 // Set the default styling options
1288 if (isset($this->objReg[$params['name']])) {
1289 $checkboxValue = 'checked';
1290 $defaultTyposcriptStyle = 'style="display:none;"';
1291 } else {
1292 $checkboxValue = '';
1293 $userTyposcriptStyle = 'style="display:none;"';
1294 $defaultTyposcriptStyle = '';
1295 }
1296 $deleteIconHTML = IconUtility::getSpriteIcon('actions-edit-undo', array(
1297 'class' => 'typo3-tstemplate-ceditor-control undoIcon',
1298 'alt' => 'Revert to default Constant',
1299 'title' => 'Revert to default Constant',
1300 'rel' => $idName
1301 ));
1302 $editIconHTML = IconUtility::getSpriteIcon('actions-document-open', array(
1303 'class' => 'typo3-tstemplate-ceditor-control editIcon',
1304 'alt' => 'Edit this Constant',
1305 'title' => 'Edit this Constant',
1306 'rel' => $idName
1307 ));
1308 $constantCheckbox = '<input type="hidden" name="' . $checkboxName . '" id="' . $checkboxID . '" value="' . $checkboxValue . '"/>';
1309 // If there's no default value for the field, use a static label.
1310 if (!$params['default_value']) {
1311 $params['default_value'] = '[Empty]';
1312 }
1313 $constantDefaultRow = '<div class="typo3-tstemplate-ceditor-row" id="' . $defaultTyposcriptID . '" '
1314 . $defaultTyposcriptStyle . '>' . $editIconHTML . htmlspecialchars($params['default_value'])
1315 . $color . '</div>';
1316 }
1317 $constantEditRow = '<div class="typo3-tstemplate-ceditor-row" id="' . $userTyposcriptID . '" '
1318 . $userTyposcriptStyle . '>' . $deleteIconHTML . $p_field . $color . '</div>';
1319 $constantLabel = '<dt class="typo3-tstemplate-ceditor-label">' . htmlspecialchars($head) . '</dt>';
1320 $constantName = '<dt class="text-muted">[' . $params['name'] . ']</dt>';
1321 $constantDescription = $body ? '<dd>' . htmlspecialchars($body) . '</dd>' : '';
1322 $constantData = '<dd>' . $constantCheckbox . $constantEditRow . $constantDefaultRow . '</dd>';
1323 $output .= '<a name="' . $raname . '"></a>' . $help['constants'][$params['name']];
1324 $output .= '<dl class="typo3-tstemplate-ceditor-constant">' . $constantLabel . $constantName . $constantDescription . $constantData . '</dl>';
1325 } else {
1326 debug('Error. Constant did not exist. Should not happen.');
1327 }
1328 }
1329 }
1330 return $output;
1331 }
1332
1333 /***************************
1334 *
1335 * Processing input values
1336 *
1337 ***************************/
1338 /**
1339 * @param string $constants
1340 * @return void
1341 */
1342 public function ext_regObjectPositions($constants) {
1343 // This runs through the lines of the constants-field of the active template and registers the constants-names
1344 // and line positions in an array, $this->objReg
1345 $this->raw = explode(LF, $constants);
1346 $this->rawP = 0;
1347 // Resetting the objReg if the divider is found!!
1348 $this->objReg = array();
1349 $this->ext_regObjects('');
1350 }
1351
1352 /**
1353 * @param string $pre
1354 * @return void
1355 */
1356 public function ext_regObjects($pre) {
1357 // Works with regObjectPositions. "expands" the names of the TypoScript objects
1358 while (isset($this->raw[$this->rawP])) {
1359 $line = ltrim($this->raw[$this->rawP]);
1360 if (strstr($line, $this->edit_divider)) {
1361 // Resetting the objReg if the divider is found!!
1362 $this->objReg = array();
1363 }
1364 $this->rawP++;
1365 if ($line) {
1366 if ($line[0] === '[') {
1367
1368 } elseif (strcspn($line, '}#/') != 0) {
1369 $varL = strcspn($line, ' {=<');
1370 $var = substr($line, 0, $varL);
1371 $line = ltrim(substr($line, $varL));
1372 switch ($line[0]) {
1373 case '=':
1374 $this->objReg[$pre . $var] = $this->rawP - 1;
1375 break;
1376 case '{':
1377 $this->ext_inBrace++;
1378 $this->ext_regObjects($pre . $var . '.');
1379 break;
1380 }
1381 $this->lastComment = '';
1382 } elseif ($line[0] === '}') {
1383 $this->lastComment = '';
1384 $this->ext_inBrace--;
1385 if ($this->ext_inBrace < 0) {
1386 $this->ext_inBrace = 0;
1387 } else {
1388 break;
1389 }
1390 }
1391 }
1392 }
1393 }
1394
1395 /**
1396 * @param string $key
1397 * @param string $var
1398 * @return void
1399 */
1400 public function ext_putValueInConf($key, $var) {
1401 // Puts the value $var to the TypoScript value $key in the current lines of the templates.
1402 // If the $key is not found in the template constants field, a new line is inserted in the bottom.
1403 $theValue = ' ' . trim($var);
1404 if (isset($this->objReg[$key])) {
1405 $lineNum = $this->objReg[$key];
1406 $parts = explode('=', $this->raw[$lineNum], 2);
1407 if (count($parts) === 2) {
1408 $parts[1] = $theValue;
1409 }
1410 $this->raw[$lineNum] = implode($parts, '=');
1411 } else {
1412 $this->raw[] = $key . ' =' . $theValue;
1413 }
1414 $this->changed = TRUE;
1415 }
1416
1417 /**
1418 * @param string $key
1419 * @return void
1420 */
1421 public function ext_removeValueInConf($key) {
1422 // Removes the value in the configuration
1423 if (isset($this->objReg[$key])) {
1424 $lineNum = $this->objReg[$key];
1425 unset($this->raw[$lineNum]);
1426 }
1427 $this->changed = TRUE;
1428 }
1429
1430 /**
1431 * @param array $arr
1432 * @param array $settings
1433 * @return array
1434 */
1435 public function ext_depthKeys($arr, $settings) {
1436 $tsbrArray = array();
1437 foreach ($arr as $theK => $theV) {
1438 $theKeyParts = explode('.', $theK);
1439 $depth = '';
1440 $c = count($theKeyParts);
1441 $a = 0;
1442 foreach ($theKeyParts as $p) {
1443 $a++;
1444 $depth .= ($depth ? '.' : '') . $p;
1445 $tsbrArray[$depth] = $c == $a ? $theV : 1;
1446 }
1447 }
1448 // Modify settings
1449 foreach ($tsbrArray as $theK => $theV) {
1450 if ($theV) {
1451 $settings[$theK] = 1;
1452 } else {
1453 unset($settings[$theK]);
1454 }
1455 }
1456 return $settings;
1457 }
1458
1459 /**
1460 * Proces input
1461 *
1462 * @param array $http_post_vars
1463 * @param array $http_post_files (not used anymore)
1464 * @param array $theConstants
1465 * @param array $tplRow Not used
1466 * @return void
1467 */
1468 public function ext_procesInput($http_post_vars, $http_post_files, $theConstants, $tplRow) {
1469 $data = $http_post_vars['data'];
1470 $check = $http_post_vars['check'];
1471 $Wdata = $http_post_vars['Wdata'];
1472 $W2data = $http_post_vars['W2data'];
1473 $W3data = $http_post_vars['W3data'];
1474 $W4data = $http_post_vars['W4data'];
1475 $W5data = $http_post_vars['W5data'];
1476 if (is_array($data)) {
1477 foreach ($data as $key => $var) {
1478 if (isset($theConstants[$key])) {
1479 // If checkbox is set, update the value
1480 if ($this->ext_dontCheckIssetValues || isset($check[$key])) {
1481 // Exploding with linebreak, just to make sure that no multiline input is given!
1482 list($var) = explode(LF, $var);
1483 $typeDat = $this->ext_getTypeData($theConstants[$key]['type']);
1484 switch ($typeDat['type']) {
1485 case 'int':
1486 if ($typeDat['paramstr']) {
1487 $var = MathUtility::forceIntegerInRange($var, $typeDat['params'][0], $typeDat['params'][1]);
1488 } else {
1489 $var = (int)$var;
1490 }
1491 break;
1492 case 'int+':
1493 $var = max(0, (int)$var);
1494 break;
1495 case 'color':
1496 $col = array();
1497 if ($var && !GeneralUtility::inList($this->HTMLcolorList, strtolower($var))) {
1498 $var = preg_replace('/[^A-Fa-f0-9]*/', '', $var);
1499 $useFulHex = strlen($var) > 3;
1500 $col[] = HexDec($var[0]);
1501 $col[] = HexDec($var[1]);
1502 $col[] = HexDec($var[2]);
1503 if ($useFulHex) {
1504 $col[] = HexDec($var[3]);
1505 $col[] = HexDec($var[4]);
1506 $col[] = HexDec($var[5]);
1507 }
1508 $var = substr(('0' . DecHex($col[0])), -1) . substr(('0' . DecHex($col[1])), -1) . substr(('0' . DecHex($col[2])), -1);
1509 if ($useFulHex) {
1510 $var .= substr(('0' . DecHex($col[3])), -1) . substr(('0' . DecHex($col[4])), -1) . substr(('0' . DecHex($col[5])), -1);
1511 }
1512 $var = '#' . strtoupper($var);
1513 }
1514 break;
1515 case 'comment':
1516 if ($var) {
1517 $var = '#';
1518 } else {
1519 $var = '';
1520 }
1521 break;
1522 case 'wrap':
1523 if (isset($Wdata[$key])) {
1524 $var .= '|' . $Wdata[$key];
1525 }
1526 break;
1527 case 'offset':
1528 if (isset($Wdata[$key])) {
1529 $var = (int)$var . ',' . (int)$Wdata[$key];
1530 if (isset($W2data[$key])) {
1531 $var .= ',' . (int)$W2data[$key];
1532 if (isset($W3data[$key])) {
1533 $var .= ',' . (int)$W3data[$key];
1534 if (isset($W4data[$key])) {
1535 $var .= ',' . (int)$W4data[$key];
1536 if (isset($W5data[$key])) {
1537 $var .= ',' . (int)$W5data[$key];
1538 }
1539 }
1540 }
1541 }
1542 }
1543 break;
1544 case 'boolean':
1545 if ($var) {
1546 $var = $typeDat['paramstr'] ? $typeDat['paramstr'] : 1;
1547 }
1548 break;
1549 }
1550 if ($this->ext_printAll || (string)$theConstants[$key]['value'] !== (string)$var) {
1551 // Put value in, if changed.
1552 $this->ext_putValueInConf($key, $var);
1553 }
1554 // Remove the entry because it has been "used"
1555 unset($check[$key]);
1556 } else {
1557 $this->ext_removeValueInConf($key);
1558 }
1559 }
1560 }
1561 }
1562 // Remaining keys in $check indicates fields that are just clicked "on" to be edited.
1563 // Therefore we get the default value and puts that in the template as a start...
1564 if (!$this->ext_dontCheckIssetValues && is_array($check)) {
1565 foreach ($check as $key => $var) {
1566 if (isset($theConstants[$key])) {
1567 $dValue = $theConstants[$key]['default_value'];
1568 $this->ext_putValueInConf($key, $dValue);
1569 }
1570 }
1571 }
1572 }
1573
1574 /**
1575 * @param int $id
1576 * @param string $perms_clause
1577 * @return array
1578 */
1579 public function ext_prevPageWithTemplate($id, $perms_clause) {
1580 $rootLine = BackendUtility::BEgetRootLine($id, $perms_clause ? ' AND ' . $perms_clause : '');
1581 foreach ($rootLine as $p) {
1582 if ($this->ext_getFirstTemplate($p['uid'])) {
1583 return $p;
1584 }
1585 }
1586 return array();
1587 }
1588
1589 /**
1590 * @return array
1591 */
1592 protected function getRootLine() {
1593 return isset($GLOBALS['rootLine']) ? $GLOBALS['rootLine'] : array();
1594 }
1595
1596 /**
1597 * @return DatabaseConnection
1598 */
1599 protected function getDatabaseConnection() {
1600 return $GLOBALS['TYPO3_DB'];
1601 }
1602
1603 /**
1604 * @return LanguageService
1605 */
1606 protected function getLanguageService() {
1607 return $GLOBALS['LANG'];
1608 }
1609
1610 /**
1611 * @return DocumentTemplate
1612 */
1613 protected function getDocumentTemplate() {
1614 return $GLOBALS['TBE_TEMPLATE'];
1615 }
1616
1617 }