43fbf35d9d113ea6de2c5ca8471e04184bd43dd4
[Packages/TYPO3.CMS.git] / typo3 / sysext / cshmanual / Classes / Controller / HelpModuleController.php
1 <?php
2 namespace TYPO3\CMS\Cshmanual\Controller;
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\Core\Utility\GeneralUtility;
19
20 /**
21 * Script Class for rendering the Context Sensitive Help documents,
22 * either the single display in the small pop-up window or the full-table view in the larger window.
23 */
24 class HelpModuleController {
25
26 /**
27 * @var string
28 */
29 public $allowedHTML = '<strong><em><b><i>';
30
31 /**
32 * For these vars, see init()
33 * If set access to fields and tables is checked. Should be done for TRUE database tables.
34 *
35 * @var bool
36 */
37 public $limitAccess;
38
39 /**
40 * The "table" key
41 *
42 * @var string
43 */
44 public $table;
45
46 /**
47 * The "field" key
48 *
49 * @var string
50 */
51 public $field;
52
53 /**
54 * Key used to point to the right CSH resource
55 * In simple cases, is equal to $table
56 *
57 * @var string
58 */
59 protected $mainKey;
60
61 /**
62 * Internal, static: GPvar
63 * Table/Field id
64 *
65 * @var string
66 */
67 public $tfID;
68
69 /**
70 * Back (previous tfID)
71 *
72 * @var string
73 */
74 public $back;
75
76 /**
77 * If set, then in TOC mode the FULL manual will be printed as well!
78 *
79 * @var bool
80 */
81 public $renderALL;
82
83 /**
84 * Content accumulation
85 *
86 * @var string
87 */
88 public $content;
89
90 /**
91 * URL to help module
92 *
93 * @var string
94 */
95 protected $moduleUrl;
96
97 /**
98 * Initialize the class for various input etc.
99 *
100 * @return void
101 */
102 public function init() {
103 $this->moduleUrl = BackendUtility::getModuleUrl('help_cshmanual');
104 // Setting GPvars:
105 $this->tfID = GeneralUtility::_GP('tfID');
106 // Sanitizes the tfID using whitelisting.
107 if (!preg_match('/^[a-zA-Z0-9_\\-\\.\\*]*$/', $this->tfID)) {
108 $this->tfID = '';
109 }
110 $this->back = GeneralUtility::_GP('back');
111 $this->renderALL = GeneralUtility::_GP('renderALL');
112 // Set internal table/field to the parts of "tfID" incoming var.
113 $identifierParts = explode('.', $this->tfID);
114 // The table is the first item
115 $this->table = array_shift($identifierParts);
116 $this->mainKey = $this->table;
117 // The field is the second one
118 $this->field = array_shift($identifierParts);
119 // There may be extra parts for FlexForms
120 if (!empty($identifierParts)) {
121 // There's at least one extra part
122 $extraIdentifierInformation = array();
123 $extraIdentifierInformation[] = array_shift($identifierParts);
124 // If the ds_pointerField contains a comma, it means the choice of FlexForm DS
125 // is determined by 2 parameters. In this case we have an extra identifier part
126 if (strpos($GLOBALS['TCA'][$this->table]['columns'][$this->field]['config']['ds_pointerField'], ',') !== FALSE) {
127 $extraIdentifierInformation[] = array_shift($identifierParts);
128 }
129 // The remaining parts make up the FlexForm field name itself
130 // (reassembled with dots)
131 $flexFormField = implode('.', $identifierParts);
132 // Assemble a different main key and switch field to use FlexForm field name
133 $this->mainKey .= '.' . $this->field;
134 foreach ($extraIdentifierInformation as $extraKey) {
135 $this->mainKey .= '.' . $extraKey;
136 }
137 $this->field = $flexFormField;
138 }
139 // limitAccess is checked if the $this->table really IS a table (and if the user is NOT a translator who should see all!)
140 $showAllToUser = BackendUtility::isModuleSetInTBE_MODULES('txllxmltranslateM1') && $GLOBALS['BE_USER']->check('modules', 'txllxmltranslateM1');
141 $this->limitAccess = isset($GLOBALS['TCA'][$this->table]) ? !$showAllToUser : FALSE;
142 $this->getLanguageService()->includeLLFile('EXT:lang/locallang_view_help.xlf');
143 }
144
145 /**
146 * Main function, rendering the display
147 *
148 * @return void
149 */
150 public function main() {
151 if ($this->field == '*') {
152 // If ALL fields is supposed to be shown:
153 $this->content .= $this->render_Table($this->mainKey);
154 } elseif ($this->tfID) {
155 // ... otherwise show only single field:
156 $this->content .= $this->render_Single($this->mainKey, $this->field);
157 } else {
158 // Render Table Of Contents if nothing else:
159 $this->content .= $this->render_TOC();
160 }
161
162 $this->doc = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Template\DocumentTemplate::class);
163 $this->doc->backPath = $GLOBALS['BACK_PATH'];
164 $this->doc->setModuleTemplate('EXT:cshmanual/Resources/Private/Templates/cshmanual.html');
165
166 $markers = array('CONTENT' => $this->content);
167
168 $this->content = $this->doc->moduleBody(array(), array(), $markers);
169 $this->content = $this->doc->render($this->getLanguageService()->getLL('title'), $this->content);
170 }
171
172 /**
173 * Outputting the accumulated content to screen
174 *
175 * @return void
176 */
177 public function printContent() {
178 echo $this->content;
179 }
180
181 /************************************
182 * Rendering main modes
183 ************************************/
184
185 /**
186 * Creates Table Of Contents and possibly "Full Manual" mode if selected.
187 *
188 * @return string HTML content
189 */
190 public function render_TOC() {
191 // Initialize:
192 $CSHkeys = array_flip(array_keys($GLOBALS['TCA_DESCR']));
193 $TCAkeys = array_keys($GLOBALS['TCA']);
194 $outputSections = array();
195 $tocArray = array();
196 // TYPO3 Core Features:
197 $this->getLanguageService()->loadSingleTableDescription('xMOD_csh_corebe');
198 $this->render_TOC_el('xMOD_csh_corebe', 'core', $outputSections, $tocArray, $CSHkeys);
199 // Backend Modules:
200 $loadModules = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Module\ModuleLoader::class);
201 $loadModules->load($GLOBALS['TBE_MODULES']);
202 foreach ($loadModules->modules as $mainMod => $info) {
203 $cshKey = '_MOD_' . $mainMod;
204 if ($CSHkeys[$cshKey]) {
205 $this->getLanguageService()->loadSingleTableDescription($cshKey);
206 $this->render_TOC_el($cshKey, 'modules', $outputSections, $tocArray, $CSHkeys);
207 }
208 if (is_array($info['sub'])) {
209 foreach ($info['sub'] as $subMod => $subInfo) {
210 $cshKey = '_MOD_' . $mainMod . '_' . $subMod;
211 if ($CSHkeys[$cshKey]) {
212 $this->getLanguageService()->loadSingleTableDescription($cshKey);
213 $this->render_TOC_el($cshKey, 'modules', $outputSections, $tocArray, $CSHkeys);
214 }
215 }
216 }
217 }
218 // Database Tables:
219 foreach ($TCAkeys as $table) {
220 // Load descriptions for table $table
221 $this->getLanguageService()->loadSingleTableDescription($table);
222 if (is_array($GLOBALS['TCA_DESCR'][$table]['columns']) && $GLOBALS['BE_USER']->check('tables_select', $table)) {
223 $this->render_TOC_el($table, 'tables', $outputSections, $tocArray, $CSHkeys);
224 }
225 }
226 // Extensions
227 foreach ($CSHkeys as $cshKey => $value) {
228 if (GeneralUtility::isFirstPartOfStr($cshKey, 'xEXT_') && !isset($GLOBALS['TCA'][$cshKey])) {
229 $this->getLanguageService()->loadSingleTableDescription($cshKey);
230 $this->render_TOC_el($cshKey, 'extensions', $outputSections, $tocArray, $CSHkeys);
231 }
232 }
233 // Other:
234 foreach ($CSHkeys as $cshKey => $value) {
235 if (!GeneralUtility::isFirstPartOfStr($cshKey, '_MOD_') && !isset($GLOBALS['TCA'][$cshKey])) {
236 $this->getLanguageService()->loadSingleTableDescription($cshKey);
237 $this->render_TOC_el($cshKey, 'other', $outputSections, $tocArray, $CSHkeys);
238 }
239 }
240
241 // COMPILE output:
242 $output = '';
243 $output .= '<h1>' . $this->getLanguageService()->getLL('manual_title', TRUE) . '</h1>';
244 $output .= '<p class="lead">' . $this->getLanguageService()->getLL('description', TRUE) . '</p>';
245
246 $output .= '<h2>' . $this->getLanguageService()->getLL('TOC', TRUE) . '</h2>' . $this->render_TOC_makeTocList($tocArray);
247 if (!$this->renderALL) {
248 $output .= '
249 <br/>
250 <p class="c-nav"><a href="' . htmlspecialchars($this->moduleUrl) . '&amp;renderALL=1">' . $this->getLanguageService()->getLL('full_manual', TRUE) . '</a></p>';
251 }
252 if ($this->renderALL) {
253 $output .= '
254
255 <h2>' . $this->getLanguageService()->getLL('full_manual_chapters', TRUE) . '</h2>' . implode('
256
257
258 <!-- NEW SECTION: -->
259 ', $outputSections);
260 }
261 $output .= '<hr /><p class="manual-title">' . BackendUtility::TYPO3_copyRightNotice() . '</p>';
262 return $output;
263 }
264
265 /**
266 * Creates a TOC list element and renders corresponding HELP content if "renderALL" mode is set.
267 *
268 * @param string $table CSH key / Table name
269 * @param string $tocCat TOC category keyword: "core", "modules", "tables", "other
270 * @param array $outputSections Array for accumulation of rendered HELP Content (in "renderALL" mode). Passed by reference!
271 * @param array $tocArray TOC array; Here TOC index elements are created. Passed by reference!
272 * @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!
273 * @return void
274 */
275 public function render_TOC_el($table, $tocCat, &$outputSections, &$tocArray, &$CSHkeys) {
276 // Render full manual right here!
277 if ($this->renderALL) {
278 $outputSections[$table] = $this->render_Table($table);
279 if ($outputSections[$table]) {
280 $outputSections[$table] = '
281
282 <!-- New CSHkey/Table: ' . $table . ' -->
283 <p class="c-nav"><a name="ANCHOR_' . $table . '" href="#">' . $this->getLanguageService()->getLL('to_top', TRUE) . '</a></p>
284 <h2>' . $this->getTableFieldLabel($table) . '</h2>
285
286 ' . $outputSections[$table];
287 $tocArray[$tocCat][$table] = '<a href="#ANCHOR_' . $table . '">' . $this->getTableFieldLabel($table) . '</a>';
288 } else {
289 unset($outputSections[$table]);
290 }
291 } else {
292 // Only TOC:
293 $tocArray[$tocCat][$table] = '<p><a href="' . htmlspecialchars($this->moduleUrl) . '&amp;tfID=' . rawurlencode(($table . '.*')) . '">' . $this->getTableFieldLabel($table) . '</a></p>';
294 }
295 // Unset CSH key:
296 unset($CSHkeys[$table]);
297 }
298
299 /**
300 * Renders the TOC index as a HTML bullet list from TOC array
301 *
302 * @param array $tocArray ToC Array.
303 * @return string HTML bullet list for index.
304 */
305 public function render_TOC_makeTocList($tocArray) {
306 // The Various manual sections:
307 $keys = explode(',', 'core,modules,tables,extensions,other');
308 // Create TOC bullet list:
309 $output = '';
310 foreach ($keys as $tocKey) {
311 if (is_array($tocArray[$tocKey])) {
312 $output .= '
313 <li>' . $this->getLanguageService()->getLL(('TOC_' . $tocKey), TRUE) . '
314 <ul>
315 <li>' . implode('</li>
316 <li>', $tocArray[$tocKey]) . '</li>
317 </ul>
318 </li>';
319 }
320 }
321 // Compile TOC:
322 $output = '
323
324 <!-- TOC: -->
325 <div class="c-toc">
326 <ul>
327 ' . $output . '
328 </ul>
329 </div>';
330 return $output;
331 }
332
333 /**
334 * Render CSH for a full cshKey/table
335 *
336 * @param string $key Full CSH key (may be different from table name)
337 * @param string $table CSH key / table name
338 * @return string HTML output
339 */
340 public function render_Table($key, $table = NULL) {
341 $output = '';
342 // Take default key if not explicitly specified
343 if ($table === NULL) {
344 $table = $key;
345 }
346 // Load descriptions for table $table
347 $this->getLanguageService()->loadSingleTableDescription($key);
348 if (is_array($GLOBALS['TCA_DESCR'][$key]['columns']) && (!$this->limitAccess || $GLOBALS['BE_USER']->check('tables_select', $table))) {
349 // Initialize variables:
350 $parts = array();
351 // Reserved for header of table
352 $parts[0] = '';
353 // Traverse table columns as listed in TCA_DESCR
354 foreach ($GLOBALS['TCA_DESCR'][$key]['columns'] as $field => $_) {
355 $fieldValue = isset($GLOBALS['TCA'][$key]) && (string)$field !== '' ? $GLOBALS['TCA'][$key]['columns'][$field] : array();
356 if (is_array($fieldValue) && (!$this->limitAccess || !$fieldValue['exclude'] || $GLOBALS['BE_USER']->check('non_exclude_fields', $table . ':' . $field))) {
357 if (!$field) {
358 // Header
359 $parts[0] = $this->printItem($key, '', 1);
360 } else {
361 // Field
362 $parts[] = $this->printItem($key, $field, 1);
363 }
364 }
365 }
366 if (!$parts[0]) {
367 unset($parts[0]);
368 }
369 $output .= implode('<br />', $parts);
370 }
371 // TOC link:
372 if (!$this->renderALL) {
373 $tocLink = '<p class="c-nav"><a href="' . htmlspecialchars($this->moduleUrl) . '">' . $this->getLanguageService()->getLL('goToToc', TRUE) . '</a></p>';
374 $output = $tocLink . '
375 <br/>' . $output . '
376 <br />' . $tocLink;
377 }
378 return $output;
379 }
380
381 /**
382 * Renders CSH for a single field.
383 *
384 * @param string $key CSH key / table name
385 * @param string $field Sub key / field name
386 * @return string HTML output
387 */
388 public function render_Single($key, $field) {
389 $output = '';
390 // Load the description field
391 $this->getLanguageService()->loadSingleTableDescription($key);
392 // Render single item
393 $output .= $this->printItem($key, $field);
394 // Link to Full table description and TOC:
395 $getLLKey = $this->limitAccess ? 'fullDescription' : 'fullDescription_module';
396 $output .= '<br />
397 <p class="c-nav"><a href="' . htmlspecialchars($this->moduleUrl) . '&amp;tfID=' . rawurlencode(($key . '.*')) . '">' . $this->getLanguageService()->getLL($getLLKey, TRUE) . '</a></p>
398 <p class="c-nav"><a href="' . htmlspecialchars($this->moduleUrl) . '">' . $this->getLanguageService()->getLL('goToToc', TRUE) . '</a></p>';
399 return $output;
400 }
401
402 /************************************
403 * Rendering CSH items
404 ************************************/
405
406 /**
407 * Make seeAlso links from $value
408 *
409 * @param string $value See-also input codes
410 * @param string $anchorTable If $anchorTable is set to a tablename, then references to this table will be made as anchors, not URLs.
411 * @return string See-also links HTML
412 */
413 public function make_seeAlso($value, $anchorTable = '') {
414 // Split references by comma or linebreak
415 $items = preg_split('/[,' . LF . ']/', $value);
416 $lines = array();
417 foreach ($items as $val) {
418 $val = trim($val);
419 if ($val) {
420 $iP = explode(':', $val);
421 $iPUrl = GeneralUtility::trimExplode('|', $val);
422 // URL reference:
423 if (substr($iPUrl[1], 0, 4) == 'http') {
424 $lines[] = '<a href="' . htmlspecialchars($iPUrl[1]) . '" target="_blank"><em>' . htmlspecialchars($iPUrl[0]) . '</em></a>';
425 } elseif (substr($iPUrl[1], 0, 5) == 'FILE:') {
426 $fileName = GeneralUtility::getFileAbsFileName(substr($iPUrl[1], 5), 1, 1);
427 if ($fileName && @is_file($fileName)) {
428 $fileName = '../' . \TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix($fileName);
429 $lines[] = '<a href="' . htmlspecialchars($fileName) . '" target="_blank"><em>' . htmlspecialchars($iPUrl[0]) . '</em></a>';
430 }
431 } else {
432 // "table" reference
433 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])))) {
434 // Checking read access:
435 if (isset($GLOBALS['TCA_DESCR'][$iP[0]])) {
436 // Make see-also link:
437 $href = $this->renderALL || $anchorTable && $iP[0] == $anchorTable ? '#' . rawurlencode(implode('.', $iP)) : $this->moduleUrl . '&tfID=' . rawurlencode(implode('.', $iP)) . '&back=' . $this->tfID;
438 $label = $this->getTableFieldLabel($iP[0], $iP[1], ' / ');
439 $lines[] = '<a href="' . htmlspecialchars($href) . '">' . htmlspecialchars($label) . '</a>';
440 }
441 }
442 }
443 }
444 }
445 return implode('<br />', $lines);
446 }
447
448 /**
449 * Will return an image tag with description in italics.
450 *
451 * @param string $images Image file reference (list of)
452 * @param string $descr Description string (divided for each image by line break)
453 * @return string Image HTML codes
454 */
455 public function printImage($images, $descr) {
456 $code = '';
457 // Splitting:
458 $imgArray = GeneralUtility::trimExplode(',', $images, TRUE);
459 if (!empty($imgArray)) {
460 $descrArray = explode(LF, $descr, count($imgArray));
461 foreach ($imgArray as $k => $image) {
462 $descr = $descrArray[$k];
463 $absImagePath = GeneralUtility::getFileAbsFileName($image, 1, 1);
464 if ($absImagePath && @is_file($absImagePath)) {
465 $imgFile = \TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix($absImagePath);
466 $imgInfo = @getimagesize($absImagePath);
467 if (is_array($imgInfo)) {
468 $imgFile = '../' . $imgFile;
469 $code .= '<br /><img src="' . $imgFile . '" ' . $imgInfo[3] . ' class="c-inlineimg img-responsive" alt="" /><br />
470 ';
471 $code .= '<p><em>' . htmlspecialchars($descr) . '</em></p>
472 ';
473 } else {
474 $code .= '<div style="background-color: red; border: 1px solid black; color: white;">NOT AN IMAGE: ' . $imgFile . '</div>';
475 }
476 } else {
477 $code .= '<div style="background-color: red; border: 1px solid black; color: white;">IMAGE FILE NOT FOUND: ' . $image . '</div>';
478 }
479 }
480 }
481 return $code;
482 }
483
484 /**
485 * Returns header HTML content
486 *
487 * @param string $str Header text
488 * @param int $type Header type (1, 0)
489 * @return string The HTML for the header.
490 */
491 public function headerLine($str, $type = 0) {
492 switch ($type) {
493 case 1:
494 $str = '<h2>' . htmlspecialchars($str) . '</h2>
495 ';
496 break;
497 case 0:
498 $str = '<h3>' . htmlspecialchars($str) . '</h3>
499 ';
500 break;
501 }
502 return $str;
503 }
504
505 /**
506 * Returns prepared content
507 *
508 * @param string $str Content to format.
509 * @return string Formatted content.
510 */
511 public function prepareContent($str) {
512 return '<p>' . nl2br(trim(strip_tags($str, $this->allowedHTML))) . '</p>
513 ';
514 }
515
516 /**
517 * Prints a single $table/$field information piece
518 * If $anchors is set, then seeAlso references to the same table will be page-anchors, not links.
519 *
520 * @param string $key CSH key / table name
521 * @param string $field Sub key / field name
522 * @param bool $anchors If anchors is to be shown.
523 * @return string HTML content
524 */
525 public function printItem($key, $field, $anchors = FALSE) {
526 $out = '';
527 if ($key && (!$field || is_array($GLOBALS['TCA_DESCR'][$key]['columns'][$field]))) {
528 // Make seeAlso references.
529 $seeAlsoRes = $this->make_seeAlso($GLOBALS['TCA_DESCR'][$key]['columns'][$field]['seeAlso'], $anchors ? $key : '');
530 // Making item:
531 $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(($this->getLanguageService()->getLL('details') . ':')) . $this->prepareContent($GLOBALS['TCA_DESCR'][$key]['columns'][$field]['details']) : '') . ($GLOBALS['TCA_DESCR'][$key]['columns'][$field]['syntax'] ? $this->headerLine(($this->getLanguageService()->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(($this->getLanguageService()->getLL('seeAlso') . ':')) . '<p>' . $seeAlsoRes . '</p>' : '') . ($this->back ? '<br /><p><a href="' . htmlspecialchars($this->moduleUrl . '&tfID=' . rawurlencode($this->back)) . '" class="typo3-goBack">' . htmlspecialchars($this->getLanguageService()->getLL('goBack')) . '</a></p>' : '') . '<br />';
532 }
533 return $out;
534 }
535
536 /**
537 * Returns labels for a given field in a given structure
538 *
539 * @param string $key CSH key / table name
540 * @param string $field Sub key / field name
541 * @return array Table and field labels in a numeric array
542 */
543 public function getTableFieldNames($key, $field) {
544 $this->getLanguageService()->loadSingleTableDescription($key);
545 // Define the label for the key
546 $keyName = $key;
547 if (is_array($GLOBALS['TCA_DESCR'][$key]['columns']['']) && isset($GLOBALS['TCA_DESCR'][$key]['columns']['']['alttitle'])) {
548 // If there's an alternative title, use it
549 $keyName = $GLOBALS['TCA_DESCR'][$key]['columns']['']['alttitle'];
550 } elseif (isset($GLOBALS['TCA'][$key])) {
551 // Otherwise, if it's a table, use its title
552 $keyName = $GLOBALS['TCA'][$key]['ctrl']['title'];
553 } else {
554 // If no title was found, make sure to remove any "_MOD_"
555 $keyName = preg_replace('/^_MOD_/', '', $key);
556 }
557 // Define the label for the field
558 $fieldName = $field;
559 if (is_array($GLOBALS['TCA_DESCR'][$key]['columns'][$field]) && isset($GLOBALS['TCA_DESCR'][$key]['columns'][$field]['alttitle'])) {
560 // If there's an alternative title, use it
561 $fieldName = $GLOBALS['TCA_DESCR'][$key]['columns'][$field]['alttitle'];
562 } elseif (isset($GLOBALS['TCA'][$key]) && isset($GLOBALS['TCA'][$key]['columns'][$field])) {
563 // Otherwise, if it's a table, use its title
564 $fieldName = $GLOBALS['TCA'][$key]['columns'][$field]['label'];
565 }
566 return array($keyName, $fieldName);
567 }
568
569 /**
570 * Returns composite label for table/field
571 *
572 * @param string $key CSH key / table name
573 * @param string $field Sub key / field name
574 * @param string $mergeToken Token to merge the two strings with
575 * @return string Labels joined with merge token
576 * @see getTableFieldNames()
577 */
578 public function getTableFieldLabel($key, $field = '', $mergeToken = ': ') {
579 // Get table / field parts:
580 list($tableName, $fieldName) = $this->getTableFieldNames($key, $field);
581 // Create label:
582 $labelString = $this->getLanguageService()->sL($tableName) . ($field ? $mergeToken . rtrim(trim($this->getLanguageService()->sL($fieldName)), ':') : '');
583 return $labelString;
584 }
585
586 /**
587 * Returns LanguageService
588 *
589 * @return \TYPO3\CMS\Lang\LanguageService
590 */
591 protected function getLanguageService() {
592 return $GLOBALS['LANG'];
593 }
594
595 }