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