/** * function iCal2xls * * Convert iCal file to xls format and send file to browser (default) or save xls file to disk * Definition iCal : rcf2445, http://kigkonsult.se/downloads/index.php#rfc * Using iCalcreator: http://kigkonsult.se/downloads/index.php#iCalcreator * Based on PEAR Spreadsheet_Excel_Writer-0.9.1 (and OLE-1.0.0RC1) * to be installed as * pear install channel://pear.php.net/OLE-1.0.0RC1 * pear install channel://pear.php.net/Spreadsheet_Excel_Writer-0.9.1 * * @author Kjell-Inge Gustafsson <*****@*****.**> * @since 3.0 - 2011-12-21 * @param object $calendar opt. iCalcreator calendar instance * @return bool returns FALSE when error */ public function iCal2xls($calendar = FALSE) { $timeexec = array('start' => microtime(TRUE)); if ($this->log) { $this->log->log(' ********** START **********', PEAR_LOG_NOTICE); } /** check input/output directory and filename */ $inputdirFile = $outputdirFile = ''; $inputFileParts = $outputFileParts = array(); $remoteInput = $remoteOutput = FALSE; if ($calendar) { $inputdirFile = $calendar->getConfig('DIRFILE'); $inputFileParts = pathinfo($inputdirFile); $inputFileParts['dirname'] = realpath($inputFileParts['dirname']); if ($this->log) { $this->log->log('fileParts:' . var_export($inputFileParts, TRUE), PEAR_LOG_DEBUG); } } elseif (FALSE === $this->_fixIO('input', 'ics', $inputdirFile, $inputFileParts, $remoteInput)) { if ($this->log) { $this->log->log(number_format(microtime(TRUE) - $timeexec['start'], 5) . ' sec', PEAR_LOG_ERR); $this->log->log("ERROR 2, invalid input ({$inputdirFile})", PEAR_LOG_ERR); $this->log->flush(); } return FALSE; } if (FALSE === $this->_fixIO('output', FALSE, $outputdirFile, $outputFileParts, $remoteOutput)) { if (FALSE === $this->setConfig('outputfilename', $inputFileParts['filename'] . '.xls')) { if ($this->log) { $this->log->log(number_format(microtime(TRUE) - $timeexec['start'], 5) . ' sec', PEAR_LOG_ERR); $this->log->log('ERROR 3, invalid output (' . $inputFileParts['filename'] . '.csv)', PEAR_LOG_ERR); $this->log->flush(); } return FALSE; } $outputdirFile = $this->getConfig('outputdirectory') . DIRECTORY_SEPARATOR . $inputFileParts['filename'] . '.xls'; $outputFileParts = pathinfo($outputdirFile); if ($this->log) { $this->log->log("output set to '{$outputdirFile}'", PEAR_LOG_INFO); } } if ($this->log) { $this->log->log("INPUT..FILE:{$inputdirFile}", PEAR_LOG_NOTICE); $this->log->log("OUTPUT.FILE:{$outputdirFile}", PEAR_LOG_NOTICE); } $save = $this->getConfig('save'); if ($calendar) { $calnl = $calendar->getConfig('nl'); } else { /** iCalcreator set config, read and parse input iCal file */ $calendar = new vcalendar(); if (FALSE !== ($unique_id = $this->getConfig('unique_id'))) { $calendar->setConfig('unique_id', $unique_id); } $calnl = $calendar->getConfig('nl'); if ($remoteInput) { if (FALSE === $calendar->setConfig('url', $inputdirFile)) { if ($this->log) { $this->log->log("ERROR 3 INPUT FILE:'{$inputdirFile}' iCalcreator: invalid url", 3); } return FALSE; } } else { if (FALSE === $calendar->setConfig('directory', $inputFileParts['dirname'])) { if ($this->log) { $this->log->log("ERROR 4 INPUT FILE:'{$inputdirFile}' iCalcreator: invalid directory: '" . $inputFileParts['dirname'] . "'", 3); $this->log->flush(); } return FALSE; } if (FALSE === $calendar->setConfig('filename', $inputFileParts['basename'])) { if ($this->log) { $this->log->log("ERROR 5 INPUT FILE:'{$inputdirFile}' iCalcreator: invalid filename: '" . $inputFileParts['basename'] . "'", 3); $this->log->flush(); } return FALSE; } } if (FALSE === $calendar->parse()) { if ($this->log) { $this->log->log("ERROR 6 INPUT FILE:'{$inputdirFile}' iCalcreator parse error", 3); $this->log->flush(); } return FALSE; } } // end if( !$calendar ) $timeexec['fileOk'] = microtime(TRUE); if (!function_exists('iCaldate2timestamp')) { function iCaldate2timestamp($d) { if (6 > count($d)) { return mktime(0, 0, 0, $d['month'], $d['day'], $d['year']); } else { return mktime($d['hour'], $d['min'], $d['sec'], $d['month'], $d['day'], $d['year']); } } } if (!function_exists('fixiCalString')) { function fixiCalString($s) { global $calnl; $s = str_replace('\\,', ',', $s); $s = str_replace('\\;', ';', $s); $s = str_replace('\\n ', chr(10), $s); $s = str_replace('\\\\', '\\', $s); $s = str_replace("{$calnl}", chr(10), $s); return utf8_decode($s); } } /** Creating a workbook */ require_once 'Spreadsheet/Excel/Writer.php'; if ($save) { $workbook = new Spreadsheet_Excel_Writer($outputdirFile); } else { $workbook = new Spreadsheet_Excel_Writer(); } $workbook->setVersion(8); // Use Excel97/2000 Format /** opt. sending HTTP headers */ if (!$save) { $workbook->send($outputFileParts['basename']); } /** Creating a worksheet */ $worksheet =& $workbook->addWorksheet($inputFileParts['filename']); /** fix formats */ $format_bold =& $workbook->addFormat(); $format_bold->setBold(); $timeexec['wrkbkOk'] = microtime(TRUE); /** info rows */ $row = -1; $worksheet->writeString(++$row, 0, 'kigkonsult.se', $format_bold); $worksheet->writeString($row, 1, ICALCREATOR_VERSION, $format_bold); $worksheet->writeString($row, 2, ICALCNVVERSION . ' iCal2xls', $format_bold); $worksheet->writeString($row, 3, date('Y-m-d H:i:s')); $filename = $remoteInput ? $inputdirFile : $inputFileParts['basename']; $worksheet->writeString(++$row, 0, 'iCal input', $format_bold); $worksheet->writeString($row, 1, $filename); $worksheet->writeString($row, 2, 'xls output', $format_bold); $worksheet->writeString($row, 3, $outputFileParts['basename']); if (FALSE !== ($prop = $calendar->getProperty('CALSCALE'))) { $worksheet->writeString(++$row, 0, 'CALSCALE', $format_bold); $worksheet->writeString($row, 1, $prop); } if (FALSE !== ($prop = $calendar->getProperty('METHOD'))) { $worksheet->writeString(++$row, 0, 'METHOD', $format_bold); $worksheet->writeString($row, 1, $prop); } while (FALSE !== ($xprop = $calendar->getProperty())) { $worksheet->writeString(++$row, 0, $xprop[0], $format_bold); $worksheet->writeString($row, 1, $xprop[1]); } $timeexec['infoOk'] = microtime(TRUE); if (FALSE === ($propsToSkip = $this->getConfig('skip'))) { $propsToSkip = array(); } /** fix property order list */ $proporderOrg = array(); for ($key = 2; $key < 99; $key++) { if (FALSE !== ($value = $this->getConfig($key))) { $proporderOrg[$value] = $key; if ($this->log) { $this->log->log("{$value} in column {$key}", 7); } } } /** fix vtimezone property order list */ $proporder = $proporderOrg; $proporder['TYPE'] = 0; $proporder['ORDER'] = 1; $props = array('TZID', 'LAST-MODIFIED', 'TZURL', 'DTSTART', 'TZOFFSETTO', 'TZOFFSETFROM', 'COMMENT', 'RRULE', 'RDATE', 'TZNAME'); $pix = 2; foreach ($props as $prop) { if (isset($proporder[$prop])) { continue; } if (in_array($prop, $propsToSkip)) { if ($this->log) { $this->log->log("'{$prop}' removed from output", 7); } continue; } while (in_array($pix, $proporder)) { $pix++; } $proporder[$prop] = $pix++; } /** remove unused properties from and add x-props to property order list */ $maxpropix = 11; if ($maxpropix != count($proporder) - 1) { $maxpropix = count($proporder) - 1; } $compsinfo = $calendar->getConfig('compsinfo'); $potmp = array(); $potmp[0] = 'TYPE'; $potmp[1] = 'ORDER'; foreach ($compsinfo as $cix => $compinfo) { if ('vtimezone' != $compinfo['type']) { continue; } $comp = $calendar->getComponent($compinfo['ordno']); foreach ($compinfo['props'] as $propName => $propcnt) { if (!in_array($propName, $potmp) && isset($proporder[$propName])) { $potmp[$proporder[$propName]] = $propName; } elseif ('X-PROP' == $propName) { while ($xprop = $comp->getProperty()) { if (!in_array($xprop[0], $potmp)) { $maxpropix += 1; $potmp[$maxpropix] = $xprop[0]; } // end if } // end while xprop } // end X-PROP } // end $compinfo['props'] if (isset($compinfo['sub'])) { foreach ($compinfo['sub'] as $compinfo2) { foreach ($compinfo2['props'] as $propName => $propcnt) { if (!in_array($propName, $potmp) && isset($proporder[$propName])) { $potmp[$proporder[$propName]] = $propName; } elseif ('X-PROP' == $propName) { $scomp = $comp->getComponent($compinfo2['ordno']); while ($xprop = $scomp->getProperty()) { if (!in_array($xprop[0], $potmp)) { $maxpropix += 1; $potmp[$maxpropix] = $xprop[0]; } // end if } // end while xprop } // end X-PROP } // end $compinfo['sub']['props'] } // end foreach( $compinfo['sub'] } // end if( isset( $compinfo['sub'] } // end foreach compinfo - vtimezone ksort($potmp, SORT_NUMERIC); $proporder = array_flip(array_values($potmp)); if ($this->log) { $this->log->log("timezone proporder=" . implode(',', array_flip($proporder)), 7); } /** create vtimezone info */ if (2 < count($proporder)) { $row += 1; /** create vtimezone header row */ foreach ($proporder as $propName => $col) { if (isset($this->config[$propName])) { $worksheet->writeString($row, $col, $this->config[$propName], $format_bold); // check map of userfriendly name to iCal property name if ($this->log) { $this->log->log("header row, col={$col}: {$propName}, replaced by " . $this->config[$propName], 7); } } else { $worksheet->writeString($row, $col, $propName, $format_bold); } } $allowedProps = array('VTIMEZONE' => array('TZID', 'LAST-MODIFIED', 'TZURL'), 'STANDARD' => array('DTSTART', 'TZOFFSETTO', 'TZOFFSETFROM', 'COMMENT', 'RDATE', 'RRULE', 'TZNAME'), 'DAYLIGHT' => array('DTSTART', 'TZOFFSETTO', 'TZOFFSETFROM', 'COMMENT', 'RDATE', 'RRULE', 'TZNAME')); /** create vtimezone data rows */ foreach ($compsinfo as $cix => $compinfo) { if ('vtimezone' != $compinfo['type']) { continue; } $row += 1; $worksheet->writeString($row, $proporder['TYPE'], $compinfo['type']); $worksheet->writeString($row, $proporder['ORDER'], $compinfo['ordno']); $comp = $calendar->getComponent($compinfo['ordno']); foreach ($proporder as $propName => $col) { if ('TYPE' == $propName || 'ORDER' == $propName) { continue; } if ('X-' == substr($propName, 0, 2)) { continue; } if (!in_array($propName, $allowedProps['VTIMEZONE'])) { // check if component allows property if ($this->log) { $this->log->log("ERROR 7, INPUT FILE:'{$inputdirFile}' iCalcreator: unvalid property for component '" . $compinfo['type'] . "': '{$propName}'", PEAR_LOG_INFO); } continue; } if (isset($compinfo['props'][$propName])) { if ('LAST-MODIFIED' == $propName) { $fcn = 'createLastModified'; } else { $fcn = 'create' . strtoupper(substr($propName, 0, 1)) . strtolower(substr($propName, 1)); } if (!method_exists($comp, $fcn)) { if ($this->log) { $this->log->log('ERROR 8 INPUT FILE:"' . $filename . '" iCalcreator: unknown property: "' . $propName . '" (' . $fcn . ')', PEAR_LOG_INFO); } continue; } $output = str_replace("{$calnl} ", '', rtrim($comp->{$fcn}())); $output = str_replace($propName . ';', '', $output); $output = str_replace($propName . ':', '', $output); $worksheet->writeString($row, $proporder[$propName], fixiCalString($output)); } } // end foreach( $proporder if (isset($compinfo['props']['X-PROP'])) { while ($xprop = $comp->getProperty()) { $output = str_replace("{$calnl} ", '', rtrim($xprop[1])); $worksheet->writeString($row, $proporder[$xprop[0]], fixiCalString($output)); } } if (isset($compinfo['sub'])) { foreach ($compinfo['sub'] as $compinfo2) { $row += 1; $worksheet->writeString($row, $proporder['TYPE'], $compinfo2['type']); $worksheet->writeString($row, $proporder['ORDER'], $compinfo['ordno'] . ':' . $compinfo2['ordno']); $scomp = $comp->getComponent($compinfo2['ordno']); foreach ($proporder as $propName => $col) { if ('TYPE' == $propName || 'ORDER' == $propName) { continue; } if ('X-' == substr($propName, 0, 2)) { continue; } if (!in_array($propName, $allowedProps[strtoupper($compinfo2['type'])])) { // check if component allows property if ($this->log) { $this->log->log("ERROR 9, INPUT FILE:'{$inputdirFile}' iCalcreator: unvalid property for component '" . $compinfo2['type'] . "': '{$propName}'", PEAR_LOG_INFO); } continue; } if (isset($compinfo2['props'][$propName])) { $fcn = 'create' . strtoupper(substr($propName, 0, 1)) . strtolower(substr($propName, 1)); if (!method_exists($scomp, $fcn)) { if ($this->log) { $this->log->log('ERROR 10 INPUT FILE:"' . $filename . '" iCalcreator: unknown property: "' . $propName . '" (' . $fcn . ')', PEAR_LOG_INFO); } continue; } $output = str_replace("{$calnl} ", '', rtrim($scomp->{$fcn}())); $output = str_replace($propName . ';', '', $output); $output = str_replace($propName . ':', '', $output); $worksheet->writeString($row, $proporder[$propName], fixiCalString($output)); } } // end foreach( $proporder if (isset($compinfo2['props']['X-PROP'])) { while ($xprop = $scomp->getProperty()) { $output = str_replace("{$calnl} ", '', rtrim($xprop[1])); $worksheet->writeString($row, $proporder[$xprop[0]], fixiCalString($output)); } } } // end foreach( $compinfo['sub'] } // end if( isset( $compinfo['sub']['props'] )) } // end foreach } // end vtimezone $timeexec['zoneOk'] = microtime(TRUE); $maxColCount = count($proporder); /** fix property order list */ $proporder = $proporderOrg; $proporder['TYPE'] = 0; $proporder['ORDER'] = 1; $props = array('UID', 'DTSTAMP', 'SUMMARY', 'DTSTART', 'DURATION', 'DTEND', 'DUE', 'RRULE', 'RDATE', 'EXRULE', 'EXDATE', 'DESCRIPTION', 'CATEGORIES', 'ORGANIZER', 'LOCATION', 'RESOURCES', 'CONTACT', 'URL', 'COMMENT', 'PRIORITY', 'ATTENDEE', 'CLASS', 'TRANSP', 'SEQUENCE', 'STATUS', 'COMPLETED', 'CREATED', 'LAST-MODIFIED', 'ACTION', 'TRIGGER', 'REPEAT', 'ATTACH', 'FREEBUSY', 'RELATED-TO', 'REQUEST-STATUS', 'GEO', 'PERCENT-COMPLETE', 'RECURRENCE-ID'); $pix = 2; foreach ($props as $prop) { if (isset($proporder[$prop])) { continue; } if (in_array($prop, $propsToSkip)) { if ($this->log) { $this->log->log("'{$prop}' removed from output", 7); } continue; } while (in_array($pix, $proporder)) { $pix++; } $proporder[$prop] = $pix++; } /** remove unused properties from and add x-props to property order list */ if ($maxpropix < count($proporder) - 1) { $maxpropix = count($proporder) - 1; } $potmp = array(); $potmp[0] = 'TYPE'; $potmp[1] = 'ORDER'; // $potmp[2] = 'UID'; foreach ($compsinfo as $cix => $compinfo) { if ('vtimezone' == $compinfo['type']) { continue; } foreach ($compinfo['props'] as $propName => $propcnt) { if (!in_array($propName, $potmp) && isset($proporder[$propName])) { $potmp[$proporder[$propName]] = $propName; } elseif ('X-PROP' == $propName) { $comp = $calendar->getComponent($compinfo['ordno']); while ($xprop = $comp->getProperty()) { if (!in_array($xprop[0], $potmp)) { $maxpropix += 1; $potmp[$maxpropix] = $xprop[0]; } // end if } // while( $xprop } // end elseif( 'X-PROP' } // end foreach( $compinfo['props'] if (isset($compinfo['sub'])) { foreach ($compinfo['sub'] as $compinfo2) { foreach ($compinfo2['props'] as $propName => $propcnt) { if (!in_array($propName, $potmp) && isset($proporder[$propName])) { $potmp[$proporder[$propName]] = $propName; } elseif ('X-PROP' == $propName) { $scomp = $comp->getComponent($compinfo2['ordno']); while ($xprop = $scomp->getProperty()) { if (!in_array($xprop[0], $potmp)) { $maxpropix += 1; $potmp[$maxpropix] = $xprop[0]; } // end if } // end while xprop } // end X-PROP } // end $compinfo['sub']['props'] } // end foreach( $compinfo['sub'] } // end if( isset( $compinfo['sub'] } ksort($potmp, SORT_NUMERIC); $proporder = array_flip(array_values($potmp)); if ($this->log) { $this->log->log("comp proporder=" . implode(',', array_flip($proporder)), 7); } if ($maxColCount < count($proporder)) { $maxColCount = count($proporder); } /** create header row */ $row += 1; foreach ($proporder as $propName => $col) { if (isset($this->config[$propName])) { $worksheet->writeString($row, $col, $this->config[$propName], $format_bold); // check map of userfriendly name to iCal property name if ($this->log) { $this->log->log("header row, col={$col}: {$propName}, replaced by " . $this->config[$propName], 7); } } else { $worksheet->writeString($row, $col, $propName, $format_bold); } } $allowedProps = array('VEVENT' => array('ATTACH', 'ATTENDEE', 'CATEGORIES', 'CLASS', 'COMMENT', 'CONTACT', 'CREATED', 'DESCRIPTION', 'DTEND', 'DTSTAMP', 'DTSTART', 'DURATION', 'EXDATE', 'RXRULE', 'GEO', 'LAST-MODIFIED', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RDATE', 'RECURRENCE-ID', 'RELATED-TO', 'RESOURCES', 'RRULE', 'REQUEST-STATUS', 'SEQUENCE', 'STATUS', 'SUMMARY', 'TRANSP', 'UID', 'URL'), 'VTODO' => array('ATTACH', 'ATTENDEE', 'CATEGORIES', 'CLASS', 'COMMENT', 'COMPLETED', 'CONTACT', 'CREATED', 'DESCRIPTION', 'DTSTAMP', 'DTSTART', 'DUE', 'DURATION', 'EXDATE', 'EXRULE', 'GEO', 'LAST-MODIFIED', 'LOCATION', 'ORGANIZER', 'PERCENT', 'PRIORITY', 'RDATE', 'RECURRENCE-ID', 'RELATED-TO', 'RESOURCES', 'RRULE', 'REQUEST-STATUS', 'SEQUENCE', 'STATUS', 'SUMMARY', 'UID', 'URL'), 'VJOURNAL' => array('ATTACH', 'ATTENDEE', 'CATEGORIES', 'CLASS', 'COMMENT', 'CONTACT', 'CREATED', 'DESCRIPTION', 'DTSTAMP', 'DTSTART', 'EXDATE', 'EXRULE', 'LAST-MODIFIED', 'ORGANIZER', 'RDATE', 'RECURRENCE-ID', 'RELATED-TO', 'RRULE', 'REQUEST-STATUS', 'SEQUENCE', 'STATUS', 'SUMMARY', 'UID', 'URL'), 'VFREEBUSY' => array('ATTENDEE', 'COMMENT', 'CONTACT', 'DTEND', 'DTSTAMP', 'DTSTART', 'DURATION', 'FREEBUSY', 'ORGANIZER', 'UID', 'URL'), 'VALARM' => array('ACTION', 'ATTACH', 'ATTENDEE', 'DESCRIPTION', 'DURATION', 'REPEAT', 'SUMMARY', 'TRIGGER')); /** create data rows */ foreach ($compsinfo as $cix => $compinfo) { if ('vtimezone' == $compinfo['type']) { continue; } $row += 1; $worksheet->writeString($row, $proporder['TYPE'], $compinfo['type']); $worksheet->writeString($row, $proporder['ORDER'], $compinfo['ordno']); // $worksheet->write( $row, $proporder['UID'], $compinfo['uid'] ); $comp = $calendar->getComponent($compinfo['ordno']); foreach ($proporder as $propName => $col) { if ('TYPE' == $propName || 'ORDER' == $propName) { continue; } if ('X-' == substr($propName, 0, 2)) { continue; } if (!in_array($propName, $allowedProps[strtoupper($compinfo['type'])])) { // check if component allows property if ($this->log) { $this->log->log("ERROR 11, INPUT FILE:'{$inputdirFile}' iCalcreator: unvalid property for component '" . $compinfo['type'] . "': '{$propName}'", PEAR_LOG_INFO); } continue; } if (isset($compinfo['props'][$propName])) { switch ($propName) { case 'LAST-MODIFIED': $fcn = 'createLastModified'; break; case 'RECURRENCE-ID': $fcn = 'createRecurrenceid'; break; case 'RELATED-TO': $fcn = 'createRelatedTo'; break; case 'REQUEST-STATUS': $fcn = 'createRequestStatus'; break; case 'PERCENT-COMPLETE': $fcn = 'createPercentComplete'; break; default: $fcn = 'create' . strtoupper(substr($propName, 0, 1)) . strtolower(substr($propName, 1)); } if (!method_exists($comp, $fcn)) { if ($this->log) { $this->log->log("ERROR 12 INPUT FILE:'{$filename}' iCalcreator: unknown property: '{$propName}' ({$fcn})", PEAR_LOG_INFO); } continue; } $output = str_replace("{$calnl} ", '', rtrim($comp->{$fcn}())); $output = str_replace($propName . ';', '', $output); $output = str_replace($propName . ':', '', $output); $worksheet->writeString($row, $proporder[$propName], fixiCalString($output)); } } // end foreach( $proporder if (isset($compinfo['props']['X-PROP'])) { while ($xprop = $comp->getProperty()) { $output = str_replace("{$calnl} ", '', rtrim($xprop[1])); $worksheet->writeString($row, $proporder[$xprop[0]], fixiCalString($output)); } } if (isset($compinfo['sub'])) { foreach ($compinfo['sub'] as $compinfo2) { $row += 1; $worksheet->writeString($row, $proporder['TYPE'], $compinfo2['type']); $worksheet->writeString($row, $proporder['ORDER'], $compinfo['ordno'] . ':' . $compinfo2['ordno']); $scomp = $comp->getComponent($compinfo2['ordno']); foreach ($proporder as $propName => $col) { if ('TYPE' == $propName || 'ORDER' == $propName) { continue; } if ('X-' == substr($propName, 0, 2)) { continue; } if (!in_array($propName, $allowedProps[strtoupper($compinfo2['type'])])) { // check if component allows property if ($this->log) { $this->log->log("ERROR 13, INPUT FILE:'{$inputdirFile}' iCalcreator: unvalid property for component '" . $compinfo2['type'] . "': '{$propName}'", PEAR_LOG_INFO); } continue; } if (isset($compinfo2['props'][$propName])) { $fcn = 'create' . strtoupper(substr($propName, 0, 1)) . strtolower(substr($propName, 1)); if (!method_exists($scomp, $fcn)) { if ($this->log) { $this->log->log("ERROR 14 INPUT FILE:'{$filename}' iCalcreator: unknown property: '{$propName}' ({$fcn})", PEAR_LOG_INFO); } continue; } $output = str_replace("{$calnl} ", '', rtrim($scomp->{$fcn}())); $output = str_replace($propName . ';', '', $output); $output = str_replace($propName . ':', '', $output); $worksheet->writeString($row, $proporder[$propName], fixiCalString($output)); } // end if( isset( $compinfo2['props'][$propName] } // end foreach( $proporder if (isset($compinfo2['props']['X-PROP'])) { while ($xprop = $scomp->getProperty()) { $output = str_replace("{$calnl} ", '', rtrim($xprop[1])); $output = str_replace('\\n ', chr(10), $output); $worksheet->writeString($row, $proporder[$xprop[0]], fixiCalString($output)); } } // end if( isset( $compinfo2['props']['X-PROP'] } // end foreach( $compinfo['sub'] } // end if( isset( $compinfo['sub'] } // foreach( $compsinfo as if ($this->log) { $timeexec['exit'] = microtime(TRUE); $msg = "'{$filename}'"; $msg .= ' fileOk:' . number_format($timeexec['fileOk'] - $timeexec['start'], 5); $msg .= ' wrkbkOk:' . number_format($timeexec['wrkbkOk'] - $timeexec['fileOk'], 5); $msg .= ' infoOk:' . number_format($timeexec['infoOk'] - $timeexec['wrkbkOk'], 5); $msg .= ' zoneOk:' . number_format($timeexec['zoneOk'] - $timeexec['infoOk'], 5); $msg .= ' compOk:' . number_format($timeexec['exit'] - $timeexec['zoneOk'], 5); $msg .= ' total:' . number_format($timeexec['exit'] - $timeexec['start'], 5) . 'sec'; $msg .= ', ' . ($row + 1) . " rows, {$maxColCount} cols"; $this->log->log($msg, PEAR_LOG_DEBUG); $msg = "'{$filename}' (" . count($compsinfo) . ' components) start:' . date('H:i:s', $timeexec['start']); $msg .= ' total:' . number_format($timeexec['exit'] - $timeexec['start'], 5) . 'sec'; if ($save) { $msg .= " saved as '{$outputdirFile}'"; } else { $msg .= " redirected as '" . $outputFileParts['basename'] . "'"; } $this->log->log($msg, PEAR_LOG_NOTICE); } /** Close and, opt., send the file */ if ($this->log) { $this->log->flush(); } $workbook->close(); return TRUE; }
/** * function iCal2csv * * Convert iCal file to csv format and send file to browser (default) or save csv file to disk * Definition iCal : rcf2445, http://localhost/work/kigkonsult.se/downloads/index.php#rfc2445 * Definition csv : http://en.wikipedia.org/wiki/Comma-separated_values * Using iCalcreator: http://localhost/work/kigkonsult.se/downloads/index.php#iCalcreator * ical directory/file read/write error OR iCalcreator parse error will be directed to error_log/log * * @author Kjell-Inge Gustafsson <*****@*****.**> * @since 2.0 - 2009-03-27 * @param string $filename file to convert (incl. opt. directory) * @param array $conf opt, default FALSE(=array('del'=>'"','sep'=>',', 'nl'=>'\n'), delimiter, separator and newline characters * escape sequences will be expanded, '\n' will be used as "\n" etc. * also map iCal property names to user friendly names, ex. 'DTSTART' => 'startdate' * also order output columns, ex. 2 => 'DTSTART' (2=first order column, 3 next etc) * also properties to skip, ex. 'skip' => array( 'CREATED', 'PRIORITY' ); * @param bool $save opt, default FALSE, TRUE=save to disk * @param string $diskfilename opt, filename for file to save or else taken from $filename + 'csv' extension * @param object $log opt, default FALSE (error_log), writes log to file using PEAR LOG or eClog class * @return bool returns FALSE when error */ function iCal2csv($filename, $conf = FALSE, $save = FALSE, $diskfilename = FALSE, $log = FALSE) { if ($log) { $timeexec = array('start' => microtime(TRUE)); } $iCal2csv_VERSION = 'iCal2csv 2.0'; if (!function_exists('fileCheckRead')) { require_once 'fileCheck.php'; } if (!class_exists('vcalendar', FALSE)) { require_once 'iCalcreator.class.php'; } if ($log) { $log->log("{$iCal2csv_VERSION} input={$filename}, conf=" . var_export($conf, TRUE) . ", save={$save}, diskfilename={$diskfilename}", 7); } $remoteInput = 'http://' == strtolower(substr($filename, 0, 7)) || 'webcal://' == strtolower(substr($filename, 0, 9)) ? TRUE : FALSE; // field DELimiter && field SEParator && NewLine character(-s) etc. if (!$conf) { $conf = array(); } if (!isset($conf['del'])) { $conf['del'] = '"'; } if (!isset($conf['sep'])) { $conf['sep'] = ','; } if (!isset($conf['nl'])) { $conf['nl'] = "\n"; } foreach ($conf as $key => $value) { if ('skip' == $key) { foreach ($value as $six => $skipp) { $conf['skip'][$six] = strtoupper($skipp); } } elseif ('2' <= $key && '99' > $key) { $conf[$key] = strtoupper($value); if ($log) { $log->log("{$iCal2csv_VERSION} column {$key} contains " . strtoupper($value), 7); } } elseif (in_array($key, array('del', 'sep', 'nl'))) { $conf[$key] = "{$value}"; } else { $conf[strtoupper($key)] = $value; if ($log) { $log->log("{$iCal2csv_VERSION} " . strtoupper($key) . " mapped to {$value}", 7); } } } /* create path and filename */ if ($remoteInput) { $inputFileParts = parse_url($filename); $inputFileParts = array_merge($inputFileParts, pathinfo($inputFileParts['path'])); if (!$diskfilename) { $diskfilename = $inputFileParts['filename'] . '.csv'; } } else { if (FALSE === ($filename = fileCheckRead($filename, $log))) { if ($log) { $log->log("{$iCal2csv_VERSION} (" . number_format(microtime(TRUE) - $timeexec['start'], 5) . ')'); $log->flush(); } return FALSE; } $inputFileParts = pathinfo($filename); if (!$diskfilename) { $diskfilename = $inputFileParts['dirname'] . DIRECTORY_SEPARATOR . $inputFileParts['filename'] . '.csv'; } } $outputFileParts = pathinfo($diskfilename); if ($save) { if (FALSE === ($diskfilename = fileCheckWrite($outputFileParts['dirname'] . DIRECTORY_SEPARATOR . $outputFileParts['basename'], $log))) { if ($log) { $log->log("{$iCal2csv_VERSION} (" . number_format(microtime(TRUE) - $timeexec['start'], 5) . ')'); $log->flush(); } return FALSE; } } if ($log) { $msg = $iCal2csv_VERSION . ' INPUT FILE:"' . $inputFileParts['dirname'] . DIRECTORY_SEPARATOR . $inputFileParts['basename'] . '"'; if ($save) { $msg .= ' OUTPUT FILE: "' . $outputFileParts['dirname'] . DIRECTORY_SEPARATOR . $outputFileParts['basename'] . '"'; } $log->log($msg, 7); } /* iCalcreator check, read and parse input iCal file */ $calendar = new vcalendar(); $calnl = $calendar->getConfig('nl'); if ($remoteInput) { if (FALSE === $calendar->setConfig('url', $filename)) { $msg = $iCal2csv_VERSION . ' ERROR 3 INPUT FILE:"' . $filename . '" iCalcreator: invalid url'; if ($log) { $log->log($msg, 3); $log->flush(); } else { error_log($msg); } return FALSE; } } else { if (FALSE === $calendar->setConfig('directory', $inputFileParts['dirname'])) { $msg = $iCal2csv_VERSION . ' ERROR 4 INPUT FILE:"' . $filename . '" iCalcreator: invalid directory: "' . $inputFileParts['dirname'] . '"'; if ($log) { $log->log($msg, 3); $log->flush(); } else { error_log($msg); } return FALSE; } if (FALSE === $calendar->setConfig('filename', $inputFileParts['basename'])) { $msg = $iCal2csv_VERSION . ' ERROR 5 INPUT FILE:"' . $filename . '" iCalcreator: invalid filename: "' . $inputFileParts['basename'] . '"'; if ($log) { $log->log($msg, 3); $log->flush(); } else { error_log($msg); } return FALSE; } } if (FALSE === $calendar->parse()) { $msg = $iCal2csv_VERSION . ' ERROR 6 INPUT FILE:"' . $filename . '" iCalcreator parse error'; if ($log) { $log->log($msg, 3); $log->flush(); } else { error_log($msg); } return FALSE; } if ($log) { $timeexec['fileOk'] = microtime(TRUE); } if (!function_exists('iCaldate2timestamp')) { function iCaldate2timestamp($d) { if (6 > count($d)) { return mktime(0, 0, 0, $d['month'], $d['day'], $d['year']); } else { return mktime($d['hour'], $d['min'], $d['sec'], $d['month'], $d['day'], $d['year']); } } } if (!function_exists('fixiCalString')) { function fixiCalString($s) { $s = str_replace('\\,', ',', $s); $s = str_replace('\\;', ';', $s); $s = str_replace('\\n ', chr(10), $s); $s = str_replace('\\\\', '\\', $s); return $s; } } /* create output array */ $rows = array(); /* info rows */ $rows[] = array('kigkonsult.se', ICALCREATOR_VERSION, $iCal2csv_VERSION, date('Y-m-d H:i:s')); $filename = $remoteInput ? $filename : $inputFileParts['basename']; $rows[] = array('iCal input', $filename, 'csv output', $outputFileParts['basename']); if ($prop = $calendar->getProperty('CALSCALE')) { $rows[] = array('CALSCALE', $prop); } if ($prop = $calendar->getProperty('METHOD')) { $rows[] = array('METHOD', $prop); } while ($xprop = $calendar->getProperty()) { $rows[] = array($xprop[0], $xprop[1]); } if ($log) { $timeexec['infoOk'] = microtime(TRUE); } /* fix vtimezone property order list */ $proporder = array(); foreach ($conf as $key => $value) { if ('2' <= $key && '99' > $key) { $proporder[$value] = $key; if ($log) { $log->log("{$iCal2csv_VERSION} {$value} in column {$key}", 7); } } } $proporder['TYPE'] = 0; $proporder['ORDER'] = 1; $props = array('TZID', 'LAST-MODIFIED', 'TZURL', 'DTSTART', 'TZOFFSETTO', 'TZOFFSETFROM', 'TZOFFSETTFROM', 'COMMENT', 'RRULE', 'RDATE', 'TZNAME'); $pix = 2; foreach ($props as $prop) { if (isset($proporder[$prop])) { continue; } if (isset($conf['skip']) && in_array($prop, $conf['skip'])) { if ($log) { $log->log("{$iCal2csv_VERSION} {$prop} removed from output", 7); } continue; } while (in_array($pix, $proporder)) { $pix++; } $proporder[$prop] = $pix++; } /* remove unused properties from and add x-props to property order list */ $maxpropix = 11; if ($maxpropix != count($proporder) - 1) { $maxpropix = count($proporder) - 1; } $compsinfo = $calendar->getConfig('compsinfo'); $potmp = array(); $potmp[0] = 'TYPE'; $potmp[1] = 'ORDER'; foreach ($compsinfo as $cix => $compinfo) { if ('vtimezone' != $compinfo['type']) { continue; } $comp = $calendar->getComponent($compinfo['ordno']); foreach ($compinfo['props'] as $propName => $propcnt) { if (!in_array($propName, $potmp) && isset($proporder[$propName])) { $potmp[$proporder[$propName]] = $propName; } elseif ('X-PROP' == $propName) { while ($xprop = $comp->getProperty()) { if (!in_array($xprop[0], $potmp)) { $maxpropix += 1; $potmp[$maxpropix] = $xprop[0]; } // end if } // end while xprop } // end X-PROP } // end $compinfo['props'] if (isset($compinfo['sub'])) { foreach ($compinfo['sub'] as $compinfo2) { foreach ($compinfo2['props'] as $propName => $propcnt) { if (!in_array($propName, $potmp) && isset($proporder[$propName])) { $potmp[$proporder[$propName]] = $propName; } elseif ('X-PROP' == $propName) { $scomp = $comp->getComponent($compinfo2['ordno']); while ($xprop = $scomp->getProperty()) { if (!in_array($xprop[0], $potmp)) { $maxpropix += 1; $potmp[$maxpropix] = $xprop[0]; } // end if } // end while xprop } // end X-PROP } // end $compinfo['sub']['props'] } // end foreach( $compinfo['sub'] } // end if( isset( $compinfo['sub'] } // end foreach compinfo - vtimezone ksort($potmp, SORT_NUMERIC); if ('2.6' == substr(ICALCREATOR_VERSION, -3)) { foreach ($potmp as $k => $v) { // iCalcreator 2.6 bug fix if ('TZOFFSETTFROM' == $v) { $potmp[$k] = 'TZOFFSETFROM'; } } } $proporder = array_flip(array_values($potmp)); if ($log) { $log->log("{$iCal2csv_VERSION} zone proporder=" . implode(',', array_flip($proporder)), 7); } /* create vtimezone info */ $row = count($rows) - 1; if (2 < count($proporder)) { $row += 1; /* create vtimezone header row */ foreach ($proporder as $propName => $col) { if (isset($conf[$propName])) { $rows[$row][$col] = $conf[$propName]; // check map of userfriendly name to iCal property name if ($log) { $log->log("{$iCal2csv_VERSION} header row, col={$col}: {$propName}, replaced by " . $conf[$propName], 7); } } else { $rows[$row][$col] = $propName; } } $allowedProps = array('VTIMEZONE' => array('TZID', 'LAST-MODIFIED', 'TZURL'), 'STANDARD' => array('DTSTART', 'TZOFFSETTO', 'TZOFFSETFROM', 'COMMENT', 'RDATE', 'RRULE', 'TZNAME'), 'DAYLIGHT' => array('DTSTART', 'TZOFFSETTO', 'TZOFFSETFROM', 'COMMENT', 'RDATE', 'RRULE', 'TZNAME')); /* create vtimezone data rows */ foreach ($compsinfo as $cix => $compinfo) { if ('vtimezone' != $compinfo['type']) { continue; } $row += 1; foreach ($proporder as $propName => $col) { $rows[$row][] = ''; } // set all cells empty $rows[$row][$proporder['TYPE']] = $compinfo['type']; $rows[$row][$proporder['ORDER']] = $compinfo['ordno']; $comp = $calendar->getComponent($compinfo['ordno']); foreach ($proporder as $propName => $col) { if ('TYPE' == $propName || 'ORDER' == $propName) { continue; } if ('X-' == substr($propName, 0, 2)) { continue; } if (!in_array($propName, $allowedProps['VTIMEZONE'])) { // check if component allows property continue; } if (isset($compinfo['props'][$propName])) { if ('LAST-MODIFIED' == $propName) { $fcn = 'createLastModified'; } else { $fcn = 'create' . strtoupper(substr($propName, 0, 1)) . strtolower(substr($propName, 1)); } if (!method_exists($comp, $fcn)) { $msg = $iCal2csv_VERSION . ' ERROR 7 INPUT FILE:"' . $filename . '" iCalcreator: unknown property: "' . $propName . '" (' . $fcn . ')'; if ($log) { $log->log($msg, 3); } else { error_log($msg); } continue; } $output = str_replace("{$calnl} ", '', rtrim($comp->{$fcn}())); $output = str_replace($propName . ';', '', $output); $output = str_replace($propName . ':', '', $output); $rows[$row][$proporder[$propName]] = fixiCalString($output); } } // end foreach( $proporder if (isset($compinfo['props']['X-PROP'])) { while ($xprop = $comp->getProperty()) { $output = str_replace("{$calnl} ", '', rtrim($xprop[1])); $rows[$row][$proporder[$xprop[0]]] = fixiCalString($output); } } if (isset($compinfo['sub'])) { foreach ($compinfo['sub'] as $compinfo2) { $row += 1; foreach ($proporder as $propName => $col) { $rows[$row][] = ''; } // set all cells empty $rows[$row][$proporder['TYPE']] = $compinfo2['type']; $rows[$row][$proporder['ORDER']] = $compinfo['ordno'] . ':' . $compinfo2['ordno']; $scomp = $comp->getComponent($compinfo2['ordno']); foreach ($proporder as $propName => $col) { if ('TYPE' == $propName || 'ORDER' == $propName) { continue; } if ('X-' == substr($propName, 0, 2)) { continue; } if (!in_array($propName, $allowedProps[strtoupper($compinfo2['type'])])) { // check if component allows property continue; } if ('2.6' == substr(ICALCREATOR_VERSION, -3) && 'TZOFFSETFROM' == $propName) { $propName = 'TZOFFSETTFROM'; } // iCalcreator 2.6 bug fix if (isset($compinfo2['props'][$propName])) { if ('2.6' == substr(ICALCREATOR_VERSION, -3) && 'TZOFFSETTFROM' == $propName) { $propName = 'TZOFFSETFROM'; } // iCalcreator 2.6 bug fix $fcn = 'create' . strtoupper(substr($propName, 0, 1)) . strtolower(substr($propName, 1)); if (!method_exists($scomp, $fcn)) { $msg = $iCal2csv_VERSION . ' ERROR 8 INPUT FILE:"' . $filename . '" iCalcreator: unknown property: "' . $propName . '" (' . $fcn . ')'; if ($log) { $log->log($msg, 3); } else { error_log($msg); } continue; } $output = str_replace("{$calnl} ", '', rtrim($scomp->{$fcn}())); $output = str_replace($propName . ';', '', $output); $output = str_replace($propName . ':', '', $output); $rows[$row][$proporder[$propName]] = fixiCalString($output); } } // end foreach( $proporder if (isset($compinfo2['props']['X-PROP'])) { while ($xprop = $scomp->getProperty()) { $output = str_replace("{$calnl} ", '', rtrim($xprop[1])); $rows[$row][$proporder[$xprop[0]]] = fixiCalString($output); } } } // end foreach( $compinfo['sub'] } // end if( isset( $compinfo['sub']['props'] )) } // end foreach } // end vtimezone if ($log) { $timeexec['zoneOk'] = microtime(TRUE); } $maxColCount = count($proporder); /* fix property order list */ $proporder = array(); foreach ($conf as $key => $value) { if ('2' <= $key && '99' > $key) { $proporder[$value] = $key; if ($log) { $log->log("{$iCal2csv_VERSION} {$value} in column {$key}", 7); } } } $proporder['TYPE'] = 0; $proporder['ORDER'] = 1; $props = array('UID', 'DTSTAMP', 'SUMMARY', 'DTSTART', 'DURATION', 'DTEND', 'DUE', 'RRULE', 'RDATE', 'EXRULE', 'EXDATE', 'DESCRIPTION', 'CATEGORIES', 'ORGANIZER', 'LOCATION', 'RESOURCES', 'CONTACT', 'URL', 'COMMENT', 'PRIORITY', 'ATTENDEE', 'CLASS', 'TRANSP', 'SEQUENCE', 'STATUS', 'COMPLETED', 'CREATED', 'LAST-MODIFIED', 'ACTION', 'TRIGGER', 'REPEAT', 'ATTACH', 'FREEBUSY', 'RELATED-TO', 'REQUEST-STATUS', 'GEO', 'PERCENT-COMPLETE', 'RECURRENCE-ID'); $pix = 2; foreach ($props as $prop) { if (isset($proporder[$prop])) { continue; } if (isset($conf['skip']) && in_array($prop, $conf['skip'])) { if ($log) { $log->log("{$iCal2csv_VERSION} {$prop} removed from output", 7); } continue; } while (in_array($pix, $proporder)) { $pix++; } $proporder[$prop] = $pix++; } /* remove unused properties from and add x-props to property order list */ if ($maxpropix < count($proporder) - 1) { $maxpropix = count($proporder) - 1; } $potmp = array(); $potmp[0] = 'TYPE'; $potmp[1] = 'ORDER'; // $potmp[2] = 'UID'; foreach ($compsinfo as $cix => $compinfo) { if ('vtimezone' == $compinfo['type']) { continue; } foreach ($compinfo['props'] as $propName => $propcnt) { if (!in_array($propName, $potmp) && isset($proporder[$propName])) { $potmp[$proporder[$propName]] = $propName; } elseif ('X-PROP' == $propName) { $comp = $calendar->getComponent($compinfo['ordno']); while ($xprop = $comp->getProperty()) { if (!in_array($xprop[0], $potmp)) { $maxpropix += 1; $potmp[$maxpropix] = $xprop[0]; } // end if } // while( $xprop } // end elseif( 'X-PROP' } // end foreach( $compinfo['props'] if (isset($compinfo['sub'])) { foreach ($compinfo['sub'] as $compinfo2) { foreach ($compinfo2['props'] as $propName => $propcnt) { if (!in_array($propName, $potmp) && isset($proporder[$propName])) { $potmp[$proporder[$propName]] = $propName; } elseif ('X-PROP' == $propName) { $scomp = $comp->getComponent($compinfo2['ordno']); while ($xprop = $scomp->getProperty()) { if (!in_array($xprop[0], $potmp)) { $maxpropix += 1; $potmp[$maxpropix] = $xprop[0]; } // end if } // end while xprop } // end X-PROP } // end $compinfo['sub']['props'] } // end foreach( $compinfo['sub'] } // end if( isset( $compinfo['sub'] } ksort($potmp, SORT_NUMERIC); $proporder = array_flip(array_values($potmp)); if ($log) { $log->log("{$iCal2csv_VERSION} comp proporder=" . implode(',', array_flip($proporder)), 7); } if ($maxColCount < count($proporder)) { $maxColCount = count($proporder); } /* create header row */ $row += 1; foreach ($proporder as $propName => $col) { if (isset($conf[$propName])) { $rows[$row][$col] = $conf[$propName]; // check map of userfriendly name to iCal property name if ($log) { $log->log("{$iCal2csv_VERSION} header row, col={$col}: {$propName}, replaced by " . $conf[$propName], 7); } } else { $rows[$row][$col] = $propName; } } $allowedProps = array('VEVENT' => array('ATTACH', 'ATTENDEE', 'CATEGORIES', 'CLASS', 'COMMENT', 'CONTACT', 'CREATED', 'DESCRIPTION', 'DTEND', 'DTSTAMP', 'DTSTART', 'DURATION', 'EXDATE', 'RXRULE', 'GEO', 'LAST-MODIFIED', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RDATE', 'RECURRENCE-ID', 'RELATED-TO', 'RESOURCES', 'RRULE', 'REQUEST-STATUS', 'SEQUENCE', 'STATUS', 'SUMMARY', 'TRANSP', 'UID', 'URL'), 'VTODO' => array('ATTACH', 'ATTENDEE', 'CATEGORIES', 'CLASS', 'COMMENT', 'COMPLETED', 'CONTACT', 'CREATED', 'DESCRIPTION', 'DTSTAMP', 'DTSTART', 'DUE', 'DURATION', 'EXATE', 'EXRULE', 'GEO', 'LAST-MODIFIED', 'LOCATION', 'ORGANIZER', 'PERCENT', 'PRIORITY', 'RDATE', 'RECURRENCE-ID', 'RELATED-TO', 'RESOURCES', 'RRULE', 'REQUEST-STATUS', 'SEQUENCE', 'STATUS', 'SUMMARY', 'UID', 'URL'), 'VJOURNAL' => array('ATTACH', 'ATTENDEE', 'CATEGORIES', 'CLASS', 'COMMENT', 'CONTACT', 'CREATED', 'DESCRIPTION', 'DTSTAMP', 'DTSTART', 'EXDATE', 'EXRULE', 'LAST-MODIFIED', 'ORGANIZER', 'RDATE', 'RECURRENCE-ID', 'RELATED-TO', 'RRULE', 'REQUEST-STATUS', 'SEQUENCE', 'STATUS', 'SUMMARY', 'UID', 'URL'), 'VFREEBUSY' => array('ATTENDEE', 'COMMENT', 'CONTACT', 'DTEND', 'DTSTAMP', 'DTSTART', 'DURATION', 'FREEBUSY', 'ORGANIZER', 'UID', 'URL'), 'VALARM' => array('ACTION', 'ATTACH', 'ATTENDEE', 'DESCRIPTION', 'DURATION', 'REPEAT', 'TRANSP', 'TRIGGER')); /* create data rows */ foreach ($compsinfo as $cix => $compinfo) { if ('vtimezone' == $compinfo['type']) { continue; } $row += 1; foreach ($proporder as $propName => $col) { $rows[$row][] = ''; } // set all cells empty $rows[$row][$proporder['TYPE']] = $compinfo['type']; $rows[$row][$proporder['ORDER']] = $compinfo['ordno']; // $rows[$row][$proporder['UID']] = $compinfo['uid']; $comp = $calendar->getComponent($compinfo['ordno']); foreach ($proporder as $propName => $col) { if ('TYPE' == $propName || 'ORDER' == $propName) { continue; } if ('X-' == substr($propName, 0, 2)) { continue; } if (!in_array($propName, $allowedProps[strtoupper($compinfo['type'])])) { // check if component allows property continue; } if (isset($compinfo['props'][$propName])) { switch ($propName) { case 'LAST-MODIFIED': $fcn = 'createLastModified'; break; case 'RECURRENCE-ID': $fcn = 'createRecurrenceid'; break; case 'RELATED-TO': $fcn = 'createRelatedTo'; break; case 'REQUEST-STATUS': $fcn = 'createRequestStatus'; break; case 'PERCENT-COMPLETE': $fcn = 'createPercentComplete'; break; default: $fcn = 'create' . strtoupper(substr($propName, 0, 1)) . strtolower(substr($propName, 1)); } if (!method_exists($comp, $fcn)) { $msg = $iCal2csv_VERSION . ' ERROR 9 INPUT FILE:"' . $filename . '" iCalcreator: unknown property: "' . $propName . '" (' . $fcn . ')'; if ($log) { $log->log($msg, 3); } else { error_log($msg); } continue; } $output = str_replace("{$calnl} ", '', rtrim($comp->{$fcn}())); $output = str_replace($propName . ';', '', $output); $output = str_replace($propName . ':', '', $output); $rows[$row][$proporder[$propName]] = fixiCalString($output); } } // end foreach( $proporder if (isset($compinfo['props']['X-PROP'])) { while ($xprop = $comp->getProperty()) { $output = str_replace("{$calnl} ", '', rtrim($xprop[1])); $rows[$row][$proporder[$xprop[0]]] = fixiCalString($output); } } if (isset($compinfo['sub'])) { foreach ($compinfo['sub'] as $compinfo2) { $row += 1; foreach ($proporder as $propName => $col) { $rows[$row][] = ''; } // set all cells empty $rows[$row][$proporder['TYPE']] = $compinfo2['type']; $rows[$row][$proporder['ORDER']] = $compinfo['ordno'] . ':' . $compinfo2['ordno']; $scomp = $comp->getComponent($compinfo2['ordno']); foreach ($proporder as $propName => $col) { if ('TYPE' == $propName || 'ORDER' == $propName) { continue; } if ('X-' == substr($propName, 0, 2)) { continue; } if (!in_array($propName, $allowedProps[strtoupper($compinfo2['type'])])) { // check if component allows property continue; } if (isset($compinfo2['props'][$propName])) { $fcn = 'create' . strtoupper(substr($propName, 0, 1)) . strtolower(substr($propName, 1)); if (!method_exists($scomp, $fcn)) { $msg = $iCal2csv_VERSION . ' ERROR 10 INPUT FILE:"' . $filename . '" iCalcreator: unknown property: "' . $propName . '" (' . $fcn . ')'; if ($log) { $log->log($msg, 3); } else { error_log($msg); } continue; } $output = str_replace("{$calnl} ", '', rtrim($scomp->{$fcn}())); $output = str_replace($propName . ';', '', $output); $output = str_replace($propName . ':', '', $output); $rows[$row][$proporder[$propName]] = fixiCalString($output); } } // end foreach( $proporder if (isset($compinfo2['props']['X-PROP'])) { while ($xprop = $scomp->getProperty()) { $output = str_replace("{$calnl} ", '', rtrim($xprop[1])); $rows[$row][$proporder[$xprop[0]]] = fixiCalString($output); } } } // if( isset( $compinfo2['props']['X-PROP'] } // end if( isset( $compinfo['sub'] } // foreach( $compsinfo as if ($log) { $timeexec['compOk'] = microtime(TRUE); } /* fix csv format */ // fields that contain commas, double-quotes, or line-breaks must be quoted, // a quote within a field must be escaped with an additional quote immediately preceding the literal quote, // space before and after delimiter commas may be trimmed (which is prohibited by RFC 4180) // a line break within an element must be preserved. // Fields may ALWAYS be enclosed within double-quote characters, whether necessary or not. foreach ($rows as $row => $line) { for ($col = 0; $col < $maxColCount; $col++) { if (!isset($line[$col]) || empty($line[$col])) { $rows[$row][$col] = $conf['del'] . $conf['del']; continue; } if (ctype_digit($line[$col])) { continue; } $cell = str_replace($conf['del'], $conf['del'] . $conf['del'], $line[$col]); $rows[$row][$col] = $conf['del'] . $cell . $conf['del']; } $rows[$row] = implode($conf['sep'], $rows[$row]); } $output = implode($conf['nl'], $rows) . $conf['nl']; if ($log) { $timeexec['exit'] = microtime(TRUE); $msg = "{$iCal2csv_VERSION} '{$filename}'"; $msg .= ' fileOk:' . number_format($timeexec['fileOk'] - $timeexec['start'], 5); $msg .= ' infoOk:' . number_format($timeexec['infoOk'] - $timeexec['fileOk'], 5); $msg .= ' zoneOk:' . number_format($timeexec['zoneOk'] - $timeexec['infoOk'], 5); $msg .= ' compOk:' . number_format($timeexec['compOk'] - $timeexec['zoneOk'], 5); $msg .= ' csvOk:' . number_format($timeexec['exit'] - $timeexec['compOk'], 5); $msg .= ' total:' . number_format($timeexec['exit'] - $timeexec['start'], 5) . 'sec'; $log->log($msg, 7); $msg = "{$iCal2csv_VERSION} '{$filename}' (" . count($compsinfo) . ' components) start:' . date('H:i:s', $timeexec['start']); $msg .= ' total:' . number_format($timeexec['exit'] - $timeexec['start'], 5) . 'sec'; if ($save) { $msg .= " -> '{$diskfilename}'"; } $msg .= ', size=' . strlen($output); $msg .= ', ' . count($rows) . " rows, {$maxColCount} cols"; $log->log($msg, 6); } /* save or send the file */ if ($save) { if (FALSE !== file_put_contents($diskfilename, $output)) { if ($log) { $log->flush(); } return TRUE; } else { $msg = $iCal2csv_VERSION . ' ERROR 11 INPUT FILE:"' . $filename . '" Invalid write to output file : "' . $diskfilename . '"'; if ($log) { $log->log($msg, 3); $log->flush(); } else { error_log($msg); } return FALSE; } } else { if ($log) { $log->flush(); } /** return data, auto gzip */ $filezise = strlen($output); if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) { $output = gzencode($output, 9); $filezise = strlen($output); header('Content-Encoding: gzip'); header('Vary: *'); } header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename="' . $outputFileParts['basename'] . '"'); header('Cache-Control: max-age=10'); header('Content-Length: ' . $filezise); echo $output; } exit; }