[TASK] Refactor use of queryBuilder to use prepared statements
[Packages/TYPO3.CMS.git] / typo3 / sysext / linkvalidator / Classes / Report / LinkValidatorReport.php
1 <?php
2 namespace TYPO3\CMS\Linkvalidator\Report;
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 Doctrine\DBAL\Driver\Statement;
18 use TYPO3\CMS\Backend\Template\DocumentTemplate;
19 use TYPO3\CMS\Backend\Template\ModuleTemplate;
20 use TYPO3\CMS\Backend\Utility\BackendUtility;
21 use TYPO3\CMS\Core\Database\Connection;
22 use TYPO3\CMS\Core\Database\ConnectionPool;
23 use TYPO3\CMS\Core\Imaging\Icon;
24 use TYPO3\CMS\Core\Imaging\IconFactory;
25 use TYPO3\CMS\Core\Messaging\FlashMessage;
26 use TYPO3\CMS\Core\Messaging\FlashMessageService;
27 use TYPO3\CMS\Core\Service\MarkerBasedTemplateService;
28 use TYPO3\CMS\Core\Utility\GeneralUtility;
29 use TYPO3\CMS\Linkvalidator\LinkAnalyzer;
30
31 /**
32 * Module 'Linkvalidator' for the 'linkvalidator' extension
33 */
34 class LinkValidatorReport extends \TYPO3\CMS\Backend\Module\AbstractFunctionModule
35 {
36 /**
37 * @var DocumentTemplate
38 */
39 public $doc;
40
41 /**
42 * Information about the current page record
43 *
44 * @var array
45 */
46 protected $pageRecord = [];
47
48 /**
49 * Information, if the module is accessible for the current user or not
50 *
51 * @var bool
52 */
53 protected $isAccessibleForCurrentUser = false;
54
55 /**
56 * Depth for the recursive traversal of pages for the link validation
57 *
58 * @var int
59 */
60 protected $searchLevel;
61
62 /**
63 * Link validation class
64 *
65 * @var LinkAnalyzer
66 */
67 protected $linkAnalyzer;
68
69 /**
70 * TSconfig of the current module
71 *
72 * @var array
73 */
74 protected $modTS = [];
75
76 /**
77 * List of available link types to check defined in the TSconfig
78 *
79 * @var array
80 */
81 protected $availableOptions = [];
82
83 /**
84 * List of link types currently chosen in the statistics table
85 * Used to show broken links of these types only
86 *
87 * @var array
88 */
89 protected $checkOpt = [];
90
91 /**
92 * Html for the statistics table with the checkboxes of the link types
93 * and the numbers of broken links for report tab
94 *
95 * @var string
96 */
97 protected $checkOptionsHtml;
98
99 /**
100 * Html for the statistics table with the checkboxes of the link types
101 * and the numbers of broken links for check links tab
102 *
103 * @var string
104 */
105 protected $checkOptionsHtmlCheck;
106
107 /**
108 * Complete content (html) to be displayed
109 *
110 * @var string
111 */
112 protected $content;
113
114 /**
115 * @var \TYPO3\CMS\Linkvalidator\Linktype\LinktypeInterface[]
116 */
117 protected $hookObjectsArr = [];
118
119 /**
120 * @var string
121 */
122 protected $updateListHtml = '';
123
124 /**
125 * @var string
126 */
127 protected $refreshListHtml = '';
128
129 /**
130 * @var MarkerBasedTemplateService
131 */
132 protected $templateService;
133
134 /**
135 * @var IconFactory
136 */
137 protected $iconFactory;
138
139 /**
140 * Main method of modfuncreport
141 *
142 * @return string Module content
143 */
144 public function main()
145 {
146 $this->getLanguageService()->includeLLFile('EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf');
147 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
148 $this->searchLevel = GeneralUtility::_GP('search_levels');
149 if (isset($this->pObj->id)) {
150 $this->modTS = BackendUtility::getModTSconfig($this->pObj->id, 'mod.linkvalidator');
151 $this->modTS = $this->modTS['properties'];
152 }
153 $update = GeneralUtility::_GP('updateLinkList');
154 $prefix = '';
155 if (!empty($update)) {
156 $prefix = 'check';
157 }
158 $set = GeneralUtility::_GP($prefix . 'SET');
159 $this->pObj->handleExternalFunctionValue();
160 if (isset($this->searchLevel)) {
161 $this->pObj->MOD_SETTINGS['searchlevel'] = $this->searchLevel;
162 } else {
163 $this->searchLevel = $this->pObj->MOD_SETTINGS['searchlevel'];
164 }
165 if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])) {
166 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'] as $linkType => $value) {
167 // Compile list of all available types. Used for checking with button "Check Links".
168 if (strpos($this->modTS['linktypes'], $linkType) !== false) {
169 $this->availableOptions[$linkType] = 1;
170 }
171 // Compile list of types currently selected by the checkboxes
172 if ($this->pObj->MOD_SETTINGS[$linkType] && empty($set) || $set[$linkType]) {
173 $this->checkOpt[$linkType] = 1;
174 $this->pObj->MOD_SETTINGS[$linkType] = 1;
175 } else {
176 $this->pObj->MOD_SETTINGS[$linkType] = 0;
177 unset($this->checkOpt[$linkType]);
178 }
179 }
180 }
181 $this->getBackendUser()->pushModuleData('web_info', $this->pObj->MOD_SETTINGS);
182 $this->initialize();
183
184 // Localization
185 $this->getPageRenderer()->addInlineLanguageLabelFile('EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf');
186
187 if ($this->modTS['showCheckLinkTab'] == 1) {
188 $this->updateListHtml = '<input class="btn btn-default t3js-update-button" type="submit" name="updateLinkList" id="updateLinkList" value="' . htmlspecialchars($this->getLanguageService()->getLL('label_update')) . '" data-notification-message="' . htmlspecialchars($this->getLanguageService()->getLL('label_update-link-list')) . '"/>';
189 }
190 $this->refreshListHtml = '<input class="btn btn-default t3js-update-button" type="submit" name="refreshLinkList" id="refreshLinkList" value="' . htmlspecialchars($this->getLanguageService()->getLL('label_refresh')) . '" data-notification-message="' . htmlspecialchars($this->getLanguageService()->getLL('label_refresh-link-list')) . '"/>';
191 $this->linkAnalyzer = GeneralUtility::makeInstance(LinkAnalyzer::class);
192 $this->updateBrokenLinks();
193
194 $brokenLinkOverView = $this->linkAnalyzer->getLinkCounts($this->pObj->id);
195 $this->checkOptionsHtml = $this->getCheckOptions($brokenLinkOverView);
196 $this->checkOptionsHtmlCheck = $this->getCheckOptions($brokenLinkOverView, 'check');
197 $this->render();
198
199 $pageTile = '';
200 if ($this->pObj->id) {
201 $pageRecord = BackendUtility::getRecord('pages', $this->pObj->id);
202 $pageTile = '<h1>' . htmlspecialchars(BackendUtility::getRecordTitle('pages', $pageRecord)) . '</h1>';
203 }
204
205 return '<div id="linkvalidator-modfuncreport">' . $pageTile . $this->createTabs() . '</div>';
206 }
207
208 /**
209 * Create tabs to split the report and the checkLink functions
210 *
211 * @return string
212 */
213 protected function createTabs()
214 {
215 $menuItems = [
216 0 => [
217 'label' => $this->getLanguageService()->getLL('Report'),
218 'content' => $this->flush(true)
219 ],
220 ];
221
222 if ((bool)$this->modTS['showCheckLinkTab']) {
223 $menuItems[1] = [
224 'label' => $this->getLanguageService()->getLL('CheckLink'),
225 'content' => $this->flush()
226 ];
227 }
228
229 // @todo: Use $this-moduleTemplate as soon as this class extends from AbstractModule
230 /** @var ModuleTemplate $moduleTemplate */
231 $moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
232 return $moduleTemplate->getDynamicTabMenu($menuItems, 'report-linkvalidator');
233 }
234
235 /**
236 * Initializes the Module
237 *
238 * @return void
239 */
240 protected function initialize()
241 {
242 if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])) {
243 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'] as $linkType => $classRef) {
244 $this->hookObjectsArr[$linkType] = GeneralUtility::getUserObj($classRef);
245 }
246 }
247
248 $this->doc = GeneralUtility::makeInstance(DocumentTemplate::class);
249 $this->doc->setModuleTemplate('EXT:linkvalidator/Resources/Private/Templates/mod_template.html');
250
251 $this->pageRecord = BackendUtility::readPageAccess($this->pObj->id, $this->getBackendUser()->getPagePermsClause(1));
252 if ($this->pObj->id && is_array($this->pageRecord) || !$this->pObj->id && $this->isCurrentUserAdmin()) {
253 $this->isAccessibleForCurrentUser = true;
254 }
255
256 $this->getPageRenderer()->addCssFile('EXT:linkvalidator/Resources/Public/Css/linkvalidator.css', 'stylesheet', 'screen');
257 $this->getPageRenderer()->loadJquery();
258 $this->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Linkvalidator/Linkvalidator');
259
260 $this->templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
261
262 // Don't access in workspace
263 if ($this->getBackendUser()->workspace !== 0) {
264 $this->isAccessibleForCurrentUser = false;
265 }
266 }
267
268 /**
269 * Updates the table of stored broken links
270 *
271 * @return void
272 */
273 protected function updateBrokenLinks()
274 {
275 $searchFields = [];
276 // Get the searchFields from TypoScript
277 foreach ($this->modTS['searchFields.'] as $table => $fieldList) {
278 $fields = GeneralUtility::trimExplode(',', $fieldList, true);
279 foreach ($fields as $field) {
280 if (!$searchFields || !is_array($searchFields[$table]) || array_search($field, $searchFields[$table]) === false) {
281 $searchFields[$table][] = $field;
282 }
283 }
284 }
285 $rootLineHidden = $this->linkAnalyzer->getRootLineIsHidden($this->pObj->pageinfo);
286 if (!$rootLineHidden || $this->modTS['checkhidden'] == 1) {
287 // Get children pages
288 $pageList = $this->linkAnalyzer->extGetTreeList(
289 $this->pObj->id,
290 $this->searchLevel,
291 0,
292 $this->getBackendUser()->getPagePermsClause(1),
293 $this->modTS['checkhidden']
294 );
295 if ($this->pObj->pageinfo['hidden'] == 0 || $this->modTS['checkhidden']) {
296 $pageList .= $this->pObj->id;
297 }
298
299 $this->linkAnalyzer->init($searchFields, $pageList, $this->modTS);
300
301 // Check if button press
302 $update = GeneralUtility::_GP('updateLinkList');
303 if (!empty($update)) {
304 $this->linkAnalyzer->getLinkStatistics($this->checkOpt, $this->modTS['checkhidden']);
305 }
306 }
307 }
308
309 /**
310 * Renders the content of the module
311 *
312 * @return void
313 */
314 protected function render()
315 {
316 if ($this->isAccessibleForCurrentUser) {
317 $this->content = $this->renderBrokenLinksTable();
318 } else {
319 // If no access or if ID == zero
320 /** @var FlashMessage $message */
321 $message = GeneralUtility::makeInstance(
322 FlashMessage::class,
323 $this->getLanguageService()->getLL('no.access'),
324 $this->getLanguageService()->getLL('no.access.title'),
325 FlashMessage::ERROR
326 );
327 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
328 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
329 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
330 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
331 $defaultFlashMessageQueue->enqueue($message);
332 }
333 }
334
335 /**
336 * Flushes the rendered content to the browser
337 *
338 * @param bool $form
339 * @return string $content
340 */
341 protected function flush($form = false)
342 {
343 return $this->doc->moduleBody(
344 $this->pageRecord,
345 $this->getDocHeaderButtons(),
346 $form ? $this->getTemplateMarkers() : $this->getTemplateMarkersCheck()
347 );
348 }
349
350 /**
351 * Builds the selector for the level of pages to search
352 *
353 * @return string Html code of that selector
354 */
355 protected function getLevelSelector()
356 {
357 // Build level selector
358 $options = [];
359 $availableOptions = [
360 0 => $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_0'),
361 1 => $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_1'),
362 2 => $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_2'),
363 3 => $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_3'),
364 4 => $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_4'),
365 999 => $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_infi')
366 ];
367 foreach ($availableOptions as $optionValue => $optionLabel) {
368 $options[] = '<option value="' . $optionValue . '"' . ($optionValue === (int)$this->searchLevel ? ' selected="selected"' : '') . '>' . htmlspecialchars($optionLabel) . '</option>';
369 }
370 return '<select name="search_levels" class="form-control">' . implode('', $options) . '</select>';
371 }
372
373 /**
374 * Displays the table of broken links or a note if there were no broken links
375 *
376 * @return string Content of the table or of the note
377 */
378 protected function renderBrokenLinksTable()
379 {
380 $brokenLinkItems = '';
381 $brokenLinksTemplate = $this->templateService->getSubpart(
382 $this->doc->moduleTemplate,
383 '###NOBROKENLINKS_CONTENT###'
384 );
385
386 $linkTypes = [];
387 if (is_array($this->checkOpt)) {
388 $linkTypes = array_keys($this->checkOpt);
389 }
390
391 // Table header
392 $brokenLinksMarker = $this->startTable();
393
394 $rootLineHidden = $this->linkAnalyzer->getRootLineIsHidden($this->pObj->pageinfo);
395 if (!$rootLineHidden || (bool)$this->modTS['checkhidden']) {
396 $pageList = $this->getPageList($this->pObj->id);
397 $result = $this->getLinkValidatorBrokenLinks($pageList, $linkTypes);
398
399 if ($result->rowCount()) {
400 // Display table with broken links
401 $brokenLinksTemplate = $this->templateService->getSubpart(
402 $this->doc->moduleTemplate,
403 '###BROKENLINKS_CONTENT###'
404 );
405 $brokenLinksItemTemplate = $this->templateService->getSubpart(
406 $this->doc->moduleTemplate,
407 '###BROKENLINKS_ITEM###'
408 );
409
410 // Table rows containing the broken links
411 $items = [];
412 while ($row = $result->fetch()) {
413 $items[] = $this->renderTableRow($row['table_name'], $row, $brokenLinksItemTemplate);
414 }
415 $brokenLinkItems = implode(LF, $items);
416 } else {
417 $brokenLinksMarker = $this->getNoBrokenLinkMessage($brokenLinksMarker);
418 }
419 } else {
420 $brokenLinksMarker = $this->getNoBrokenLinkMessage($brokenLinksMarker);
421 }
422
423 $brokenLinksTemplate = $this->templateService->substituteMarkerArray(
424 $brokenLinksTemplate,
425 $brokenLinksMarker,
426 '###|###',
427 true
428 );
429
430 return $this->templateService->substituteSubpart($brokenLinksTemplate, '###BROKENLINKS_ITEM', $brokenLinkItems);
431 }
432
433 /**
434 * Generates an array of page uids from current pageUid.
435 * List does include pageUid itself.
436 *
437 * @param int $currentPageUid
438 * @return array
439 */
440 protected function getPageList(int $currentPageUid): array
441 {
442 $pageList = $this->linkAnalyzer->extGetTreeList(
443 $currentPageUid,
444 $this->searchLevel,
445 0,
446 $this->getBackendUser()->getPagePermsClause(1),
447 $this->modTS['checkhidden']
448 );
449 // Always add the current page, because we are just displaying the results
450 $pageList .= $currentPageUid;
451
452 return GeneralUtility::intExplode(',', $pageList, true);
453 }
454
455 /**
456 * Prepare database query with pageList and keyOpt data.
457 *
458 * @param int[] $pageList Pages to check for broken links
459 * @param string[] $linkTypes Link types to validate
460 * @return Statement
461 */
462 protected function getLinkValidatorBrokenLinks(array $pageList, array $linkTypes): Statement
463 {
464 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
465 ->getQueryBuilderForTable('tx_linkvalidator_link');
466 $queryBuilder
467 ->select('*')
468 ->from('tx_linkvalidator_link')
469 ->where(
470 $queryBuilder->expr()->in(
471 'record_pid',
472 $queryBuilder->createNamedParameter($pageList, Connection::PARAM_INT_ARRAY)
473 )
474 )
475 ->orderBy('record_uid')
476 ->addOrderBy('uid');
477
478 if (!empty($linkTypes)) {
479 $queryBuilder->andWhere(
480 $queryBuilder->expr()->in(
481 'link_type',
482 $queryBuilder->createNamedParameter($linkTypes, Connection::PARAM_STR_ARRAY)
483 )
484 );
485 }
486
487 return $queryBuilder->execute();
488 }
489
490 /**
491 * Replace $brokenLinksMarker['NO_BROKEN_LINKS] with localized flashmessage
492 *
493 * @param array $brokenLinksMarker
494 * @return array $brokenLinksMarker['NO_BROKEN_LINKS] replaced with flashmessage
495 */
496 protected function getNoBrokenLinkMessage(array $brokenLinksMarker)
497 {
498 $brokenLinksMarker['LIST_HEADER'] = '<h3>' . htmlspecialchars($this->getLanguageService()->getLL('list.header')) . '</h3>';
499 /** @var $message FlashMessage */
500 $message = GeneralUtility::makeInstance(
501 FlashMessage::class,
502 $this->getLanguageService()->getLL('list.no.broken.links'),
503 $this->getLanguageService()->getLL('list.no.broken.links.title'),
504 FlashMessage::OK
505 );
506 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
507 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
508 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
509 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
510 $defaultFlashMessageQueue->enqueue($message);
511 $brokenLinksMarker['NO_BROKEN_LINKS'] = $defaultFlashMessageQueue->renderFlashMessages();
512 return $brokenLinksMarker;
513 }
514
515 /**
516 * Displays the table header of the table with the broken links
517 *
518 * @return string Code of content
519 */
520 protected function startTable()
521 {
522 // Listing head
523 $makerTableHead = [
524 'tablehead_path' => $this->getLanguageService()->getLL('list.tableHead.path'),
525 'tablehead_element' => $this->getLanguageService()->getLL('list.tableHead.element'),
526 'tablehead_headlink' => $this->getLanguageService()->getLL('list.tableHead.headlink'),
527 'tablehead_linktarget' => $this->getLanguageService()->getLL('list.tableHead.linktarget'),
528 'tablehead_linkmessage' => $this->getLanguageService()->getLL('list.tableHead.linkmessage'),
529 'tablehead_lastcheck' => $this->getLanguageService()->getLL('list.tableHead.lastCheck'),
530 ];
531
532 // Add CSH to the header of each column
533 foreach ($makerTableHead as $column => $label) {
534 $makerTableHead[$column] = BackendUtility::wrapInHelp('linkvalidator', $column, $label);
535 }
536 // Add section header
537 $makerTableHead['list_header'] = '<h3>' . htmlspecialchars($this->getLanguageService()->getLL('list.header')) . '</h3>';
538 return $makerTableHead;
539 }
540
541 /**
542 * Displays one line of the broken links table
543 *
544 * @param string $table Name of database table
545 * @param array $row Record row to be processed
546 * @param array $brokenLinksItemTemplate Markup of the template to be used
547 * @return string HTML of the rendered row
548 */
549 protected function renderTableRow($table, array $row, $brokenLinksItemTemplate)
550 {
551 $markerArray = [];
552 $fieldName = '';
553 // Restore the linktype object
554 $hookObj = $this->hookObjectsArr[$row['link_type']];
555
556 // Construct link to edit the content element
557 $requestUri = GeneralUtility::getIndpEnv('REQUEST_URI') .
558 '&id=' . $this->pObj->id .
559 '&search_levels=' . $this->searchLevel;
560 $url = BackendUtility::getModuleUrl('record_edit', [
561 'edit' => [
562 $table => [
563 $row['record_uid'] => 'edit'
564 ]
565 ],
566 'returnUrl' => $requestUri
567 ]);
568 $actionLinkOpen = '<a href="' . htmlspecialchars($url);
569 $actionLinkOpen .= '" title="' . htmlspecialchars($this->getLanguageService()->getLL('list.edit')) . '">';
570 $actionLinkClose = '</a>';
571 $elementHeadline = $row['headline'];
572 // Get the language label for the field from TCA
573 if ($GLOBALS['TCA'][$table]['columns'][$row['field']]['label']) {
574 $fieldName = $this->getLanguageService()->sL($GLOBALS['TCA'][$table]['columns'][$row['field']]['label']);
575 // Crop colon from end if present
576 if (substr($fieldName, '-1', '1') === ':') {
577 $fieldName = substr($fieldName, '0', strlen($fieldName) - 1);
578 }
579 }
580 // Fallback, if there is no label
581 $fieldName = !empty($fieldName) ? $fieldName : $row['field'];
582 // column "Element"
583 $element = '<span title="' . htmlspecialchars($table . ':' . $row['record_uid']) . '">' . $this->iconFactory->getIconForRecord($table, $row, Icon::SIZE_SMALL)->render() . '</span>';
584 if (empty($elementHeadline)) {
585 $element .= '<i>' . htmlspecialchars($this->getLanguageService()->getLL('list.no.headline')) . '</i>';
586 } else {
587 $element .= htmlspecialchars($elementHeadline);
588 }
589 $element .= ' ' . htmlspecialchars(sprintf($this->getLanguageService()->getLL('list.field'), $fieldName));
590 $markerArray['actionlinkOpen'] = $actionLinkOpen;
591 $markerArray['actionlinkClose'] = $actionLinkClose;
592 $markerArray['actionlinkIcon'] = $this->iconFactory->getIcon('actions-open', Icon::SIZE_SMALL)->render();
593 $markerArray['path'] = BackendUtility::getRecordPath($row['record_pid'], '', 0, 0);
594 $markerArray['element'] = $element;
595 $markerArray['headlink'] = htmlspecialchars($row['link_title']);
596 $markerArray['linktarget'] = htmlspecialchars($hookObj->getBrokenUrl($row));
597 $response = unserialize($row['url_response']);
598 if ($response['valid']) {
599 $linkMessage = '<span class="valid">' . htmlspecialchars($this->getLanguageService()->getLL('list.msg.ok')) . '</span>';
600 } else {
601 $linkMessage = '<span class="error">'
602 . nl2br(
603 // Encode for output
604 htmlspecialchars(
605 $hookObj->getErrorMessage($response['errorParams']),
606 ENT_QUOTES,
607 'UTF-8',
608 false
609 )
610 )
611 . '</span>';
612 }
613 $markerArray['linkmessage'] = $linkMessage;
614
615 $lastRunDate = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], $row['last_check']);
616 $lastRunTime = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'], $row['last_check']);
617 $markerArray['lastcheck'] = htmlspecialchars(sprintf($this->getLanguageService()->getLL('list.msg.lastRun'), $lastRunDate, $lastRunTime));
618
619 // Return the table html code as string
620 return $this->templateService->substituteMarkerArray($brokenLinksItemTemplate, $markerArray, '###|###', true, true);
621 }
622
623 /**
624 * Builds the checkboxes out of the hooks array
625 *
626 * @param array $brokenLinkOverView Array of broken links information
627 * @param string $prefix
628 * @return string code content
629 */
630 protected function getCheckOptions(array $brokenLinkOverView, $prefix = '')
631 {
632 $markerArray = [];
633 if (!empty($prefix)) {
634 $additionalAttr = ' class="' . $prefix . '"';
635 } else {
636 $additionalAttr = ' class="refresh"';
637 }
638 $checkOptionsTemplate = $this->templateService->getSubpart($this->doc->moduleTemplate, '###CHECKOPTIONS_SECTION###');
639 $hookSectionTemplate = $this->templateService->getSubpart($checkOptionsTemplate, '###HOOK_SECTION###');
640 $markerArray['statistics_header'] = '<h3>' . htmlspecialchars($this->getLanguageService()->getLL('report.statistics.header')) . '</h3>';
641 $markerArray['total_count_label'] = BackendUtility::wrapInHelp('linkvalidator', 'checkboxes', $this->getLanguageService()->getLL('overviews.nbtotal'));
642 $markerArray['total_count'] = $brokenLinkOverView['brokenlinkCount'] ?: '0';
643
644 $linktypes = GeneralUtility::trimExplode(',', $this->modTS['linktypes'], true);
645 $hookSectionContent = '';
646 if (is_array($linktypes)) {
647 if (
648 !empty($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])
649 && is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])
650 ) {
651 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'] as $type => $value) {
652 if (in_array($type, $linktypes)) {
653 $hookSectionMarker = [
654 'count' => $brokenLinkOverView[$type] ?: '0',
655 ];
656
657 $translation = $this->getLanguageService()->getLL('hooks.' . $type) ?: $type;
658 $hookSectionMarker['option'] = '<input type="checkbox"' . $additionalAttr . ' id="' . $prefix . 'SET_' . $type . '" name="' . $prefix
659 . 'SET[' . $type . ']" value="1"' . ($this->pObj->MOD_SETTINGS[$type] ? ' checked="checked"' : '') . '/>' . '<label for="'
660 . $prefix . 'SET_' . $type . '">&nbsp;' . htmlspecialchars($translation) . '</label>';
661
662 $hookSectionContent .= $this->templateService->substituteMarkerArray(
663 $hookSectionTemplate,
664 $hookSectionMarker, '###|###',
665 true,
666 true
667 );
668 }
669 }
670 }
671 }
672 $checkOptionsTemplate = $this->templateService->substituteSubpart(
673 $checkOptionsTemplate,
674 '###HOOK_SECTION###',
675 $hookSectionContent
676 );
677 return $this->templateService->substituteMarkerArray($checkOptionsTemplate, $markerArray, '###|###', true, true);
678 }
679
680 /**
681 * Gets the buttons that shall be rendered in the docHeader
682 *
683 * @return array Available buttons for the docHeader
684 */
685 protected function getDocHeaderButtons()
686 {
687 return [
688 'csh' => BackendUtility::cshItem('_MOD_web_func', ''),
689 'shortcut' => $this->getShortcutButton(),
690 'save' => ''
691 ];
692 }
693
694 /**
695 * Gets the button to set a new shortcut in the backend (if current user is allowed to).
696 *
697 * @return string HTML representation of the shortcut button
698 */
699 protected function getShortcutButton()
700 {
701 $result = '';
702 if ($this->getBackendUser()->mayMakeShortcut()) {
703 $result = $this->doc->makeShortcutIcon('', 'function', $this->pObj->MCONF['name']);
704 }
705 return $result;
706 }
707
708 /**
709 * Gets the filled markers that are used in the HTML template
710 *
711 * @return array The filled marker array
712 */
713 protected function getTemplateMarkers()
714 {
715 return [
716 'FUNC_TITLE' => $this->getLanguageService()->getLL('report.func.title'),
717 'CHECKOPTIONS_TITLE' => $this->getLanguageService()->getLL('report.statistics.header'),
718 'FUNC_MENU' => $this->getLevelSelector(),
719 'CONTENT' => $this->content,
720 'CHECKOPTIONS' => $this->checkOptionsHtml,
721 'ID' => '<input type="hidden" name="id" value="' . $this->pObj->id . '" />',
722 'REFRESH' => '<input type="submit" class="btn btn-default t3js-update-button" name="refreshLinkList" id="refreshLinkList" value="' . htmlspecialchars($this->getLanguageService()->getLL('label_refresh')) . '" data-notification-message="' . htmlspecialchars($this->getLanguageService()->getLL('label_refresh-link-list')) . '" />',
723 'UPDATE' => '',
724 ];
725 }
726
727 /**
728 * Gets the filled markers that are used in the HTML template
729 *
730 * @return array The filled marker array
731 */
732 protected function getTemplateMarkersCheck()
733 {
734 return [
735 'FUNC_TITLE' => $this->getLanguageService()->getLL('checklinks.func.title'),
736 'CHECKOPTIONS_TITLE' => $this->getLanguageService()->getLL('checklinks.statistics.header'),
737 'FUNC_MENU' => $this->getLevelSelector(),
738 'CONTENT' => '',
739 'CHECKOPTIONS' => $this->checkOptionsHtmlCheck,
740 'ID' => '<input type="hidden" name="id" value="' . $this->pObj->id . '" />',
741 'REFRESH' => '',
742 'UPDATE' => '<input type="submit" class="btn btn-default t3js-update-button" name="updateLinkList" id="updateLinkList" value="' . htmlspecialchars($this->getLanguageService()->getLL('label_update')) . '" data-notification-message="' . htmlspecialchars($this->getLanguageService()->getLL('label_update-link-list')) . '"/>',
743 ];
744 }
745
746 /**
747 * Determines whether the current user is an admin
748 *
749 * @return bool Whether the current user is admin
750 */
751 protected function isCurrentUserAdmin()
752 {
753 return $this->getBackendUser()->isAdmin();
754 }
755
756 /**
757 * Returns LanguageService
758 *
759 * @return \TYPO3\CMS\Lang\LanguageService
760 */
761 protected function getLanguageService()
762 {
763 return $GLOBALS['LANG'];
764 }
765
766 /**
767 * Returns the current BE user.
768 *
769 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
770 */
771 protected function getBackendUser()
772 {
773 return $GLOBALS['BE_USER'];
774 }
775 }