forked from extern/egroupware
WIP: periodic running admin-commands
This commit is contained in:
parent
cdae6c4b01
commit
c1316beda5
@ -45,6 +45,10 @@ use EGroupware\Api\Acl;
|
||||
* foreign key into egw_admin_remote (table of remote systems administrated by this one)
|
||||
* @property-read int $account account_id of user affected by this cmd or NULL
|
||||
* @property-read string $app app-name affected by this cmd or NULL
|
||||
* @property-read string $parent parent cmd (with rrule) of single periodic execution
|
||||
* @property-read string $rrule rrule for periodic execution
|
||||
* @property int $rrule_start optional start timestamp for rrule, default $created time
|
||||
* @property string async_job_id optional name of async job for periodic-run, default "admin-cmd-$id"
|
||||
*/
|
||||
abstract class admin_cmd
|
||||
{
|
||||
@ -67,7 +71,7 @@ abstract class admin_cmd
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $status;
|
||||
protected $status = self::successful;
|
||||
|
||||
static $stati = array(
|
||||
admin_cmd::scheduled => 'scheduled',
|
||||
@ -96,6 +100,8 @@ abstract class admin_cmd
|
||||
public $remote_id;
|
||||
protected $account;
|
||||
protected $app;
|
||||
protected $rrule;
|
||||
protected $parent;
|
||||
|
||||
/**
|
||||
* Display name of command, default ucfirst(str_replace(['_cmd_', '_'], ' ', __CLASS__))
|
||||
@ -387,6 +393,16 @@ abstract class admin_cmd
|
||||
{
|
||||
admin_cmd::_set_async_job();
|
||||
}
|
||||
// schedule periodic execution, if we have an rrule
|
||||
elseif (!empty($this->rrule))
|
||||
{
|
||||
$this->set_periodic_job();
|
||||
}
|
||||
// existing object with no rrule, cancle evtl. running periodic job
|
||||
elseif($vars['id'])
|
||||
{
|
||||
$this->cancel_periodic_job();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -418,12 +434,12 @@ abstract class admin_cmd
|
||||
}
|
||||
|
||||
/**
|
||||
* reading a command from the queue returning the comand object
|
||||
* Reading a command from the queue returning the comand object
|
||||
*
|
||||
* @static
|
||||
* @param int|string $id id or uid of the command
|
||||
* @return admin_cmd or null if record not found
|
||||
* @throws Exception(lang('Unknown command %1!',$class),0);
|
||||
* @throws Api\Exception\WrongParameter if class does not exist or is no instance of admin_cmd
|
||||
*/
|
||||
static function read($id)
|
||||
{
|
||||
@ -977,6 +993,72 @@ abstract class admin_cmd
|
||||
return admin_cmd::_set_async_job();
|
||||
}
|
||||
|
||||
const PERIOD_ASYNC_ID_PREFIX = 'admin-cmd-';
|
||||
|
||||
/**
|
||||
* Schedule next execution of a periodic job
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function set_periodic_job()
|
||||
{
|
||||
if (empty($this->rrule)) return false;
|
||||
|
||||
// parse rrule and calculate next execution time
|
||||
$event = calendar_rrule::parseRrule($this->rrule, true); // true: allow HOURLY or MINUTELY
|
||||
// rrule can depend on start-time, use policy creation time by default, if rrule_start is not set
|
||||
$event['start'] = empty($this->rrule_start) ? $this->created : $this->rrule_start;
|
||||
$event['tzid'] = Api\DateTime::$server_timezone->getName();
|
||||
$rrule = calendar_rrule::event2rrule($event, false); // false = server timezone
|
||||
$rrule->rewind();
|
||||
while((($time = $rrule->current()->format('ts'))) <= time())
|
||||
{
|
||||
$rrule->next();
|
||||
}
|
||||
|
||||
// schedule run_periodic_job to run at that time
|
||||
$async = new Api\Asyncservice();
|
||||
$job_id = empty($this->async_job_id) ? self::PERIOD_ASYNC_ID_PREFIX.$this->id : $this->async_job_id;
|
||||
$async->cancel_timer($job_id); // we delete it in case a job already exists
|
||||
return $async->set_timer($time, $job_id, __CLASS__.'::run_periodic_job', $this->as_array(), $this->creator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel evtl. existing periodic job
|
||||
*
|
||||
* @return boolean true if job was canceled, false otherwise
|
||||
*/
|
||||
protected function cancel_periodic_job()
|
||||
{
|
||||
$async = new Api\Asyncservice();
|
||||
$job_id = empty($this->async_job_id) ? self::PERIOD_ASYNC_ID_PREFIX.$this->id : $this->async_job_id;
|
||||
$async->cancel_timer($job_id); // we delete it in case a job already exists
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a periodic job, record it's result and schedule next run
|
||||
*/
|
||||
static function run_periodic_job($data)
|
||||
{
|
||||
$cmd = admin_cmd::read($data['id']);
|
||||
|
||||
// schedule next execution
|
||||
$cmd->set_periodic_job();
|
||||
|
||||
// instanciate single periodic execution object
|
||||
$single = $cmd->as_array();
|
||||
$single['parent'] = $single['id'];
|
||||
unset($single['id'], $single['uid'], $single['rrule'], $single['created'], $single['modified'], $single['modifier']);
|
||||
$periodic = admin_cmd::instanciate($single);
|
||||
|
||||
try {
|
||||
$periodic->run(null, false);
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
error_log(__METHOD__."(".array2string($data).") periodic execution failed: ".$ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of defined remote instances
|
||||
*
|
||||
|
@ -48,7 +48,8 @@ class admin_cmds
|
||||
$row['title'] = $e->getMessage();
|
||||
}
|
||||
$row['data'] = !($data = json_php_unserialize($row['data'])) ? '' :
|
||||
json_encode($data, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
|
||||
json_encode($data+(empty($row['rrule'])?array():array('rrule' => $row['rrule'])),
|
||||
JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
|
||||
|
||||
if ($row['status'] == admin_cmd::scheduled)
|
||||
{
|
||||
|
@ -9,7 +9,6 @@
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package calendar
|
||||
* @subpackage export
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
use EGroupware\Api;
|
||||
@ -28,15 +27,6 @@ class calendar_ical extends calendar_boupdate
|
||||
*/
|
||||
var $supportedFields;
|
||||
|
||||
var $recur_days_1_0 = array(
|
||||
MCAL_M_MONDAY => 'MO',
|
||||
MCAL_M_TUESDAY => 'TU',
|
||||
MCAL_M_WEDNESDAY => 'WE',
|
||||
MCAL_M_THURSDAY => 'TH',
|
||||
MCAL_M_FRIDAY => 'FR',
|
||||
MCAL_M_SATURDAY => 'SA',
|
||||
MCAL_M_SUNDAY => 'SU',
|
||||
);
|
||||
/**
|
||||
* @var array $status_egw2ical conversation of the participant status egw => ical
|
||||
*/
|
||||
@ -2640,160 +2630,8 @@ class calendar_ical extends calendar_boupdate
|
||||
$vcardData['location'] = str_replace("\r\n", "\n", $attributes['value']);
|
||||
break;
|
||||
case 'RRULE':
|
||||
$recurence = $attributes['value'];
|
||||
$vcardData['recur_interval'] = 1;
|
||||
$type = preg_match('/FREQ=([^;: ]+)/i',$recurence,$matches) ? $matches[1] : $recurence[0];
|
||||
// vCard 2.0 values for all types
|
||||
if (preg_match('/UNTIL=([0-9TZ]+)/',$recurence,$matches))
|
||||
{
|
||||
$vcardData['recur_enddate'] = $this->vCalendar->_parseDateTime($matches[1]);
|
||||
// If it couldn't be parsed, treat it as not set
|
||||
if(is_string($vcardData['recur_enddate']))
|
||||
{
|
||||
unset($vcardData['recur_enddate']);
|
||||
}
|
||||
else
|
||||
{
|
||||
// iCal defines enddate to be a time and eg. Apple sends 1s less then next recurance, if they split events
|
||||
self::check_fix_endate($vcardData);
|
||||
}
|
||||
}
|
||||
elseif (preg_match('/COUNT=([0-9]+)/',$recurence,$matches))
|
||||
{
|
||||
$vcardData['recur_count'] = (int)$matches[1];
|
||||
}
|
||||
if (preg_match('/INTERVAL=([0-9]+)/',$recurence,$matches))
|
||||
{
|
||||
$vcardData['recur_interval'] = (int) $matches[1] ? (int) $matches[1] : 1;
|
||||
}
|
||||
$vcardData['recur_data'] = 0;
|
||||
switch($type)
|
||||
{
|
||||
case 'D': // 1.0
|
||||
$recurenceMatches = null;
|
||||
if (preg_match('/D(\d+) #(\d+)/', $recurence, $recurenceMatches))
|
||||
{
|
||||
$vcardData['recur_interval'] = $recurenceMatches[1];
|
||||
$vcardData['recur_count'] = $recurenceMatches[2];
|
||||
}
|
||||
elseif (preg_match('/D(\d+) (.*)/', $recurence, $recurenceMatches))
|
||||
{
|
||||
$vcardData['recur_interval'] = $recurenceMatches[1];
|
||||
$vcardData['recur_enddate'] = $this->vCalendar->_parseDateTime(trim($recurenceMatches[2]));
|
||||
}
|
||||
else break;
|
||||
// fall-through
|
||||
case 'DAILY': // 2.0
|
||||
$vcardData['recur_type'] = MCAL_RECUR_DAILY;
|
||||
if (stripos($recurence, 'BYDAY') === false) break;
|
||||
// hack to handle TYPE=DAILY;BYDAY= as WEEKLY, which is true as long as there's no interval
|
||||
// fall-through
|
||||
case 'W':
|
||||
case 'WEEKLY':
|
||||
$days = array();
|
||||
if (preg_match('/W(\d+) *((?i: [AEFHMORSTUW]{2})+)?( +([^ ]*))$/',$recurence, $recurenceMatches)) // 1.0
|
||||
{
|
||||
$vcardData['recur_interval'] = $recurenceMatches[1];
|
||||
if (empty($recurenceMatches[2]))
|
||||
{
|
||||
$days[0] = strtoupper(substr(date('D', $vcardData['start']),0,2));
|
||||
}
|
||||
else
|
||||
{
|
||||
$days = explode(' ',trim($recurenceMatches[2]));
|
||||
}
|
||||
|
||||
$repeatMatches = null;
|
||||
if (preg_match('/#(\d+)/',$recurenceMatches[4],$repeatMatches))
|
||||
{
|
||||
if ($repeatMatches[1]) $vcardData['recur_count'] = $repeatMatches[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
$vcardData['recur_enddate'] = $this->vCalendar->_parseDateTime($recurenceMatches[4]);
|
||||
}
|
||||
|
||||
$recur_days = $this->recur_days_1_0;
|
||||
}
|
||||
elseif (preg_match('/BYDAY=([^;: ]+)/',$recurence,$recurenceMatches)) // 2.0
|
||||
{
|
||||
$days = explode(',',$recurenceMatches[1]);
|
||||
$recur_days = $this->recur_days;
|
||||
}
|
||||
else // no day given, use the day of dtstart
|
||||
{
|
||||
$vcardData['recur_data'] |= 1 << (int)date('w',$vcardData['start']);
|
||||
$vcardData['recur_type'] = MCAL_RECUR_WEEKLY;
|
||||
}
|
||||
if ($days)
|
||||
{
|
||||
foreach ($recur_days as $id => $day)
|
||||
{
|
||||
if (in_array(strtoupper(substr($day,0,2)),$days))
|
||||
{
|
||||
$vcardData['recur_data'] |= $id;
|
||||
}
|
||||
}
|
||||
$vcardData['recur_type'] = MCAL_RECUR_WEEKLY;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
if (preg_match('/MD(\d+)(?: [^ ]+)? #(\d+)/', $recurence, $recurenceMatches))
|
||||
{
|
||||
$vcardData['recur_type'] = MCAL_RECUR_MONTHLY_MDAY;
|
||||
$vcardData['recur_interval'] = $recurenceMatches[1];
|
||||
$vcardData['recur_count'] = $recurenceMatches[2];
|
||||
}
|
||||
elseif (preg_match('/MD(\d+)(?: [^ ]+)? ([0-9TZ]+)/',$recurence, $recurenceMatches))
|
||||
{
|
||||
$vcardData['recur_type'] = MCAL_RECUR_MONTHLY_MDAY;
|
||||
$vcardData['recur_interval'] = $recurenceMatches[1];
|
||||
$vcardData['recur_enddate'] = $this->vCalendar->_parseDateTime($recurenceMatches[2]);
|
||||
}
|
||||
elseif (preg_match('/MP(\d+) (.*) (.*) (.*)/',$recurence, $recurenceMatches))
|
||||
{
|
||||
$vcardData['recur_type'] = MCAL_RECUR_MONTHLY_WDAY;
|
||||
$vcardData['recur_interval'] = $recurenceMatches[1];
|
||||
if (preg_match('/#(\d+)/',$recurenceMatches[4],$recurenceMatches))
|
||||
{
|
||||
$vcardData['recur_count'] = $recurenceMatches[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
$vcardData['recur_enddate'] = $this->vCalendar->_parseDateTime(trim($recurenceMatches[4]));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Y': // 1.0
|
||||
if (preg_match('/YM(\d+)(?: [^ ]+)? #(\d+)/', $recurence, $recurenceMatches))
|
||||
{
|
||||
$vcardData['recur_interval'] = $recurenceMatches[1];
|
||||
$vcardData['recur_count'] = $recurenceMatches[2];
|
||||
}
|
||||
elseif (preg_match('/YM(\d+)(?: [^ ]+)? ([0-9TZ]+)/',$recurence, $recurenceMatches))
|
||||
{
|
||||
$vcardData['recur_interval'] = $recurenceMatches[1];
|
||||
$vcardData['recur_enddate'] = $this->vCalendar->_parseDateTime($recurenceMatches[2]);
|
||||
} else break;
|
||||
// fall-through
|
||||
case 'YEARLY': // 2.0
|
||||
if (strpos($recurence, 'BYDAY') === false)
|
||||
{
|
||||
$vcardData['recur_type'] = MCAL_RECUR_YEARLY;
|
||||
break;
|
||||
}
|
||||
// handle FREQ=YEARLY;BYDAY= as FREQ=MONTHLY;BYDAY= with 12*INTERVAL
|
||||
$vcardData['recur_interval'] = $vcardData['recur_interval'] ?
|
||||
12*$vcardData['recur_interval'] : 12;
|
||||
// fall-through
|
||||
case 'MONTHLY':
|
||||
// does currently NOT parse BYDAY or BYMONTH, it has to be specified/identical to DTSTART
|
||||
$vcardData['recur_type'] = strpos($recurence,'BYDAY') !== false ?
|
||||
MCAL_RECUR_MONTHLY_WDAY : MCAL_RECUR_MONTHLY_MDAY;
|
||||
break;
|
||||
}
|
||||
$vcardData += calendar_rrule::parseRrule($attributes['value']);
|
||||
if (!empty($vcardData['recur_enddate'])) self::check_fix_endate ($vcardData);
|
||||
break;
|
||||
case 'EXDATE': // current Horde_Icalendar returns dates, no timestamps
|
||||
if ($attributes['values'])
|
||||
|
@ -56,6 +56,15 @@ class calendar_rrule implements Iterator
|
||||
* Yearly recurrance
|
||||
*/
|
||||
const YEARLY = 5;
|
||||
/**
|
||||
* Hourly recurrance
|
||||
*/
|
||||
const HOURLY = 8;
|
||||
/**
|
||||
* Minutely recurrance
|
||||
*/
|
||||
const MINUTELY = 7;
|
||||
|
||||
/**
|
||||
* Translate recure types to labels
|
||||
*
|
||||
@ -67,7 +76,7 @@ class calendar_rrule implements Iterator
|
||||
self::WEEKLY => 'Weekly',
|
||||
self::MONTHLY_WDAY => 'Monthly (by day)',
|
||||
self::MONTHLY_MDAY => 'Monthly (by date)',
|
||||
self::YEARLY => 'Yearly'
|
||||
self::YEARLY => 'Yearly',
|
||||
);
|
||||
|
||||
/**
|
||||
@ -79,6 +88,8 @@ class calendar_rrule implements Iterator
|
||||
self::MONTHLY_WDAY => 'MONTHLY', // BYDAY={1..7, -1}{MO..SO, last workday}
|
||||
self::MONTHLY_MDAY => 'MONTHLY', // BYMONHTDAY={1..31, -1 for last day of month}
|
||||
self::YEARLY => 'YEARLY',
|
||||
self::HOURLY => 'HOURLY',
|
||||
self::MINUTELY => 'MINUTELY',
|
||||
);
|
||||
|
||||
/**
|
||||
@ -250,7 +261,7 @@ class calendar_rrule implements Iterator
|
||||
|
||||
$this->time = $time instanceof Api\DateTime ? $time : new Api\DateTime($time);
|
||||
|
||||
if (!in_array($type,array(self::NONE, self::DAILY, self::WEEKLY, self::MONTHLY_MDAY, self::MONTHLY_WDAY, self::YEARLY)))
|
||||
if (!in_array($type,array(self::NONE, self::DAILY, self::WEEKLY, self::MONTHLY_MDAY, self::MONTHLY_WDAY, self::YEARLY, self::HOURLY, self::MINUTELY)))
|
||||
{
|
||||
throw new Api\Exception\WrongParameter(__METHOD__."($time,$type,$interval,$enddate,$weekdays,...) type $type is NOT valid!");
|
||||
}
|
||||
@ -451,6 +462,14 @@ class calendar_rrule implements Iterator
|
||||
$this->current->modify($this->interval.' year');
|
||||
break;
|
||||
|
||||
case self::HOURLY:
|
||||
$this->current->modify($this->interval.' hour');
|
||||
break;
|
||||
|
||||
case self::MINUTELY:
|
||||
$this->current->modify($this->interval.' minute');
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Api\Exception\AssertionFailed(__METHOD__."() invalid type #$this->type !");
|
||||
}
|
||||
@ -787,6 +806,7 @@ class calendar_rrule implements Iterator
|
||||
|
||||
/**
|
||||
* Generate a rrule from a string generated by __toString().
|
||||
*
|
||||
* @param String $rrule Recurrence rule in string format, as generated by __toString()
|
||||
* @param DateTime date Optional date to work from, defaults to today
|
||||
*/
|
||||
@ -799,16 +819,16 @@ class calendar_rrule implements Iterator
|
||||
$weekdays = 0;
|
||||
$exceptions = array();
|
||||
|
||||
list($type, $sub, $conditions) = explode(' (', $rrule);
|
||||
if(!$conditions)
|
||||
list($type, $sub, $conds) = explode(' (', $rrule);
|
||||
if(!$conds)
|
||||
{
|
||||
$conditions = $sub;
|
||||
$conds = $sub;
|
||||
}
|
||||
else
|
||||
{
|
||||
$type .= " ($sub";
|
||||
}
|
||||
$conditions = explode(', ', substr($conditions, 0, -1));
|
||||
$conditions = explode(', ', substr($conds, 0, -1));
|
||||
|
||||
foreach(static::$types as $id => $type_name)
|
||||
{
|
||||
@ -859,13 +879,14 @@ class calendar_rrule implements Iterator
|
||||
}
|
||||
else if ($condition_name == lang('ends'))
|
||||
{
|
||||
list($dow, $date) = explode(', ', $value);
|
||||
list(, $date) = explode(', ', $value);
|
||||
$enddate = new DateTime($date);
|
||||
}
|
||||
}
|
||||
|
||||
return new calendar_rrule($time,$type_id,$interval,$enddate,$weekdays,$exceptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recurrence data (keys 'recur_*') to merge into an event
|
||||
*
|
||||
@ -939,6 +960,189 @@ class calendar_rrule implements Iterator
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a DateTime field and returns a unix timestamp. If the
|
||||
* field cannot be parsed then the original text is returned
|
||||
* unmodified.
|
||||
*
|
||||
* @param string $text The Icalendar datetime field value.
|
||||
* @param string $tzid =null A timezone identifier.
|
||||
*
|
||||
* @return integer A unix timestamp.
|
||||
*/
|
||||
private static function parseIcalDateTime($text, $tzid=null)
|
||||
{
|
||||
static $vcal = null;
|
||||
if (!isset($vcal)) $vcal = new Horde_Icalendar;
|
||||
|
||||
return $vcal->_parseDateTime($text, $tzid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an iCal recurrence-rule string
|
||||
*
|
||||
* @param type $recurence
|
||||
* @param bool $support_below_daily =false true: support FREQ=HOURLY|MINUTELY
|
||||
* @return type
|
||||
*/
|
||||
public static function parseRrule($recurence, $support_below_daily=false)
|
||||
{
|
||||
$vcardData = array();
|
||||
$vcardData['recur_interval'] = 1;
|
||||
$matches = null;
|
||||
$type = preg_match('/FREQ=([^;: ]+)/i',$recurence,$matches) ? $matches[1] : $recurence[0];
|
||||
// vCard 2.0 values for all types
|
||||
if (preg_match('/UNTIL=([0-9TZ]+)/',$recurence,$matches))
|
||||
{
|
||||
$vcardData['recur_enddate'] = self::parseIcalDateTime($matches[1]);
|
||||
// If it couldn't be parsed, treat it as not set
|
||||
if(is_string($vcardData['recur_enddate']))
|
||||
{
|
||||
unset($vcardData['recur_enddate']);
|
||||
}
|
||||
}
|
||||
elseif (preg_match('/COUNT=([0-9]+)/',$recurence,$matches))
|
||||
{
|
||||
$vcardData['recur_count'] = (int)$matches[1];
|
||||
}
|
||||
if (preg_match('/INTERVAL=([0-9]+)/',$recurence,$matches))
|
||||
{
|
||||
$vcardData['recur_interval'] = (int) $matches[1] ? (int) $matches[1] : 1;
|
||||
}
|
||||
$vcardData['recur_data'] = 0;
|
||||
switch($type)
|
||||
{
|
||||
case 'D': // 1.0
|
||||
$recurenceMatches = null;
|
||||
if (preg_match('/D(\d+) #(\d+)/', $recurence, $recurenceMatches))
|
||||
{
|
||||
$vcardData['recur_interval'] = $recurenceMatches[1];
|
||||
$vcardData['recur_count'] = $recurenceMatches[2];
|
||||
}
|
||||
elseif (preg_match('/D(\d+) (.*)/', $recurence, $recurenceMatches))
|
||||
{
|
||||
$vcardData['recur_interval'] = $recurenceMatches[1];
|
||||
$vcardData['recur_enddate'] = self::parseIcalDateTime(trim($recurenceMatches[2]));
|
||||
}
|
||||
else break;
|
||||
// fall-through
|
||||
case 'DAILY': // 2.0
|
||||
$vcardData['recur_type'] = self::DAILY;
|
||||
if (stripos($recurence, 'BYDAY') === false) break;
|
||||
// hack to handle TYPE=DAILY;BYDAY= as WEEKLY, which is true as long as there's no interval
|
||||
// fall-through
|
||||
case 'W':
|
||||
case 'WEEKLY':
|
||||
$days = array();
|
||||
if (preg_match('/W(\d+) *((?i: [AEFHMORSTUW]{2})+)?( +([^ ]*))$/',$recurence, $recurenceMatches)) // 1.0
|
||||
{
|
||||
$vcardData['recur_interval'] = $recurenceMatches[1];
|
||||
if (empty($recurenceMatches[2]))
|
||||
{
|
||||
$days[0] = strtoupper(substr(date('D', $vcardData['start']),0,2));
|
||||
}
|
||||
else
|
||||
{
|
||||
$days = explode(' ',trim($recurenceMatches[2]));
|
||||
}
|
||||
|
||||
$repeatMatches = null;
|
||||
if (preg_match('/#(\d+)/',$recurenceMatches[4],$repeatMatches))
|
||||
{
|
||||
if ($repeatMatches[1]) $vcardData['recur_count'] = $repeatMatches[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
$vcardData['recur_enddate'] = self::parseIcalDateTime($recurenceMatches[4]);
|
||||
}
|
||||
}
|
||||
elseif (preg_match('/BYDAY=([^;: ]+)/',$recurence,$recurenceMatches)) // 2.0
|
||||
{
|
||||
$days = explode(',',$recurenceMatches[1]);
|
||||
}
|
||||
else // no day given, use the day of dtstart
|
||||
{
|
||||
$vcardData['recur_data'] |= 1 << (int)date('w',$vcardData['start']);
|
||||
$vcardData['recur_type'] = self::WEEKLY;
|
||||
}
|
||||
if ($days)
|
||||
{
|
||||
foreach (self::$days as $id => $day)
|
||||
{
|
||||
if (in_array(strtoupper(substr($day,0,2)),$days))
|
||||
{
|
||||
$vcardData['recur_data'] |= $id;
|
||||
}
|
||||
}
|
||||
$vcardData['recur_type'] = self::WEEKLY;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
if (preg_match('/MD(\d+)(?: [^ ]+)? #(\d+)/', $recurence, $recurenceMatches))
|
||||
{
|
||||
$vcardData['recur_type'] = self::MONTHLY_MDAY;
|
||||
$vcardData['recur_interval'] = $recurenceMatches[1];
|
||||
$vcardData['recur_count'] = $recurenceMatches[2];
|
||||
}
|
||||
elseif (preg_match('/MD(\d+)(?: [^ ]+)? ([0-9TZ]+)/',$recurence, $recurenceMatches))
|
||||
{
|
||||
$vcardData['recur_type'] = self::MONTHLY_MDAY;
|
||||
$vcardData['recur_interval'] = $recurenceMatches[1];
|
||||
$vcardData['recur_enddate'] = self::parseIcalDateTime($recurenceMatches[2]);
|
||||
}
|
||||
elseif (preg_match('/MP(\d+) (.*) (.*) (.*)/',$recurence, $recurenceMatches))
|
||||
{
|
||||
$vcardData['recur_type'] = self::MONTHLY_WDAY;
|
||||
$vcardData['recur_interval'] = $recurenceMatches[1];
|
||||
if (preg_match('/#(\d+)/',$recurenceMatches[4],$recurenceMatches))
|
||||
{
|
||||
$vcardData['recur_count'] = $recurenceMatches[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
$vcardData['recur_enddate'] = self::parseIcalDateTime(trim($recurenceMatches[4]));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Y': // 1.0
|
||||
if (preg_match('/YM(\d+)(?: [^ ]+)? #(\d+)/', $recurence, $recurenceMatches))
|
||||
{
|
||||
$vcardData['recur_interval'] = $recurenceMatches[1];
|
||||
$vcardData['recur_count'] = $recurenceMatches[2];
|
||||
}
|
||||
elseif (preg_match('/YM(\d+)(?: [^ ]+)? ([0-9TZ]+)/',$recurence, $recurenceMatches))
|
||||
{
|
||||
$vcardData['recur_interval'] = $recurenceMatches[1];
|
||||
$vcardData['recur_enddate'] = self::parseIcalDateTime($recurenceMatches[2]);
|
||||
} else break;
|
||||
// fall-through
|
||||
case 'YEARLY': // 2.0
|
||||
if (strpos($recurence, 'BYDAY') === false)
|
||||
{
|
||||
$vcardData['recur_type'] = self::YEARLY;
|
||||
break;
|
||||
}
|
||||
// handle FREQ=YEARLY;BYDAY= as FREQ=MONTHLY;BYDAY= with 12*INTERVAL
|
||||
$vcardData['recur_interval'] = $vcardData['recur_interval'] ?
|
||||
12*$vcardData['recur_interval'] : 12;
|
||||
// fall-through
|
||||
case 'MONTHLY':
|
||||
// does currently NOT parse BYDAY or BYMONTH, it has to be specified/identical to DTSTART
|
||||
$vcardData['recur_type'] = strpos($recurence,'BYDAY') !== false ?
|
||||
self::MONTHLY_WDAY : self::MONTHLY_MDAY;
|
||||
break;
|
||||
case 'HOURLY':
|
||||
if ($support_below_daily) $vcardData['recur_type'] = self::HOURLY;
|
||||
break;
|
||||
case 'MINUTELY':
|
||||
if ($support_below_daily) $vcardData['recur_type'] = self::MINUTELY;
|
||||
break;
|
||||
}
|
||||
return $vcardData;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_SERVER['SCRIPT_FILENAME']) && $_SERVER['SCRIPT_FILENAME'] == __FILE__) // some tests
|
||||
|
Loading…
Reference in New Issue
Block a user