[TASK] Improve link validator ux
[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\ConnectionPool;
22 use TYPO3\CMS\Core\Imaging\Icon;
23 use TYPO3\CMS\Core\Imaging\IconFactory;
24 use TYPO3\CMS\Core\Messaging\FlashMessage;
25 use TYPO3\CMS\Core\Messaging\FlashMessageService;
26 use TYPO3\CMS\Core\Service\MarkerBasedTemplateService;
27 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
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 = array();
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 = array();
75
76 /**
77 * List of available link types to check defined in the TSconfig
78 *
79 * @var array
80 */
81 protected $availableOptions = array();
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 = array();
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 = array();
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 = array(
216 0 => array(
217 'label' => $this->getLanguageService()->getLL('Report'),
218 'content' => $this->flush(true)
219 ),
220 );
221
222 if ((bool)$this->modTS['showCheckLinkTab']) {
223 $menuItems[1] = array(
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->doc->addStyleSheet('module', ExtensionManagementUtility::extRelPath('linkvalidator') . 'Resources/Public/Css/linkvalidator.css');
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 = array();
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 = array();
359 $availableOptions = array(
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('record_pid', $pageList)
471 )
472 ->orderBy('record_uid')
473 ->addOrderBy('uid');
474
475 if (!empty($linkTypes)) {
476 $placeholders = array_map(
477 function ($linkType) use ($queryBuilder) {
478 return $queryBuilder->createNamedParameter($linkType);
479 },
480 $linkTypes
481 );
482 $queryBuilder->andWhere($queryBuilder->expr()->in('link_type', $placeholders));
483 }
484
485 return $queryBuilder->execute();
486 }
487
488 /**
489 * Replace $brokenLinksMarker['NO_BROKEN_LINKS] with localized flashmessage
490 *
491 * @param array $brokenLinksMarker
492 * @return array $brokenLinksMarker['NO_BROKEN_LINKS] replaced with flashmessage
493 */
494 protected function getNoBrokenLinkMessage(array $brokenLinksMarker)
495 {
496 $brokenLinksMarker['LIST_HEADER'] = '<h3>' . htmlspecialchars($this->getLanguageService()->getLL('list.header')) . '</h3>';
497 /** @var $message FlashMessage */
498 $message = GeneralUtility::makeInstance(
499 FlashMessage::class,
500 $this->getLanguageService()->getLL('list.no.broken.links'),
501 $this->getLanguageService()->getLL('list.no.broken.links.title'),
502 FlashMessage::OK
503 );
504 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
505 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
506 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
507 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
508 $defaultFlashMessageQueue->enqueue($message);
509 $brokenLinksMarker['NO_BROKEN_LINKS'] = $defaultFlashMessageQueue->renderFlashMessages();
510 return $brokenLinksMarker;
511 }
512
513 /**
514 * Displays the table header of the table with the broken links
515 *
516 * @return string Code of content
517 */
518 protected function startTable()
519 {
520 // Listing head
521 $makerTableHead = array(
522 'tablehead_path' => $this->getLanguageService()->getLL('list.tableHead.path'),
523 'tablehead_element' => $this->getLanguageService()->getLL('list.tableHead.element'),
524 'tablehead_headlink' => $this->getLanguageService()->getLL('list.tableHead.headlink'),
525 'tablehead_linktarget' => $this->getLanguageService()->getLL('list.tableHead.linktarget'),
526 'tablehead_linkmessage' => $this->getLanguageService()->getLL('list.tableHead.linkmessage'),
527 'tablehead_lastcheck' => $this->getLanguageService()->getLL('list.tableHead.lastCheck'),
528 );
529
530 // Add CSH to the header of each column
531 foreach ($makerTableHead as $column => $label) {
532 $makerTableHead[$column] = BackendUtility::wrapInHelp('linkvalidator', $column, $label);
533 }
534 // Add section header
535 $makerTableHead['list_header'] = '<h3>' . htmlspecialchars($this->getLanguageService()->getLL('list.header')) . '</h3>';
536 return $makerTableHead;
537 }
538
539 /**
540 * Displays one line of the broken links table
541 *
542 * @param string $table Name of database table
543 * @param array $row Record row to be processed
544 * @param array $brokenLinksItemTemplate Markup of the template to be used
545 * @return string HTML of the rendered row
546 */
547 protected function renderTableRow($table, array $row, $brokenLinksItemTemplate)
548 {
549 $markerArray = array();
550 $fieldName = '';
551 // Restore the linktype object
552 $hookObj = $this->hookObjectsArr[$row['link_type']];
553
554 // Construct link to edit the content element
555 $requestUri = GeneralUtility::getIndpEnv('REQUEST_URI') .
556 '&id=' . $this->pObj->id .
557 '&search_levels=' . $this->searchLevel;
558 $url = BackendUtility::getModuleUrl('record_edit', [
559 'edit' => [
560 $table => [
561 $row['record_uid'] => 'edit'
562 ]
563 ],
564 'returnUrl' => $requestUri
565 ]);
566 $actionLinkOpen = '<a href="' . htmlspecialchars($url);
567 $actionLinkOpen .= '" title="' . htmlspecialchars($this->getLanguageService()->getLL('list.edit')) . '">';
568 $actionLinkClose = '</a>';
569 $elementHeadline = $row['headline'];
570 // Get the language label for the field from TCA
571 if ($GLOBALS['TCA'][$table]['columns'][$row['field']]['label']) {
572 $fieldName = $this->getLanguageService()->sL($GLOBALS['TCA'][$table]['columns'][$row['field']]['label']);
573 // Crop colon from end if present
574 if (substr($fieldName, '-1', '1') === ':') {
575 $fieldName = substr($fieldName, '0', strlen($fieldName) - 1);
576 }
577 }
578 // Fallback, if there is no label
579 $fieldName = !empty($fieldName) ? $fieldName : $row['field'];
580 // column "Element"
581 $element = '<span title="' . htmlspecialchars($table . ':' . $row['record_uid']) . '">' . $this->iconFactory->getIconForRecord($table, $row, Icon::SIZE_SMALL)->render() . '</span>';
582 if (empty($elementHeadline)) {
583 $element .= '<i>' . htmlspecialchars($this->getLanguageService()->getLL('list.no.headline')) . '</i>';
584 } else {
585 $element .= htmlspecialchars($elementHeadline);
586 }
587 $element .= ' ' . htmlspecialchars(sprintf($this->getLanguageService()->getLL('list.field'), $fieldName));
588 $markerArray['actionlinkOpen'] = $actionLinkOpen;
589 $markerArray['actionlinkClose'] = $actionLinkClose;
590 $markerArray['actionlinkIcon'] = $this->iconFactory->getIcon('actions-document-open', Icon::SIZE_SMALL)->render();
591 $markerArray['path'] = BackendUtility::getRecordPath($row['record_pid'], '', 0, 0);
592 $markerArray['element'] = $element;
593 $markerArray['headlink'] = htmlspecialchars($row['link_title']);
594 $markerArray['linktarget'] = htmlspecialchars($hookObj->getBrokenUrl($row));
595 $response = unserialize($row['url_response']);
596 if ($response['valid']) {
597 $linkMessage = '<span class="valid">' . htmlspecialchars($this->getLanguageService()->getLL('list.msg.ok')) . '</span>';
598 } else {
599 $linkMessage = '<span class="error">'
600 . nl2br(
601 // Encode for output
602 htmlspecialchars(
603 $hookObj->getErrorMessage($response['errorParams']),
604 ENT_QUOTES,
605 'UTF-8',
606 false
607 )
608 )
609 . '</span>';
610 }
611 $markerArray['linkmessage'] = $linkMessage;
612
613 $lastRunDate = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], $row['last_check']);
614 $lastRunTime = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'], $row['last_check']);
615 $markerArray['lastcheck'] = htmlspecialchars(sprintf($this->getLanguageService()->getLL('list.msg.lastRun'), $lastRunDate, $lastRunTime));
616
617 // Return the table html code as string
618 return $this->templateService->substituteMarkerArray($brokenLinksItemTemplate, $markerArray, '###|###', true, true);
619 }
620
621 /**
622 * Builds the checkboxes out of the hooks array
623 *
624 * @param array $brokenLinkOverView Array of broken links information
625 * @param string $prefix
626 * @return string code content
627 */
628 protected function getCheckOptions(array $brokenLinkOverView, $prefix = '')
629 {
630 $markerArray = array();
631 if (!empty($prefix)) {
632 $additionalAttr = ' class="' . $prefix . '"';
633 } else {
634 $additionalAttr = ' class="refresh"';
635 }
636 $checkOptionsTemplate = $this->templateService->getSubpart($this->doc->moduleTemplate, '###CHECKOPTIONS_SECTION###');
637 $hookSectionTemplate = $this->templateService->getSubpart($checkOptionsTemplate, '###HOOK_SECTION###');
638 $markerArray['statistics_header'] = '<h3>' . htmlspecialchars($this->getLanguageService()->getLL('report.statistics.header')) . '</h3>';
639 $markerArray['total_count_label'] = BackendUtility::wrapInHelp('linkvalidator', 'checkboxes', $this->getLanguageService()->getLL('overviews.nbtotal'));
640 $markerArray['total_count'] = $brokenLinkOverView['brokenlinkCount'] ?: '0';
641
642 $linktypes = GeneralUtility::trimExplode(',', $this->modTS['linktypes'], true);
643 $hookSectionContent = '';
644 if (is_array($linktypes)) {
645 if (
646 !empty($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])
647 && is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])
648 ) {
649 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'] as $type => $value) {
650 if (in_array($type, $linktypes)) {
651 $hookSectionMarker = array(
652 'count' => $brokenLinkOverView[$type] ?: '0',
653 );
654
655 $translation = $this->getLanguageService()->getLL('hooks.' . $type) ?: $type;
656 $hookSectionMarker['option'] = '<input type="checkbox"' . $additionalAttr . ' id="' . $prefix . 'SET_' . $type . '" name="' . $prefix
657 . 'SET[' . $type . ']" value="1"' . ($this->pObj->MOD_SETTINGS[$type] ? ' checked="checked"' : '') . '/>' . '<label for="'
658 . $prefix . 'SET_' . $type . '">&nbsp;' . htmlspecialchars($translation) . '</label>';
659
660 $hookSectionContent .= $this->templateService->substituteMarkerArray(
661 $hookSectionTemplate,
662 $hookSectionMarker, '###|###',
663 true,
664 true
665 );
666 }
667 }
668 }
669 }
670 $checkOptionsTemplate = $this->templateService->substituteSubpart(
671 $checkOptionsTemplate,
672 '###HOOK_SECTION###',
673 $hookSectionContent
674 );
675 return $this->templateService->substituteMarkerArray($checkOptionsTemplate, $markerArray, '###|###', true, true);
676 }
677
678 /**
679 * Gets the buttons that shall be rendered in the docHeader
680 *
681 * @return array Available buttons for the docHeader
682 */
683 protected function getDocHeaderButtons()
684 {
685 return array(
686 'csh' => BackendUtility::cshItem('_MOD_web_func', ''),
687 'shortcut' => $this->getShortcutButton(),
688 'save' => ''
689 );
690 }
691
692 /**
693 * Gets the button to set a new shortcut in the backend (if current user is allowed to).
694 *
695 * @return string HTML representation of the shortcut button
696 */
697 protected function getShortcutButton()
698 {
699 $result = '';
700 if ($this->getBackendUser()->mayMakeShortcut()) {
701 $result = $this->doc->makeShortcutIcon('', 'function', $this->pObj->MCONF['name']);
702 }
703 return $result;
704 }
705
706 /**
707 * Gets the filled markers that are used in the HTML template
708 *
709 * @return array The filled marker array
710 */
711 protected function getTemplateMarkers()
712 {
713 return array(
714 'FUNC_TITLE' => $this->getLanguageService()->getLL('report.func.title'),
715 'CHECKOPTIONS_TITLE' => $this->getLanguageService()->getLL('report.statistics.header'),
716 'FUNC_MENU' => $this->getLevelSelector(),
717 'CONTENT' => $this->content,
718 'CHECKOPTIONS' => $this->checkOptionsHtml,
719 'ID' => '<input type="hidden" name="id" value="' . $this->pObj->id . '" />',
720 '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')) . '" />',
721 'UPDATE' => '',
722 );
723 }
724
725 /**
726 * Gets the filled markers that are used in the HTML template
727 *
728 * @return array The filled marker array
729 */
730 protected function getTemplateMarkersCheck()
731 {
732 return array(
733 'FUNC_TITLE' => $this->getLanguageService()->getLL('checklinks.func.title'),
734 'CHECKOPTIONS_TITLE' => $this->getLanguageService()->getLL('checklinks.statistics.header'),
735 'FUNC_MENU' => $this->getLevelSelector(),
736 'CONTENT' => '',
737 'CHECKOPTIONS' => $this->checkOptionsHtmlCheck,
738 'ID' => '<input type="hidden" name="id" value="' . $this->pObj->id . '" />',
739 'REFRESH' => '',
740 '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')) . '"/>',
741 );
742 }
743
744 /**
745 * Determines whether the current user is an admin
746 *
747 * @return bool Whether the current user is admin
748 */
749 protected function isCurrentUserAdmin()
750 {
751 return $this->getBackendUser()->isAdmin();
752 }
753
754 /**
755 * Returns LanguageService
756 *
757 * @return \TYPO3\CMS\Lang\LanguageService
758 */
759 protected function getLanguageService()
760 {
761 return $GLOBALS['LANG'];
762 }
763
764 /**
765 * Returns the current BE user.
766 *
767 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
768 */
769 protected function getBackendUser()
770 {
771 return $GLOBALS['BE_USER'];
772 }
773 }