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