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