Sample public calendar for ownCloud using ICS parser
Par Mathieu le samedi 30 juillet 2016, 08:09 - Hacks - Lien permanent
When ownCloud removed the ability to share a calendar publicly, I had no other choice than forcing my acquaintances to register to my ownCloud.
I didn't want that, so I implemented my own solution.
1. Abstract
My calendar works like this :
I write events in a particular calendar.
I share this calendar with a special ownCloud user, dedicated for this.
The script will login on the CalDAV API with the credential of the special user, and get the content of the shared calendar.
The script compile the events and compile a nice table, with the content of the calendar.
Please note that the events I write are special: I only use the title field to specify a "color" that will be displayed for the day. The colors are corresponding to my « level of availability » (to show my holidays dates, for instance)
2. Create the calendar and the user
In owncloud, create the second user. I called it public, for instance.
In ownCloud, create the shared calendar on your own account, and give public access to it. Do not tick write permissions.
Create a test event, and log out.
Log in back with the public user, and check that you see the shared calendar and its content.
3. Download the library
I used this ICS parser to parse the CalDAV respponses. Download it on your web hosting to the direcory ics-parser/.
4. Install the script
Here is the script I use:
<?php // Require ICS parser library require_once('ics-parser/class.iCalReader.php'); // Use it to debug, the previously included parser disables the errors error_reporting(E_ALL); ini_set('display_errors', '1'); // Simple caching system, feel free to change the delay if (file_exists('calendar.cache.html')) { $last_update = filemtime('calendar.cache.html'); } else { $last_update = 0; } if ($last_update + 60*60 < time()) { // Get events $headers = array( 'Content-Type: application/xml; charset=utf-8', 'Depth: 1', 'Prefer: return-minimal' ); // Setup the calendar URL and the credentials here $calendar_url = 'https://example.com/remote.php/caldav/calendars/public/public_shared_by_YOU'; $calendar_user = 'public'; $calendar_password = 'public_password'; // Prepare request body $doc = new DOMDocument('1.0', 'utf-8'); $doc->formatOutput = true; $query = $doc->createElement('c:calendar-query'); $query->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:c', 'urn:ietf:params:xml:ns:caldav'); $query->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:d', 'DAV:'); $prop = $doc->createElement('d:prop'); $prop->appendChild($doc->createElement('d:getetag')); $prop->appendChild($doc->createElement('c:calendar-data')); $query->appendChild($prop); $prop = $doc->createElement('c:filter'); $filter = $doc->createElement('c:comp-filter'); $filter->setAttribute('name', 'VCALENDAR'); $prop->appendChild($filter); $query->appendChild($prop); $doc->appendChild($query); $body = $doc->saveXML(); // Debugging purpose //echo '<pre>' . htmlspecialchars($body) . '</pre>'; // Prepare cURL request $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $calendar_url); curl_setopt($ch, CURLOPT_USERPWD, $calendar_user . ':' . $calendar_password); curl_setopt($ch, CURLOPT_VERBOSE, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'REPORT'); curl_setopt($ch, CURLOPT_POSTFIELDS, $body); $response = curl_exec($ch); if (curl_error($ch)) { //echo curl_error($ch); exit(); } curl_close($ch); // Debugging purpose //echo '<pre>' . htmlspecialchars($response) . '</pre>'; // Get the useful part of the response $xml = simplexml_load_string($response); $data = $xml->xpath('//cal:calendar-data'); // Debugging purpose //echo '<pre>' . htmlspecialchars($data[0]) . '</pre>'; // Parse events $calendar_events = array(); foreach ($data as $vcalendars) { $ical = new ICal(); $vcalendars = $vcalendars->__tostring(); $lines = explode("\n", $vcalendars); $ical->initLines($lines); $events = $ical->events(); foreach ($events as $event) { $start = $ical->iCalDateToUnixTimestamp($event['DTSTART']); $end = $ical->iCalDateToUnixTimestamp($event['DTEND']); $summary = $event['SUMMARY']; $calendar_events[] = array( 'start' => $start, 'end' => $end, 'summary' => $summary, ); } } // Function to sort the events by start date function _sort_events($a, $b) { return ($a['start'] > $b['start']); } // Sort the events by start date // By doing so, you can have overlapping events, the newest event will superseed the previous usort($calendar_events, '_sort_events'); // Function to convert my "special titles" into hex colors function color2hex($color) { $color = strtolower($color); switch($color) { case 'green': return '#00ff00'; break; case 'orange': return '#ffaa00'; break; case 'red': return '#ff0000': break; case 'black': return '#000000'; break; case 'blue': return '#0000ff'; break; } } // Bufferize output for caching system ob_start(); // Get current date $now = mktime(0,0,0); // Initiate the color array // Each day in our calendar table will have a color // Each color is an hex color, corresponding tu the event title (see the switch-case block upper) $day_color = array(); // Loop through ALL events (old events included) foreach ($calendar_events as $event) { // Discard passed events if ($event['end'] < time()) { continue; } // Calculate the timestamp of the "start" date of the event $current_day = mktime(0,0,0, date('n', $event['start']), date('j', $event['start']), date('Y', $event['start'])); $first_loop = true; // Loop to fill the color array from the event start date to the event end date for ($d = $current_day; ($first_loop || $d < $event['end']); $d+=(60*60*24)) { // Debugging purpose //var_dump(date('r', $current_day) . ' ' . date('r', $d) . ' ' . date('r', $event['start']) . ' ' . date('r', $event['end'])); // Handle overlapping events: stack color at the beginning of the sub-array if (isset($day_color[$d]) && is_array($day_color[$d])) { array_unshift($day_color[$d], color2hex($event['summary'])); } else { $day_color[$d][0] = color2hex($event['summary']); } $first_loop = false; } } // Debugging purpose //var_dump($day_color); // Output the final table / My week starts on Monday, but you can change it $week_day = date('N', $now); echo '<table class="forecast">'; echo '<tr><th></th><th>Mon</th><th>Tue</th><th>Wed</th><th>Thu</th><th>Fri</th><th>Sat</th><tH>Sun</th></tr>'; // Loop through the color array for ($row = 0; $row < 10; $row++) { echo '<tr>'; echo '<td class="week">' . (date('W', $now) + $row) . '</td>'; for ($col = 0; $col < 7; $col++) { // Skip past days from the first week (leave it blank) if ($row == 0 && $col < $week_day-1) { echo '<td></td>'; continue; } $current_day = $now + ($row)*(7*60*60*24) + ($col-($week_day-1))*(60*60*24); $current_day = mktime(0,0,0, date('n', $current_day), date('j', $current_day), date('Y', $current_day)); // Working around daylight saving time if (isset($day_color[$current_day]) && isset($day_color[$current_day][0])) { // We choose to get the first color of the array, // but if you want to display overlapping events colors, // you can use the other values of the array $color = $day_color[$current_day][0]; echo '<td class="' . $color . '">'; } else { echo '<td>'; } // Debugging purpose //echo '<pre>' . $current_day . '</pre>'; //echo date('r', $current_day); // Label the cell with the current day echo date('j', $current_day) . ' ' . date('M', $current_day) . ' ' . date('Y', $current_day); echo '</td>'; } echo '</tr>'; } echo '</table>'; $html = ob_get_clean(); ob_end_flush(); file_put_contents('calendar.cache.html', $html); } else { $html = file_get_contents('calendar.cache.html'); } echo $html;
Copy and paste it to an empty PHP file.
Please note that it has a small caching system (calendar.cache.html) to prevent fetching events every time someone loads the page. You can easily reduce the delay or deactivate this feature.
Refer to the comments to extend it.