[TASK] Call explicit render() on icon objects
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / Controller / TranslationStatusController.php
1 <?php
2
3 namespace TYPO3\CMS\Frontend\Controller;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use TYPO3\CMS\Backend\Tree\View\PageTreeView;
19 use TYPO3\CMS\Backend\Utility\BackendUtility;
20 use TYPO3\CMS\Core\Imaging\Icon;
21 use TYPO3\CMS\Core\Imaging\IconFactory;
22 use TYPO3\CMS\Core\Utility\GeneralUtility;
23
24 /**
25 * Class for displaying translation status of pages in the tree.
26 */
27 class TranslationStatusController extends \TYPO3\CMS\Backend\Module\AbstractFunctionModule {
28
29 /**
30 * @var IconFactory
31 */
32 protected $iconFactory;
33
34 /**
35 * Construct for initialize class variables
36 */
37 public function __construct() {
38 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
39 }
40
41 /**
42 * Returns the menu array
43 *
44 * @return array
45 */
46 public function modMenu() {
47 $lang = $this->getLanguageService();
48 $menuArray = array(
49 'depth' => array(
50 0 => $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_0'),
51 1 => $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_1'),
52 2 => $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_2'),
53 3 => $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_3'),
54 4 => $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_4'),
55 999 => $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_infi')
56 )
57 );
58 // Languages:
59 $lang = $this->getSystemLanguages();
60 $menuArray['lang'] = array(
61 0 => '[All]'
62 );
63 foreach ($lang as $langRec) {
64 $menuArray['lang'][$langRec['uid']] = $langRec['title'];
65 }
66 return $menuArray;
67 }
68
69 /**
70 * MAIN function for page information of localization
71 *
72 * @return string Output HTML for the module.
73 */
74 public function main() {
75 $theOutput = $this->pObj->doc->header($this->getLanguageService()->sL('LLL:EXT:frontend/Resources/Private/Language/locallang_webinfo.xlf:lang_title'));
76 if ($this->pObj->id) {
77 // Depth selector:
78 $theOutput .= '<div class="form-inline form-inline-spaced">';
79 $h_func = BackendUtility::getDropdownMenu($this->pObj->id, 'SET[depth]', $this->pObj->MOD_SETTINGS['depth'], $this->pObj->MOD_MENU['depth']);
80 $h_func .= BackendUtility::getDropdownMenu($this->pObj->id, 'SET[lang]', $this->pObj->MOD_SETTINGS['lang'], $this->pObj->MOD_MENU['lang']);
81 $theOutput .= $h_func;
82 // Add CSH:
83 $theOutput .= BackendUtility::cshItem('_MOD_web_info', 'lang', NULL, '|<br />');
84 $theOutput .= '</div>';
85 // Showing the tree:
86 // Initialize starting point of page tree:
87 $treeStartingPoint = (int)$this->pObj->id;
88 $treeStartingRecord = BackendUtility::getRecordWSOL('pages', $treeStartingPoint);
89 $depth = $this->pObj->MOD_SETTINGS['depth'];
90 // Initialize tree object:
91 $tree = GeneralUtility::makeInstance(PageTreeView::class);
92 $tree->init('AND ' . $this->getBackendUser()->getPagePermsClause(1));
93 $tree->addField('l18n_cfg');
94 // Creating top icon; the current page
95 $HTML = $this->iconFactory->getIconForRecord('pages', $treeStartingRecord, Icon::SIZE_SMALL)->render();
96 $tree->tree[] = array(
97 'row' => $treeStartingRecord,
98 'HTML' => $HTML
99 );
100 // Create the tree from starting point:
101 if ($depth) {
102 $tree->getTree($treeStartingPoint, $depth, '');
103 }
104 // Render information table:
105 $theOutput .= $this->renderL10nTable($tree);
106 }
107 return $theOutput;
108 }
109
110 /**
111 * Rendering the localization information table.
112 *
113 * @param array $tree The Page tree data
114 * @return string HTML for the localization information table.
115 */
116 public function renderL10nTable(&$tree) {
117 $lang = $this->getLanguageService();
118 // System languages retrieved:
119 $languages = $this->getSystemLanguages();
120 // Title length:
121 $titleLen = $this->getBackendUser()->uc['titleLen'];
122 // Put together the TREE:
123 $output = '';
124 $newOL_js = array();
125 $langRecUids = array();
126 foreach ($tree->tree as $data) {
127 $tCells = array();
128 $langRecUids[0][] = $data['row']['uid'];
129 // Page icons / titles etc.
130 $tCells[] = '<td' . ($data['row']['_CSSCLASS'] ? ' class="' . $data['row']['_CSSCLASS'] . '"' : '') . '>' .
131 ($data['depthData'] ?: '') .
132 $GLOBALS['SOBE']->doc->wrapClickMenuOnIcon($data['HTML'], 'pages', $data['row']['uid']) .
133 '<a href="#" onclick="' . htmlspecialchars(
134 'top.loadEditId(' . (int)$data['row']['uid'] . ',"&SET[language]=0"); return false;'
135 ) . '" title="' . $lang->sL('LLL:EXT:frontend/Resources/Private/Language/locallang_webinfo.xlf:lang_renderl10n_editPage') . '">' .
136 htmlspecialchars(GeneralUtility::fixed_lgd_cs($data['row']['title'], $titleLen)) .
137 '</a>' .
138 ((string)$data['row']['nav_title'] !== '' ? ' [Nav: <em>' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($data['row']['nav_title'], $titleLen)) . '</em>]' : '') .
139 '</td>';
140 // DEFAULT language:
141 // "View page" link is created:
142 $viewPageLink = '<a href="#" onclick="' . htmlspecialchars(BackendUtility::viewOnClick(
143 $data['row']['uid'], '', '', '', '', '&L=###LANG_UID###')
144 ) . '" title="' . $lang->sL('LLL:EXT:frontend/Resources/Private/Language/locallang_webinfo.xlf:lang_renderl10n_viewPage') . '">' .
145 $this->iconFactory->getIcon('actions-document-view', Icon::SIZE_SMALL)->render() . '</a>';
146 $status = $data['row']['l18n_cfg'] & 1 ? 'danger' : 'success';
147 // Create links:
148 $info = '';
149 $editUid = $data['row']['uid'];
150 $params = '&edit[pages][' . $editUid . ']=edit';
151 $info .= '<a href="#" onclick="' . htmlspecialchars(BackendUtility::editOnClick($params))
152 . '" title="' . $lang->sL(
153 'LLL:EXT:frontend/Resources/Private/Language/locallang_webinfo.xlf:lang_renderl10n_editDefaultLanguagePage'
154 ) . '">' . $this->iconFactory->getIcon('actions-document-open', Icon::SIZE_SMALL)->render() . '</a>';
155 $info .= str_replace('###LANG_UID###', '0', $viewPageLink);
156 $info .= '&nbsp;';
157 $info .= $data['row']['l18n_cfg'] & 1 ? '<span title="' . $lang->sL('LLL:EXT:frontend/Resources/Private/Language/locallang_tca.xlf:pages.l18n_cfg.I.1', TRUE) . '">D</span>' : '&nbsp;';
158 $info .= GeneralUtility::hideIfNotTranslated($data['row']['l18n_cfg']) ? '<span title="' . $lang->sL('LLL:EXT:frontend/Resources/Private/Language/locallang_tca.xlf:pages.l18n_cfg.I.2', TRUE) . '">N</span>' : '&nbsp;';
159 // Put into cell:
160 $tCells[] = '<td class="' . $status . ' col-border-left">' . $info . '</td>';
161 $tCells[] = '<td class="' . $status . '" title="' . $lang->sL(
162 'LLL:EXT:frontend/Resources/Private/Language/locallang_webinfo.xlf:lang_renderl10n_CEcount'
163 ) . '" align="center">' . $this->getContentElementCount($data['row']['uid'], 0) . '</td>';
164 $modSharedTSconfig = BackendUtility::getModTSconfig($data['row']['uid'], 'mod.SHARED');
165 $disableLanguages = isset($modSharedTSconfig['properties']['disableLanguages']) ? GeneralUtility::trimExplode(',', $modSharedTSconfig['properties']['disableLanguages'], TRUE) : array();
166 // Traverse system languages:
167 foreach ($languages as $langRow) {
168 if ($this->pObj->MOD_SETTINGS['lang'] == 0 || (int)$this->pObj->MOD_SETTINGS['lang'] === (int)$langRow['uid']) {
169 $row = $this->getLangStatus($data['row']['uid'], $langRow['uid']);
170 $info = '';
171 if (is_array($row)) {
172 $langRecUids[$langRow['uid']][] = $row['uid'];
173 $status = $row['_HIDDEN'] ? (GeneralUtility::hideIfNotTranslated($data['row']['l18n_cfg']) || $data['row']['l18n_cfg'] & 1 ? 'danger' : '') : 'success';
174 $icon = $this->iconFactory->getIconForRecord('pages_language_overlay', $row, Icon::SIZE_SMALL)->render();
175 $info = $icon . htmlspecialchars(
176 GeneralUtility::fixed_lgd_cs($row['title'], $titleLen)
177 ) . ((string)$row['nav_title'] !== '' ? ' [Nav: <em>' . htmlspecialchars(
178 GeneralUtility::fixed_lgd_cs($row['nav_title'], $titleLen)
179 ) . '</em>]' : '') . ($row['_COUNT'] > 1 ? '<div>' . $lang->sL(
180 'LLL:EXT:frontend/Resources/Private/Language/locallang_webinfo.xlf:lang_renderl10n_badThingThereAre'
181 ) . '</div>' : '');
182 $tCells[] = '<td class="' . $status . ' col-border-left">' .
183 '<a href="#" onclick="' . htmlspecialchars(
184 'top.loadEditId(' . (int)$data['row']['uid'] . ',"&SET[language]=' . $langRow['uid'] . '"); return false;'
185 ) . '" title="' . $lang->sL(
186 'LLL:EXT:frontend/Resources/Private/Language/locallang_webinfo.xlf:lang_renderl10n_editPageLang'
187 ) . '">' . $info . '</a></td>';
188 // Edit whole record:
189 $info = '';
190 $editUid = $row['uid'];
191 $params = '&edit[pages_language_overlay][' . $editUid . ']=edit';
192 $info .= '<a href="#" onclick="' . htmlspecialchars(BackendUtility::editOnClick($params))
193 . '" title="' . $lang->sL(
194 'LLL:EXT:frontend/Resources/Private/Language/locallang_webinfo.xlf:lang_renderl10n_editLanguageOverlayRecord'
195 ) . '">' . $this->iconFactory->getIcon('actions-document-open', Icon::SIZE_SMALL)->render() . '</a>';
196 $info .= str_replace('###LANG_UID###', $langRow['uid'], $viewPageLink);
197 $tCells[] = '<td class="' . $status . '">' . $info . '</td>';
198 $tCells[] = '<td class="' . $status . '" title="' . $lang->sL(
199 'LLL:EXT:frontend/Resources/Private/Language/locallang_webinfo.xlf:lang_renderl10n_CEcount'
200 ) . '" align="center">' . $this->getContentElementCount($data['row']['uid'], $langRow['uid']) . '</td>';
201 } else {
202 if (in_array($langRow['uid'], $disableLanguages)) {
203 // Language has been disabled for this page
204 $status = 'danger';
205 $info = '';
206 } else {
207 $status = GeneralUtility::hideIfNotTranslated($data['row']['l18n_cfg']) || $data['row']['l18n_cfg'] & 1 ? 'danger' : '';
208 $info = '<input type="checkbox" name="newOL[' . $langRow['uid'] . '][' . $data['row']['uid'] . ']" value="1" />';
209 $newOL_js[$langRow['uid']] .= '
210 +(document.webinfoForm['
211 . GeneralUtility::quoteJSvalue('newOL[' . $langRow['uid'] . '][' . $data['row']['uid'] . ']')
212 . '].checked ? '
213 . GeneralUtility::quoteJSvalue('&edit[pages_language_overlay][' . $data['row']['uid'] . ']=new')
214 . ' : \'\')
215 ';
216 }
217 $tCells[] = '<td class="' . $status . ' col-border-left">&nbsp;</td>';
218 $tCells[] = '<td class="' . $status . '">&nbsp;</td>';
219 $tCells[] = '<td class="' . $status . '">' . $info . '</td>';
220 }
221 }
222 }
223 $output .= '
224 <tr>
225 ' . implode('
226 ', $tCells) . '
227 </tr>';
228 }
229 // Put together HEADER:
230 $tCells = array();
231 $tCells[] = '<td>' . $lang->sL('LLL:EXT:frontend/Resources/Private/Language/locallang_webinfo.xlf:lang_renderl10n_page') . ':</td>';
232 if (is_array($langRecUids[0])) {
233 $params = '&edit[pages][' . implode(',', $langRecUids[0]) . ']=edit&columnsOnly=title,nav_title,l18n_cfg,hidden';
234 $editIco = '<a href="#" onclick="' . htmlspecialchars(BackendUtility::editOnClick($params))
235 . '" title="' . $lang->sL(
236 'LLL:EXT:frontend/Resources/Private/Language/locallang_webinfo.xlf:lang_renderl10n_editPageProperties'
237 ) . '">' . $this->iconFactory->getIcon('actions-document-open', Icon::SIZE_SMALL)->render() . '</a>';
238 } else {
239 $editIco = '';
240 }
241 $tCells[] = '<td class="col-border-left" colspan="2">' . $lang->sL(
242 'LLL:EXT:frontend/Resources/Private/Language/locallang_webinfo.xlf:lang_renderl10n_default'
243 ) . ':' . $editIco . '</td>';
244 foreach ($languages as $langRow) {
245 if ($this->pObj->MOD_SETTINGS['lang'] == 0 || (int)$this->pObj->MOD_SETTINGS['lang'] === (int)$langRow['uid']) {
246 // Title:
247 $tCells[] = '<td class="col-border-left">' . htmlspecialchars($langRow['title']) . '</td>';
248 // Edit language overlay records:
249 if (is_array($langRecUids[$langRow['uid']])) {
250 $params = '&edit[pages_language_overlay][' .
251 implode(',', $langRecUids[$langRow['uid']]) .
252 ']=edit&columnsOnly=title,nav_title,hidden';
253 $tCells[] = '<td><a href="#" onclick="' . htmlspecialchars(BackendUtility::editOnClick($params))
254 . '" title="' . $lang->sL(
255 'LLL:EXT:frontend/Resources/Private/Language/locallang_webinfo.xlf:lang_renderl10n_editLangOverlays'
256 ) . '">' . $this->iconFactory->getIcon('actions-document-open', Icon::SIZE_SMALL)->render() . '</a></td>';
257 } else {
258 $tCells[] = '<td>&nbsp;</td>';
259 }
260 // Create new overlay records:
261 $params = '&columnsOnly=title,hidden,sys_language_uid&overrideVals[pages_language_overlay][sys_language_uid]=' . $langRow['uid'];
262 $onClick = BackendUtility::editOnClick($params);
263 if (!empty($newOL_js[$langRow['uid']])) {
264 $onClickArray = explode('?', $onClick, 2);
265 $lastElement = array_pop($onClickArray);
266 array_push($onClickArray, '\'' . $newOL_js[$langRow['uid']] . ' + \'&' . $lastElement);
267 $onClick = implode('?', $onClickArray);
268 }
269 $tCells[] = '<td><a href="#" onclick="' . htmlspecialchars($onClick)
270 . '" title="' . $lang->sL(
271 'LLL:EXT:frontend/Resources/Private/Language/locallang_webinfo.xlf:lang_getlangsta_createNewTranslationHeaders'
272 ) . '">' . $this->iconFactory->getIcon('actions-document-new', Icon::SIZE_SMALL)->render() . '</a></td>';
273 }
274 }
275
276 $output =
277 '<div class="table-fit">' .
278 '<table class="table table-striped table-hover" id="langTable">' .
279 '<thead>' .
280 '<tr>' .
281 implode('', $tCells) .
282 '</tr>' .
283 '</thead>' .
284 '<tbody>' .
285 $output .
286 '</tbody>' .
287 '</table>' .
288 '</div>';
289 return $output;
290 }
291
292 /**
293 * Selects all system languages (from sys_language)
294 *
295 * @return array System language records in an array.
296 */
297 public function getSystemLanguages() {
298 if (!$this->getBackendUser()->user['admin'] && $this->getBackendUser()->groupData['allowed_languages'] !== '') {
299 $allowed_languages = array_flip(explode(',', $this->getBackendUser()->groupData['allowed_languages']));
300 }
301 $res = $this->getDatabaseConnection()->exec_SELECTquery('*', 'sys_language', '1=1' . BackendUtility::deleteClause('sys_language'));
302 $outputArray = array();
303 while ($row = $this->getDatabaseConnection()->sql_fetch_assoc($res)) {
304 if (is_array($allowed_languages) && !empty($allowed_languages)) {
305 if (isset($allowed_languages[$row['uid']])) {
306 $outputArray[] = $row;
307 }
308 } else {
309 $outputArray[] = $row;
310 }
311 }
312 $this->getDatabaseConnection()->sql_free_result($res);
313 return $outputArray;
314 }
315
316 /**
317 * Get an alternative language record for a specific page / language
318 *
319 * @param int $pageId Page ID to look up for.
320 * @param int $langId Language UID to select for.
321 * @return array pages_languages_overlay record
322 */
323 public function getLangStatus($pageId, $langId) {
324 $res = $this->getDatabaseConnection()->exec_SELECTquery(
325 '*',
326 'pages_language_overlay',
327 'pid=' . (int)$pageId .
328 ' AND sys_language_uid=' . (int)$langId .
329 BackendUtility::deleteClause('pages_language_overlay') .
330 BackendUtility::versioningPlaceholderClause('pages_language_overlay')
331 );
332 $row = $this->getDatabaseConnection()->sql_fetch_assoc($res);
333 BackendUtility::workspaceOL('pages_language_overlay', $row);
334 if (is_array($row)) {
335 $row['_COUNT'] = $this->getDatabaseConnection()->sql_num_rows($res);
336 $row['_HIDDEN'] = $row['hidden'] || (int)$row['endtime'] > 0 && (int)$row['endtime'] < $GLOBALS['EXEC_TIME'] || $GLOBALS['EXEC_TIME'] < (int)$row['starttime'];
337 }
338 return $row;
339 }
340
341 /**
342 * Counting content elements for a single language on a page.
343 *
344 * @param int $pageId Page id to select for.
345 * @param int $sysLang Sys language uid
346 * @return int Number of content elements from the PID where the language is set to a certain value.
347 */
348 public function getContentElementCount($pageId, $sysLang) {
349 $count = $this->getDatabaseConnection()->exec_SELECTcountRows('uid', 'tt_content', 'pid=' . (int)$pageId . ' AND sys_language_uid=' . (int)$sysLang . BackendUtility::deleteClause('tt_content') . BackendUtility::versioningPlaceholderClause('tt_content'));
350 return $count ?: '-';
351 }
352
353 /**
354 * Returns LanguageService
355 *
356 * @return \TYPO3\CMS\Lang\LanguageService
357 */
358 protected function getLanguageService() {
359 return $GLOBALS['LANG'];
360 }
361
362 /**
363 * Returns the database connection
364 *
365 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
366 */
367 protected function getDatabaseConnection() {
368 return $GLOBALS['TYPO3_DB'];
369 }
370
371 /**
372 * Returns the current BE user.
373 *
374 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
375 */
376 protected function getBackendUser() {
377 return $GLOBALS['BE_USER'];
378 }
379
380 }