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