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