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