1<?php
2# handle {{linkcheck>on|off}}  to enable/disable link-checking within the page.
3if(!defined('DOKU_INC')) die();
4require_once(DOKU_PLUGIN.'syntax.php');
5require_once __DIR__.'/helperfunctions.php';
6
7class syntax_plugin_linkcheck extends DokuWiki_Syntax_Plugin {
8	protected $helper;
9	public function __construct()
10	{
11			$this->helper = $this->loadHelper('linkcheck', false);
12	}
13
14	function getType(){ return 'substition'; }
15  function getPType(){ return 'normal'; }
16	function getSort(){ return 196; }
17	function connectTo($mode) {
18		$this->Lexer->addSpecialPattern('{{linkcheck>[^}]+}}',$mode,'plugin_linkcheck');
19	}
20
21	#$s is $R->doc that we are modifying.
22function injectclassforlinks(&$doc,$urlmap,$otherurls){
23	#We ignore the unlikely possibility that a url may appear in different linkcheck:on/off blocks. If a url appears in a linkcheck:on block, the same url will also be registered for linkcheck in other blocks.
24	if(strpos($doc,'urlextern')===false) return;
25
26	preg_match_all('#<a [^>]*href="([^"]+)" [^>]*class="[^"]*\b(urlextern)\b[^"]*"#',$doc,$ums,PREG_SET_ORDER|PREG_OFFSET_CAPTURE);
27	if(!$ums) return;
28
29	if($this->helper->getusecache()){
30			$expirytime = $this->helper->getcacheexpirytime();
31			$otherurlmap=array_flip($otherurls);
32
33			for($j=sizeof($ums)-1; $j>=0; $j--){
34					$m=$ums[$j];
35					$url=$m[1][0];
36					$r=&$urlmap[$url];
37
38					#up-to-date urls are written javascript:LINKCHECKDATA variable. in action.php: ontplmetaheaderoutput().
39					#We handle them in javascript and update their class accordingly. no need to inject a class here.
40					#CAVEAT: We get lastcheck/codegroup information during parse time, so we may not have the latest information here. For such cases, the link will be ajax-check in javascript. No big deal. If the page is edited/rendered again, the latest information will be available.
41					if(isset($r)&&$r['lastcheck']>=$expirytime){
42							#msg("Skipping [ $url ], b/c it will be included in javascript variable LINKCHECKDATA ...");
43					}
44					#for others, inject the linkcheck class to trigger an ajax call in javascript.
45					elseif(isset($otherurlmap[$url])){
46							$doc=substr($doc,0,$m[2][1])."linkcheck ".substr($doc,$m[2][1]);
47					}
48			}
49	}
50	#when cache is not being used, we inject the linkcheck class to trigger an ajax call in javascript for all links.
51	else{
52		#$doc=preg_replace('#(<a [^>]*href="[^"]+" [^>]*class="[^"]*)(\burlextern\b[^"]*")#','$1linkcheck $2',$doc);
53		#only inject for the urls that are in lincheck:on blocks.
54		for($j=sizeof($ums)-1; $j>=0; $j--){
55			$m=$ums[$j];
56			$url=$m[1][0];
57			if(isset($otherurlmap[$url])) $doc=substr($doc,0,$m[2][1])."linkcheck ".substr($doc,$m[2][1]);
58		}
59	}
60}
61
62	#--------------------------------------------------------------
63	function render($mode, Doku_Renderer $R, $data) {
64		static $currentstate;
65		if(!isset($currentstate)) $currentstate=$this->getConf('enabledbydefault');
66		list($state,$data)=$data;
67
68		#these is the $urlmap injected by the action.php: onparserhandlerdone() function.
69		#this also marks the end of a page, so we go back and modify the externurl links in R->doc.
70		if(is_array($data)){
71			$id=$data[0]; #this would not be the same as the global $ID when this is a rendering of an include'd page.
72			$urlmap=$data[1];
73			$otherurls=$data[2];
74			if($mode=='metadata'){
75				#we may have multiple wiki pages being processed (sidebar, mainpage, etc.)
76				if(!isset($R->meta['linkcheck_urlmap'])) $R->meta['linkcheck_urlmap']=$urlmap;
77				else $R->meta['linkcheck_urlmap']=array_merge($R->meta['linkcheck_urlmap'],$urlmap);
78			}
79			elseif($mode=='xhtml'){
80				$this->injectclassforlinks($R->doc,$urlmap,$otherurls);
81				#restore the default state
82				$currentstate=$this->getConf('enabledbydefault');
83			}
84		}
85		elseif($mode=='xhtml'){
86			if($data=='on'||$data=='off'){
87				$currentstate=$data=='on';
88			}
89			else{
90				$R->info['cache'] = FALSE; #otherwise msg() will not work after the first call.
91				msg("Invalid linkcheck state: [".htmlspecialchars($data)."]. Must be 'on' or 'off'.");
92				$currentstate=$this->getConf('enabledbydefault');
93			}
94		}
95		return false;
96	}
97
98	#parse task, args, and options.
99	function handle($match, $state, $pos, Doku_Handler $handler)
100	{
101		preg_match('#{{linkcheck>([^}]+)}}#',$match,$m);
102		$data=trim($m[1]);
103		return [$state,$data];
104	}
105}
106