1<?php 2 3/** 4 * Plugin iCalendar: Renders an iCal .ics file into HTML. 5 * 6 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 7 * @version 1.4 8 * @date November 2011 9 * @author J. Drost-Tenfelde <info@drost-tenfelde.de> 10 * 11 * This plugin is based on the iCalEvents plugin by Robert Rackl <wiki@doogie.de>. 12 * 13 */ 14 15// must be run within Dokuwiki 16if(!defined('DOKU_INC')) die(); 17 18if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 19require_once(DOKU_PLUGIN.'syntax.php'); 20 21include_once(DOKU_INC.'lib/plugins/iCalendar/functions.php'); 22 23/** 24 * This plugin gets an iCalendar file via HTTP and parses it into HTML. 25 * 26 * Usage: {{iCalendar>http://host/myCalendar.ics#from=today&previewDays=30}} 27 * 28 * You can filter the events that are shown with two parametes: 29 * 1. 'from' a date from which on to show events. MUST be in the format MM/dd/yyyy 30 * or you can simplay say "from=today". 31 * If from is ommited, then all events are shown. 32 * 2. 'previewDays' amount of days to preview into the future. 33 * 34 * <code>from <= eventdate <= from+(previewDays*24*60*3600)</code> 35 * 36 * There are some more configuration settins in plugins/iCalendar/conf/default.php 37 * 38 * @see http://de.wikipedia.org/wiki/ICalendar 39 */ 40class syntax_plugin_iCalendar extends DokuWiki_Syntax_Plugin 41{ 42 function getInfo() { 43 return array( 44 'author' => 'J. Drost-Tenfelde', 45 'email' => 'info@drost-tenfelde.de', 46 'date' => '2011-09-28', 47 'name' => 'iCalendar', 48 'desc' => 'Parses an iCalendar .ics file into HTML', 49 'url' => 'http://www.drost-tenfelde.de/?id=dokuwiki:plugins:icalendar', 50 ); 51 } 52 53 // implement necessary Dokuwiki_Syntax_Plugin methods 54 function getType() { return 'substition'; } 55 function getSort() { return 42; } 56 function connectTo($mode) { $this->Lexer->addSpecialPattern('\{\{iCalendar>.*?\}\}',$mode,'plugin_iCalendar'); } 57 58 /** 59 * parse parameters from the {{iCalendar>...}} tag. 60 * @return an array that will be passed to the renderer function 61 */ 62 function handle($match, $state, $pos, &$handler) { 63 64 $match = substr($match, 13, -2); // strip {{iCalendar> from start and }} from end 65 list($icsURL, $flagStr) = explode('#', $match); 66 parse_str($flagStr, $params); 67 68 // Get the from parameter 69 if ($params['from'] == 'today') { 70 $from = time(); 71 } else if (preg_match('#(\d\d)/(\d\d)/(\d\d\d\d)#', $params['from'], $fromDate)) { 72 // must be MM/dd/yyyy 73 $from = mktime(0, 0, 0, $fromDate[1], $fromDate[2], $fromDate[3]); 74 } else if (preg_match('/\d+/', $params['from'])) { 75 $from = $params['from']; 76 } 77 // Get the to parameter 78 if ($params['to'] == 'today') { 79 $to = mktime(24, 0, 0, date("m") , date("d"), date("Y")); 80 81 } else if (preg_match('#(\d\d)/(\d\d)/(\d\d\d\d)#', $params['to'], $toDate)) { 82 // must be MM/dd/yyyy 83 $to = mktime(0, 0, 0, $toDate[1], $toDate[2], $toDate[3]); 84 } else if (preg_match('/\d+/', $params['to'])) { 85 $to = $params['to']; 86 } 87 88 // Get the numberOfEntries parameter 89 if ($params['numberOfEntries']) { 90 $numberOfEntries = $params['numberOfEntries']; 91 } else { 92 $numberOfEntries = -1; 93 } 94 95 // Get the show end dates parameter 96 if ($params['showEndDates'] == 1 ) { 97 $showEndDates = true; 98 } else { 99 $showEndDates = false; 100 } 101 // Get the show as list parameter 102 if ( $params['showAs'] ) { 103 $showAs = $params['showAs']; 104 } 105 else { 106 $showAs = 'default'; 107 } 108 109 // Get the showAs parameter (since v1.4) 110 if ( $params['showAs'] ) { 111 $showAs = $params['showAs']; 112 } 113 else { 114 // Backward compatibiltiy of v1.3 or earlier 115 if ($params['showAsList'] == 1) { 116 $showAs = 'list'; 117 } else { 118 $showAs = 'default'; 119 } 120 } 121 // Get the appropriate template 122 $template = $this->getConf($showAs); 123 if ( !isset($template ) || $template == '' ) { 124 $template = $this->getConf('default'); 125 } 126 127 // Find out if the events should be sorted in reserve 128 $sort_descending = false; 129 if ( $params['sort'] == 'DESC') { 130 $sort_descending = true; 131 } 132 //echo $rsort; 133 //exit( 0 ); 134 135 // Get the previewDays parameter 136 if ( $params['previewDays'] ) { 137 $previewDays = $params['previewDays']; 138 } 139 else { 140 $previewDays = -1; 141 } 142 143 #echo "url=$icsURL from = $from numberOfEntries = $numberOfEntries<br>"; 144 return array($icsURL, $from, $to, $previewDays, $numberOfEntries, $showEndDates, $template, $sort_descending); 145 } 146 147 /** 148 * loads the ics file via HTTP, parses it and renders an HTML table. 149 */ 150 function render($mode, &$renderer, $data) { 151 list($url, $from, $to, $previewDays, $numberOfEntries, $showEndDates, $template, $sort_descending) = $data; 152 $ret = ''.$mediadir; 153 154 if ($mode == 'xhtml') { 155 # parse the ICS file 156 $entries = $this->_parseIcs($url, $from, $to, $previewDays, $numberOfEntries, $sort_descending); 157 158 if ($this->error) { 159 $renderer->doc .= "Error in Plugin iCalendar: ".$this->error; 160 return true; 161 } 162 163 #loop over entries and create a table row for each one. 164 $rowCount = 0; 165 166 foreach ($entries as $entry) { 167 $rowCount++; 168 169 # Get the html for the entries 170 $entryTemplate = $template; 171 172 // {description} 173 $entryTemplate = str_replace('{description}', $entry['description'], $entryTemplate ); 174 175 // {summary} 176 $entryTemplate = str_replace('{summary}', $entry['summary'], $entryTemplate ); 177 178 // {summary_link} 179 $summary_link = array(); 180 $summary_link['class'] = 'urlintern'; 181 $summary_link['style'] = 'background-image: url(lib/plugins/iCalendar/ics.png); background-repeat:no-repeat; padding-left:16px; text-decoration: none;'; 182 $summary_link['pre'] = ''; 183 $summary_link['suf'] = ''; 184 $summary_link['more'] = 'rel="nofollow"'; 185 $summary_link['target'] = ''; 186 $summary_link['title'] = $entry['summary']; 187 $summary_link['url'] = 'lib/plugins/iCalendar/vevent.php?vevent='.urlencode( $entry['vevent'] ); 188 $summary_link['name'] = $entry['summary']; 189 $entryTemplate = str_replace('{summary_link}', '<html>'.$renderer->_formatLink($summary_link).'</html>', $entryTemplate ); 190 191 // See if a location was set 192 $location = $entry['location']; 193 if ( $location != '' ) { 194 // {location} 195 $entryTemplate = str_replace('{location}', $location, $entryTemplate ); 196 197 // {location_link} 198 $location_link = 'http://maps.google.com/maps?q='.str_replace(' ', '+', str_replace(',', ' ', $location)); 199 $entryTemplate = str_replace('{location_link}', '[['.$location_link.'|'.$location.']]', $entryTemplate ); 200 } 201 else { 202 // {location} 203 $entryTemplate = str_replace('{location}', 'Unknown', $entryTemplate ); 204 // {location_link} 205 $entryTemplate = str_replace('{location_link}', 'Unknown', $entryTemplate ); 206 } 207 208 $dateString = ""; 209 210 // Get the start and end day 211 $startDay = date("Ymd", $entry['startunixdate']); 212 $endDay = date("Ymd", $entry['endunixdate']); 213 214 if ( $endDay > $startDay ) 215 { 216 if ( $entry['allday'] ) 217 { 218 $dateString = $entry['startdate'].'-'.$entry['enddate']; 219 } 220 else { 221 $dateString = $entry['startdate'].' '.$entry['starttime'].'-'.$entry['enddate'].' '.$entry['endtime']; 222 } 223 } 224 else { 225 if ( $showEndDates ) { 226 if ( $entry['allday'] ) 227 { 228 $dateString = $entry['startdate']; 229 } 230 else { 231 $dateString = $entry['startdate'].' '.$entry['starttime'].'-'.$entry['endtime']; 232 } 233 } 234 else { 235 $dateString = $entry['startdate']; 236 } 237 } 238 239 // {date} 240 $entryTemplate = str_replace('{date}', $dateString, $entryTemplate ); 241 242 $ret .= $entryTemplate.' 243'; 244 245 } 246 //$renderer->doc .= $ret; 247 $html = p_render($mode, p_get_instructions( $ret ), $info ); 248 $html = str_replace( '\\n', '<br />', $html ); 249 $renderer->doc .= $html; 250 251 return true; 252 } 253 return false; 254 } 255 256 /** 257 * Load the iCalendar file from 'url' and parse all 258 * events that are within the range 259 * from <= eventdate <= from+previewSec 260 * 261 * @param url HTTP URL of an *.ics file 262 * @param from unix timestamp in seconds (may be null) 263 * @param to unix timestamp in seconds (may be null) 264 * @param previewDays Limit the entries to 30 days in the future 265 * @param numberOfEntries Number of entries to display 266 * @param $sort_descending 267 * @return an array of entries sorted by their startdate 268 */ 269 function _parseIcs($url, $from, $to, $previewDays, $numberOfEntries, $sort_descending ) { 270 global $conf; 271 272 $http = new DokuHTTPClient(); 273 if (!$http->get($url)) { 274 $this->error = "Could not get '$url': ".$http->status; 275 return array(); 276 } 277 $content = $http->resp_body; 278 $entries = array(); 279 280 # If dateformat is set in plugin configuration ('dformat'), then use it. 281 # Otherwise fall back to dokuwiki's default dformat from the global /conf/dokuwiki.php. 282 $dateFormat = $this->getConf('dformat') ? $this->getConf('dformat') : $conf['dformat']; 283 //$timeFormat = $this->getConf('tformat') ? $this->getConf('tformat') : $conf['tformat']; 284 285 # regular expressions for items that we want to extract from the iCalendar file 286 $regex_vevent = '/BEGIN:VEVENT(.*?)END:VEVENT/s'; 287 288 #split the whole content into VEVENTs 289 preg_match_all($regex_vevent, $content, $matches, PREG_PATTERN_ORDER); 290 291 if ( $previewDays > 0 ) 292 { 293 $previewSec = $previewDays * 24 * 3600; 294 } 295 else { 296 $previewSec = -1; 297 } 298 299 // loop over VEVENTs and parse out some itmes 300 foreach ($matches[1] as $vevent) { 301 $entry = parse_vevent( $vevent, $dateFormat ); 302 303 // if entry is to old then filter it 304 if ($from && $entry['endunixdate']) { 305 if ($entry['endunixdate'] < $from) { continue; } 306 if (($previewSec > 0) && ($entry['startunixdate'] > time()+$previewSec)) { continue; } 307 } 308 309 // if entry is to new then filter it 310 if ($to && $entry['startunixdate']) { 311 if ($entry['startunixdate'] > $to) { continue; } 312 } 313 314 $entries[] = $entry; 315 } 316 317 if ( $to && ($from == null) ) 318 { 319 // sort entries by startunixdate 320 usort($entries, 'compareByEndUnixDate'); 321 } else if ( $from ) { 322 // sort entries by startunixdate 323 usort($entries, 'compareByStartUnixDate'); 324 } 325 else if ( $sort_descending ) { 326 $entries = array_reverse( $entries, true ); 327 } 328 329 // See if a maximum number of entries was set 330 if ( $numberOfEntries > 0 ) 331 { 332 $entries = array_slice( $entries, 0, $numberOfEntries ); 333 334 335 // Reverse array? 336 if ( $from && $sort_descending) { 337 $entries = array_reverse( $entries, true ); 338 } 339 else if ( $to && !$from && (!$sort_descending)) { 340 $entries = array_reverse( $entries, true ); 341 } 342 } 343 344 return $entries; 345 } 346} 347 348/** compares two entries by their startunixdate value */ 349function compareByStartUnixDate($a, $b) { 350 return strnatcmp($a['startunixdate'], $b['startunixdate']); 351} 352 353/** compares two entries by their startunixdate value */ 354function compareByEndUnixDate($a, $b) { 355 return strnatcmp($b['endunixdate'], $a['endunixdate']); 356} 357 358?>