[TASK] CGL Cleanup: Fix TYPO3.Strings.ConcatenationSpacing-Sniff
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Tree / View / AbstractTreeView.php
1 <?php
2 namespace TYPO3\CMS\Backend\Tree\View;
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 * Base class for creating a browsable array/page/folder tree in HTML
23 *
24 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
25 * @author René Fritz <r.fritz@colorcube.de>
26 */
27 abstract class AbstractTreeView {
28
29 // EXTERNAL, static:
30 // If set, the first element in the tree is always expanded.
31 /**
32 * @todo Define visibility
33 */
34 public $expandFirst = 0;
35
36 // If set, then ALL items will be expanded, regardless of stored settings.
37 /**
38 * @todo Define visibility
39 */
40 public $expandAll = 0;
41
42 // Holds the current script to reload to.
43 /**
44 * @todo Define visibility
45 */
46 public $thisScript = '';
47
48 // Which HTML attribute to use: alt/title. See init().
49 /**
50 * @todo Define visibility
51 */
52 public $titleAttrib = 'title';
53
54 // If TRUE, no context menu is rendered on icons. If set to "titlelink" the
55 // icon is linked as the title is.
56 /**
57 * @todo Define visibility
58 */
59 public $ext_IconMode = FALSE;
60
61 // If set, the id of the mounts will be added to the internal ids array
62 /**
63 * @todo Define visibility
64 */
65 public $addSelfId = 0;
66
67 // Used if the tree is made of records (not folders for ex.)
68 /**
69 * @todo Define visibility
70 */
71 public $title = 'no title';
72
73 // If TRUE, a default title attribute showing the UID of the record is shown.
74 // This cannot be enabled by default because it will destroy many applications
75 // where another title attribute is in fact applied later.
76 /**
77 * @todo Define visibility
78 */
79 public $showDefaultTitleAttribute = FALSE;
80
81 // If TRUE, pages containing child records which has versions will be
82 // highlighted in yellow. This might be too expensive in terms
83 // of processing power.
84 /**
85 * @todo Define visibility
86 */
87 public $highlightPagesWithVersions = TRUE;
88
89 /**
90 * Needs to be initialized with $GLOBALS['BE_USER']
91 * Done by default in init()
92 *
93 * @var \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
94 * @todo Define visibility
95 */
96 public $BE_USER = '';
97
98 /**
99 * Needs to be initialized with e.g. $GLOBALS['WEBMOUNTS']
100 * Default setting in init() is 0 => 0
101 * The keys are mount-ids (can be anything basically) and the
102 * values are the ID of the root element (COULD be zero or anything else.
103 * For pages that would be the uid of the page, zero for the pagetree root.)
104 *
105 * @todo Define visibility
106 */
107 public $MOUNTS = '';
108
109 /**
110 * Database table to get the tree data from.
111 * Leave blank if data comes from an array.
112 *
113 * @todo Define visibility
114 */
115 public $table = '';
116
117 /**
118 * Defines the field of $table which is the parent id field (like pid for table pages).
119 *
120 * @todo Define visibility
121 */
122 public $parentField = 'pid';
123
124 /**
125 * WHERE clause used for selecting records for the tree. Is set by function init.
126 * Only makes sense when $this->table is set.
127 *
128 * @see init()
129 * @todo Define visibility
130 */
131 public $clause = '';
132
133 /**
134 * Field for ORDER BY. Is set by function init.
135 * Only makes sense when $this->table is set.
136 *
137 * @see init()
138 * @todo Define visibility
139 */
140 public $orderByFields = '';
141
142 /**
143 * Default set of fields selected from the tree table.
144 * Make SURE that these fields names listed herein are actually possible to select from $this->table (if that variable is set to a TCA table name)
145 *
146 * @see addField()
147 * @todo Define visibility
148 */
149 public $fieldArray = array('uid', 'title');
150
151 /**
152 * List of other fields which are ALLOWED to set (here, based on the "pages" table!)
153 *
154 * @see addField()
155 * @todo Define visibility
156 */
157 public $defaultList = 'uid,pid,tstamp,sorting,deleted,perms_userid,perms_groupid,perms_user,perms_group,perms_everybody,crdate,cruser_id';
158
159 /**
160 * Unique name for the tree.
161 * Used as key for storing the tree into the BE users settings.
162 * Used as key to pass parameters in links.
163 * MUST NOT contain underscore chars.
164 * etc.
165 *
166 * @todo Define visibility
167 */
168 public $treeName = '';
169
170 /**
171 * A prefix for table cell id's which will be wrapped around an item.
172 * Can be used for highlighting by JavaScript.
173 * Needs to be unique if multiple trees are on one HTML page.
174 *
175 * @see printTree()
176 * @todo Define visibility
177 */
178 public $domIdPrefix = 'row';
179
180 /**
181 * Back path for icons
182 *
183 * @todo Define visibility
184 */
185 public $backPath;
186
187 /**
188 * Icon file path.
189 *
190 * @todo Define visibility
191 */
192 public $iconPath = '';
193
194 /**
195 * Icon file name for item icons.
196 *
197 * @todo Define visibility
198 */
199 public $iconName = 'default.gif';
200
201 /**
202 * If TRUE, HTML code is also accumulated in ->tree array during rendering of the tree.
203 * If 2, then also the icon prefix code (depthData) is stored
204 *
205 * @todo Define visibility
206 */
207 public $makeHTML = 1;
208
209 /**
210 * If TRUE, records as selected will be stored internally in the ->recs array
211 *
212 * @todo Define visibility
213 */
214 public $setRecs = 0;
215
216 /**
217 * Sets the associative array key which identifies a new sublevel if arrays are used for trees.
218 * This value has formerly been "subLevel" and "--sublevel--"
219 *
220 * @todo Define visibility
221 */
222 public $subLevelID = '_SUB_LEVEL';
223
224 // *********
225 // Internal
226 // *********
227 // For record trees:
228 // one-dim array of the uid's selected.
229 /**
230 * @todo Define visibility
231 */
232 public $ids = array();
233
234 // The hierarchy of element uids
235 /**
236 * @todo Define visibility
237 */
238 public $ids_hierarchy = array();
239
240 // The hierarchy of versioned element uids
241 /**
242 * @todo Define visibility
243 */
244 public $orig_ids_hierarchy = array();
245
246 // Temporary, internal array
247 /**
248 * @todo Define visibility
249 */
250 public $buffer_idH = array();
251
252 // For FOLDER trees:
253 // Special UIDs for folders (integer-hashes of paths)
254 /**
255 * @todo Define visibility
256 */
257 public $specUIDmap = array();
258
259 // For arrays:
260 // Holds the input data array
261 /**
262 * @todo Define visibility
263 */
264 public $data = FALSE;
265
266 // Holds an index with references to the data array.
267 /**
268 * @todo Define visibility
269 */
270 public $dataLookup = FALSE;
271
272 // For both types
273 // Tree is accumulated in this variable
274 /**
275 * @todo Define visibility
276 */
277 public $tree = array();
278
279 // Holds (session stored) information about which items in the tree are unfolded and which are not.
280 /**
281 * @todo Define visibility
282 */
283 public $stored = array();
284
285 // Points to the current mountpoint key
286 /**
287 * @todo Define visibility
288 */
289 public $bank = 0;
290
291 // Accumulates the displayed records.
292 /**
293 * @todo Define visibility
294 */
295 public $recs = array();
296
297 /**
298 * Sets the script url depending on being a module or script request
299 */
300 protected function determineScriptUrl() {
301 if ($moduleName = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('M')) {
302 $this->thisScript = \TYPO3\CMS\Backend\Utility\BackendUtility::getModuleUrl($moduleName);
303 } else {
304 $this->thisScript = \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('SCRIPT_NAME');
305 }
306 }
307
308 /**
309 * @return string
310 */
311 protected function getThisScript() {
312 return strpos($this->thisScript, '?') === FALSE ? $this->thisScript . '?' : $this->thisScript . '&';
313 }
314
315 /**
316 * Initialize the tree class. Needs to be overwritten
317 * Will set ->fieldsArray, ->backPath and ->clause
318 *
319 * @param string Record WHERE clause
320 * @param string Record ORDER BY field
321 * @return void
322 * @todo Define visibility
323 */
324 public function init($clause = '', $orderByFields = '') {
325 // Setting BE_USER by default
326 $this->BE_USER = $GLOBALS['BE_USER'];
327 // Setting title attribute to use.
328 $this->titleAttrib = 'title';
329 // Setting backpath.
330 $this->backPath = $GLOBALS['BACK_PATH'];
331 // Setting clause
332 if ($clause) {
333 $this->clause = $clause;
334 }
335 if ($orderByFields) {
336 $this->orderByFields = $orderByFields;
337 }
338 if (!is_array($this->MOUNTS)) {
339 // Dummy
340 $this->MOUNTS = array(0 => 0);
341 }
342 $this->setTreeName();
343 // Setting this to FALSE disables the use of array-trees by default
344 $this->data = FALSE;
345 $this->dataLookup = FALSE;
346 }
347
348 /**
349 * Sets the tree name which is used to identify the tree
350 * Used for JavaScript and other things
351 *
352 * @param string $treeName Default is the table name. Underscores are stripped.
353 * @return void
354 * @todo Define visibility
355 */
356 public function setTreeName($treeName = '') {
357 $this->treeName = $treeName ?: $this->treeName;
358 $this->treeName = $this->treeName ?: $this->table;
359 $this->treeName = str_replace('_', '', $this->treeName);
360 }
361
362 /**
363 * Adds a fieldname to the internal array ->fieldArray
364 *
365 * @param string $field Field name to
366 * @param boolean $noCheck If set, the fieldname will be set no matter what. Otherwise the field name must either be found as key in $GLOBALS['TCA'][$table]['columns'] or in the list ->defaultList
367 * @return void
368 * @todo Define visibility
369 */
370 public function addField($field, $noCheck = 0) {
371 if ($noCheck || is_array($GLOBALS['TCA'][$this->table]['columns'][$field]) || GeneralUtility::inList($this->defaultList, $field)) {
372 $this->fieldArray[] = $field;
373 }
374 }
375
376 /**
377 * Resets the tree, recs, ids, ids_hierarchy and orig_ids_hierarchy internal variables. Use it if you need it.
378 *
379 * @return void
380 * @todo Define visibility
381 */
382 public function reset() {
383 $this->tree = array();
384 $this->recs = array();
385 $this->ids = array();
386 $this->ids_hierarchy = array();
387 $this->orig_ids_hierarchy = array();
388 }
389
390 /*******************************************
391 *
392 * output
393 *
394 *******************************************/
395 /**
396 * Will create and return the HTML code for a browsable tree
397 * Is based on the mounts found in the internal array ->MOUNTS (set in the constructor)
398 *
399 * @return string HTML code for the browsable tree
400 * @todo Define visibility
401 */
402 public function getBrowsableTree() {
403 // Get stored tree structure AND updating it if needed according to incoming PM GET var.
404 $this->initializePositionSaving();
405 // Init done:
406 $treeArr = array();
407 // Traverse mounts:
408 foreach ($this->MOUNTS as $idx => $uid) {
409 // Set first:
410 $this->bank = $idx;
411 $isOpen = $this->stored[$idx][$uid] || $this->expandFirst;
412 // Save ids while resetting everything else.
413 $curIds = $this->ids;
414 $this->reset();
415 $this->ids = $curIds;
416 // Set PM icon for root of mount:
417 $cmd = $this->bank . '_' . ($isOpen ? '0_' : '1_') . $uid . '_' . $this->treeName;
418 $icon = IconUtility::getSpriteIcon('treeline-' . ($isOpen ? 'minus' : 'plus') . 'only');
419
420 $firstHtml = $this->PM_ATagWrap($icon, $cmd);
421 // Preparing rootRec for the mount
422 if ($uid) {
423 $rootRec = $this->getRecord($uid);
424 $firstHtml .= $this->getIcon($rootRec);
425 } else {
426 // Artificial record for the tree root, id=0
427 $rootRec = $this->getRootRecord($uid);
428 $firstHtml .= $this->getRootIcon($rootRec);
429 }
430 if (is_array($rootRec)) {
431 // In case it was swapped inside getRecord due to workspaces.
432 $uid = $rootRec['uid'];
433 // Add the root of the mount to ->tree
434 $this->tree[] = array('HTML' => $firstHtml, 'row' => $rootRec, 'bank' => $this->bank);
435 // If the mount is expanded, go down:
436 if ($isOpen) {
437 // Set depth:
438 $depthD = IconUtility::getSpriteIcon('treeline-blank');
439 if ($this->addSelfId) {
440 $this->ids[] = $uid;
441 }
442 $this->getTree($uid, 999, $depthD, '', $rootRec['_SUBCSSCLASS']);
443 }
444 // Add tree:
445 $treeArr = array_merge($treeArr, $this->tree);
446 }
447 }
448 return $this->printTree($treeArr);
449 }
450
451 /**
452 * Compiles the HTML code for displaying the structure found inside the ->tree array
453 *
454 * @param array $treeArr "tree-array" - if blank string, the internal ->tree array is used.
455 * @return string The HTML code for the tree
456 * @todo Define visibility
457 */
458 public function printTree($treeArr = '') {
459 $titleLen = (int)$this->BE_USER->uc['titleLen'];
460 if (!is_array($treeArr)) {
461 $treeArr = $this->tree;
462 }
463 $out = '';
464 // put a table around it with IDs to access the rows from JS
465 // not a problem if you don't need it
466 // In XHTML there is no "name" attribute of <td> elements -
467 // but Mozilla will not be able to highlight rows if the name
468 // attribute is NOT there.
469 $out .= '
470
471 <!--
472 TYPO3 tree structure.
473 -->
474 <table cellpadding="0" cellspacing="0" border="0" id="typo3-tree">';
475 foreach ($treeArr as $k => $v) {
476 $idAttr = htmlspecialchars($this->domIdPrefix . $this->getId($v['row']) . '_' . $v['bank']);
477 $out .= '
478 <tr>
479 <td id="' . $idAttr . '"' . ($v['row']['_CSSCLASS'] ? ' class="' . $v['row']['_CSSCLASS'] . '"' : '') . '>' . $v['HTML'] . $this->wrapTitle($this->getTitleStr($v['row'], $titleLen), $v['row'], $v['bank']) . '</td>
480 </tr>
481 ';
482 }
483 $out .= '
484 </table>';
485 return $out;
486 }
487
488 /*******************************************
489 *
490 * rendering parts
491 *
492 *******************************************/
493 /**
494 * Generate the plus/minus icon for the browsable tree.
495 *
496 * @param array $row Record for the entry
497 * @param integer $a The current entry number
498 * @param integer $c The total number of entries. If equal to $a, a "bottom" element is returned.
499 * @param integer $nextCount The number of sub-elements to the current element.
500 * @param boolean $exp The element was expanded to render subelements if this flag is set.
501 * @return string Image tag with the plus/minus icon.
502 * @access private
503 * @see \TYPO3\CMS\Backend\Tree\View\PageTreeView::PMicon()
504 * @todo Define visibility
505 */
506 public function PMicon($row, $a, $c, $nextCount, $exp) {
507 $PM = $nextCount ? ($exp ? 'minus' : 'plus') : 'join';
508 $BTM = $a == $c ? 'bottom' : '';
509 $icon = IconUtility::getSpriteIcon('treeline-' . $PM . $BTM);
510 if ($nextCount) {
511 $cmd = $this->bank . '_' . ($exp ? '0_' : '1_') . $row['uid'] . '_' . $this->treeName;
512 $bMark = $this->bank . '_' . $row['uid'];
513 $icon = $this->PM_ATagWrap($icon, $cmd, $bMark);
514 }
515 return $icon;
516 }
517
518 /**
519 * Wrap the plus/minus icon in a link
520 *
521 * @param string $icon HTML string to wrap, probably an image tag.
522 * @param string $cmd Command for 'PM' get var
523 * @param boolean $bMark If set, the link will have a anchor point (=$bMark) and a name attribute (=$bMark)
524 * @return string Link-wrapped input string
525 * @access private
526 * @todo Define visibility
527 */
528 public function PM_ATagWrap($icon, $cmd, $bMark = '') {
529 if ($this->thisScript) {
530 if ($bMark) {
531 $anchor = '#' . $bMark;
532 $name = ' name="' . $bMark . '"';
533 }
534 $aUrl = $this->getThisScript() . 'PM=' . $cmd . $anchor;
535 return '<a href="' . htmlspecialchars($aUrl) . '"' . $name . '>' . $icon . '</a>';
536 } else {
537 return $icon;
538 }
539 }
540
541 /**
542 * Wrapping $title in a-tags.
543 *
544 * @param string $title Title string
545 * @param string $row Item record
546 * @param integer $bank Bank pointer (which mount point number)
547 * @return string
548 * @access private
549 * @todo Define visibility
550 */
551 public function wrapTitle($title, $row, $bank = 0) {
552 $aOnClick = 'return jumpTo(\'' . $this->getJumpToParam($row) . '\',this,\'' . $this->domIdPrefix . $this->getId($row) . '\',' . $bank . ');';
553 return '<a href="#" onclick="' . htmlspecialchars($aOnClick) . '">' . $title . '</a>';
554 }
555
556 /**
557 * Wrapping the image tag, $icon, for the row, $row (except for mount points)
558 *
559 * @param string $icon The image tag for the icon
560 * @param array $row The row for the current element
561 * @return string The processed icon input value.
562 * @access private
563 * @todo Define visibility
564 */
565 public function wrapIcon($icon, $row) {
566 return $icon;
567 }
568
569 /**
570 * Adds attributes to image tag.
571 *
572 * @param string $icon Icon image tag
573 * @param string $attr Attributes to add, eg. ' border="0"'
574 * @return string Image tag, modified with $attr attributes added.
575 * @todo Define visibility
576 */
577 public function addTagAttributes($icon, $attr) {
578 return preg_replace('/ ?\\/?>$/', '', $icon) . ' ' . $attr . ' />';
579 }
580
581 /**
582 * Adds a red "+" to the input string, $str, if the field "php_tree_stop" in the $row (pages) is set
583 *
584 * @param string $str Input string, like a page title for the tree
585 * @param array $row record row with "php_tree_stop" field
586 * @return string Modified string
587 * @access private
588 * @todo Define visibility
589 */
590 public function wrapStop($str, $row) {
591 if ($row['php_tree_stop']) {
592 $str .= '<span class="typo3-red"><a href="' . htmlspecialchars(GeneralUtility::linkThisScript(array('setTempDBmount' => $row['uid']))) . '" class="typo3-red">+</a> </span>';
593 }
594 return $str;
595 }
596
597 /*******************************************
598 *
599 * tree handling
600 *
601 *******************************************/
602 /**
603 * Returns TRUE/FALSE if the next level for $id should be expanded - based on
604 * data in $this->stored[][] and ->expandAll flag.
605 * Extending parent function
606 *
607 * @param integer $id Record id/key
608 * @return boolean
609 * @access private
610 * @see \TYPO3\CMS\Backend\Tree\View\PageTreeView::expandNext()
611 * @todo Define visibility
612 */
613 public function expandNext($id) {
614 return $this->stored[$this->bank][$id] || $this->expandAll ? 1 : 0;
615 }
616
617 /**
618 * Get stored tree structure AND updating it if needed according to incoming PM GET var.
619 *
620 * @return void
621 * @access private
622 * @todo Define visibility
623 */
624 public function initializePositionSaving() {
625 // Get stored tree structure:
626 $this->stored = unserialize($this->BE_USER->uc['browseTrees'][$this->treeName]);
627 // PM action
628 // (If an plus/minus icon has been clicked, the PM GET var is sent and we
629 // must update the stored positions in the tree):
630 // 0: mount key, 1: set/clear boolean, 2: item ID (cannot contain "_"), 3: treeName
631 $PM = explode('_', GeneralUtility::_GP('PM'));
632 if (count($PM) == 4 && $PM[3] == $this->treeName) {
633 if (isset($this->MOUNTS[$PM[0]])) {
634 // set
635 if ($PM[1]) {
636 $this->stored[$PM[0]][$PM[2]] = 1;
637 $this->savePosition();
638 } else {
639 unset($this->stored[$PM[0]][$PM[2]]);
640 $this->savePosition();
641 }
642 }
643 }
644 }
645
646 /**
647 * Saves the content of ->stored (keeps track of expanded positions in the tree)
648 * $this->treeName will be used as key for BE_USER->uc[] to store it in
649 *
650 * @return void
651 * @access private
652 * @todo Define visibility
653 */
654 public function savePosition() {
655 $this->BE_USER->uc['browseTrees'][$this->treeName] = serialize($this->stored);
656 $this->BE_USER->writeUC();
657 }
658
659 /******************************
660 *
661 * Functions that might be overwritten by extended classes
662 *
663 ********************************/
664 /**
665 * Returns the root icon for a tree/mountpoint (defaults to the globe)
666 *
667 * @param array $rec Record for root.
668 * @return string Icon image tag.
669 * @todo Define visibility
670 */
671 public function getRootIcon($rec) {
672 return $this->wrapIcon(IconUtility::getSpriteIcon('apps-pagetree-root'), $rec);
673 }
674
675 /**
676 * Get icon for the row.
677 * If $this->iconPath and $this->iconName is set, try to get icon based on those values.
678 *
679 * @param array $row Item row.
680 * @return string Image tag.
681 * @todo Define visibility
682 */
683 public function getIcon($row) {
684 if ($this->iconPath && $this->iconName) {
685 $icon = '<img' . IconUtility::skinImg('', ($this->iconPath . $this->iconName), 'width="18" height="16"') . ' alt=""' . ($this->showDefaultTitleAttribute ? ' title="UID: ' . $row['uid'] . '"' : '') . ' />';
686 } else {
687 $icon = IconUtility::getSpriteIconForRecord($this->table, $row, array(
688 'title' => $this->showDefaultTitleAttribute ? 'UID: ' . $row['uid'] : $this->getTitleAttrib($row),
689 'class' => 'c-recIcon'
690 ));
691 }
692 return $this->wrapIcon($icon, $row);
693 }
694
695 /**
696 * Returns the title for the input record. If blank, a "no title" label (localized) will be returned.
697 * Do NOT htmlspecialchar the string from this function - has already been done.
698 *
699 * @param array $row The input row array (where the key "title" is used for the title)
700 * @param integer $titleLen Title length (30)
701 * @return string The title.
702 * @todo Define visibility
703 */
704 public function getTitleStr($row, $titleLen = 30) {
705 if ($this->ext_showNavTitle && strlen(trim($row['nav_title'])) > 0) {
706 $title = '<span title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_tca.xlf:title', TRUE) . ' ' . htmlspecialchars(trim($row['title'])) . '">' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($row['nav_title'], $titleLen)) . '</span>';
707 } else {
708 $title = htmlspecialchars(GeneralUtility::fixed_lgd_cs($row['title'], $titleLen));
709 if (strlen(trim($row['nav_title'])) > 0) {
710 $title = '<span title="' . $GLOBALS['LANG']->sL('LLL:EXT:cms/locallang_tca.xlf:pages.nav_title', TRUE) . ' ' . htmlspecialchars(trim($row['nav_title'])) . '">' . $title . '</span>';
711 }
712 $title = strlen(trim($row['title'])) == 0 ? '<em>[' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.no_title', TRUE) . ']</em>' : $title;
713 }
714 return $title;
715 }
716
717 /**
718 * Returns the value for the image "title" attribute
719 *
720 * @param array $row The input row array (where the key "title" is used for the title)
721 * @return string The attribute value (is htmlspecialchared() already)
722 * @see wrapIcon()
723 * @todo Define visibility
724 */
725 public function getTitleAttrib($row) {
726 return htmlspecialchars($row['title']);
727 }
728
729 /**
730 * Returns the id from the record (typ. uid)
731 *
732 * @param array $row Record array
733 * @return integer The "uid" field value.
734 * @todo Define visibility
735 */
736 public function getId($row) {
737 return $row['uid'];
738 }
739
740 /**
741 * Returns jump-url parameter value.
742 *
743 * @param array $row The record array.
744 * @return string The jump-url parameter.
745 * @todo Define visibility
746 */
747 public function getJumpToParam($row) {
748 return $this->getId($row);
749 }
750
751 /********************************
752 *
753 * tree data buidling
754 *
755 ********************************/
756 /**
757 * Fetches the data for the tree
758 *
759 * @param integer $uid item id for which to select subitems (parent id)
760 * @param integer $depth Max depth (recursivity limit)
761 * @param string $depthData HTML-code prefix for recursive calls.
762 * @param string $blankLineCode ? (internal)
763 * @param string $subCSSclass CSS class to use for <td> sub-elements
764 * @return integer The count of items on the level
765 * @todo Define visibility
766 */
767 public function getTree($uid, $depth = 999, $depthData = '', $blankLineCode = '', $subCSSclass = '') {
768 // Buffer for id hierarchy is reset:
769 $this->buffer_idH = array();
770 // Init vars
771 $depth = (int)$depth;
772 $HTML = '';
773 $a = 0;
774 $res = $this->getDataInit($uid, $subCSSclass);
775 $c = $this->getDataCount($res);
776 $crazyRecursionLimiter = 999;
777 $idH = array();
778 // Traverse the records:
779 while ($crazyRecursionLimiter > 0 && ($row = $this->getDataNext($res, $subCSSclass))) {
780 if (!$GLOBALS['BE_USER']->isInWebMount($row['uid'])) {
781 // Current record is not within web mount => skip it
782 continue;
783 }
784
785 $a++;
786 $crazyRecursionLimiter--;
787 $newID = $row['uid'];
788 if ($newID == 0) {
789 throw new \RuntimeException('Endless recursion detected: TYPO3 has detected an error in the database. Please fix it manually (e.g. using phpMyAdmin) and change the UID of ' . $this->table . ':0 to a new value.<br /><br />See <a href="http://forge.typo3.org/issues/16150" target="_blank">forge.typo3.org/issues/16150</a> to get more information about a possible cause.', 1294586383);
790 }
791 // Reserve space.
792 $this->tree[] = array();
793 end($this->tree);
794 // Get the key for this space
795 $treeKey = key($this->tree);
796 $LN = $a == $c ? 'blank' : 'line';
797 // If records should be accumulated, do so
798 if ($this->setRecs) {
799 $this->recs[$row['uid']] = $row;
800 }
801 // Accumulate the id of the element in the internal arrays
802 $this->ids[] = ($idH[$row['uid']]['uid'] = $row['uid']);
803 $this->ids_hierarchy[$depth][] = $row['uid'];
804 $this->orig_ids_hierarchy[$depth][] = $row['_ORIG_uid'] ?: $row['uid'];
805
806 // Make a recursive call to the next level
807 $HTML_depthData = $depthData . IconUtility::getSpriteIcon('treeline-' . $LN);
808 if ($depth > 1 && $this->expandNext($newID) && !$row['php_tree_stop']) {
809 $nextCount = $this->getTree($newID, $depth - 1, $this->makeHTML ? $HTML_depthData : '', $blankLineCode . ',' . $LN, $row['_SUBCSSCLASS']);
810 if (count($this->buffer_idH)) {
811 $idH[$row['uid']]['subrow'] = $this->buffer_idH;
812 }
813 // Set "did expand" flag
814 $exp = 1;
815 } else {
816 $nextCount = $this->getCount($newID);
817 // Clear "did expand" flag
818 $exp = 0;
819 }
820 // Set HTML-icons, if any:
821 if ($this->makeHTML) {
822 $HTML = $depthData . $this->PMicon($row, $a, $c, $nextCount, $exp);
823 $HTML .= $this->wrapStop($this->getIcon($row), $row);
824 }
825 // Finally, add the row/HTML content to the ->tree array in the reserved key.
826 $this->tree[$treeKey] = array(
827 'row' => $row,
828 'HTML' => $HTML,
829 'HTML_depthData' => $this->makeHTML == 2 ? $HTML_depthData : '',
830 'invertedDepth' => $depth,
831 'blankLineCode' => $blankLineCode,
832 'bank' => $this->bank
833 );
834 }
835 $this->getDataFree($res);
836 $this->buffer_idH = $idH;
837 return $c;
838 }
839
840 /********************************
841 *
842 * Data handling
843 * Works with records and arrays
844 *
845 ********************************/
846 /**
847 * Returns the number of records having the parent id, $uid
848 *
849 * @param integer $uid Id to count subitems for
850 * @return integer
851 * @access private
852 * @todo Define visibility
853 */
854 public function getCount($uid) {
855 if (is_array($this->data)) {
856 $res = $this->getDataInit($uid);
857 return $this->getDataCount($res);
858 } else {
859 return $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('uid', $this->table, $this->parentField . '=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($uid, $this->table) . BackendUtility::deleteClause($this->table) . BackendUtility::versioningPlaceholderClause($this->table) . $this->clause);
860 }
861 }
862
863 /**
864 * Returns root record for uid (<=0)
865 *
866 * @param integer $uid uid, <= 0 (normally, this does not matter)
867 * @return array Array with title/uid keys with values of $this->title/0 (zero)
868 * @todo Define visibility
869 */
870 public function getRootRecord($uid) {
871 return array('title' => $this->title, 'uid' => 0);
872 }
873
874 /**
875 * Returns the record for a uid.
876 * For tables: Looks up the record in the database.
877 * For arrays: Returns the fake record for uid id.
878 *
879 * @param integer $uid UID to look up
880 * @return array The record
881 * @todo Define visibility
882 */
883 public function getRecord($uid) {
884 if (is_array($this->data)) {
885 return $this->dataLookup[$uid];
886 } else {
887 return BackendUtility::getRecordWSOL($this->table, $uid);
888 }
889 }
890
891 /**
892 * Getting the tree data: Selecting/Initializing data pointer to items for a certain parent id.
893 * For tables: This will make a database query to select all children to "parent"
894 * For arrays: This will return key to the ->dataLookup array
895 *
896 * @param integer $parentId parent item id
897 * @param string $subCSSclass Class for sub-elements.
898 * @return mixed Data handle (Tables: An sql-resource, arrays: A parentId integer. -1 is returned if there were NO subLevel.)
899 * @access private
900 * @todo Define visibility
901 */
902 public function getDataInit($parentId, $subCSSclass = '') {
903 if (is_array($this->data)) {
904 if (!is_array($this->dataLookup[$parentId][$this->subLevelID])) {
905 $parentId = -1;
906 } else {
907 reset($this->dataLookup[$parentId][$this->subLevelID]);
908 }
909 return $parentId;
910 } else {
911 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(implode(',', $this->fieldArray), $this->table, $this->parentField . '=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($parentId, $this->table) . BackendUtility::deleteClause($this->table) . BackendUtility::versioningPlaceholderClause($this->table) . $this->clause, '', $this->orderByFields);
912 return $res;
913 }
914 }
915
916 /**
917 * Getting the tree data: Counting elements in resource
918 *
919 * @param mixed $res Data handle
920 * @return integer number of items
921 * @access private
922 * @see getDataInit()
923 * @todo Define visibility
924 */
925 public function getDataCount(&$res) {
926 if (is_array($this->data)) {
927 return count($this->dataLookup[$res][$this->subLevelID]);
928 } else {
929 $c = $GLOBALS['TYPO3_DB']->sql_num_rows($res);
930 return $c;
931 }
932 }
933
934 /**
935 * Getting the tree data: next entry
936 *
937 * @param mixed $res Data handle
938 * @param string $subCSSclass CSS class for sub elements (workspace related)
939 * @return array item data array OR FALSE if end of elements.
940 * @access private
941 * @see getDataInit()
942 * @todo Define visibility
943 */
944 public function getDataNext(&$res, $subCSSclass = '') {
945 if (is_array($this->data)) {
946 if ($res < 0) {
947 $row = FALSE;
948 } else {
949 list(, $row) = each($this->dataLookup[$res][$this->subLevelID]);
950 // Passing on default <td> class for subelements:
951 if (is_array($row) && $subCSSclass !== '') {
952 $row['_CSSCLASS'] = ($row['_SUBCSSCLASS'] = $subCSSclass);
953 }
954 }
955 return $row;
956 } else {
957 while ($row = @$GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
958 BackendUtility::workspaceOL($this->table, $row, $this->BE_USER->workspace, TRUE);
959 if (is_array($row)) {
960 break;
961 }
962 }
963 // Passing on default <td> class for subelements:
964 if (is_array($row) && $subCSSclass !== '') {
965 if ($this->table === 'pages' && $this->highlightPagesWithVersions && !isset($row['_CSSCLASS']) && count(BackendUtility::countVersionsOfRecordsOnPage($this->BE_USER->workspace, $row['uid']))) {
966 $row['_CSSCLASS'] = 'ver-versions';
967 }
968 if (!isset($row['_CSSCLASS'])) {
969 $row['_CSSCLASS'] = $subCSSclass;
970 }
971 if (!isset($row['_SUBCSSCLASS'])) {
972 $row['_SUBCSSCLASS'] = $subCSSclass;
973 }
974 }
975 return $row;
976 }
977 }
978
979 /**
980 * Getting the tree data: frees data handle
981 *
982 * @param mixed $res Data handle
983 * @return void
984 * @access private
985 * @todo Define visibility
986 */
987 public function getDataFree(&$res) {
988 if (!is_array($this->data)) {
989 $GLOBALS['TYPO3_DB']->sql_free_result($res);
990 }
991 }
992
993 /**
994 * Used to initialize class with an array to browse.
995 * The array inputted will be traversed and an internal index for lookup is created.
996 * The keys of the input array are perceived as "uid"s of records which means that keys GLOBALLY must be unique like uids are.
997 * "uid" and "pid" "fakefields" are also set in each record.
998 * All other fields are optional.
999 *
1000 * @param array $dataArr The input array, see examples below in this script.
1001 * @param boolean $traverse Internal, for recursion.
1002 * @param integer $pid Internal, for recursion.
1003 * @return void
1004 * @todo Define visibility
1005 */
1006 public function setDataFromArray(&$dataArr, $traverse = FALSE, $pid = 0) {
1007 if (!$traverse) {
1008 $this->data = &$dataArr;
1009 $this->dataLookup = array();
1010 // Add root
1011 $this->dataLookup[0][$this->subLevelID] = &$dataArr;
1012 }
1013 foreach ($dataArr as $uid => $val) {
1014 $dataArr[$uid]['uid'] = $uid;
1015 $dataArr[$uid]['pid'] = $pid;
1016 // Gives quick access to id's
1017 $this->dataLookup[$uid] = &$dataArr[$uid];
1018 if (is_array($val[$this->subLevelID])) {
1019 $this->setDataFromArray($dataArr[$uid][$this->subLevelID], TRUE, $uid);
1020 }
1021 }
1022 }
1023
1024 /**
1025 * Sets the internal data arrays
1026 *
1027 * @param array $treeArr Content for $this->data
1028 * @param array $treeLookupArr Content for $this->dataLookup
1029 * @return void
1030 * @todo Define visibility
1031 */
1032 public function setDataFromTreeArray(&$treeArr, &$treeLookupArr) {
1033 $this->data = &$treeArr;
1034 $this->dataLookup = &$treeLookupArr;
1035 }
1036
1037 }