123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485 |
- <?php
- /* Copyright (C) 2006 Roman Ozana <ozana@omdesign.cz>
- * Copyright (C) 2011 Juanjo Menent <jmenent@2byte.es>
- * Copyright (C) 2013-2014 Laurent Destailleur <eldy@users.sourceforge.net>
- * Copyright (C) 2012 Regis Houssin <regis.houssin@inodbox.com>
- * Copyright (C) 2019 Frédéric France <frederic.france@netlogic.fr>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- /**
- * \file htdocs/comm/action/class/ical.class.php
- * \ingroup agenda
- * \brief File of class to parse ical calendars
- */
- require_once DOL_DOCUMENT_ROOT.'/core/lib/xcal.lib.php';
- require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
- /**
- * Class to read/parse ICal calendars
- */
- class ICal
- {
- /**
- * @var string Name of remote HTTP file to read
- */
- public $file;
- // Text in file
- public $file_text;
- public $cal; // Array to save iCalendar parse data
- public $event_count; // Number of Events
- public $todo_count; // Number of Todos
- public $freebusy_count; // Number of Freebusy
- public $last_key; //Help variable save last key (multiline string)
- public $error;
- /**
- * Constructor
- */
- public function __construct()
- {
- }
- // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
- /**
- * Read text file, icalender text file
- *
- * @param string $file File
- * @return string|null Content of remote file read or null if error
- */
- public function read_file($file)
- {
- // phpcs:enable
- $this->file = $file;
- $file_text = '';
- $tmpresult = getURLContent($file, 'GET');
- if ($tmpresult['http_code'] != 200) {
- $file_text = null;
- $this->error = 'Error: '.$tmpresult['http_code'].' '.$tmpresult['content'];
- } else {
- $file_text = preg_replace("/[\r\n]{1,} /", "", $tmpresult['content']);
- }
- //var_dump($tmpresult);
- return $file_text; // return all text
- }
- // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
- /**
- * Returns the number of calendar events
- *
- * @return int
- */
- public function get_event_count()
- {
- // phpcs:enable
- return $this->event_count;
- }
- // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
- /**
- * Returns the number of to do
- *
- * @return int
- */
- public function get_todo_count()
- {
- // phpcs:enable
- return $this->todo_count;
- }
- /**
- * Translate Calendar
- *
- * @param string $uri Url
- * @param string $usecachefile Full path of a cache file to use a cache file
- * @param string $delaycache Delay in seconds for cache (by default 3600 secondes)
- * @return array|string
- */
- public function parse($uri, $usecachefile = '', $delaycache = 3600)
- {
- $this->cal = array(); // new empty array
- $this->event_count = -1;
- $this->file_text = null;
- // Save file into a cache
- if ($usecachefile) {
- include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
- $datefile = dol_filemtime($usecachefile);
- $now = dol_now('gmt');
- //print $datefile.' '.$now.' ...';
- if ($datefile && $datefile > ($now - $delaycache)) {
- // We reuse the cache file
- $this->file_text = file_get_contents($usecachefile);
- }
- }
- // read FILE text
- if (is_null($this->file_text)) {
- $this->file_text = $this->read_file($uri);
- if ($usecachefile && !is_null($this->file_text)) {
- // Save the file content into cache file
- file_put_contents($usecachefile, $this->file_text, LOCK_EX);
- dolChmod($usecachefile);
- }
- }
- $this->file_text = preg_split("[\n]", $this->file_text);
- // is this text vcalendar standard text ? on line 1 is BEGIN:VCALENDAR
- if (!stristr($this->file_text[0], 'BEGIN:VCALENDAR')) {
- return 'error not VCALENDAR';
- }
- $insidealarm = 0;
- $tmpkey = '';
- $tmpvalue = '';
- $type = '';
- foreach ($this->file_text as $text) {
- $text = trim($text); // trim one line
- if (!empty($text)) {
- // get Key and Value VCALENDAR:Begin -> Key = VCALENDAR, Value = begin
- list($key, $value) = $this->retun_key_value($text);
- //var_dump($text.' -> '.$key.' - '.$value);
- switch ($text) { // search special string
- case "BEGIN:VTODO":
- $this->todo_count = $this->todo_count + 1; // new to do begin
- $type = "VTODO";
- break;
- case "BEGIN:VEVENT":
- $this->event_count = $this->event_count + 1; // new event begin
- $type = "VEVENT";
- break;
- case "BEGIN:VFREEBUSY":
- $this->freebusy_count = $this->freebusy_count + 1; // new event begin
- $type = "VFREEBUSY";
- break;
- case "BEGIN:VCALENDAR": // all other special string
- case "BEGIN:DAYLIGHT":
- case "BEGIN:VTIMEZONE":
- case "BEGIN:STANDARD":
- $type = $value; // save array under value key
- break;
- case "END:VTODO": // end special text - goto VCALENDAR key
- case "END:VEVENT":
- case "END:VFREEBUSY":
- case "END:VCALENDAR":
- case "END:DAYLIGHT":
- case "END:VTIMEZONE":
- case "END:STANDARD":
- $type = "VCALENDAR";
- break;
- // Manage VALARM that are inside a VEVENT to avoid fields of VALARM to overwrites fields of VEVENT
- case "BEGIN:VALARM":
- $insidealarm = 1;
- break;
- case "END:VALARM":
- $insidealarm = 0;
- break;
- default: // no special string (SUMMARY, DESCRIPTION, ...)
- if ($tmpvalue) {
- $tmpvalue .= $text;
- if (!preg_match('/=$/', $text)) { // No more lines
- $key = $tmpkey;
- $value = quotedPrintDecode(preg_replace('/^ENCODING=QUOTED-PRINTABLE:/i', '', $tmpvalue));
- $tmpkey = '';
- $tmpvalue = '';
- }
- } elseif (preg_match('/^ENCODING=QUOTED-PRINTABLE:/i', $value)) {
- if (preg_match('/=$/', $value)) {
- $tmpkey = $key;
- $tmpvalue = $tmpvalue.preg_replace('/=$/', "", $value); // We must wait to have next line to have complete message
- } else {
- $value = quotedPrintDecode(preg_replace('/^ENCODING=QUOTED-PRINTABLE:/i', '', $tmpvalue.$value));
- }
- } //$value=quotedPrintDecode($tmpvalue.$value);
- if (!$insidealarm && !$tmpkey) {
- $this->add_to_array($type, $key, $value); // add to array
- }
- break;
- }
- }
- }
- //var_dump($this->cal);
- return $this->cal;
- }
- // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
- /**
- * Add to $this->ical array one value and key.
- *
- * @param string $type Type ('VTODO', 'VEVENT', 'VFREEBUSY', 'VCALENDAR'...)
- * @param string $key Key ('DTSTART', ...). Note: Field is never 'DTSTART;TZID=...' because ';...' was before removed and added as another property
- * @param string $value Value
- * @return void
- */
- public function add_to_array($type, $key, $value)
- {
- // phpcs:enable
- //print 'type='.$type.' key='.$key.' value='.$value.'<br>'."\n";
- if (empty($key)) {
- $key = $this->last_key;
- switch ($type) {
- case 'VEVENT':
- $value = $this->cal[$type][$this->event_count][$key].$value;
- break;
- case 'VFREEBUSY':
- $value = $this->cal[$type][$this->freebusy_count][$key].$value;
- break;
- case 'VTODO':
- $value = $this->cal[$type][$this->todo_count][$key].$value;
- break;
- }
- }
- if (($key == "DTSTAMP") || ($key == "LAST-MODIFIED") || ($key == "CREATED")) {
- $value = $this->ical_date_to_unix($value);
- }
- //if ($key == "RRULE" ) $value = $this->ical_rrule($value);
- if (stristr($key, "DTSTART") || stristr($key, "DTEND") || stristr($key, "DTSTART;VALUE=DATE") || stristr($key, "DTEND;VALUE=DATE")) {
- if (stristr($key, "DTSTART;VALUE=DATE") || stristr($key, "DTEND;VALUE=DATE")) {
- list($key, $value) = array($key, $value);
- } else {
- list($key, $value) = $this->ical_dt_date($key, $value);
- }
- }
- switch ($type) {
- case "VTODO":
- $this->cal[$type][$this->todo_count][$key] = $value;
- break;
- case "VEVENT":
- $this->cal[$type][$this->event_count][$key] = $value;
- break;
- case "VFREEBUSY":
- $this->cal[$type][$this->freebusy_count][$key] = $value;
- break;
- default:
- $this->cal[$type][$key] = $value;
- break;
- }
- $this->last_key = $key;
- }
- // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
- /**
- * Parse text "XXXX:value text some with : " and return array($key = "XXXX", $value="value");
- *
- * @param string $text Text
- * @return array
- */
- public function retun_key_value($text)
- {
- // phpcs:enable
- /*
- preg_match("/([^:]+)[:]([\w\W]+)/", $text, $matches);
- if (empty($matches))
- {
- return array(false,$text);
- }
- else
- {
- $matches = array_splice($matches, 1, 2);
- return $matches;
- }*/
- return explode(':', $text, 2);
- }
- // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
- /**
- * Parse RRULE return array
- *
- * @param string $value string
- * @return array
- */
- public function ical_rrule($value)
- {
- // phpcs:enable
- $result = array();
- $rrule = explode(';', $value);
- foreach ($rrule as $line) {
- $rcontent = explode('=', $line);
- $result[$rcontent[0]] = $rcontent[1];
- }
- return $result;
- }
- // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
- /**
- * Return Unix time from ical date time fomrat (YYYYMMDD[T]HHMMSS[Z] or YYYYMMDD[T]HHMMSS)
- *
- * @param string $ical_date String date
- * @return int
- */
- public function ical_date_to_unix($ical_date)
- {
- // phpcs:enable
- $ical_date = str_replace('T', '', $ical_date);
- $ical_date = str_replace('Z', '', $ical_date);
- $ntime = 0;
- // TIME LIMITED EVENT
- if (preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{0,2})([0-9]{0,2})([0-9]{0,2})/', $ical_date, $date)) {
- $ntime = dol_mktime($date[4], $date[5], $date[6], $date[2], $date[3], $date[1], true);
- }
- //if (empty($date[4])) print 'Error bad date: '.$ical_date.' - date1='.$date[1];
- //print dol_print_date($ntime,'dayhour');exit;
- return $ntime; // ntime is a GTM time
- }
- // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
- /**
- * Return unix date from iCal date format
- *
- * @param string $key Key
- * @param string $value Value
- * @return array
- */
- public function ical_dt_date($key, $value)
- {
- // phpcs:enable
- $return_value = array();
- $value = $this->ical_date_to_unix($value);
- // Analyse TZID
- $temp = explode(";", $key);
- if (empty($temp[1])) { // not TZID
- $value = str_replace('T', '', $value);
- return array($key, $value);
- }
- $key = $temp[0];
- $temp = explode("=", $temp[1]);
- $return_value[$temp[0]] = $temp[1];
- $return_value['unixtime'] = $value;
- return array($key, $return_value);
- }
- // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
- /**
- * Return sorted eventlist as array or false if calendar is empty
- *
- * @return array|false
- */
- public function get_sort_event_list()
- {
- // phpcs:enable
- $temp = $this->get_event_list();
- if (!empty($temp)) {
- usort($temp, array(&$this, "ical_dtstart_compare"));
- return $temp;
- } else {
- return false;
- }
- }
- // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
- /**
- * Compare two unix timestamp
- *
- * @param array $a Operand a
- * @param array $b Operand b
- * @return integer
- */
- public function ical_dtstart_compare($a, $b)
- {
- // phpcs:enable
- return strnatcasecmp($a['DTSTART']['unixtime'], $b['DTSTART']['unixtime']);
- }
- // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
- /**
- * Return eventlist array (not sorted eventlist array)
- *
- * @return array
- */
- public function get_event_list()
- {
- // phpcs:enable
- return (empty($this->cal['VEVENT']) ? array() : $this->cal['VEVENT']);
- }
- // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
- /**
- * Return freebusy array (not sort eventlist array)
- *
- * @return array
- */
- public function get_freebusy_list()
- {
- // phpcs:enable
- return (empty($this->cal['VFREEBUSY']) ? array() : $this->cal['VFREEBUSY']);
- }
- // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
- /**
- * Return to do array (not sorted todo array)
- *
- * @return array
- */
- public function get_todo_list()
- {
- // phpcs:enable
- return $this->cal['VTODO'];
- }
- // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
- /**
- * Return base calendar data
- *
- * @return array
- */
- public function get_calender_data()
- {
- // phpcs:enable
- return $this->cal['VCALENDAR'];
- }
- // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
- /**
- * Return array with all data
- *
- * @return array
- */
- public function get_all_data()
- {
- // phpcs:enable
- return $this->cal;
- }
- }
|