Merged Linkvalidator (tagged as 4.5beta4)
[Packages/TYPO3.CMS.git] / typo3 / sysext / linkvalidator / modfunc1 / class.tx_linkvalidator_modfunc1.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2005 - 2010 Michael Miousse (michael.miousse@infoglobe.ca)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 *
17 * This script is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * This copyright notice MUST APPEAR in all copies of the script!
23 ***************************************************************/
24
25 /**
26 * Module 'Link Validator' for the 'linkvalidator' extension.
27 *
28 * @author Michael Miousse <michael.miousse@infoglobe.ca>
29 * @author Jochen Rieger <j.rieger@connecta.ag>
30 * @package TYPO3
31 * @subpackage linkvalidator
32 */
33 class tx_linkvalidator_modfunc1 extends t3lib_extobjbase {
34
35 /**
36 * @var template
37 */
38 public $doc;
39 protected $relativePath;
40 protected $pageRecord = array();
41 protected $isAccessibleForCurrentUser = FALSE;
42
43 protected $processing;
44
45 /**
46 * Main method of modfunc1
47 *
48 * @return html Module content
49 */
50 public function main() {
51 $GLOBALS['LANG']->includeLLFile('EXT:linkvalidator/modfunc1/locallang.xml');
52
53 $this->search_level = t3lib_div::_GP('search_levels');
54
55 if (isset($this->pObj->id)) {
56 $this->modTS = t3lib_BEfunc::getModTSconfig($this->pObj->id, 'mod.linkvalidator');
57 $this->modTS = $this->modTS['properties'];
58 }
59
60 if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])) {
61 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'] as $linkType => $value) {
62 // Compile list of all available types. Used for checking with button "Check Links".
63 if (strpos($this->modTS['linktypes'], $linkType) !== FALSE) {
64 $this->availableOptions[$linkType] = 1;
65 }
66 // Compile list of types currently selected by the checkboxes.
67 if ($this->pObj->MOD_SETTINGS[$linkType]) {
68 $this->checkOpt[$linkType] = 1;
69 }
70 }
71 }
72
73 $this->initialize();
74
75 $this->firstSteps = $GLOBALS['LANG']->getLL('first.steps');
76
77 if ($this->modTS['showUpdateButton'] == 1) {
78 $this->firstSteps .= ' ' . $GLOBALS['LANG']->getLL('first.steps.info.update.button');
79 $this->updateListHtml = '<input type="submit" name="updateLinkList" value="' . $GLOBALS['LANG']->getLL('label_update') . '"/>';
80 }
81
82 if (t3lib_extMgm::isLoaded('scheduler')) {
83 if ($GLOBALS['BE_USER']->isAdmin()) {
84 $this->firstSteps .= ' ' .
85 sprintf($GLOBALS['LANG']->getLL('first.steps.info.scheduler'),
86 '<a href="' . t3lib_div::getIndpEnv('TYPO3_SITE_URL') . 'typo3/mod.php?M=tools_txschedulerM1">', '</a>'
87 );
88 } else {
89 $this->firstSteps .= ' ' . $GLOBALS['LANG']->getLL('first.steps.info.scheduler.admin');
90 }
91 }
92
93 $this->refreshListHtml = '<input type="submit" name="refreshLinkList" value="' . $GLOBALS['LANG']->getLL('label_refresh') . '"/>';
94
95 $this->processing = t3lib_div::makeInstance('tx_linkvalidator_processing');
96 $this->updateBrokenLinks();
97
98 $brokenLinkOverView = $this->processing->getLinkCounts($this->pObj->id);
99 $this->checkOptHtml = $this->getCheckOptions($brokenLinkOverView);
100
101 $this->render();
102
103
104 return $this->flush();
105 }
106
107
108 /**
109 * Initializes the menu array internally.
110 *
111 * @return Module menu
112 */
113 public function modMenu() {
114 $modMenu = array (
115 'checkAllLink' => 0,
116 );
117
118 if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])) {
119 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'] as $linkType => $value) {
120 $modMenu[$linkType] = 1;
121 }
122 }
123
124 return $modMenu;
125 }
126
127
128 /**
129 * Initializes the Module.
130 *
131 * @return void
132 */
133 protected function initialize() {
134 if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])) {
135 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'] as $linkType => $classRef) {
136 $this->hookObjectsArr[$linkType] = &t3lib_div::getUserObj($classRef);
137 }
138 }
139
140 $this->doc = t3lib_div::makeInstance('template');
141 $this->doc->setModuleTemplate(t3lib_extMgm::extPath('linkvalidator') . 'modfunc1/mod_template.html');
142 $this->doc->backPath = $GLOBALS['BACK_PATH'];
143
144 $this->relativePath = t3lib_extMgm::extRelPath('linkvalidator');
145 $this->pageRecord = t3lib_BEfunc::readPageAccess($this->pObj->id, $this->perms_clause);
146
147 $this->isAccessibleForCurrentUser = FALSE;
148 if ($this->pObj->id && is_array($this->pageRecord) || !$this->pObj->id && $this->isCurrentUserAdmin()) {
149 $this->isAccessibleForCurrentUser = TRUE;
150 }
151
152 $this->loadHeaderData();
153
154 // Don't access in workspace
155 if ($GLOBALS['BE_USER']->workspace !== 0) {
156 $this->isAccessibleForCurrentUser = FALSE;
157 }
158 }
159
160
161 /**
162 * Updates the table of stored broken links.
163 *
164 * @param array Processing object
165 * @return void
166 */
167 protected function updateBrokenLinks() {
168 $searchFields = array();
169
170 // get the searchFields from TypoScript
171 foreach ($this->modTS['searchFields.'] as $table => $fieldList) {
172 $fields = t3lib_div::trimExplode(',', $fieldList);
173 foreach ($fields as $field) {
174 if (!$searchFields || !is_array($searchFields[$table]) || array_search($field, $searchFields[$table]) == FALSE) {
175 $searchFields[$table][] = $field;
176 }
177 }
178 }
179 // get children pages
180 $pageList = $this->processing->extGetTreeList(
181 $this->pObj->id,
182 $this->search_level,
183 0,
184 $GLOBALS['BE_USER']->getPagePermsClause(1)
185 );
186 $pageList .= $this->pObj->id;
187
188 $this->processing->init($searchFields, $pageList);
189
190 // check if button press
191 $update = t3lib_div::_GP('updateLinkList');
192
193 if (!empty($update)) {
194 $this->processing->getLinkStatistics($this->availableOptions, $this->modTS['checkhidden']);
195 }
196 }
197
198
199 /**
200 * Renders the content of the module.
201 *
202 * @return void
203 */
204 protected function render() {
205 if ($this->isAccessibleForCurrentUser) {
206 $this->content = $this->drawBrokenLinksTable();
207 } else {
208 // If no access or if ID == zero
209 $this->content .= $this->doc->spacer(10);
210 }
211 }
212
213
214 /**
215 * Flushes the rendered content to the browser.
216 *
217 * @return void
218 */
219 protected function flush() {
220 $content.= $this->doc->moduleBody(
221 $this->pageRecord,
222 $this->getDocHeaderButtons(),
223 $this->getTemplateMarkers()
224 );
225
226 return $content;
227 }
228
229
230 /**
231 * Builds the selector for the level of pages to search.
232 *
233 * @return string Html code of that selector
234 */
235 private function getLevelSelector() {
236 // Make level selector:
237 $opt = array();
238 $parts = array(
239 0 => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xml:labels.depth_0'),
240 1 => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xml:labels.depth_1'),
241 2 => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xml:labels.depth_2'),
242 3 => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xml:labels.depth_3'),
243 999 => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xml:labels.depth_infi'),
244 );
245
246 foreach ($parts as $kv => $label) {
247 $opt[] = '<option value="' . $kv . '"' . ($kv == intval($this->search_level) ? ' selected="selected"' : '') . '>' . htmlspecialchars($label) . '</option>';
248 }
249 $lMenu = '<select name="search_levels">' . implode('', $opt) . '</select>';
250 return $lMenu;
251 }
252
253 /**
254 * Displays the table of broken links or a note if there were no broken links.
255 *
256 * @return html Content of the table or of the note
257 */
258 private function drawBrokenLinksTable() {
259 $content = '';
260 $items = array();
261 $brokenLinkItems = '';
262 $keyOpt = array();
263
264 if (is_array($this->checkOpt)) {
265 $keyOpt = array_keys($this->checkOpt);
266 }
267
268 $pageList = $this->processing->extGetTreeList(
269 $this->pObj->id,
270 $this->search_level,
271 0,
272 $GLOBALS['BE_USER']->getPagePermsClause(1)
273 );
274 $pageList .= $this->pObj->id;
275
276 if (($res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
277 '*',
278 'tx_linkvalidator_links',
279 'recpid in (' . $pageList . ') and typelinks in (\'' . implode("','", $keyOpt) . '\')',
280 '',
281 'recuid ASC, uid ASC')
282 )) {
283 // Display table with broken links
284 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res) > 0) {
285 $brokenLinksTemplate = t3lib_parsehtml::getSubpart($this->doc->moduleTemplate, '###BROKENLINKS_CONTENT###');
286
287 $brokenLinksItemTemplate = t3lib_parsehtml::getSubpart($this->doc->moduleTemplate, '###BROKENLINKS_ITEM###');
288
289 // Table header
290 $brokenLinksMarker = $this->startTable();
291
292 // Table rows containing the broken links
293 while (($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))) {
294 $items[] = $this->drawTableRow($row['tablename'], $row, $brokenLinksItemTemplate);
295 }
296 $brokenLinkItems = implode(chr(10), $items);
297
298 // Display note that there are no broken links to display
299 } else {
300 $brokenLinksTemplate = t3lib_parsehtml::getSubpart($this->doc->moduleTemplate, '###NOBROKENLINKS_CONTENT###');
301
302 $brokenLinksMarker['LIST_HEADER'] = $this->doc->sectionHeader($GLOBALS['LANG']->getLL('list.header'));
303 $message = t3lib_div::makeInstance(
304 't3lib_FlashMessage',
305 $GLOBALS['LANG']->getLL('list.no.broken.links'),
306 $GLOBALS['LANG']->getLL('list.no.broken.links.title'),
307 t3lib_FlashMessage::OK
308 );
309 $brokenLinksMarker['NO_BROKEN_LINKS'] = $message->render();
310 }
311 }
312 $brokenLinksTemplate = t3lib_parsehtml::substituteMarkerArray($brokenLinksTemplate, $brokenLinksMarker, '###|###', TRUE);
313
314 $content = t3lib_parsehtml::substituteSubpart($brokenLinksTemplate, '###BROKENLINKS_ITEM', $brokenLinkItems);
315
316 return $content;
317 }
318
319
320
321 /**
322 * Displays the table header of the table with the broken links.
323 *
324 * @return html Code of content
325 */
326 private function startTable() {
327 global $TYPO3_CONF_VARS;
328
329 // Listing head
330 $makerTableHead = array();
331
332 $makerTableHead['tablehead_path'] = $GLOBALS['LANG']->getLL('list.tableHead.path');
333 $makerTableHead['tablehead_element'] = $GLOBALS['LANG']->getLL('list.tableHead.element');
334 $makerTableHead['tablehead_headlink'] = $GLOBALS['LANG']->getLL('list.tableHead.headlink');
335 $makerTableHead['tablehead_linktarget'] = $GLOBALS['LANG']->getLL('list.tableHead.linktarget');
336 $makerTableHead['tablehead_linkmessage'] = $GLOBALS['LANG']->getLL('list.tableHead.linkmessage');
337 $makerTableHead['tablehead_lastcheck'] = $GLOBALS['LANG']->getLL('list.tableHead.lastCheck');
338
339 // Add CSH to the header of each column
340 foreach($makerTableHead as $column => $label) {
341 $label = t3lib_BEfunc::wrapInHelp('linkvalidator', $column, $label);
342 $makerTableHead[$column] = $label;
343 }
344
345 // Add section header
346 $makerTableHead['list_header'] = $this->doc->sectionHeader($GLOBALS['LANG']->getLL('list.header'));
347
348 return $makerTableHead;
349 }
350
351
352 /**
353 * Displays one line of the broken links table.
354 *
355 * @param string table
356 * @param string row record
357 * @return html code of content
358 */
359 private function drawTableRow($table, $row, $brokenLinksItemTemplate) {
360 $markerArray = array();
361 if (is_array($row) && !empty($row['typelinks'])) {
362 if (($hookObj = $this->hookObjectsArr[$row['typelinks']])) {
363 $brokenUrl = $hookObj->getBrokenUrl($row);
364 }
365 }
366
367 $params = '&edit[' . $table . '][' . $row['recuid'] . ']=edit';
368 $actionLinks = '<a href="#" onclick="' .
369 t3lib_BEfunc::editOnClick(
370 $params,
371 $GLOBALS['BACK_PATH'],
372 t3lib_div::getIndpEnv('REQUEST_URI') . '?id=' . $this->pObj->id . '&search_levels=' . $this->search_level
373 ) . '"' .
374 ' title="' . $GLOBALS['LANG']->getLL('list.edit') . '">' .
375 t3lib_iconWorks::getSpriteIcon('actions-document-open') . '</a>';
376
377 $elementHeadline = $row['headline'];
378 if (empty($elementHeadline)) {
379 $elementHeadline = $GLOBALS['LANG']->getLL('list.no.headline');
380 }
381
382 // Get the language label for the field from TCA
383 if ($GLOBALS['TCA'][$table]['columns'][$row['field']]['label']) {
384 $fieldName = $GLOBALS['TCA'][$table]['columns'][$row['field']]['label'];
385 $fieldName = $GLOBALS['LANG']->sL($fieldName);
386 // Crop colon from end if present.
387 if (substr($fieldName, '-1', '1') === ':') {
388 $fieldName = substr($fieldName, '0', strlen($fieldName)-1);
389 }
390 }
391 // Fallback, if there is no label
392 $fieldName = $fieldName ? $fieldName : $row['field'];
393
394 // column "Element"
395 $element = t3lib_iconWorks::getSpriteIconForRecord($table, $row, array('title' => $table . ':' . $row['recuid']));
396 $element .= $elementHeadline;
397 $element .= ' ' . sprintf($GLOBALS['LANG']->getLL('list.field'), $fieldName);
398
399 $markerArray['actionlink'] = $actionLinks;
400 $markerArray['path'] = t3lib_BEfunc::getRecordPath($row['recpid'], '', 0, 0);
401 $markerArray['element'] = $element;
402 $markerArray['headlink'] = $row['linktitle'];
403 $markerArray['linktarget'] = $brokenUrl;
404
405 $response = unserialize($row['urlresponse']);
406 if ($response['valid']) {
407 $linkMessage = '<span style="color: green;">' . $GLOBALS['LANG']->getLL('list.msg.ok') . '</span>';
408 } else {
409 $linkMessage = '<span style="color: red;">' . $hookObj->getErrorMessage($response['errorParams']) . '</span>';
410 }
411 $markerArray['linkmessage'] = $linkMessage;
412
413 $lastRunDate = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], $row['lastcheck']);
414 $lastRunTime = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'], $row['lastcheck']);
415 $message = sprintf($GLOBALS['LANG']->getLL('list.msg.lastRun'), $lastRunDate, $lastRunTime);
416 $markerArray['lastcheck'] = $message;
417
418 // Return the table html code as string
419 return t3lib_parsehtml::substituteMarkerArray($brokenLinksItemTemplate, $markerArray, '###|###', TRUE, TRUE);
420 }
421
422
423 /**
424 * Builds the checkboxes out of the hooks array.
425 *
426 * @param array array of broken links information
427 * @return html code content
428 */
429 private function getCheckOptions($brokenLinkOverView) {
430 $content = '';
431 $checkOptionsTemplate = '';
432 $checkOptionsTemplate = t3lib_parsehtml::getSubpart($this->doc->moduleTemplate, '###CHECKOPTIONS_SECTION###');
433
434 $hookSectionContent = '';
435 $hookSectionTemplate = t3lib_parsehtml::getSubpart($checkOptionsTemplate, '###HOOK_SECTION###');
436
437 $markerArray['statistics_header'] = $this->doc->sectionHeader($GLOBALS['LANG']->getLL('overviews.statistics.header'));
438
439 $totalCountLabel = $GLOBALS['LANG']->getLL('overviews.nbtotal');
440 $totalCountLabel = t3lib_BEfunc::wrapInHelp('linkvalidator', 'checkboxes', $totalCountLabel);
441 $markerArray['total_count_label'] = $totalCountLabel;
442
443 if (empty($brokenLinkOverView['brokenlinkCount'])) {
444 $markerArray['total_count'] = '0';
445 } else {
446 $markerArray['total_count'] = $brokenLinkOverView['brokenlinkCount'];
447 }
448
449 $linktypes = t3lib_div::trimExplode(',', $this->modTS['linktypes'], 1);
450 $hookSectionContent = '';
451
452 if (is_array($linktypes)) {
453 if (!empty($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])
454 && is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])
455 ) {
456 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'] as $type => $value) {
457 if (in_array($type, $linktypes)) {
458 $hookSectionMarker = array();
459 if (empty($brokenLinkOverView[$type])) {
460 $hookSectionMarker['count'] = '0';
461 } else {
462 $hookSectionMarker['count'] = $brokenLinkOverView[$type];
463 }
464 $translation = $GLOBALS['LANG']->getLL('hooks.' . $type);
465 $translation = $translation ? $translation : $type;
466 $option = t3lib_BEfunc::getFuncCheck(
467 array('id' => $this->pObj->id, 'search_levels' => $this->search_level),
468 'SET[' . $type . ']',
469 $this->pObj->MOD_SETTINGS[$type],
470 '',
471 '',
472 'id="SET[' . $type . ']"'
473 ) . '<label for="SET[' . $type . ']">' . $translation . '</label>';
474 $hookSectionMarker['option'] = $option;
475 $hookSectionContent .= t3lib_parsehtml::substituteMarkerArray($hookSectionTemplate, $hookSectionMarker, '###|###', TRUE, TRUE);
476 }
477 }
478 }
479 }
480
481 $checkOptionsTemplate = t3lib_parsehtml::substituteSubpart($checkOptionsTemplate, '###HOOK_SECTION###', $hookSectionContent);
482
483 return t3lib_parsehtml::substituteMarkerArray($checkOptionsTemplate, $markerArray, '###|###', TRUE, TRUE);
484 }
485
486
487 /**
488 * Loads data in the HTML head section (e.g. JavaScript or stylesheet information).
489 *
490 * @return void
491 */
492 private function loadHeaderData() {
493 $this->doc->addStyleSheet('linkvalidator', $this->relativePath . 'res/linkvalidator.css', 'linkvalidator');
494 }
495
496
497 /**
498 * Gets the buttons that shall be rendered in the docHeader.
499 *
500 * @return array Available buttons for the docHeader
501 */
502 private function getDocHeaderButtons() {
503 $buttons = array(
504 'csh' => t3lib_BEfunc::cshItem('_MOD_web_func', '', $GLOBALS['BACK_PATH']),
505 'shortcut' => $this->getShortcutButton(),
506 'save' => ''
507 );
508 return $buttons;
509 }
510
511
512 /**
513 * Gets the button to set a new shortcut in the backend (if current user is allowed to).
514 *
515 * @return string HTML representiation of the shortcut button
516 */
517 private function getShortcutButton() {
518 $result = '';
519 if ($GLOBALS['BE_USER']->mayMakeShortcut()) {
520 $result = $this->doc->makeShortcutIcon('', 'function', $this->MCONF['name']);
521 }
522 return $result;
523 }
524
525
526 /**
527 * Gets the filled markers that are used in the HTML template.
528 *
529 * @return array The filled marker array
530 */
531 private function getTemplateMarkers() {
532
533 $markers = array(
534 'FIRST_STEPS' => $this->firstSteps,
535 'FUNC_MENU' => $this->getLevelSelector(),
536 'CONTENT' => $this->content,
537 'TITLE' => $GLOBALS['LANG']->getLL('title'),
538 'CHECKALLLINK' => $this->checkAllHtml,
539 'CHECKOPTIONS' => $this->checkOptHtml,
540 'ID' => '<input type="hidden" name="id" value="' . $this->pObj->id . '"/>',
541 'REFRESH' => $this->refreshListHtml,
542 'UPDATE' => $this->updateListHtml
543 );
544
545 return $markers;
546 }
547
548
549 /**
550 * Determines whether the current user is an admin.
551 *
552 * @return boolean Whether the current user is admin
553 */
554 private function isCurrentUserAdmin() {
555 return ((bool) $GLOBALS['BE_USER']->user['admin']);
556 }
557 }
558
559 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/linkvalidator/modfunc1/class.tx_linkvalidator_modfunc1.php'])) {
560 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/linkvalidator/modfunc1/class.tx_linkvalidator_modfunc1.php']);
561 }
562
563 ?>