1<?php 2# ================================================================================================= 3# gCalendar "gcal_read.php" - responsible for reading the data into the big gCal_data-array 4# ================================================================================================= 5 6/** 7 * read the pages given in the pages=(...) parameter into the "gCal_data"-array 8 * 9 * @author Frank Hinkel <frank@hi-sys.de> 10 * 11 * @param array $options contains a list of parameters passed to the plugin at the wiki-page 12 * @param array pages the wiki-pages to read the data from 13 * @param default_date if year ist omitted it is taken from this date. 14 * so you can write "31.07." for a birthdate which shows up every year 15 */ 16function read_pages_into_calendar(&$options,&$pages,$default_date) { 17 global $gCal_data; # this array receives all the date-entries 18 19 # reset data-array 20 $gCal_data = array(); 21 22 # memory for wiki-pages allready read to avoid duplicates 23 $pages_allready_read = array(); 24 25 foreach($pages as $page_key=>$page) 26 { 27 list($page_name,$page_list) = explode("(",$page,2); 28 $page_list = substr($page_list,0,-1); 29 30 if($page_list != ""){ 31 $page_list = explode("|",$page_list); 32 }else{ 33 $page_list = array($page_name); 34 } 35 36 # expand namespaces (i.e. ":wiki:*") expands to all files in that ns 37 # if option 'nested' is given, this is done recursive (all subnamespaces) 38 $page_list = expand_ns($page_list,isset($options['nested'])); 39 40 foreach($page_list as $wikipage) 41 { 42 # split section from wikipage 43 list($wikipage,$section)=explode('#',$wikipage,2); 44 45 $wikipage=cleanID($wikipage); 46 47 # skip pages allready read, except if allowed with option: "showdup" 48 if(!isset($options["showdup"]) && in_array($wikipage,$pages_allready_read)) continue; 49 $pages_allready_read[] = $wikipage; 50 51 # check if user is allowed to see this page 52 $perm = auth_quickaclcheck(cleanID($wikipage)); 53 if ($perm < AUTH_READ) continue; # to next page 54 55 # if we have more than one page to include or option pagelinks is set to 'show', 56 # generate a link to the wiki page, except option pagelinks is set to 'hide'. 57 if( ($options["pagelinks"]=="show") || 58 (($options["pagelinks"]!="hide") && count($page_list)>1) ) { 59 $pagelink = " <span class='gCal_pagelink'><a href='".wl($wikipage)."'>".noNS($wikipage)."</a></span>"; 60 }else{ 61 $pagelink=""; 62 } 63 64 # now read this page into the calendar-array 65 read_wikipage_into_calendar($options,$page_key,$wikipage,$section,$pagelink,$default_date); 66 } 67 } 68} 69 70 71/** 72 * read a page into the gCal_data-array 73 * 74 * @author Frank Hinkel <frank@hi-sys.de> 75 * 76 * @param array $options contains a list of parameters passed to the plugin at the wiki-page 77 * @param array page_key the column number where the data is stored into the gCal_data-array 78 * @param string wikipage the wiki-page to read the data from 79 * @param string section optionally given section inside the wikipage to read from 80 * @param default_date if year ist omitted it is taken from this date. 81 * so you can write "31.07." for a birthdate which shows every year 82 * 83 */ 84function read_wikipage_into_calendar(&$options,$page_key,$wikipage,$section,$pagelink,$default_date) { 85 global $gCal_data; # this array contains all the date-entries 86 global $conf; 87 88 # read categorypattern from config 89 $match_category = $conf['gCal_match_category']; 90 if(!is_array($match_category)) return; 91 92 # read eventpattern from config. 93 $match_event = $conf['gCal_match_event']; 94 if(!is_array($match_event)) return; 95 96 # find path to actual wiki-page. skip if file not exists 97 $filepath = wikiFN($wikipage); 98 if(!file_exists($filepath)) return; 99 100 # read data from wiki-page 101 $handle = fopen ($filepath, "r"); 102 103 while (!feof($handle)) { 104 $buffer = trim(fgets($handle, 4096)); 105 106 # check line against all category-patterns. see user/conf.php 107 foreach($match_category as $pattern) { 108 if(preg_match($pattern, $buffer, $subpattern)) { 109 $category = strtoupper($subpattern[1]); 110 break; 111 } 112 } 113 114 if(is_string($section) && strlen($section)>0 && (strtoupper($section)!=$category)) continue; 115 116 # check line against all event-patterns. see user/conf.php 117 foreach($match_event as $pattern) { 118 if(preg_match($pattern, $buffer, $subpattern)) { 119 $buffer = $subpattern[1]; 120 121 # grab date- and time-spans from the beginning of each line 122 $start_date = $end_date = fetch_date($buffer,$default_date); 123 $end_time = ""; 124 $start_time = fetch_time($buffer); 125 126 # check for time-spans indicated by a dash 127 if($buffer{0}=="-") { 128 $buffer = trim(substr($buffer,1)); # remove dash 129 $end_date = fetch_date($buffer,$default_date); 130 # end_date equals start_date by default 131 if ( strlen($end_date)==0 ) $end_date = $start_date; 132 $end_time = fetch_time($buffer); 133 } 134 135 $cat = strtoupper(trim($category." ".fetch_inline_category($buffer))); 136 137 # insert the event into the whole date-range 138 for($d=$start_date ; $d <= $end_date ; $d++) { 139 $entry = $buffer; 140 141 # initialize event with event-source 142 $event = array("source"=>$subpattern[1]); 143 144 # set the category, even when there is no event-text 145 $event["categories"] = $cat; 146 147 # generate an event, when event-text or start-time or end-time is given 148 if(($entry!="") || ($start_time!="") || ($end_time!="")) { 149 # special character ">" will be suppressed, when it is the first character 150 # of the event-text. can be used to force an event-icon without text 151 if($entry{0}==">") $buffer=substr($entry,1); 152 153 # apply basic rendering like bold, italic, links, etc. 154 $entry = fast_p_render($entry); 155 156 # add the backlink to the original wikipage 157 $entry .= $pagelink; 158 159 # attach start_time at start_date and end_time at end_date. I hope this is allways logical? 160 if(is_array($end_time) && ($d==$end_date) ) { 161 $entry = "- ".$end_time[0]. " ".$entry; 162 $event["end_time"] = $end_time[1]; 163 } 164 if(is_array($start_time) && ($d==$start_date)) { 165 $entry = $start_time[0]." ".$entry; 166 $event["start_time"] = $start_time[1]; 167 } 168 169 # write the event-entry to global array 170 $cat_classes = 'gCal_cat_'.implode(' gCal_cat_',explode(' ',$cat))." "; 171 172 $event["content"] = "<span class='$cat_classes gCal_event'>".$entry."</span>"; 173 } 174 $gCal_data[$page_key][$d][] = $event; 175 } 176 break; 177 } 178 } 179 } 180 fclose($handle); 181} 182 183 184# ================================================================================================= 185# Utility-functions 186# ================================================================================================= 187 188/* 189 * returns date at the beginning of the text. if date found it is removed from the text. 190 */ 191function fetch_date(&$text,$default_date) { 192 global $conf; 193 194 if($text=="") return; 195 196 # '#^' --> # string has to start with the pattern. 197 # '(?=($|\D))#' --> # look-ahead => any non-digit or end-of-string terminates pattern 198 199 if(preg_match('#^'.$conf['gCal_date_dmy'].'(?=($|\D))#',$text,$match)) { 200 $d=$match[1];$m=$match[2];$y=$match[3]; 201 }elseif(preg_match('#^'.$conf['gCal_date_mdy'].'(?=($|\D))#',$text,$match)) { 202 $d=$match[2];$m=$match[1];$y=$match[3]; 203 }elseif(preg_match('#^'.$conf['gCal_date_ymd'].'(?=($|\D))#',$text,$match)) { 204 $d=$match[3];$m=$match[2];$y=$match[1]; 205 }else{ 206 return; 207 } 208 209 if(strlen($d)==1) $d='0'.$d; 210 if(strlen($m)==1) $m='0'.$m; 211 if(strlen($y)==0) $y=date('Y',$default_date); 212 if(strlen($y)==2) $y='20'.$y; 213 214 $text=trim(substr($text,strlen($match[0]))); 215 return $y.$m.$d; // return format yyyymmdd 216} 217 218 219/* 220 * returns time at the beginning of the text 221 */ 222function fetch_time(&$text) { 223global $conf; 224 225 if($text=="") return; 226 227 # allowed formats are: 1:23 , 01:23, 01:23am. 1:23 Am, etc. 228 $pattern = '([0-9]{1,2})\:([0-9]{2})\s*(am|pm|)'; 229 $pattern = '#^'.$pattern; # string has to start with the pattern 230 $pattern .= '(?=($|\\D))#i'; # look-ahead => any non-digit or end-of-string terminates pattern 231 232 if(preg_match($pattern,$text, $match)) { 233 $text=trim(substr($text,strlen($match[0]))); 234 235 $time = str_replace(array('##','#h','#m','#r'),$match,$conf['gCal_time']); 236 if(strlen($match[1])==1) $match[1] = '0'.$match[1]; 237 if(strtolower($match[3])=='pm') $match[1] += 12; 238 $euro = $match[1].":".$match[2]; 239 240 return array($time,$euro); 241 } 242} 243 244/* 245 * returns the inline-category 246 */ 247function fetch_inline_category(&$text) { 248global $conf; 249 250 if($text=="") return; 251 252 # get the preg-expr from $conf. sting has to start with this pattern 253 254 $pattern = '#'.$conf['gCal_inline_Category_visible'].'#i'; 255 if(preg_match($pattern,$text, $match)) { 256 $text=trim($match[1].substr($text,strlen($match[0]))); 257 return strtoupper($match[1]); 258 } 259 260 $pattern = '#'.$conf['gCal_inline_Category_hidden'].'#i'; 261 if(preg_match($pattern,$text, $match)) { 262 $text=trim(substr($text,strlen($match[0]))); 263 return strtoupper($match[1]); 264 } 265} 266 267/** 268 * Expand namespaces in the form :namespace:* to every wiki-page in this ns. 269 * 270 * @author Frank Hinkel <frank@hi-sys.de> 271 * 272 * @param array page_list on input : array of pages and namespaces 273 * @param array page_list on output : array of pages (namespaces are expanded) 274 * @param boolean nested if true -> subnamespaces are also expanded into single-pages 275 * 276 */ 277function expand_ns($page_list,$nested=false) { 278 global $conf; 279 $pl = array(); 280 281 foreach($page_list as $page) { 282 if(substr($page,-1)=="*") { 283 # when page ends with *, expand namespace 284 $start = noNS($conf['start']); 285 $dir = wikiFN(substr($page,0,-1).$start); 286 $dir = substr($dir,0,-(strlen($start)+4)); # strip filename - only the dir is needed 287 288 if(!@$handle=opendir($dir)) continue; 289 290 while ($file = readdir ($handle)) { 291 if($file == "." || $file == ".." || trim($file)=="") continue; 292 293 if(is_file($dir.$file)) { 294 $pl[] = substr($page,0,-1).substr($file,0,-4); 295 }elseif($nested && is_dir($dir.$file)){ 296 $subNS[] = substr($page,0,-2).':'.cleanID($file).':*'; 297 } 298 } 299 300 closedir($handle); 301 }else{ 302 # leave page as it is 303 $pl[] = $page; 304 } 305 } 306 307 # if $nested is true, add all files of all subnamespaces to the list 308 if(is_array($subNS)) { 309 $subNS = expand_ns($subNS,$nested,false); 310 $pl = array_merge($pl,$subNS); 311 } 312 313 return $pl; 314} 315 316 317/** 318 * quick rendering, where not all the power of p_render is needed 319 * do basic formatting and linking. returns the rendered string 320 * 321 * @author Frank Hinkel <frank@hi-sys.de> 322 * @param string $text text to be rendered 323 * 324 * @todo : add more rendering 325 */ 326function fast_p_render($text) { 327 global $conf; 328 329 # do some fundamental-rendering: bold, italic, underline, ... 330 $text = preg_replace('#\*\*(.*)\*\*#sU', '<strong>\1</strong>', $text); 331 $text = preg_replace('#(?<!\:)\/\/(.*)(?<!\:)\/\/#sU', '<em>\1</em>', $text); 332 $text = preg_replace('#\_\_(.*)\_\_#sU', '<em class="u">\1</em>', $text); 333 $text = preg_replace('#\'\'(.*)\'\'#sU', '<code>\1</code>', $text); 334 $text = preg_replace('#\\\\\\\\(\s+|$)#m', '<br/>', $text); 335 336 $text = str_replace('=>','⇒',$text); 337 $text = str_replace('<=','⇐',$text); 338 $text = str_replace('->','→',$text); 339 $text = str_replace('<-','←',$text); 340 341 # some methods of the class 'Doku_Renderer_xhtml' needed here 342 static $drx; if(!isset($drx)) $drx = new Doku_Renderer_xhtml; 343 344 # process all links enclosed in double square brackets 345 preg_match_all("=\[\[.+\]\]=sU",$text,$wiki_links); 346 347 foreach($wiki_links[0] as $wl) { 348 # reset rendered content 349 $drx->doc = ''; 350 351 list($link,$name)=explode("|",substr($wl,2,strlen($wl)-4),2); 352 353 if ( preg_match('/^[a-zA-Z\.]+>{1}.*$/u',$link )) { 354 // Interwiki 355 if(count($drx->interwiki)==0) $drx->interwiki = getInterwiki(); 356 357 list($wikiname,$wikiuri) = preg_split('/>/u',$link,2); 358 $drx->interwikilink('',$name,strtolower($wikiname),$wikiuri); 359 }elseif ( preg_match('/^\\\\\\\\[\w.:?\-;,]+?\\\\/u',$link) ) { 360 // Windows Share 361 $drx->windowssharelink($link,$name); 362 }elseif ( preg_match('#^([a-z0-9\-\.+]+?)://#i',$link) ) { 363 // external link (accepts all protocols) 364 $drx->externallink($link,$name); 365 }elseif ( preg_match('#([a-z0-9\-_.]+?)@([\w\-]+\.([\w\-\.]+\.)*[\w]+)#i',$link) ) { 366 // email-link 367 $drx->emaillink($link,$name); 368 }elseif ( preg_match('!^#.+!',$link) ){ 369 // local link 370 $drx->locallink($link,$name); 371 }else { 372 $drx->internallink($link,$name); 373 } 374 375 $text=str_replace($wl,$drx->doc,$text); 376 } 377 378 # remove all html-tags which are not explicitly allowed 379 return strip_tags($text,$conf['gCal_allowed_tags']); 380} 381