TYPO3  7.6
View/PageTreeView.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Backend\View;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
22 
27 {
31  public $ext_showPageId = false;
32 
36  public $ext_showNavTitle = false;
37 
41  public $ext_IconMode;
42 
47 
52 
58  public $ajaxStatus = false;
59 
63  public function __construct()
64  {
65  parent::__construct();
66  $this->init();
67  }
68 
76  public function wrapIcon($thePageIcon, &$row)
77  {
79  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
80  // If the record is locked, present a warning sign.
81  if ($lockInfo = BackendUtility::isRecordLocked('pages', $row['uid'])) {
82  $aOnClick = 'alert(' . GeneralUtility::quoteJSvalue($lockInfo['msg']) . ');return false;';
83  $lockIcon = '<a href="#" onclick="' . htmlspecialchars($aOnClick) . '">'
84  . '<span title="' . htmlspecialchars($lockInfo['msg']) . '">' . $iconFactory->getIcon('status-warning-in-use', Icon::SIZE_SMALL)->render() . '</span></a>';
85  } else {
86  $lockIcon = '';
87  }
88  // Wrap icon in click-menu link.
89  if (!$this->ext_IconMode) {
90  $thePageIcon = BackendUtility::wrapClickMenuOnIcon($thePageIcon, 'pages', $row['uid'], 0, '&bank=' . $this->bank);
91  } elseif ($this->ext_IconMode === 'titlelink') {
92  $aOnClick = 'return jumpTo(' . GeneralUtility::quoteJSvalue($this->getJumpToParam($row)) . ',this,' . GeneralUtility::quoteJSvalue($this->treeName) . ');';
93  $thePageIcon = '<a href="#" onclick="' . htmlspecialchars($aOnClick) . '">' . $thePageIcon . '</a>';
94  }
95  // Wrap icon in a drag/drop span.
96  $dragDropIcon = '<span class="list-tree-icon dragIcon" id="dragIconID_' . $row['uid'] . '">' . $thePageIcon . '</span> ';
97  // Add Page ID:
98  $pageIdStr = '';
99  if ($this->ext_showPageId) {
100  $pageIdStr = '<span class="dragId">[' . $row['uid'] . ']</span> ';
101  }
102  // Call stats information hook
103  $stat = '';
104  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['recStatInfoHooks'])) {
105  $_params = array('pages', $row['uid']);
106  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['recStatInfoHooks'] as $_funcRef) {
107  $stat .= GeneralUtility::callUserFunction($_funcRef, $_params, $this);
108  }
109  }
110  return $dragDropIcon . $lockIcon . $pageIdStr . $stat;
111  }
112 
122  public function wrapTitle($title, $row, $bank = 0)
123  {
124  // Hook for overriding the page title
125  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.webpagetree.php']['pageTitleOverlay'])) {
126  $_params = array('title' => &$title, 'row' => &$row);
127  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.webpagetree.php']['pageTitleOverlay'] as $_funcRef) {
128  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
129  }
130  unset($_params);
131  }
132  $aOnClick = 'return jumpTo(' . GeneralUtility::quoteJSvalue($this->getJumpToParam($row)) . ',this,' . GeneralUtility::quoteJSvalue($this->domIdPrefix . $this->getId($row)) . ',' . $bank . ');';
133  $clickMenuParts = BackendUtility::wrapClickMenuOnIcon('', 'pages', $row['uid'], 0, ('&bank=' . $this->bank), '', true);
134 
135  $thePageTitle = '<a href="#" onclick="' . htmlspecialchars($aOnClick) . '"' . GeneralUtility::implodeAttributes($clickMenuParts) . '>' . $title . '</a>';
136  // Wrap title in a drag/drop span.
137  return '<span class="list-tree-title dragTitle" id="dragTitleID_' . $row['uid'] . '">' . $thePageTitle . '</span>';
138  }
139 
146  public function printTree($treeArr = '')
147  {
148  $titleLen = (int)$this->BE_USER->uc['titleLen'];
149  if (!is_array($treeArr)) {
150  $treeArr = $this->tree;
151  }
152  $out = '<ul class="list-tree list-tree-root">';
153  // -- evaluate AJAX request
154  // IE takes anchor as parameter
155  $PM = GeneralUtility::_GP('PM');
156  if (($PMpos = strpos($PM, '#')) !== false) {
157  $PM = substr($PM, 0, $PMpos);
158  }
159  $PM = explode('_', $PM);
160 
161  $doCollapse = false;
162  $doExpand = false;
163  $expandedPageUid = null;
164  $collapsedPageUid = null;
165  if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_AJAX && is_array($PM) && count($PM) === 4 && $PM[2] != 0) {
166  if ($PM[1]) {
167  $expandedPageUid = $PM[2];
168  $doExpand = true;
169  } else {
170  $collapsedPageUid = $PM[2];
171  $doCollapse = true;
172  }
173  }
174  // We need to count the opened <ul>'s every time we dig into another level,
175  // so we know how many we have to close when all children are done rendering
176  $closeDepth = array();
177  $ajaxOutput = '';
178  $invertedDepthOfAjaxRequestedItem = 0;
179  foreach ($treeArr as $k => $treeItem) {
180  $classAttr = $treeItem['row']['_CSSCLASS'];
181  $uid = $treeItem['row']['uid'];
182  $idAttr = htmlspecialchars($this->domIdPrefix . $this->getId($treeItem['row']) . '_' . $treeItem['bank']);
183  $itemHTML = '';
184  // If this item is the start of a new level,
185  // then a new level <ul> is needed, but not in ajax mode
186  if ($treeItem['isFirst'] && !$doCollapse && (!$doExpand || (int)$expandedPageUid !== (int)$uid)) {
187  $itemHTML = '<ul class="list-tree">';
188  }
189 
190  // Add CSS classes to the list item
191  if ($treeItem['hasSub']) {
192  $classAttr .= ' list-tree-control-open';
193  }
194  $itemHTML .= '<li id="' . $idAttr . '" ' . ($classAttr ? ' class="' . trim($classAttr) . '"' : '')
195  . '><span class="list-tree-group">' . $treeItem['HTML']
196  . $this->wrapTitle($this->getTitleStr($treeItem['row'], $titleLen), $treeItem['row'], $treeItem['bank']) . '</span>';
197  if (!$treeItem['hasSub']) {
198  $itemHTML .= '</li>';
199  }
200 
201  // We have to remember if this is the last one
202  // on level X so the last child on level X+1 closes the <ul>-tag
203  if ($treeItem['isLast'] && !($doExpand && $expandedPageUid == $uid)) {
204  $closeDepth[$treeItem['invertedDepth']] = 1;
205  }
206  // If this is the last one and does not have subitems, we need to close
207  // the tree as long as the upper levels have last items too
208  if ($treeItem['isLast'] && !$treeItem['hasSub'] && !$doCollapse && !($doExpand && $expandedPageUid == $uid)) {
209  for ($i = $treeItem['invertedDepth']; $closeDepth[$i] == 1; $i++) {
210  $closeDepth[$i] = 0;
211  $itemHTML .= '</ul></li>';
212  }
213  }
214  // Ajax request: collapse
215  if ($doCollapse && (int)$collapsedPageUid === (int)$uid) {
216  $this->ajaxStatus = true;
217  return $itemHTML;
218  }
219  // ajax request: expand
220  if ($doExpand && (int)$expandedPageUid === (int)$uid) {
221  $ajaxOutput .= $itemHTML;
222  $invertedDepthOfAjaxRequestedItem = $treeItem['invertedDepth'];
223  } elseif ($invertedDepthOfAjaxRequestedItem) {
224  if ($treeItem['invertedDepth'] < $invertedDepthOfAjaxRequestedItem) {
225  $ajaxOutput .= $itemHTML;
226  } else {
227  $this->ajaxStatus = true;
228  return $ajaxOutput;
229  }
230  }
231  $out .= $itemHTML;
232  }
233  if ($ajaxOutput) {
234  $this->ajaxStatus = true;
235  return $ajaxOutput;
236  }
237  // Finally close the first ul
238  $out .= '</ul>';
239  return $out;
240  }
241 
254  public function PMicon($row, $a, $c, $nextCount, $exp)
255  {
256  $icon = '';
257  if ($nextCount) {
258  $cmd = $this->bank . '_' . ($exp ? '0_' : '1_') . $row['uid'] . '_' . $this->treeName;
259  $icon = $this->PMiconATagWrap($icon, $cmd, !$exp);
260  }
261  return $icon;
262  }
263 
273  public function PMiconATagWrap($icon, $cmd, $isExpand = true)
274  {
275  if ($this->thisScript) {
276  // Activate dynamic ajax-based tree
277  $js = htmlspecialchars('Tree.load(' . GeneralUtility::quoteJSvalue($cmd) . ', ' . (int)$isExpand . ', this);');
278  return '<a class="list-tree-control' . (!$isExpand ? ' list-tree-control-open' : ' list-tree-control-closed') . '" onclick="' . $js . '"><i class="fa"></i></a>';
279  } else {
280  return $icon;
281  }
282  }
283 
290  public function getBrowsableTree()
291  {
292  // Get stored tree structure AND updating it if needed according to incoming PM GET var.
293  $this->initializePositionSaving();
294  // Init done:
295  $treeArr = array();
296  // Traverse mounts:
297  $firstHtml = '';
298  foreach ($this->MOUNTS as $idx => $uid) {
299  // Set first:
300  $this->bank = $idx;
301  $isOpen = $this->stored[$idx][$uid] || $this->expandFirst || $uid === '0';
302  // Save ids while resetting everything else.
303  $curIds = $this->ids;
304  $this->reset();
305  $this->ids = $curIds;
306  // Only, if not for uid 0
307  if ($uid) {
308  // Set PM icon for root of mount:
309  $cmd = $this->bank . '_' . ($isOpen ? '0_' : '1_') . $uid . '_' . $this->treeName;
310  $firstHtml = '<a class="list-tree-control list-tree-control-' . ($isOpen ? 'open' : 'closed')
311  . '" href="' . htmlspecialchars($this->getThisScript() . 'PM=' . $cmd) . '"><i class="fa"></i></a>';
312  }
313  // Preparing rootRec for the mount
314  if ($uid) {
315  $rootRec = $this->getRecord($uid);
316  $firstHtml .= $this->getIcon($rootRec);
317  } else {
318  // Artificial record for the tree root, id=0
319  $rootRec = $this->getRootRecord();
320  $firstHtml .= $this->getRootIcon($rootRec);
321  }
322  if (is_array($rootRec)) {
323  // In case it was swapped inside getRecord due to workspaces.
324  $uid = $rootRec['uid'];
325  // Add the root of the mount to ->tree
326  $this->tree[] = array('HTML' => $firstHtml, 'row' => $rootRec, 'bank' => $this->bank, 'hasSub' => true, 'invertedDepth' => 1000);
327  // If the mount is expanded, go down:
328  if ($isOpen) {
329  // Set depth:
330  if ($this->addSelfId) {
331  $this->ids[] = $uid;
332  }
333  $this->getTree($uid);
334  }
335  // Add tree:
336  $treeArr = array_merge($treeArr, $this->tree);
337  }
338  }
339  return $this->printTree($treeArr);
340  }
341 
342 }