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