[FEATURE] Intgrate CSS class "lead" for introductions
[Packages/TYPO3.CMS.git] / typo3 / sysext / cshmanual / Classes / Controller / HelpModuleController.php
1 <?php
2 namespace TYPO3\CMS\Cshmanual\Controller;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 1999-2013 Kasper Skårhøj (kasperYYYY@typo3.com)
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the textfile GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29
30 /**
31 * Script Class for rendering the Context Sensitive Help documents, either the single display in the small pop-up window or the full-table view in the larger window.
32 *
33 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
34 */
35 class HelpModuleController {
36
37 /**
38 * @todo Define visibility
39 */
40 public $allowedHTML = '<strong><em><b><i>';
41
42 /**
43 * For these vars, see init()
44 * If set access to fields and tables is checked. Should be done for TRUE database tables.
45 *
46 * @todo Define visibility
47 */
48 public $limitAccess;
49
50 /**
51 * The "table" key
52 *
53 * @todo Define visibility
54 */
55 public $table;
56
57 /**
58 * The "field" key
59 *
60 * @todo Define visibility
61 */
62 public $field;
63
64 /**
65 * Key used to point to the right CSH resource
66 * In simple cases, is equal to $table
67 *
68 * @var string
69 */
70 protected $mainKey;
71
72 /**
73 * Internal, static: GPvar
74 * Table/Field id
75 *
76 * @todo Define visibility
77 */
78 public $tfID;
79
80 /**
81 * Back (previous tfID)
82 *
83 * @todo Define visibility
84 */
85 public $back;
86
87 /**
88 * If set, then in TOC mode the FULL manual will be printed as well!
89 *
90 * @todo Define visibility
91 */
92 public $renderALL;
93
94 /**
95 * Content accumulation
96 *
97 * @todo Define visibility
98 */
99 public $content;
100
101 /**
102 * Glossary words
103 *
104 * @todo Define visibility
105 */
106 public $glossaryWords;
107
108 /**
109 * Initialize the class for various input etc.
110 *
111 * @return void
112 * @todo Define visibility
113 */
114 public function init() {
115 // Setting GPvars:
116 $this->tfID = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('tfID');
117 // Sanitizes the tfID using whitelisting.
118 if (!preg_match('/^[a-zA-Z0-9_\\-\\.\\*]*$/', $this->tfID)) {
119 $this->tfID = '';
120 }
121 $this->back = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('back');
122 $this->renderALL = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('renderALL');
123 // Set internal table/field to the parts of "tfID" incoming var.
124 $identifierParts = explode('.', $this->tfID);
125 // The table is the first item
126 $this->table = array_shift($identifierParts);
127 $this->mainKey = $this->table;
128 // The field is the second one
129 $this->field = array_shift($identifierParts);
130 // There may be extra parts for FlexForms
131 if (count($identifierParts) > 0) {
132 // There's at least one extra part
133 $extraIdentifierInformation = array();
134 $extraIdentifierInformation[] = array_shift($identifierParts);
135 // If the ds_pointerField contains a comma, it means the choice of FlexForm DS
136 // is determined by 2 parameters. In this case we have an extra identifier part
137 if (strpos($GLOBALS['TCA'][$this->table]['columns'][$this->field]['config']['ds_pointerField'], ',') !== FALSE) {
138 $extraIdentifierInformation[] = array_shift($identifierParts);
139 }
140 // The remaining parts make up the FlexForm field name itself
141 // (reassembled with dots)
142 $flexFormField = implode('.', $identifierParts);
143 // Assemble a different main key and switch field to use FlexForm field name
144 $this->mainKey .= '.' . $this->field;
145 foreach ($extraIdentifierInformation as $extraKey) {
146 $this->mainKey .= '.' . $extraKey;
147 }
148 $this->field = $flexFormField;
149 }
150 // limitAccess is checked if the $this->table really IS a table (and if the user is NOT a translator who should see all!)
151 $showAllToUser = \TYPO3\CMS\Backend\Utility\BackendUtility::isModuleSetInTBE_MODULES('txllxmltranslateM1') && $GLOBALS['BE_USER']->check('modules', 'txllxmltranslateM1');
152 $this->limitAccess = isset($GLOBALS['TCA'][$this->table]) ? !$showAllToUser : FALSE;
153 $GLOBALS['LANG']->includeLLFile('EXT:lang/locallang_view_help.xlf', 1);
154 }
155
156 /**
157 * Main function, rendering the display
158 *
159 * @return void
160 * @todo Define visibility
161 */
162 public function main() {
163 // Start HTML output accumulation:
164 $GLOBALS['TBE_TEMPLATE']->divClass = 'typo3-view-help';
165 $this->content .= $GLOBALS['TBE_TEMPLATE']->startPage($GLOBALS['LANG']->getLL('title'));
166 if ($this->field == '*') {
167 // If ALL fields is supposed to be shown:
168 $this->createGlossaryIndex();
169 $this->content .= $this->render_Table($this->mainKey);
170 } elseif ($this->tfID) {
171 // ... otherwise show only single field:
172 $this->createGlossaryIndex();
173 $this->content .= $this->render_Single($this->mainKey, $this->field);
174 } else {
175 // Render Table Of Contents if nothing else:
176 $this->content .= $this->render_TOC();
177 }
178 // End page:
179 $this->content .= '<br/>';
180 $this->content .= $GLOBALS['TBE_TEMPLATE']->endPage();
181 }
182
183 /**
184 * Outputting the accumulated content to screen
185 *
186 * @return void
187 * @todo Define visibility
188 */
189 public function printContent() {
190 echo $this->content;
191 }
192
193 /************************************
194 * Rendering main modes
195 ************************************/
196
197 /**
198 * Creates Table Of Contents and possibly "Full Manual" mode if selected.
199 *
200 * @return string HTML content
201 * @todo Define visibility
202 */
203 public function render_TOC() {
204 // Initialize:
205 $CSHkeys = array_flip(array_keys($GLOBALS['TCA_DESCR']));
206 $TCAkeys = array_keys($GLOBALS['TCA']);
207 $outputSections = array();
208 $tocArray = array();
209 // TYPO3 Core Features:
210 $GLOBALS['LANG']->loadSingleTableDescription('xMOD_csh_corebe');
211 $this->render_TOC_el('xMOD_csh_corebe', 'core', $outputSections, $tocArray, $CSHkeys);
212 // Backend Modules:
213 $loadModules = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Module\\ModuleLoader');
214 $loadModules->load($GLOBALS['TBE_MODULES']);
215 foreach ($loadModules->modules as $mainMod => $info) {
216 $cshKey = '_MOD_' . $mainMod;
217 if ($CSHkeys[$cshKey]) {
218 $GLOBALS['LANG']->loadSingleTableDescription($cshKey);
219 $this->render_TOC_el($cshKey, 'modules', $outputSections, $tocArray, $CSHkeys);
220 }
221 if (is_array($info['sub'])) {
222 foreach ($info['sub'] as $subMod => $subInfo) {
223 $cshKey = '_MOD_' . $mainMod . '_' . $subMod;
224 if ($CSHkeys[$cshKey]) {
225 $GLOBALS['LANG']->loadSingleTableDescription($cshKey);
226 $this->render_TOC_el($cshKey, 'modules', $outputSections, $tocArray, $CSHkeys);
227 }
228 }
229 }
230 }
231 // Database Tables:
232 foreach ($TCAkeys as $table) {
233 // Load descriptions for table $table
234 $GLOBALS['LANG']->loadSingleTableDescription($table);
235 if (is_array($GLOBALS['TCA_DESCR'][$table]['columns']) && $GLOBALS['BE_USER']->check('tables_select', $table)) {
236 $this->render_TOC_el($table, 'tables', $outputSections, $tocArray, $CSHkeys);
237 }
238 }
239 // Extensions
240 foreach ($CSHkeys as $cshKey => $value) {
241 if (\TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($cshKey, 'xEXT_') && !isset($GLOBALS['TCA'][$cshKey])) {
242 $GLOBALS['LANG']->loadSingleTableDescription($cshKey);
243 $this->render_TOC_el($cshKey, 'extensions', $outputSections, $tocArray, $CSHkeys);
244 }
245 }
246 // Glossary
247 foreach ($CSHkeys as $cshKey => $value) {
248 if (\TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($cshKey, 'xGLOSSARY_') && !isset($GLOBALS['TCA'][$cshKey])) {
249 $GLOBALS['LANG']->loadSingleTableDescription($cshKey);
250 $this->render_TOC_el($cshKey, 'glossary', $outputSections, $tocArray, $CSHkeys);
251 }
252 }
253 // Other:
254 foreach ($CSHkeys as $cshKey => $value) {
255 if (!\TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($cshKey, '_MOD_') && !isset($GLOBALS['TCA'][$cshKey])) {
256 $GLOBALS['LANG']->loadSingleTableDescription($cshKey);
257 $this->render_TOC_el($cshKey, 'other', $outputSections, $tocArray, $CSHkeys);
258 }
259 }
260
261 // COMPILE output:
262 $output = '';
263 $output .= '<h1>' . $GLOBALS['LANG']->getLL('manual_title', TRUE) . '</h1>';
264 $output .= '<p class="lead">' . $GLOBALS['LANG']->getLL('description', TRUE) . '</p>';
265
266 $output .= '<h2>' . $GLOBALS['LANG']->getLL('TOC', TRUE) . '</h2>' . $this->render_TOC_makeTocList($tocArray);
267 if (!$this->renderALL) {
268 $output .= '
269 <br/>
270 <p class="c-nav"><a href="mod.php?M=help_cshmanual&renderALL=1">' . $GLOBALS['LANG']->getLL('full_manual', TRUE) . '</a></p>';
271 }
272 if ($this->renderALL) {
273 $output .= '
274
275 <h2>' . $GLOBALS['LANG']->getLL('full_manual_chapters', TRUE) . '</h2>' . implode('
276
277
278 <!-- NEW SECTION: -->
279 ', $outputSections);
280 }
281 $output .= '<hr /><p class="manual-title">' . \TYPO3\CMS\Backend\Utility\BackendUtility::TYPO3_copyRightNotice() . '</p>';
282 return $output;
283 }
284
285 /**
286 * Creates a TOC list element and renders corresponding HELP content if "renderALL" mode is set.
287 *
288 * @param string $table CSH key / Table name
289 * @param string $tocCat TOC category keyword: "core", "modules", "tables", "other
290 * @param array $outputSections Array for accumulation of rendered HELP Content (in "renderALL" mode). Passed by reference!
291 * @param array $tocArray TOC array; Here TOC index elements are created. Passed by reference!
292 * @param array $CSHkeys CSH keys array. Every item rendered will be unset in this array so finally we can see what CSH keys are not processed yet. Passed by reference!
293 * @return void
294 * @todo Define visibility
295 */
296 public function render_TOC_el($table, $tocCat, &$outputSections, &$tocArray, &$CSHkeys) {
297 // Render full manual right here!
298 if ($this->renderALL) {
299 $outputSections[$table] = $this->render_Table($table);
300 if ($outputSections[$table]) {
301 $outputSections[$table] = '
302
303 <!-- New CSHkey/Table: ' . $table . ' -->
304 <p class="c-nav"><a name="ANCHOR_' . $table . '" href="#">' . $GLOBALS['LANG']->getLL('to_top', TRUE) . '</a></p>
305 <h2>' . $this->getTableFieldLabel($table) . '</h2>
306
307 ' . $outputSections[$table];
308 $tocArray[$tocCat][$table] = '<a href="#ANCHOR_' . $table . '">' . $this->getTableFieldLabel($table) . '</a>';
309 } else {
310 unset($outputSections[$table]);
311 }
312 } else {
313 // Only TOC:
314 $tocArray[$tocCat][$table] = '<p><a href="mod.php?M=help_cshmanual&tfID=' . rawurlencode(($table . '.*')) . '">' . $this->getTableFieldLabel($table) . '</a></p>';
315 }
316 // Unset CSH key:
317 unset($CSHkeys[$table]);
318 }
319
320 /**
321 * Renders the TOC index as a HTML bullet list from TOC array
322 *
323 * @param array $tocArray ToC Array.
324 * @return string HTML bullet list for index.
325 * @todo Define visibility
326 */
327 public function render_TOC_makeTocList($tocArray) {
328 // The Various manual sections:
329 $keys = explode(',', 'core,modules,tables,extensions,glossary,other');
330 // Create TOC bullet list:
331 $output = '';
332 foreach ($keys as $tocKey) {
333 if (is_array($tocArray[$tocKey])) {
334 $output .= '
335 <li>' . $GLOBALS['LANG']->getLL(('TOC_' . $tocKey), TRUE) . '
336 <ul>
337 <li>' . implode('</li>
338 <li>', $tocArray[$tocKey]) . '</li>
339 </ul>
340 </li>';
341 }
342 }
343 // Compile TOC:
344 $output = '
345
346 <!-- TOC: -->
347 <div class="c-toc">
348 <ul>
349 ' . $output . '
350 </ul>
351 </div>';
352 return $output;
353 }
354
355 /**
356 * Render CSH for a full cshKey/table
357 *
358 * @param string $key Full CSH key (may be different from table name)
359 * @param string $table CSH key / table name
360 * @return string HTML output
361 * @todo Define visibility
362 */
363 public function render_Table($key, $table = NULL) {
364 $output = '';
365 // Take default key if not explicitly specified
366 if ($table === NULL) {
367 $table = $key;
368 }
369 // Load descriptions for table $table
370 $GLOBALS['LANG']->loadSingleTableDescription($key);
371 if (is_array($GLOBALS['TCA_DESCR'][$key]['columns']) && (!$this->limitAccess || $GLOBALS['BE_USER']->check('tables_select', $table))) {
372 // Initialize variables:
373 $parts = array();
374 // Reserved for header of table
375 $parts[0] = '';
376 // Traverse table columns as listed in TCA_DESCR
377 foreach ($GLOBALS['TCA_DESCR'][$key]['columns'] as $field => $_) {
378 $fieldValue = isset($GLOBALS['TCA'][$key]) && strcmp($field, '') ? $GLOBALS['TCA'][$key]['columns'][$field] : array();
379 if (is_array($fieldValue) && (!$this->limitAccess || !$fieldValue['exclude'] || $GLOBALS['BE_USER']->check('non_exclude_fields', $table . ':' . $field))) {
380 if (!$field) {
381 // Header
382 $parts[0] = $this->printItem($key, '', 1);
383 } else {
384 // Field
385 $parts[] = $this->printItem($key, $field, 1);
386 }
387 }
388 }
389 if (!$parts[0]) {
390 unset($parts[0]);
391 }
392 $output .= implode('<br />', $parts);
393 }
394 // Substitute glossary words:
395 $output = $this->substituteGlossaryWords($output);
396 // TOC link:
397 if (!$this->renderALL) {
398 $tocLink = '<p class="c-nav"><a href="mod.php?M=help_cshmanual">' . $GLOBALS['LANG']->getLL('goToToc', TRUE) . '</a></p>';
399 $output = $tocLink . '
400 <br/>' . $output . '
401 <br />' . $tocLink;
402 }
403 return $output;
404 }
405
406 /**
407 * Renders CSH for a single field.
408 *
409 * @param string $key CSH key / table name
410 * @param string $field Sub key / field name
411 * @return string HTML output
412 * @todo Define visibility
413 */
414 public function render_Single($key, $field) {
415 $output = '';
416 // Load the description field
417 $GLOBALS['LANG']->loadSingleTableDescription($key);
418 // Render single item
419 $output .= $this->printItem($key, $field);
420 // Substitute glossary words:
421 $output = $this->substituteGlossaryWords($output);
422 // Link to Full table description and TOC:
423 $getLLKey = $this->limitAccess ? 'fullDescription' : 'fullDescription_module';
424 $output .= '<br />
425 <p class="c-nav"><a href="mod.php?M=help_cshmanual&tfID=' . rawurlencode(($key . '.*')) . '">' . $GLOBALS['LANG']->getLL($getLLKey, TRUE) . '</a></p>
426 <p class="c-nav"><a href="mod.php?M=help_cshmanual">' . $GLOBALS['LANG']->getLL('goToToc', TRUE) . '</a></p>';
427 return $output;
428 }
429
430 /************************************
431 * Rendering CSH items
432 ************************************/
433
434 /**
435 * Make seeAlso links from $value
436 *
437 * @param string $value See-also input codes
438 * @param string $anchorTable If $anchorTable is set to a tablename, then references to this table will be made as anchors, not URLs.
439 * @return string See-also links HTML
440 * @todo Define visibility
441 */
442 public function make_seeAlso($value, $anchorTable = '') {
443 // Split references by comma or linebreak
444 $items = preg_split('/[,' . LF . ']/', $value);
445 $lines = array();
446 foreach ($items as $val) {
447 $val = trim($val);
448 if ($val) {
449 $iP = explode(':', $val);
450 $iPUrl = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode('|', $val);
451 // URL reference:
452 if (substr($iPUrl[1], 0, 4) == 'http') {
453 $lines[] = '<a href="' . htmlspecialchars($iPUrl[1]) . '" target="_blank"><em>' . htmlspecialchars($iPUrl[0]) . '</em></a>';
454 } elseif (substr($iPUrl[1], 0, 5) == 'FILE:') {
455 $fileName = \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName(substr($iPUrl[1], 5), 1, 1);
456 if ($fileName && @is_file($fileName)) {
457 $fileName = '../' . substr($fileName, strlen(PATH_site));
458 $lines[] = '<a href="' . htmlspecialchars($fileName) . '" target="_blank"><em>' . htmlspecialchars($iPUrl[0]) . '</em></a>';
459 }
460 } else {
461 // "table" reference
462 if (!isset($GLOBALS['TCA'][$iP[0]]) || (!$iP[1] || is_array($GLOBALS['TCA'][$iP[0]]['columns'][$iP[1]])) && (!$this->limitAccess || $GLOBALS['BE_USER']->check('tables_select', $iP[0]) && (!$iP[1] || !$GLOBALS['TCA'][$iP[0]]['columns'][$iP[1]]['exclude'] || $GLOBALS['BE_USER']->check('non_exclude_fields', $iP[0] . ':' . $iP[1])))) {
463 // Checking read access:
464 if (isset($GLOBALS['TCA_DESCR'][$iP[0]])) {
465 // Make see-also link:
466 $href = $this->renderALL || $anchorTable && $iP[0] == $anchorTable ? '#' . implode('.', $iP) : 'mod.php?M=help_cshmanual&tfID=' . rawurlencode(implode('.', $iP)) . '&back=' . $this->tfID;
467 $label = $this->getTableFieldLabel($iP[0], $iP[1], ' / ');
468 $lines[] = '<a href="' . htmlspecialchars($href) . '">' . htmlspecialchars($label) . '</a>';
469 }
470 }
471 }
472 }
473 }
474 return implode('<br />', $lines);
475 }
476
477 /**
478 * Will return an image tag with description in italics.
479 *
480 * @param string $images Image file reference (list of)
481 * @param string $descr Description string (divided for each image by line break)
482 * @return string Image HTML codes
483 * @todo Define visibility
484 */
485 public function printImage($images, $descr) {
486 $code = '';
487 // Splitting:
488 $imgArray = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $images, TRUE);
489 if (count($imgArray)) {
490 $descrArray = explode(LF, $descr, count($imgArray));
491 foreach ($imgArray as $k => $image) {
492 $descr = $descrArray[$k];
493 $absImagePath = \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName($image, 1, 1);
494 if ($absImagePath && @is_file($absImagePath)) {
495 $imgFile = substr($absImagePath, strlen(PATH_site));
496 $imgInfo = @getimagesize($absImagePath);
497 if (is_array($imgInfo)) {
498 $imgFile = '../' . $imgFile;
499 $code .= '<br /><img src="' . $imgFile . '" ' . $imgInfo[3] . ' class="c-inlineimg" alt="" /><br />
500 ';
501 $code .= '<p><em>' . htmlspecialchars($descr) . '</em></p>
502 ';
503 } else {
504 $code .= '<div style="background-color: red; border: 1px solid black; color: white;">NOT AN IMAGE: ' . $imgFile . '</div>';
505 }
506 } else {
507 $code .= '<div style="background-color: red; border: 1px solid black; color: white;">IMAGE FILE NOT FOUND: ' . $image . '</div>';
508 }
509 }
510 }
511 return $code;
512 }
513
514 /**
515 * Returns header HTML content
516 *
517 * @param string $str Header text
518 * @param integer $type Header type (1, 0)
519 * @return string The HTML for the header.
520 * @todo Define visibility
521 */
522 public function headerLine($str, $type = 0) {
523 switch ($type) {
524 case 1:
525 $str = '<h2 class="t3-row-header">' . htmlspecialchars($str) . '</h2>
526 ';
527 break;
528 case 0:
529 $str = '<h3 class="divider">' . htmlspecialchars($str) . '</h3>
530 ';
531 break;
532 }
533 return $str;
534 }
535
536 /**
537 * Returns prepared content
538 *
539 * @param string $str Content to format.
540 * @return string Formatted content.
541 * @todo Define visibility
542 */
543 public function prepareContent($str) {
544 return '<p>' . nl2br(trim(strip_tags($str, $this->allowedHTML))) . '</p>
545 ';
546 }
547
548 /**
549 * Prints a single $table/$field information piece
550 * If $anchors is set, then seeAlso references to the same table will be page-anchors, not links.
551 *
552 * @param string $key CSH key / table name
553 * @param string $field Sub key / field name
554 * @param boolean $anchors If anchors is to be shown.
555 * @return string HTML content
556 * @todo Define visibility
557 */
558 public function printItem($key, $field, $anchors = FALSE) {
559 $out = '';
560 if ($key && (!$field || is_array($GLOBALS['TCA_DESCR'][$key]['columns'][$field]))) {
561 // Make seeAlso references.
562 $seeAlsoRes = $this->make_seeAlso($GLOBALS['TCA_DESCR'][$key]['columns'][$field]['seeAlso'], $anchors ? $key : '');
563 // Making item:
564 $out = '<a name="' . $key . '.' . $field . '"></a>' . $this->headerLine($this->getTableFieldLabel($key, $field), 1) . $this->prepareContent($GLOBALS['TCA_DESCR'][$key]['columns'][$field]['description']) . ($GLOBALS['TCA_DESCR'][$key]['columns'][$field]['details'] ? $this->headerLine(($GLOBALS['LANG']->getLL('details') . ':')) . $this->prepareContent($GLOBALS['TCA_DESCR'][$key]['columns'][$field]['details']) : '') . ($GLOBALS['TCA_DESCR'][$key]['columns'][$field]['syntax'] ? $this->headerLine(($GLOBALS['LANG']->getLL('syntax') . ':')) . $this->prepareContent($GLOBALS['TCA_DESCR'][$key]['columns'][$field]['syntax']) : '') . ($GLOBALS['TCA_DESCR'][$key]['columns'][$field]['image'] ? $this->printImage($GLOBALS['TCA_DESCR'][$key]['columns'][$field]['image'], $GLOBALS['TCA_DESCR'][$key]['columns'][$field]['image_descr']) : '') . ($GLOBALS['TCA_DESCR'][$key]['columns'][$field]['seeAlso'] && $seeAlsoRes ? $this->headerLine(($GLOBALS['LANG']->getLL('seeAlso') . ':')) . '<p>' . $seeAlsoRes . '</p>' : '') . ($this->back ? '<br /><p><a href="' . htmlspecialchars(('mod.php?M=help_cshmanual&tfID=' . rawurlencode($this->back))) . '" class="typo3-goBack">' . htmlspecialchars($GLOBALS['LANG']->getLL('goBack')) . '</a></p>' : '') . '<br />';
565 }
566 return $out;
567 }
568
569 /**
570 * Returns labels for a given field in a given structure
571 *
572 * @param string $key CSH key / table name
573 * @param string $field Sub key / field name
574 * @return array Table and field labels in a numeric array
575 * @todo Define visibility
576 */
577 public function getTableFieldNames($key, $field) {
578 $GLOBALS['LANG']->loadSingleTableDescription($key);
579 // Define the label for the key
580 $keyName = $key;
581 if (is_array($GLOBALS['TCA_DESCR'][$key]['columns']['']) && isset($GLOBALS['TCA_DESCR'][$key]['columns']['']['alttitle'])) {
582 // If there's an alternative title, use it
583 $keyName = $GLOBALS['TCA_DESCR'][$key]['columns']['']['alttitle'];
584 } elseif (isset($GLOBALS['TCA'][$key])) {
585 // Otherwise, if it's a table, use its title
586 $keyName = $GLOBALS['TCA'][$key]['ctrl']['title'];
587 } else {
588 // If no title was found, make sure to remove any "_MOD_"
589 $keyName = preg_replace('/^_MOD_/', '', $key);
590 }
591 // Define the label for the field
592 $fieldName = $field;
593 if (is_array($GLOBALS['TCA_DESCR'][$key]['columns'][$field]) && isset($GLOBALS['TCA_DESCR'][$key]['columns'][$field]['alttitle'])) {
594 // If there's an alternative title, use it
595 $fieldName = $GLOBALS['TCA_DESCR'][$key]['columns'][$field]['alttitle'];
596 } elseif (isset($GLOBALS['TCA'][$key]) && isset($GLOBALS['TCA'][$key]['columns'][$field])) {
597 // Otherwise, if it's a table, use its title
598 $fieldName = $GLOBALS['TCA'][$key]['columns'][$field]['label'];
599 }
600 return array($keyName, $fieldName);
601 }
602
603 /**
604 * Returns composite label for table/field
605 *
606 * @param string $key CSH key / table name
607 * @param string $field Sub key / field name
608 * @param string $mergeToken Token to merge the two strings with
609 * @return string Labels joined with merge token
610 * @see getTableFieldNames()
611 * @todo Define visibility
612 */
613 public function getTableFieldLabel($key, $field = '', $mergeToken = ': ') {
614 $tableName = '';
615 $fieldName = '';
616 // Get table / field parts:
617 list($tableName, $fieldName) = $this->getTableFieldNames($key, $field);
618 // Create label:
619 $labelString = $GLOBALS['LANG']->sL($tableName) . ($field ? $mergeToken . rtrim(trim($GLOBALS['LANG']->sL($fieldName)), ':') : '');
620 return $labelString;
621 }
622
623 /******************************
624 * Glossary related
625 ******************************/
626
627 /**
628 * Creates glossary index in $this->glossaryWords
629 * Glossary is cached in cache_hash cache and so will be updated only when cache is cleared.
630 *
631 * @return void
632 * @todo Define visibility
633 */
634 public function createGlossaryIndex() {
635 // Create hash string and try to retrieve glossary array:
636 $hash = md5('typo3/mod.php?M=help_cshmanual:glossary');
637 list($this->glossaryWords, $this->substWords) = unserialize(\TYPO3\CMS\Backend\Utility\BackendUtility::getHash($hash));
638 // Generate glossary words if not found:
639 if (!is_array($this->glossaryWords)) {
640 // Initialize:
641 $this->glossaryWords = array();
642 $this->substWords = array();
643 $CSHkeys = array_flip(array_keys($GLOBALS['TCA_DESCR']));
644 // Glossary
645 foreach ($CSHkeys as $cshKey => $value) {
646 if (\TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($cshKey, 'xGLOSSARY_') && !isset($GLOBALS['TCA'][$cshKey])) {
647 $GLOBALS['LANG']->loadSingleTableDescription($cshKey);
648 if (is_array($GLOBALS['TCA_DESCR'][$cshKey]['columns'])) {
649 // Traverse table columns as listed in TCA_DESCR
650 foreach ($GLOBALS['TCA_DESCR'][$cshKey]['columns'] as $field => $data) {
651 if ($field) {
652 $this->glossaryWords[$cshKey . '.' . $field] = array(
653 'title' => trim($data['alttitle'] ? $data['alttitle'] : $cshKey),
654 'description' => str_replace('%22', '%23%23%23', rawurlencode($data['description']))
655 );
656 }
657 }
658 }
659 }
660 }
661 // First, create unique list of words:
662 foreach ($this->glossaryWords as $key => $value) {
663 // Making word lowercase in order to filter out same words in different cases.
664 $word = strtolower($value['title']);
665 if ($word !== '') {
666 $this->substWords[$word] = $value;
667 $this->substWords[$word]['key'] = $key;
668 }
669 }
670 krsort($this->substWords);
671 \TYPO3\CMS\Backend\Utility\BackendUtility::storeHash($hash, serialize(array($this->glossaryWords, $this->substWords)), 'Glossary');
672 }
673 }
674
675 /**
676 * Processing of all non-HTML content in the output
677 * Will be done by a call-back to ->substituteGlossaryWords_htmlcleaner_callback()
678 *
679 * @param string $code Input HTML code
680 * @return string Output HTML code
681 * @todo Define visibility
682 */
683 public function substituteGlossaryWords($code) {
684 $htmlParser = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\CMS\Core\Html\HtmlParser');
685 $htmlParser->pObj = $this;
686 $code = $htmlParser->HTMLcleaner($code, array(), 1);
687 return $code;
688 }
689
690 /**
691 * Substituting glossary words in the CSH
692 * This is a call-back function from "class local_t3lib_parsehtml
693 * extends \TYPO3\CMS\Core\Html\HtmlParser", see top of this script
694 *
695 * @param string $code Input HTML string
696 * @return string HTML with substituted words in.
697 * @todo Define visibility
698 */
699 public function substituteGlossaryWords_htmlcleaner_callback($code) {
700 if (is_array($this->substWords) && count($this->substWords) && strlen(trim($code))) {
701 // Substitute words:
702 foreach ($this->substWords as $wordKey => $wordSet) {
703 // quoteMeta used so special chars (which should not occur though) in words will not break the regex. Seemed to work (- kasper)
704 $parts = preg_split('/( |[\\(])(' . quoteMeta($wordSet['title']) . ')([\\.\\!\\)\\?\\:\\,]+| )/i', ' ' . $code . ' ', 2, PREG_SPLIT_DELIM_CAPTURE);
705 if (count($parts) == 5) {
706 $parts[2] = '<a class="glossary-term" href="' . htmlspecialchars(('mod.php?M=help_cshmanual&tfID=' . rawurlencode($wordSet['key']) . '&back=' . $this->tfID)) . '" title="' . rawurlencode(htmlspecialchars(\TYPO3\CMS\Core\Utility\GeneralUtility::fixed_lgd_cs(rawurldecode($wordSet['description']), 80))) . '">' . htmlspecialchars($parts[2]) . '</a>';
707 $code = substr(implode('', $parts), 1, -1);
708 // Disable entry so it doesn't get used next time:
709 unset($this->substWords[$wordKey]);
710 }
711 }
712 $code = str_replace('###', '&quot;', rawurldecode($code));
713 }
714 return $code;
715 }
716
717 }
718
719 ?>