[TASK] Fix CGL errors
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / View / PageTreeView.php
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
17 use TYPO3\CMS\Backend\Tree\View\BrowseTreeView;
18 use TYPO3\CMS\Backend\Utility\BackendUtility;
19 use TYPO3\CMS\Core\Imaging\Icon;
20 use TYPO3\CMS\Core\Imaging\IconFactory;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22
23 /**
24 * Browse pages in Web module
25 */
26 class PageTreeView extends BrowseTreeView
27 {
28 /**
29 * @var bool
30 */
31 public $ext_showPageId = false;
32
33 /**
34 * @var bool
35 */
36 public $ext_showNavTitle = false;
37
38 /**
39 * @var string
40 */
41 public $ext_IconMode;
42
43 /**
44 * @var string
45 */
46 public $ext_separateNotinmenuPages;
47
48 /**
49 * @var string
50 */
51 public $ext_alphasortNotinmenuPages;
52
53 /**
54 * Indicates, whether the ajax call was successful, i.e. the requested page has been found
55 *
56 * @var bool
57 */
58 public $ajaxStatus = false;
59
60 /**
61 * Calls init functions
62 */
63 public function __construct()
64 {
65 parent::__construct();
66 $this->init();
67 }
68
69 /**
70 * Wrapping icon in browse tree
71 *
72 * @param string $thePageIcon Icon IMG code
73 * @param array $row Data row for element.
74 * @return string Page icon
75 */
76 public function wrapIcon($thePageIcon, &$row)
77 {
78 /** @var $iconFactory IconFactory */
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
113 /**
114 * Wrapping $title in a-tags.
115 *
116 * @param string $title Title string
117 * @param string $row Item record
118 * @param int $bank Bank pointer (which mount point number)
119 * @return string
120 * @access private
121 */
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
140 /**
141 * Compiles the HTML code for displaying the structure found inside the ->tree array
142 *
143 * @param array|string $treeArr "tree-array" - if blank string, the internal ->tree array is used.
144 * @return string The HTML code for the tree
145 */
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
242 /**
243 * Generate the plus/minus icon for the browsable tree.
244 *
245 * @param array $row Record for the entry
246 * @param int $a The current entry number
247 * @param int $c The total number of entries. If equal to $a, a "bottom" element is returned.
248 * @param int $nextCount The number of sub-elements to the current element.
249 * @param bool $exp The element was expanded to render subelements if this flag is set.
250 * @return string Image tag with the plus/minus icon.
251 * @access private
252 * @see \TYPO3\CMS\Backend\Tree\View\PageTreeView::PMicon()
253 */
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
264 /**
265 * Wrap the plus/minus icon in a link
266 *
267 * @param string $icon HTML string to wrap, probably an image tag.
268 * @param string $cmd Command for 'PM' get var
269 * @param bool $isExpand Link-wrapped input string
270 * @return string
271 * @access private
272 */
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
284 /**
285 * Will create and return the HTML code for a browsable tree
286 * Is based on the mounts found in the internal array ->MOUNTS (set in the constructor)
287 *
288 * @return string HTML code for the browsable tree
289 */
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 }