3600, 'mandatorid' => -1, 'calendar' => -1, 'format' => 'icalendar', 'date' => 'now', 'monthpre' => 0, 'monthpost' => 12, 'decorator' => array( 'CalendarEvent' ) ); /** * @var WBDatasource_Calendar */ private $cal; /** * Temporary File * @var WBFile */ private $tmp; /** * 2nd Constructor * * Mend config for parent class */ protected function init() { parent::init(); $this->cal = WBClass::create('WBDatasource_Calendar', $this->config); $this->cal->setBaseDate($this->config['date']); } /** * Run * * * @return array parameter list */ public function run() { /** @var WBMandator */ $man = WBClass::create('WBMandator'); $man->setId($this->config['mandatorid']); if (!$man->isValid()) { $this->setStatusCode(404); $this->loadTemplates('invalidMandator'); return $this->config; } $mime = 'application/octet-stream'; $name = 'calendar'; switch (strtolower($this->config['format'])) { case 'icalendar': case 'ics': $mime = 'text/calendar'; $name .= 'ics'; break; default: $this->setStatusCode(404); $this->loadTemplates('invalidFormat'); return $this->config; break; } $cache = sprintf('/var/cache/calendar/export/%s', md5(serialize($this->config))); $this->tmp = WBClass::create('WBFile'); if ($this->tmp->exists($cache) && $this->tmp->isYounger($this->config['ttl'])) { $this->downloadFile($this->tmp->realpath(), $mime, $name); // this will never happen return $this->config; } $item = $this->getItems(); $this->tmp->touch($cache); $this->tmp->open('w'); $this->iCalenderStart(); foreach ($item as $i) { $this->iCalenderAddItem($i); } $this->iCalenderEnd($mime, $name); $this->tmp->close(); $this->downloadFile($this->tmp->realpath(), $mime, $name); // this will never happen return $this->config; } /** * Get List Of Calendar Items * * Walk through calendar and collect day items * * @return array */ private function getItems() { $this->startDecorator(); $list = array(); // month' before current one if (0 < $this->config['monthpre']) { for ($i = $this->config['monthpre']; $i > 0; --$i) { $this->cal->sub('P' . $i . 'M'); $this->cal->getDays4Month($list); } $this->cal->sub('P0M'); } // current month $this->cal->getDays4Month($list); // month' after current one if (0 < $this->config['monthpost']) { for ($i = 1; $i <= $this->config['monthpost']; ++$i) { $this->cal->add('P' . $i . 'M'); $this->cal->getDays4Month($list); } } $item = array(); foreach ($list as $l) { for ($dow = 1; $dow <= 7; ++$dow) { if (empty($l['day' . $dow])) { continue; } if (empty($l['day' . $dow . '_item'])) { continue; } foreach ($l['day' . $dow . '_item'] as $i) { $item[] = $i; } } } return $item; } private function iCalenderStart() { $this->putICalendarLine('BEGIN:VCALENDAR'); $this->putICalendarLine(sprintf('PRODID:%s calendar', WBString::replaceSuperPlaceholders('[[SERVER]][[SELF]]'))); $this->putICalendarLine('VERSION:2.0'); $this->putICalendarDefaultTimeZone(); } private function iCalenderAddItem($item) { // type of event times: whole day or start and end $wholeDay = false; if (empty($item['starttime'])) { $wholeDay = true; } if (empty($item['endday'])) { $item['endday'] = $item['startday']; if ($wholeDay) { $item['endday'] = date('Y-m-d', strtotime($item['endday'] . ' 00:00:00 + 1day')); $item['endtime'] = '00:00:00'; } $item['end'] = $item['endday'] . ' ' . $item['endtime']; } else if ($wholeDay) { $item['endday'] = date('Y-m-d', strtotime($item['endday'] . ' 00:00:00 + 1day')); $item['end'] = $item['endday'] . ' 00:00:00'; } $t0 = strtotime($item['start']); $t1 = strtotime($item['end']); $this->putICalendarLine('BEGIN:VEVENT'); $this->putICalendarLine(sprintf('UID:%s-%s@%s', $item['xtype'], $item['xid'], WBString::replaceSuperPlaceholders('[[SERVER]]'))); $this->putICalendarLine(sprintf('DTSTAMP:%s', date('Ymd\THis', $t0))); $this->putICalendarLine('STATUS:CONFIRMED'); // $this->putICalendarLine('ORGANIZER: Cool Bloke'); $this->putICalendarLine(sprintf('SUMMARY:%s', $item['title'])); $this->putICalendarLine(sprintf('DESCRIPTION:%s', $item['brief'])); if ($wholeDay) { $this->putICalendarLine(sprintf('DTSTART;VALUE=DATE:%s', date('Ymd', $t0))); $this->putICalendarLine(sprintf('DTEND;VALUE=DATE:%s', date('Ymd', $t1))); } else { $this->putICalendarLine(sprintf('DTSTART:%s', date('Ymd\THis', $t0))); $this->putICalendarLine(sprintf('DTEND:%s', date('Ymd\THis', $t1))); } $this->putICalendarLine('END:VEVENT'); } private function iCalenderEnd() { $this->putICalendarLine('END:VCALENDAR'); } /** * Add TimeZone Information * * Download from URL and add it * See http://tzurl.org */ private function putICalendarDefaultTimeZone() { /** @var WBConfig */ $config = WBClass::create('WBConfig'); $config->load('locale'); $tzInfo = file_get_contents(sprintf('http://tzurl.org/zoneinfo-outlook/%s', $config->get('locale/timezone', 'Europe/Berlin'))); $tzInfo = explode("\n", $tzInfo); $output = false; foreach ($tzInfo as $line) { $line = trim($line); if ($output) { $this->putICalendarLine($line); if ('END:VTIMEZONE' == $line) { $output = false; break; } continue; } if ('BEGIN:VTIMEZONE' == $line) { $output = true; $this->putICalendarLine($line); } } } private function putICalendarLine($line) { $line = trim($line); $line = str_replace("\n", "\r\n", $line); $this->tmp->put($line . "\r\n"); } /** * Start Decorators * * @todo implement support for deleted records */ private function startDecorator() { $config = $this->config; unset($config['decorator']); $config['withdeleted'] = 1; foreach ($this->config['decorator'] as $c) { $c = trim($c); if (empty($c)) { continue; } // switch off by paramater $p = 'usedecorator' . strtolower($c); if (isset($this->config[$p]) && 'no' == strtolower($this->config[$p])) { continue; } /** @var WBDatasource_Calendar_Decorator */ $dec = WBClass::create('WBDatasource_Calendar_Decorator_' . $c); $dec->configure($config); $this->cal->attach($dec); } } }