17 use Doctrine\DBAL\Driver\Statement;
53 'pObj' =>
'Using LinkValidatorReport::$pObj is deprecated and will not be possible anymore in TYPO3 v10.0.',
54 'doc' =>
'Using LinkValidatorReport::$doc is deprecated and will not be possible anymore in TYPO3 v10.0.',
55 'function_key' =>
'Using LinkValidatorReport::$function_key is deprecated, property will be removed in TYPO3 v10.0.',
56 'extClassConf' =>
'Using LinkValidatorReport::$extClassConf is deprecated, property will be removed in TYPO3 v10.0.',
57 'localLangFile' =>
'Using LinkValidatorReport::$localLangFile is deprecated, property will be removed in TYPO3 v10.0.',
58 'extObj' =>
'Using LinkValidatorReport::$extObj is deprecated, property will be removed in TYPO3 v10.0.',
65 'extObjContent' =>
'Using LinkValidatorReport::extObjContent() is deprecated, method will be removed in TYPO3 v10.0.',
123 protected $checkOpt = [
'report' => [],
'check' => []];
214 if (!empty($this->localLangFile)) {
216 $languageService->includeLLFile($this->localLangFile);
218 $this->
id = (int)GeneralUtility::_GP(
'id');
226 public function main()
229 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
230 $update = GeneralUtility::_GP(
'updateLinkList');
234 if (empty($update)) {
240 $this->searchLevel[$prefix] = GeneralUtility::_GP($prefix .
'_search_levels');
241 if (isset($this->
id)) {
244 if (isset($this->searchLevel[$prefix])) {
245 $this->pObj->MOD_SETTINGS[$prefix .
'_searchlevel'] = $this->searchLevel[$prefix];
247 $this->searchLevel[$prefix] = $this->pObj->MOD_SETTINGS[$prefix .
'_searchlevel'];
249 if (isset($this->pObj->MOD_SETTINGS[$other .
'_searchlevel'])) {
250 $this->searchLevel[$other] = $this->pObj->MOD_SETTINGS[$other .
'_searchlevel'];
254 $set = GeneralUtility::_GP($prefix .
'_SET');
256 foreach (
$GLOBALS[
'TYPO3_CONF_VARS'][
'EXTCONF'][
'linkvalidator'][
'checkLinks'] ?? [] as $linkType => $value) {
258 if (strpos($this->modTS[
'linktypes'], $linkType) !==
false) {
259 $this->availableOptions[$linkType] = 1;
265 unset($this->checkOpt[$prefix][$linkType]);
266 if (!empty(GeneralUtility::_GP($prefix .
'_values'))) {
267 if (isset($set[$linkType])) {
268 $this->checkOpt[$prefix][$linkType] = $set[$linkType];
270 $this->checkOpt[$prefix][$linkType] =
'0';
272 $this->pObj->MOD_SETTINGS[$prefix .
'_' . $linkType] = $this->checkOpt[$prefix][$linkType];
273 } elseif (isset($this->pObj->MOD_SETTINGS[$prefix .
'_' . $linkType])) {
274 $this->checkOpt[$prefix][$linkType] = $this->pObj->MOD_SETTINGS[$prefix .
'_' . $linkType];
277 $this->checkOpt[$prefix][$linkType] =
'0';
278 $this->pObj->MOD_SETTINGS[$prefix .
'_' . $linkType] = $this->checkOpt[$prefix][$linkType];
280 if (isset($this->pObj->MOD_SETTINGS[$other .
'_' . $linkType])) {
281 $this->checkOpt[$other][$linkType] = $this->pObj->MOD_SETTINGS[$other .
'_' . $linkType];
292 if ($this->modTS[
'showCheckLinkTab'] == 1) {
293 $this->updateListHtml =
'<input class="btn btn-default t3js-update-button" type="submit" name="updateLinkList" id="updateLinkList" value="'
295 .
'" data-notification-message="'
299 $this->refreshListHtml =
'<input class="btn btn-default t3js-update-button" type="submit" name="refreshLinkList" id="refreshLinkList" value="'
301 .
'" data-notification-message="'
304 $this->linkAnalyzer = GeneralUtility::makeInstance(LinkAnalyzer::class);
307 $brokenLinkOverView = $this->linkAnalyzer->getLinkCounts($this->
id);
308 $this->checkOptionsHtml[
'report'] = $this->
getCheckOptions($brokenLinkOverView,
'report');
309 $this->checkOptionsHtml[
'check'] = $this->
getCheckOptions($brokenLinkOverView,
'check');
318 return '<div id="linkvalidator-modfuncreport">' . $pageTile . $this->
createTabs() .
'</div>';
331 'label' => $languageService->getLL(
'Report'),
332 'content' => $this->
flush(
true)
336 if ((
bool)$this->modTS[
'showCheckLinkTab']) {
338 'label' => $languageService->getLL(
'CheckLink'),
339 'content' => $this->
flush()
343 $moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
344 return $moduleTemplate->getDynamicTabMenu($menuItems,
'report-linkvalidator');
352 foreach (
$GLOBALS[
'TYPO3_CONF_VARS'][
'EXTCONF'][
'linkvalidator'][
'checkLinks'] ?? [] as $linkType => $className) {
353 $this->hookObjectsArr[$linkType] = GeneralUtility::makeInstance($className);
356 $this->doc = GeneralUtility::makeInstance(DocumentTemplate::class);
357 $this->doc->setModuleTemplate(
'EXT:linkvalidator/Resources/Private/Templates/mod_template.html');
360 if ($this->
id && is_array($this->pageRecord) || !$this->
id && $this->
isCurrentUserAdmin()) {
361 $this->isAccessibleForCurrentUser =
true;
365 $pageRenderer->addCssFile(
'EXT:linkvalidator/Resources/Public/Css/linkvalidator.css',
'stylesheet',
'screen');
366 $pageRenderer->loadRequireJsModule(
'TYPO3/CMS/Linkvalidator/Linkvalidator');
368 $this->templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
372 $this->isAccessibleForCurrentUser =
false;
383 foreach ($this->modTS[
'searchFields.'] as $table => $fieldList) {
384 $fields = GeneralUtility::trimExplode(
',', $fieldList,
true);
386 if (!$searchFields || !is_array($searchFields[$table]) || !in_array($field, $searchFields[$table],
true)) {
387 $searchFields[$table][] = $field;
391 $rootLineHidden = $this->linkAnalyzer->getRootLineIsHidden($this->pObj->pageinfo);
392 if (!$rootLineHidden || $this->modTS[
'checkhidden'] == 1) {
395 $pageList = $this->linkAnalyzer->extGetTreeList(
397 $this->searchLevel[
'check'],
400 $this->modTS[
'checkhidden']
402 if ($this->pObj->pageinfo[
'hidden'] == 0 || $this->modTS[
'checkhidden']) {
407 $this->linkAnalyzer->init($searchFields, $pageList, $this->modTS);
410 $update = GeneralUtility::_GP(
'updateLinkList');
411 if (!empty($update)) {
412 $this->linkAnalyzer->getLinkStatistics($this->checkOpt[
'check'], $this->modTS[
'checkhidden']);
422 if ($this->isAccessibleForCurrentUser) {
427 $message = GeneralUtility::makeInstance(
429 $languageService->getLL(
'no.access'),
430 $languageService->getLL(
'no.access.title'),
434 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
436 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
437 $defaultFlashMessageQueue->enqueue($message);
447 protected function flush($form =
false)
449 return $this->doc->moduleBody(
469 0 => $languageService->sL(
'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_0'),
470 1 => $languageService->sL(
'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_1'),
471 2 => $languageService->sL(
'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_2'),
472 3 => $languageService->sL(
'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_3'),
473 4 => $languageService->sL(
'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_4'),
474 999 => $languageService->sL(
'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_infi')
477 $options[] =
'<option value="' . $optionValue .
'"' . ($optionValue === (int)$this->searchLevel[$prefix] ?
' selected="selected"' :
'') .
'>' . htmlspecialchars($optionLabel) .
'</option>';
479 return '<select name="' . $prefix .
'_search_levels" class="form-control">' . implode(
'', $options) .
'</select>';
489 $brokenLinkItems =
'';
490 $brokenLinksTemplate = $this->templateService->getSubpart(
491 $this->doc->moduleTemplate,
492 '###NOBROKENLINKS_CONTENT###'
496 if (is_array($this->checkOpt[
'report'])) {
497 $linkTypes = array_keys($this->checkOpt[
'report'],
'1');
503 $rootLineHidden = $this->linkAnalyzer->getRootLineIsHidden($this->pObj->pageinfo);
504 if (!$rootLineHidden || (
bool)$this->modTS[
'checkhidden']) {
507 if (!empty($linkTypes)) {
511 if ($result && $result->rowCount()) {
513 $brokenLinksTemplate = $this->templateService->getSubpart(
514 $this->doc->moduleTemplate,
515 '###BROKENLINKS_CONTENT###'
517 $brokenLinksItemTemplate = $this->templateService->getSubpart(
518 $this->doc->moduleTemplate,
519 '###BROKENLINKS_ITEM###'
524 while ($row = $result->fetch()) {
525 $items[] = $this->
renderTableRow($row[
'table_name'], $row, $brokenLinksItemTemplate);
527 $brokenLinkItems = implode(LF, $items);
535 $brokenLinksTemplate = $this->templateService->substituteMarkerArray(
536 $brokenLinksTemplate,
542 return $this->templateService->substituteSubpart($brokenLinksTemplate,
'###BROKENLINKS_ITEM', $brokenLinkItems);
554 $pageList = $this->linkAnalyzer->extGetTreeList(
556 $this->searchLevel[
'report'],
559 $this->modTS[
'checkhidden']
565 return GeneralUtility::intExplode(
',', $pageList,
true);
577 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
578 ->getQueryBuilderForTable(
'tx_linkvalidator_link');
581 ->from(
'tx_linkvalidator_link')
583 $queryBuilder->expr()->orX(
584 $queryBuilder->expr()->andX(
585 $queryBuilder->expr()->in(
587 $queryBuilder->createNamedParameter($pageList, Connection::PARAM_INT_ARRAY)
589 $queryBuilder->expr()->eq(
'table_name', $queryBuilder->createNamedParameter(
'pages'))
591 $queryBuilder->expr()->andX(
592 $queryBuilder->expr()->in(
594 $queryBuilder->createNamedParameter($pageList, Connection::PARAM_INT_ARRAY)
596 $queryBuilder->expr()->neq(
'table_name', $queryBuilder->createNamedParameter(
'pages'))
600 ->orderBy(
'record_uid')
603 if (!empty($linkTypes)) {
604 $queryBuilder->andWhere(
605 $queryBuilder->expr()->in(
607 $queryBuilder->createNamedParameter($linkTypes, Connection::PARAM_STR_ARRAY)
612 return $queryBuilder->execute();
624 $brokenLinksMarker[
'LIST_HEADER'] =
'<h3>' . htmlspecialchars($languageService->getLL(
'list.header')) .
'</h3>';
626 $message = GeneralUtility::makeInstance(
628 $languageService->getLL(
'list.no.broken.links'),
629 $languageService->getLL(
'list.no.broken.links.title'),
632 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
634 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
635 $defaultFlashMessageQueue->enqueue($message);
636 $brokenLinksMarker[
'NO_BROKEN_LINKS'] = $defaultFlashMessageQueue->renderFlashMessages();
637 return $brokenLinksMarker;
650 'tablehead_path' => $languageService->getLL(
'list.tableHead.path'),
651 'tablehead_element' => $languageService->getLL(
'list.tableHead.element'),
652 'tablehead_headlink' => $languageService->getLL(
'list.tableHead.headlink'),
653 'tablehead_linktarget' => $languageService->getLL(
'list.tableHead.linktarget'),
654 'tablehead_linkmessage' => $languageService->getLL(
'list.tableHead.linkmessage'),
655 'tablehead_lastcheck' => $languageService->getLL(
'list.tableHead.lastCheck'),
659 foreach ($makerTableHead as $column => $label) {
663 $makerTableHead[
'list_header'] =
'<h3>' . htmlspecialchars($languageService->getLL(
'list.header')) .
'</h3>';
664 return $makerTableHead;
675 protected function renderTableRow($table, array $row, $brokenLinksItemTemplate)
681 $hookObj = $this->hookObjectsArr[$row[
'link_type']];
684 $requestUri = GeneralUtility::getIndpEnv(
'REQUEST_URI') .
686 '&search_levels=' . $this->searchLevel[
'report'];
688 $uriBuilder = GeneralUtility::makeInstance(\
TYPO3\CMS\Backend\Routing\UriBuilder::class);
689 $url = (string)$uriBuilder->buildUriFromRoute(
'record_edit', [
692 $row[
'record_uid'] =>
'edit'
695 'columnsOnly' => $row[
'field'],
696 'returnUrl' => $requestUri
698 $actionLinkOpen =
'<a href="' . htmlspecialchars($url);
699 $actionLinkOpen .=
'" title="' . htmlspecialchars($languageService->getLL(
'list.edit')) .
'">';
700 $actionLinkClose =
'</a>';
701 $elementHeadline = $row[
'headline'];
703 if (
$GLOBALS[
'TCA'][$table][
'columns'][$row[
'field']][
'label']) {
704 $fieldName = $languageService->sL(
$GLOBALS[
'TCA'][$table][
'columns'][$row[
'field']][
'label']);
706 if (substr($fieldName,
'-1',
'1') ===
':') {
707 $fieldName = substr($fieldName,
'0', strlen($fieldName) - 1);
711 $fieldName = !empty($fieldName) ? $fieldName : $row[
'field'];
713 $element =
'<span title="' . htmlspecialchars($table .
':' . $row[
'record_uid']) .
'">' . $this->iconFactory->getIconForRecord($table, $row,
Icon::SIZE_SMALL)->render() .
'</span>';
714 if (empty($elementHeadline)) {
715 $element .=
'<i>' . htmlspecialchars($languageService->getLL(
'list.no.headline')) .
'</i>';
717 $element .= htmlspecialchars($elementHeadline);
719 $element .=
' ' . htmlspecialchars(sprintf($languageService->getLL(
'list.field'), $fieldName));
720 $markerArray[
'actionlinkOpen'] = $actionLinkOpen;
721 $markerArray[
'actionlinkClose'] = $actionLinkClose;
722 $markerArray[
'actionlinkIcon'] = $this->iconFactory->getIcon(
'actions-open',
Icon::SIZE_SMALL)->render();
724 $markerArray[
'element'] = $element;
725 $markerArray[
'headlink'] = htmlspecialchars($row[
'link_title']);
726 $markerArray[
'linktarget'] = htmlspecialchars($hookObj->getBrokenUrl($row));
727 $response = unserialize($row[
'url_response']);
728 if ($response[
'valid']) {
729 $linkMessage =
'<span class="valid">' . htmlspecialchars($languageService->getLL(
'list.msg.ok')) .
'</span>';
731 $linkMessage =
'<span class="error">'
735 $hookObj->getErrorMessage($response[
'errorParams']),
743 $markerArray[
'linkmessage'] = $linkMessage;
745 $lastRunDate = date(
$GLOBALS[
'TYPO3_CONF_VARS'][
'SYS'][
'ddmmyy'], $row[
'last_check']);
746 $lastRunTime = date(
$GLOBALS[
'TYPO3_CONF_VARS'][
'SYS'][
'hhmm'], $row[
'last_check']);
747 $markerArray[
'lastcheck'] = htmlspecialchars(sprintf($languageService->getLL(
'list.msg.lastRun'), $lastRunDate, $lastRunTime));
750 return $this->templateService->substituteMarkerArray($brokenLinksItemTemplate, $markerArray,
'###|###',
true,
true);
760 protected function getCheckOptions(array $brokenLinkOverView, $prefix =
'report')
764 if (!empty($prefix)) {
765 $additionalAttr =
' class="' . $prefix .
'"';
767 $additionalAttr =
' class="refresh"';
769 $checkOptionsTemplate = $this->templateService->getSubpart($this->doc->moduleTemplate,
'###CHECKOPTIONS_SECTION###');
770 $hookSectionTemplate = $this->templateService->getSubpart($checkOptionsTemplate,
'###HOOK_SECTION###');
771 $markerArray[
'statistics_header'] =
'<h3>' . htmlspecialchars($languageService->getLL(
'report.statistics.header')) .
'</h3>';
772 $markerArray[
'total_count_label'] =
BackendUtility::wrapInHelp(
'linkvalidator',
'checkboxes', $languageService->getLL(
'overviews.nbtotal'));
773 $markerArray[
'total_count'] = $brokenLinkOverView[
'brokenlinkCount'] ?:
'0';
775 $linktypes = GeneralUtility::trimExplode(
',', $this->modTS[
'linktypes'],
true);
776 $hookSectionContent =
'';
777 if (is_array($linktypes)) {
779 !empty(
$GLOBALS[
'TYPO3_CONF_VARS'][
'EXTCONF'][
'linkvalidator'][
'checkLinks'])
780 && is_array(
$GLOBALS[
'TYPO3_CONF_VARS'][
'EXTCONF'][
'linkvalidator'][
'checkLinks'])
782 foreach (
$GLOBALS[
'TYPO3_CONF_VARS'][
'EXTCONF'][
'linkvalidator'][
'checkLinks'] as $type => $value) {
783 if (in_array($type, $linktypes)) {
784 $hookSectionMarker = [
785 'count' => $brokenLinkOverView[$type] ?:
'0',
788 $translation = $languageService->getLL(
'hooks.' . $type) ?: $type;
790 $checked = $this->checkOpt[$prefix][$type] ?
'checked="checked"' :
'';
792 $hookSectionMarker[
'option'] =
'<input type="checkbox"' . $additionalAttr
793 .
' id="' . $prefix .
'_SET_' . $type
794 .
'" name="' . $prefix .
'_SET[' . $type .
']" value="1"'
795 .
' ' . $checked .
'/>' .
'<label for="'
796 . $prefix .
'_SET_' . $type .
'"> ' . htmlspecialchars($translation) .
'</label>';
798 $hookSectionContent .= $this->templateService->substituteMarkerArray(
799 $hookSectionTemplate,
809 $checkOptionsTemplate = $this->templateService->substituteSubpart(
810 $checkOptionsTemplate,
811 '###HOOK_SECTION###',
816 $checkOptionsTemplate .=
'<input type="hidden" name="' . $prefix .
'_values" value="1">';
818 return $this->templateService->substituteMarkerArray($checkOptionsTemplate, $markerArray,
'###|###',
true,
true);
844 $result = $this->doc->makeShortcutIcon(
'',
'function',
'web_info');
859 'FUNC_TITLE' => $languageService->getLL(
'report.func.title'),
860 'CHECKOPTIONS_TITLE' => $languageService->getLL(
'report.statistics.header'),
863 'CHECKOPTIONS' => $this->checkOptionsHtml[
'report'],
864 'ID' =>
'<input type="hidden" name="id" value="' . $this->
id .
'" />',
865 'REFRESH' =>
'<input type="submit" class="btn btn-default t3js-update-button" name="refreshLinkList" id="refreshLinkList" value="'
866 . htmlspecialchars($languageService->getLL(
'label_refresh'))
867 .
'" data-notification-message="'
868 . htmlspecialchars($languageService->getLL(
'label_refresh-link-list')) .
'" />',
883 'FUNC_TITLE' => $languageService->getLL(
'checklinks.func.title'),
884 'CHECKOPTIONS_TITLE' => $languageService->getLL(
'checklinks.statistics.header'),
887 'CHECKOPTIONS' => $this->checkOptionsHtml[
'check'],
888 'ID' =>
'<input type="hidden" name="id" value="' . $this->
id .
'" />',
890 'UPDATE' =>
'<input type="submit" class="btn btn-default t3js-update-button" name="updateLinkList" id="updateLinkList" value="'
891 . htmlspecialchars($languageService->getLL(
'label_update'))
892 .
'" data-notification-message="'
893 . htmlspecialchars($languageService->getLL(
'label_update-link-list'))
915 if (is_array($this->extClassConf) && $this->extClassConf[
'name']) {
916 $this->extObj = GeneralUtility::makeInstance($this->extClassConf[
'name']);
917 $this->extObj->init($this->pObj, $this->extClassConf);
930 if (is_object($this->extObj)) {
931 return $this->extObj->main();
956 return GeneralUtility::makeInstance(PageRenderer::class);
966 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(
'pages');
967 $queryBuilder->getRestrictions()
969 ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
971 $result = $queryBuilder
972 ->select(
'uid',
'title',
'hidden')
975 $queryBuilder->expr()->eq(
977 $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
983 while ($row = $result->fetch()) {
984 if ($row[
'hidden'] === 0 || $this->modTS[
'checkhidden']) {
985 $theList .=
',' . $row[
'uid'];