Use new rrule iterator class for RRULE generation

This commit is contained in:
Jörg Lehrke 2009-11-12 19:08:23 +00:00
parent 47b2216554
commit db25947a38
2 changed files with 141 additions and 100 deletions

View File

@ -24,16 +24,6 @@ class calendar_ical extends calendar_boupdate
*/ */
var $supportedFields; 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 * @var array $status_egw2ical conversation of the participant status egw => ical
*/ */
@ -74,28 +64,6 @@ class calendar_ical extends calendar_boupdate
4 => 3, 3 => 3, 2 => 3, 1 => 3, // high 4 => 3, 3 => 3, 2 => 3, 1 => 3, // high
); );
/**
* @var array $recur_egw2ical_2_0 converstaion of egw recur-type => ical FREQ
*/
var $recur_egw2ical_2_0 = array(
MCAL_RECUR_DAILY => 'DAILY',
MCAL_RECUR_WEEKLY => 'WEEKLY',
MCAL_RECUR_MONTHLY_MDAY => 'MONTHLY', // BYMONHTDAY={1..31}
MCAL_RECUR_MONTHLY_WDAY => 'MONTHLY', // BYDAY={1..5}{MO..SO}
MCAL_RECUR_YEARLY => 'YEARLY',
);
/**
* @var array $recur_egw2ical_1_0 converstaion of egw recur-type => ical FREQ
*/
var $recur_egw2ical_1_0 = array(
MCAL_RECUR_DAILY => 'D',
MCAL_RECUR_WEEKLY => 'W',
MCAL_RECUR_MONTHLY_MDAY => 'MD', // BYMONHTDAY={1..31}
MCAL_RECUR_MONTHLY_WDAY => 'MP', // BYDAY={1..5}{MO..SO}
MCAL_RECUR_YEARLY => 'YM',
);
/** /**
* manufacturer and name of the sync-client * manufacturer and name of the sync-client
* *
@ -390,74 +358,14 @@ class calendar_ical extends calendar_boupdate
case 'RRULE': case 'RRULE':
if ($event['recur_type'] == MCAL_RECUR_NONE) break; // no recuring event if ($event['recur_type'] == MCAL_RECUR_NONE) break; // no recuring event
$rriter = calendar_rrule::event2rrule($event,true); // true = we operate in usertime, like the rest of calendar_bo
$rrule = $rriter->generate_rrule($version);
if ($version == '1.0') if ($version == '1.0')
{ {
$interval = ($event['recur_interval'] > 1) ? $event['recur_interval'] : 1;
$rrule = array('FREQ' => $this->recur_egw2ical_1_0[$event['recur_type']].$interval);
switch ($event['recur_type'])
{
case MCAL_RECUR_WEEKLY:
$days = array();
foreach ($this->recur_days_1_0 as $id => $day)
{
if ($event['recur_data'] & $id) $days[] = strtoupper(substr($day,0,2));
}
$rrule['BYDAY'] = implode(' ',$days);
$rrule['FREQ'] = $rrule['FREQ'].' '.$rrule['BYDAY'];
break;
case MCAL_RECUR_MONTHLY_MDAY: // date of the month: BYMONTDAY={1..31}
break;
case MCAL_RECUR_MONTHLY_WDAY: // weekday of the month: BDAY={1..5}{MO..SO}
$rrule['BYDAY'] = (1 + (int) ((date('d',$event['start'])-1) / 7)).'+ '.
strtoupper(substr(date('l',$event['start']),0,2));
$rrule['FREQ'] = $rrule['FREQ'].' '.$rrule['BYDAY'];
break;
}
if ($event['recur_enddate'])
{
$rrule['UNTIL'] = $vcal->_exportDateTime($recur_enddate);
}
else
{
$rrule['UNTIL'] = '#0';
}
$attributes['RRULE'] = $rrule['FREQ'].' '.$rrule['UNTIL']; $attributes['RRULE'] = $rrule['FREQ'].' '.$rrule['UNTIL'];
} }
else // $version == '2.0' else // $version == '2.0'
{ {
$rrule = array('FREQ' => $this->recur_egw2ical_2_0[$event['recur_type']]);
switch ($event['recur_type'])
{
case MCAL_RECUR_WEEKLY:
$days = array();
foreach ($this->recur_days as $id => $day)
{
if ($event['recur_data'] & $id) $days[] = strtoupper(substr($day,0,2));
}
$rrule['BYDAY'] = implode(',',$days);
break;
case MCAL_RECUR_MONTHLY_MDAY: // date of the month: BYMONTDAY={1..31}
$rrule['BYMONTHDAY'] = (int) date('d',$event['start']);
break;
case MCAL_RECUR_MONTHLY_WDAY: // weekday of the month: BDAY={1..5}{MO..SO}
$rrule['BYDAY'] = (1 + (int) ((date('d',$event['start'])-1) / 7)).
strtoupper(substr(date('l',$event['start']),0,2));
break;
}
if ($event['recur_interval'] > 1)
{
$rrule['INTERVAL'] = $event['recur_interval'];
}
if ($event['recur_enddate'])
{
// UNTIL should be a UTC timestamp
$rrule['UNTIL'] = $vcal->_exportDateTime($recur_enddate);
}
$attributes['RRULE'] = ''; $attributes['RRULE'] = '';
$parameters['RRULE'] = $rrule; $parameters['RRULE'] = $rrule;
} }

View File

@ -1,6 +1,6 @@
<?php <?php
/** /**
* eGroupWare - Calendar recurance rules * eGroupWare - Calendar recurrence rules
* *
* @link http://www.egroupware.org * @link http://www.egroupware.org
* @package calendar * @package calendar
@ -11,7 +11,7 @@
*/ */
/** /**
* Recurance rule iterator * Recurrence rule iterator
* *
* The constructor accepts times only as DateTime (or decendents like egw_date) to work timezone-correct. * The constructor accepts times only as DateTime (or decendents like egw_date) to work timezone-correct.
* The timezone of the event is determined by timezone of the startime, other times get converted to that timezone. * The timezone of the event is determined by timezone of the startime, other times get converted to that timezone.
@ -23,7 +23,7 @@
* *
* There's an interactive test-form, if the class get's called directly: http://localhost/egroupware/calendar/inc/class.calendar_rrule.inc.php * There's an interactive test-form, if the class get's called directly: http://localhost/egroupware/calendar/inc/class.calendar_rrule.inc.php
* *
* @todo Integrate iCal import and export, so all recurence code resides just in this class * @todo Integrate iCal import and export, so all recurrence code resides just in this class
* @todo Implement COUNT, can be stored in enddate assuming counts are far smaller then timestamps (eg. < 1000 is a count) * @todo Implement COUNT, can be stored in enddate assuming counts are far smaller then timestamps (eg. < 1000 is a count)
* @todo Implement WKST (week start day), currently WKST=SU is used (this is not stored in current DB schema, it's a user preference) * @todo Implement WKST (week start day), currently WKST=SU is used (this is not stored in current DB schema, it's a user preference)
*/ */
@ -67,6 +67,28 @@ class calendar_rrule implements Iterator
self::YEARLY => 'Yearly' self::YEARLY => 'Yearly'
); );
/**
* @var array $recur_egw2ical_2_0 converstaion of egw recur-type => ical FREQ
*/
static private $recur_egw2ical_2_0 = array(
self::DAILY => 'DAILY',
self::WEEKLY => 'WEEKLY',
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',
);
/**
* @var array $recur_egw2ical_1_0 converstaion of egw recur-type => ical FREQ
*/
static private $recur_egw2ical_1_0 = array(
self::DAILY => 'D',
self::WEEKLY => 'W',
self::MONTHLY_WDAY => 'MP', // BYDAY={1..7,-1}{MO..SO, last workday}
self::MONTHLY_MDAY => 'MD', // BYMONHTDAY={1..31,-1}
self::YEARLY => 'YM',
);
/** /**
* RRule type: NONE, DAILY, WEEKLY, MONTHLY_MDAY, MONTHLY_WDAY, YEARLY * RRule type: NONE, DAILY, WEEKLY, MONTHLY_MDAY, MONTHLY_WDAY, YEARLY
* *
@ -82,7 +104,7 @@ class calendar_rrule implements Iterator
public $interval = 1; public $interval = 1;
/** /**
* Number for monthly byday: 1, ..., 5, -1=last weekday of month * Number for monthly byday: 1, ..., 5, -1=last workday of month
* *
* EGroupware Calendar does NOT explicitly store it, it's only implicitly defined by series start date * EGroupware Calendar does NOT explicitly store it, it's only implicitly defined by series start date
* *
@ -163,6 +185,14 @@ class calendar_rrule implements Iterator
*/ */
public $current; public $current;
/**
* Last day of the week according to user preferences
*
* @var int
*/
protected $lastdayofweek;
/** /**
* Constructor * Constructor
* *
@ -178,6 +208,19 @@ class calendar_rrule implements Iterator
*/ */
public function __construct(DateTime $time,$type,$interval=1,DateTime $enddate=null,$weekdays=0,array $exceptions=null) public function __construct(DateTime $time,$type,$interval=1,DateTime $enddate=null,$weekdays=0,array $exceptions=null)
{ {
$weekdaystarts = $GLOBALS['egw_info']['user']['preferences']['calendar']['weekdaystarts'];
switch($weekdaystarts)
{
case 'Sunday':
$this->lastdayofweek = self::SATURDAY;
break;
case 'Saturday':
$this->lastdayofweek = self::FRIDAY;
break;
default: // Monday
$this->lastdayofweek = self::SUNDAY;
}
$this->time = $time; $this->time = $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)))
@ -306,8 +349,8 @@ class calendar_rrule implements Iterator
do do
{ {
// interval in weekly means event runs on valid days eg. each 2. week // interval in weekly means event runs on valid days eg. each 2. week
// --> on saturday we have to additionally advance interval-1 weeks // --> on the last day of the week we have to additionally advance interval-1 weeks
if ($this->interval > 1 && self::getWeekday($this->current) == self::SATURDAY) if ($this->interval > 1 && self::getWeekday($this->current) == $this->lastdayofweek)
{ {
$this->current->modify(($this->interval-1).' week'); $this->current->modify(($this->interval-1).' week');
} }
@ -466,6 +509,96 @@ class calendar_rrule implements Iterator
return $str; return $str;
} }
/**
* Generate a VEVENT RRULE
* @param string $version='1.0' could be '2.0', too
*/
public function generate_rrule($version='1.0')
{
static $utc;
$repeat_days = array();
$rrule = array();
if (is_null($utc))
{
$utc = calendar_timezones::DateTimeZone('UTC');
}
if ($this->type == self::NONE) return false; // no recuring event
if ($version == '1.0')
{
$rrule['FREQ'] = self::$recur_egw2ical_1_0[$this->type] . $this->interval;
switch ($this->type)
{
case self::WEEKLY:
foreach (self::$days as $mask => $label)
{
if ($this->weekdays & $mask)
{
$repeat_days[] = strtoupper(substr($label,0,2));
}
}
$rrule['BYDAY'] = implode(' ', $repeat_days);
$rrule['FREQ'] = $rrule['FREQ'].' '.$rrule['BYDAY'];
break;
case self::MONTHLY_MDAY: // date of the month: BYMONTDAY={1..31}
break;
case self::MONTHLY_WDAY: // weekday of the month: BDAY={1..5}{MO..SO}
$rrule['BYDAY'] = $this->monthly_byday_num .
strtoupper(substr($this->time->format('l'),0,2));
$rrule['FREQ'] = $rrule['FREQ'].' '.$rrule['BYDAY'];
break;
}
if (!$this->enddate)
{
$rrule['UNTIL'] = '#0';
}
}
else // $version == '2.0'
{
$rrule['FREQ'] = self::$recur_egw2ical_2_0[$this->type];
switch ($this->type)
{
case self::WEEKLY:
foreach (self::$days as $mask => $label)
{
if ($this->weekdays & $mask)
{
$repeat_days[] = strtoupper(substr($label,0,2));
}
}
$rrule['BYDAY'] = implode(',', $repeat_days);
break;
case self::MONTHLY_MDAY: // date of the month: BYMONTDAY={1..31}
$rrule['BYMONTHDAY'] = $this->monthly_bymonthday;
break;
case MCAL_RECUR_MONTHLY_WDAY: // weekday of the month: BDAY={1..5}{MO..SO}
$rrule['BYDAY'] = $this->monthly_byday_num .
strtoupper(substr($this->time->format('l'),0,2));
break;
}
if ($this->interval > 1)
{
$rrule['INTERVAL'] = $this->interval;
}
}
if ($this->enddate)
{
$enddate = clone $this->enddate;
$enddate->setTimezone($utc);
$rrule['UNTIL'] = $enddate->format('Ymd\THis\Z');
}
return $rrule;
}
/** /**
* Get instance for a given event array * Get instance for a given event array
* *