Back to index

d-push  2.0
Public Member Functions | Public Attributes
TaskRecurrence Class Reference
Inheritance diagram for TaskRecurrence:
Inheritance graph
[legend]
Collaboration diagram for TaskRecurrence:
Collaboration graph
[legend]

List of all members.

Public Member Functions

 TaskRecurrence ($store, $message)
 setRecurrence (&$recur)
 Function which saves recurrence and also regenerates task if necessary.
 setFirstOccurrence ()
 Sets task object to first occurrence if startdate/duedate of task object is different from first occurrence.
 moveToNextOccurrence ()
 Function which creates new task as current occurrence and moves the existing task to next occurrence.
 getNextOccurrence ()
 Function which return properties of next occurrence.
 regenerateTask ($markComplete)
 Function which clones current occurrence and sets appropriate properties.
 processOccurrenceItem (&$items, $start, $end, $now)
 processOccurrenceItem, adds an item to a list of occurrences, but only if the resulting occurrence starts or ends in the interval <$start, $end>
 markOccurrenceComplete (&$recur)
 Function which marks existing occurrence to 'Complete'.
 setReminder ($nextOccurrence)
 Function which sets reminder on recurring task after existing occurrence has been deleted or marked complete.
 deleteOccurrence ($action)
 Function which recurring task to next occurrence.
 windows1252_to_utf8 ($string)
 Convert from windows-1252 encoded string to UTF-8 string.
 BaseRecurrence ($store, $message)
 Constructor.
 getRecurrence ()
 getFullRecurrenceBlob ()
 parseRecurrence ($rdata)
 Function for parsing the Recurrence value of a Calendar item.
 saveRecurrence ()
 Saves the recurrence data to the recurrence property.
 recurDataToUnixData ($rdate)
 Function which converts a recurrence date timestamp to an unix date timestamp.
 unixDataToRecurData ($date)
 Function which converts an unix date timestamp to recurrence date timestamp.
 GetTZOffset ($ts)
 gmtime() doesn't exist in standard PHP, so we have to implement it ourselves
 gmtime ($time)
 gmtime() doesn't exist in standard PHP, so we have to implement it ourselves
 isLeapYear ($year)
 getMonthInSeconds ($year, $month)
 getDateByYearMonthWeekDayHour ($year, $month, $week, $day, $hour)
 Function to get a date by Year Nr, Month Nr, Week Nr, Day Nr, and hour.
 getTimezone ($tz, $date)
 getTimezone gives the timezone offset (in minutes) of the given local date/time according to the given TZ info
 getWeekNr ($date)
 getWeekNr() returns the week nr of the month (ie first week of february is 1)
 parseTimezone ($data)
 parseTimezone parses the timezone as specified in named property 0x8233 in Outlook calendar messages.
 getTimezoneData ($tz)
 createTimezone ($tz)
 createTimezone creates the timezone as specified in the named property 0x8233 see also parseTimezone() $tz is an array with the timezone data
 toGMT ($tz, $date)
 toGMT returns a timestamp in GMT time for the time and timezone given
 fromGMT ($tz, $date)
 fromGMT returns a timestamp in the local timezone given from the GMT time given
 dayStartOf ($date)
 Function to get timestamp of the beginning of the day of the timestamp given.
 monthStartOf ($date)
 Function to get timestamp of the beginning of the month of the timestamp given.
 yearStartOf ($date)
 Function to get timestamp of the beginning of the year of the timestamp given.
 getItems ($start, $end, $limit=0, $remindersonly=false)
 Function which returns the items in a given interval.
 sortStarttime ($a, $b)
 daysInMonth ($date, $months)
 daysInMonth
 monthOfYear ($minutes)
 sortExceptionStart ($a, $b)

Public Attributes

 $tz = false
 Timezone info which is always false for task.
 $store
 $message
 $messageprops
 $proptags
 $recur

Detailed Description

Definition at line 54 of file class.taskrecurrence.php.


Member Function Documentation

BaseRecurrence::BaseRecurrence ( store,
message 
) [inherited]

Constructor.

Parameters:
resource$storeMAPI Message Store Object
resource$messagethe MAPI (appointment) message
array$propertiesthe list of MAPI properties the message has.

Definition at line 94 of file class.baserecurrence.php.

        {
            $this->store = $store;

            if(is_array($message)) {
                $this->messageprops = $message;
            } else {
                $this->message = $message;
                $this->messageprops = mapi_getprops($this->message, $this->proptags);
            }

            if(isset($this->messageprops[$this->proptags["recurring_data"]])) {
                // There is a possibility that recurr blob can be more than 255 bytes so get full blob through stream interface
                if (strlen($this->messageprops[$this->proptags["recurring_data"]]) >= 255) {
                    $this->getFullRecurrenceBlob();
                }

                $this->recur = $this->parseRecurrence($this->messageprops[$this->proptags["recurring_data"]]);
            }
            if(isset($this->proptags["timezone_data"]) && isset($this->messageprops[$this->proptags["timezone_data"]])) {
                $this->tz = $this->parseTimezone($this->messageprops[$this->proptags["timezone_data"]]);
            }
        }

Here is the call graph for this function:

Here is the caller graph for this function:

BaseRecurrence::createTimezone ( tz) [inherited]

createTimezone creates the timezone as specified in the named property 0x8233 see also parseTimezone() $tz is an array with the timezone data

Definition at line 1570 of file class.baserecurrence.php.

        {
            $data = pack("lxxxxlxxxxlvvxxxxxxxxxxlvvxxxxxx",
                        $tz["timezone"],
                        array_key_exists("timezonedst",$tz)?$tz["timezonedst"]:0,
                        array_key_exists("dstendmonth",$tz)?$tz["dstendmonth"]:0,
                        array_key_exists("dstendweek",$tz)?$tz["dstendweek"]:0,
                        array_key_exists("dstendhour",$tz)?$tz["dstendhour"]:0,
                        array_key_exists("dststartmonth",$tz)?$tz["dststartmonth"]:0,
                        array_key_exists("dststartweek",$tz)?$tz["dststartweek"]:0,
                        array_key_exists("dststarthour",$tz)?$tz["dststarthour"]:0
                    );

            return $data;
        }
BaseRecurrence::daysInMonth ( date,
months 
) [inherited]

daysInMonth

Returns the number of days in the upcoming number of months. If you specify 1 month as $months it will give you the number of days in the month of $date. If you specify more it will also count the days in the upcomming months and add that to the number of days. So if you have a date in march and you specify $months as 2 it will return 61.

Parameters:
Integer$dateSpecified date as timestamp from which you want to know the number of days in the month.
Integer$monthsNumber of months you want to know the number of days in.
Returns:
Integer Number of days in the specified amount of months.

Definition at line 1919 of file class.baserecurrence.php.

                                             {
            $days = 0;

            for($i=0;$i<$months;$i++) {
                $days += date("t", $date + $days * 24 * 60 * 60);
            }

            return $days;
        }

Here is the caller graph for this function:

BaseRecurrence::dayStartOf ( date) [inherited]

Function to get timestamp of the beginning of the day of the timestamp given.

Parameters:
date$date
Returns:
date timestamp referring to same day but at 00:00:00

Definition at line 1611 of file class.baserecurrence.php.

        {
            $time1 = $this->gmtime($date);

            return gmmktime(0, 0, 0, $time1["tm_mon"] + 1, $time1["tm_mday"], $time1["tm_year"] + 1900);
        }

Here is the call graph for this function:

Here is the caller graph for this function:

Function which recurring task to next occurrence.

It simply doesn't regenerate task

Parameters:
array$action

Definition at line 436 of file class.taskrecurrence.php.

        {
            $this->tz = false;
            $this->action = $action;
            $result = $this->moveToNextOccurrence();

            mapi_savechanges($this->message);

            return $result;
        }

Here is the call graph for this function:

BaseRecurrence::fromGMT ( tz,
date 
) [inherited]

fromGMT returns a timestamp in the local timezone given from the GMT time given

Definition at line 1600 of file class.baserecurrence.php.

                                     {
            $offset = $this->getTimezone($tz, $date);

            return $date - $offset * 60;
        }

Here is the call graph for this function:

Here is the caller graph for this function:

BaseRecurrence::getDateByYearMonthWeekDayHour ( year,
month,
week,
day,
hour 
) [inherited]

Function to get a date by Year Nr, Month Nr, Week Nr, Day Nr, and hour.

Parameters:
int$year
int$month
int$week
int$day
int$hour
Returns:
returns the timestamp of the given date, timezone-independant

Definition at line 1474 of file class.baserecurrence.php.

        {
            // get first day of month
            $date = gmmktime(0,0,0,$month,0,$year + 1900);

            // get wday info
            $gmdate = $this->gmtime($date);

            $date -= $gmdate["tm_wday"] * 24 * 60 * 60; // back up to start of week

            $date += $week * 7 * 24 * 60 * 60; // go to correct week nr
            $date += $day * 24 * 60 * 60;
            $date += $hour * 60 * 60;

            $gmdate = $this->gmtime($date);

            // if we are in the next month, then back up a week, because week '5' means
            // 'last week of month'

            if($gmdate["tm_mon"]+1 != $month)
                $date -= 7 * 24 * 60 * 60;

            return $date;
        }

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 123 of file class.baserecurrence.php.

        {
            $message = mapi_msgstore_openentry($this->store, $this->messageprops[PR_ENTRYID]);

            $recurrBlob = '';
            $stream = mapi_openproperty($message, $this->proptags["recurring_data"], IID_IStream, 0, 0);
            $stat = mapi_stream_stat($stream);

            for ($i = 0; $i < $stat['cb']; $i += 1024) {
                $recurrBlob .= mapi_stream_read($stream, 1024);
            }

            if (!empty($recurrBlob)) {
                $this->messageprops[$this->proptags["recurring_data"]] = $recurrBlob;
            }
        }

Here is the caller graph for this function:

BaseRecurrence::getItems ( start,
end,
limit = 0,
remindersonly = false 
) [inherited]

Function which returns the items in a given interval.

This included expansion of the recurrence and processing of exceptions (modified and deleted).

Parameters:
string$entryidthe entryid of the message
array$propsthe properties of the message
date$startstart time of the interval (GMT)
date$endend time of the interval (GMT)

checks weather the next coming day in recurrence pattern is less than or equal to end day of the * recurring item.Also check weather the coming day in recurrence pattern is greater than or equal to start * of recurring pattern, so that appointment that fall under the recurrence range are only displayed.

Definition at line 1652 of file class.baserecurrence.php.

        {
            $items = array();

            if(isset($this->recur)) {

                // Optimization: remindersonly and default reminder is off; since only exceptions with reminder set will match, just look which
                // exceptions are in range and have a reminder set
                if($remindersonly && (!isset($this->messageprops[$this->proptags["reminder"]]) || $this->messageprops[$this->proptags["reminder"]] == false)) {
                    // Sort exceptions by start time
                    uasort($this->recur["changed_occurences"], array($this, "sortExceptionStart"));

                    // Loop through all changed exceptions
                    foreach($this->recur["changed_occurences"] as $exception) {
                        // Check reminder set
                        if(!isset($exception["reminder"]) || $exception["reminder"] == false)
                            continue;

                        // Convert to GMT
                        $occstart = $this->toGMT($this->tz, $exception["start"]); // seb changed $tz to $this->tz
                        $occend = $this->toGMT($this->tz, $exception["end"]); // seb changed $tz to $this->tz

                        // Check range criterium
                        if($occstart > $end || $occend < $start)
                            continue;

                        // OK, add to items.
                        array_push($items, $this->getExceptionProperties($exception));
                        if($limit && (count($items) == $limit))
                            break;
                    }

                    uasort($items, array($this, "sortStarttime"));

                    return $items;
                }

                // From here on, the dates of the occurrences are calculated in local time, so the days we're looking
                // at are calculated from the local time dates of $start and $end

                if ($this->recur['regen'] && isset($this->action['datecompleted'])) {
                    $daystart = $this->dayStartOf($this->action['datecompleted']);
                } else {
                    $daystart = $this->dayStartOf($this->recur["start"]); // start on first day of occurrence
                }

                // Calculate the last day on which we want to be looking at a recurrence; this is either the end of the view
                // or the end of the recurrence, whichever comes first
                if($end > $this->toGMT($this->tz, $this->recur["end"])) {
                    $rangeend = $this->toGMT($this->tz, $this->recur["end"]);
                } else {
                    $rangeend = $end;
                }

                $dayend = $this->dayStartOf($this->fromGMT($this->tz, $rangeend));

                // Loop through the entire recurrence range of dates, and check for each occurrence whether it is in the view range.

                switch($this->recur["type"])
                {
                case 10:
                    // Daily
                    if($this->recur["everyn"] <= 0)
                        $this->recur["everyn"] = 1440;

                    if($this->recur["subtype"] == 0) {
                        // Every Nth day
                        for($now = $daystart; $now <= $dayend && ($limit == 0 || count($items) < $limit); $now += 60 * $this->recur["everyn"]) {
                            $this->processOccurrenceItem($items, $start, $end, $now, $this->recur["startocc"], $this->recur["endocc"], $this->tz, $remindersonly);
                        }
                    } else {
                        // Every workday
                        for($now = $daystart; $now <= $dayend && ($limit == 0 || count($items) < $limit); $now += 60 * 1440)
                        {
                            $nowtime = $this->gmtime($now);
                            if ($nowtime["tm_wday"] > 0 && $nowtime["tm_wday"] < 6) { // only add items in the given timespace
                                $this->processOccurrenceItem($items, $start, $end, $now, $this->recur["startocc"], $this->recur["endocc"], $this->tz, $remindersonly);
                            }
                        }
                    }
                    break;
                case 11:
                    // Weekly
                    if($this->recur["everyn"] <= 0)
                        $this->recur["everyn"] = 1;

                    // If sliding flag is set then move to 'n' weeks
                    if ($this->recur['regen']) $daystart += (60 * 60 * 24 * 7 * $this->recur["everyn"]);

                    for($now = $daystart; $now <= $dayend && ($limit == 0 || count($items) < $limit); $now += (60 * 60 * 24 * 7 * $this->recur["everyn"]))
                    {
                        if ($this->recur['regen']) {
                            $this->processOccurrenceItem($items, $start, $end, $now, $this->recur["startocc"], $this->recur["endocc"], $this->tz, $remindersonly);
                        } else {
                            // Loop through the whole following week to the first occurrence of the week, add each day that is specified
                            for($wday = 0; $wday < 7; $wday++)
                            {
                                $daynow = $now + $wday * 60 * 60 * 24;
                                //checks weather the next coming day in recurring pattern is less than or equal to end day of the recurring item
                                if ($daynow <= $dayend){
                                    $nowtime = $this->gmtime($daynow); // Get the weekday of the current day
                                    if(($this->recur["weekdays"] &(1 << $nowtime["tm_wday"]))) { // Selected ?
                                        $this->processOccurrenceItem($items, $start, $end, $daynow, $this->recur["startocc"], $this->recur["endocc"], $this->tz, $remindersonly);
                                    }
                                }
                            }
                        }
                    }
                    break;
                case 12:
                    // Monthly
                    if($this->recur["everyn"] <= 0)
                        $this->recur["everyn"] = 1;

                    // Loop through all months from start to end of occurrence, starting at beginning of first month
                    for($now = $this->monthStartOf($daystart); $now <= $dayend && ($limit == 0 || count($items) < $limit); $now += $this->daysInMonth($now, $this->recur["everyn"]) * 24 * 60 * 60 )
                    {
                        if(isset($this->recur["monthday"]) &&($this->recur['monthday'] != "undefined") && !$this->recur['regen']) { // Day M of every N months
                            $difference = 1;
                            if ($this->daysInMonth($now, $this->recur["everyn"]) < $this->recur["monthday"]) {
                                $difference = $this->recur["monthday"] - $this->daysInMonth($now, $this->recur["everyn"]) + 1;
                            }
                            $daynow = $now + (($this->recur["monthday"] - $difference) * 24 * 60 * 60);
                            //checks weather the next coming day in recurrence pattern is less than or equal to end day of the recurring item
                            if ($daynow <= $dayend){
                                $this->processOccurrenceItem($items, $start, $end, $daynow, $this->recur["startocc"], $this->recur["endocc"], $this->tz, $remindersonly);
                            }
                        }
                        else if(isset($this->recur["nday"]) && isset($this->recur["weekdays"])) { // Nth [weekday] of every N months
                            // Sanitize input
                            if($this->recur["weekdays"] == 0)
                                $this->recur["weekdays"] = 1;

                            // If nday is not set to the last day in the month
                            if ($this->recur["nday"] < 5) {
                                // keep the track of no. of time correct selection pattern(like 2nd weekday, 4th fiday, etc.)is matched
                                $ndaycounter = 0;
                                // Find matching weekday in this month
                                for($day = 0; $day < $this->daysInMonth($now, 1); $day++)
                                {
                                    $daynow = $now + $day * 60 * 60 * 24;
                                    $nowtime = $this->gmtime($daynow); // Get the weekday of the current day

                                    if($this->recur["weekdays"] & (1 << $nowtime["tm_wday"])) { // Selected ?
                                        $ndaycounter ++;
                                    }
                                    // check the selected pattern is same as asked Nth weekday,If so set the firstday
                                    if($this->recur["nday"] == $ndaycounter){
                                        $firstday = $day;
                                        break;
                                    }
                                }
                                // $firstday is the day of the month on which the asked pattern of nth weekday matches
                                $daynow = $now + $firstday * 60 * 60 * 24;
                            }else{
                                // Find last day in the month ($now is the firstday of the month)
                                $NumDaysInMonth =  $this->daysInMonth($now, 1);
                                $daynow = $now + (($NumDaysInMonth-1) * 24*60*60);

                                $nowtime = $this->gmtime($daynow);
                                while (($this->recur["weekdays"] & (1 << $nowtime["tm_wday"]))==0){
                                    $daynow -= 86400;
                                    $nowtime = $this->gmtime($daynow);
                                }
                            }

                            if ($daynow <= $dayend && $daynow >= $daystart){
                                $this->processOccurrenceItem($items, $start, $end, $daynow, $this->recur["startocc"], $this->recur["endocc"], $this->tz , $remindersonly);
                            }
                        } else if ($this->recur['regen']) {
                            $next_month_start = $now + ($this->daysInMonth($now, 1) * 24 * 60 * 60);
                            $now = $daystart +($this->daysInMonth($next_month_start, $this->recur['everyn']) * 24 * 60 * 60);

                            if ($now <= $dayend) {
                                $this->processOccurrenceItem($items, $daystart, $end, $now, $this->recur["startocc"], $this->recur["endocc"], $this->tz, $remindersonly);
                            }
                        }
                    }
                    break;
                case 13:
                    // Yearly
                    if($this->recur["everyn"] <= 0)
                        $this->recur["everyn"] = 12;

                    for($now = $this->yearStartOf($daystart); $now <= $dayend && ($limit == 0 || count($items) < $limit); $now += $this->daysInMonth($now, $this->recur["everyn"]) * 24 * 60 * 60 )
                    {
                        if(isset($this->recur["monthday"]) && !$this->recur['regen']) { // same as monthly, but in a specific month
                            // recur["month"] is in minutes since the beginning of the year
                            $month = $this->monthOfYear($this->recur["month"]); // $month is now month of year [0..11]
                            $monthday = $this->recur["monthday"]; // $monthday is day of the month [1..31]
                            $monthstart = $now + $this->daysInMonth($now, $month) * 24 * 60 * 60; // $monthstart is the timestamp of the beginning of the month
                            if($monthday > $this->daysInMonth($monthstart, 1))
                                $monthday = $this->daysInMonth($monthstart, 1);    // Cap $monthday on month length (eg 28 feb instead of 29 feb)
                            $daynow = $monthstart + ($monthday-1) * 24 * 60 * 60;
                            $this->processOccurrenceItem($items, $start, $end, $daynow, $this->recur["startocc"], $this->recur["endocc"], $this->tz, $remindersonly);
                        }
                        else if(isset($this->recur["nday"]) && isset($this->recur["weekdays"])) { // Nth [weekday] in month X of every N years

                            // Go the correct month
                            $monthnow = $now + $this->daysInMonth($now, $this->monthOfYear($this->recur["month"])) * 24 * 60 * 60;

                            // Find first matching weekday in this month
                            for($wday = 0; $wday < 7; $wday++)
                            {
                                $daynow = $monthnow + $wday * 60 * 60 * 24;
                                $nowtime = $this->gmtime($daynow); // Get the weekday of the current day

                                if($this->recur["weekdays"] & (1 << $nowtime["tm_wday"])) { // Selected ?
                                    $firstday = $wday;
                                    break;
                                }
                            }

                            // Same as above (monthly)
                            $daynow = $monthnow + ($firstday + ($this->recur["nday"]-1)*7) * 60 * 60 * 24;

                            while($this->monthStartOf($daynow) != $this->monthStartOf($monthnow)) {
                                $daynow -= 7 * 60 * 60 * 24;
                            }

                            $this->processOccurrenceItem($items, $start, $end, $daynow, $this->recur["startocc"], $this->recur["endocc"], $this->tz, $remindersonly);
                        } else if ($this->recur['regen']) {
                            $year_starttime = $this->gmtime($now);
                            $is_next_leapyear = $this->isLeapYear($year_starttime['tm_year'] + 1900 + 1);    // +1 next year
                            $now = $daystart + ($is_next_leapyear ? 31622400 /* Leap year in seconds */ : 31536000 /*year in seconds*/);

                            if ($now <= $dayend) {
                                $this->processOccurrenceItem($items, $daystart, $end, $now, $this->recur["startocc"], $this->recur["endocc"], $this->tz, $remindersonly);
                            }
                        }
                    }
                }
                //to get all exception items
                if (!empty($this->recur['changed_occurences']))
                    $this->processExceptionItems($items, $start, $end);
            }

            // sort items on starttime
            usort($items, array($this, "sortStarttime"));

            // Return the MAPI-compatible list of items for this object
            return $items;
        }

Here is the call graph for this function:

Here is the caller graph for this function:

BaseRecurrence::getMonthInSeconds ( year,
month 
) [inherited]

Definition at line 1451 of file class.baserecurrence.php.

        {
            if( in_array($month, array(1,3,5,7,8,10,12) ) ) {
                $day = 31;
            } else if( in_array($month, array(4,6,9,11) ) ) {
                $day = 30;
            } else {
                $day = 28;
                if( $this->isLeapYear($year) == 1 )
                    $day++;
            }
            return $day * 24 * 60 * 60;
        }

Here is the call graph for this function:

Here is the caller graph for this function:

Function which return properties of next occurrence.

Returns:
array startdate/enddate of next occurrence

Definition at line 241 of file class.taskrecurrence.php.

        {
            if ($this->recur) {
                $items = array();

                //@TODO: fix start of range
                $start = isset($this->messageprops[$this->proptags["duedate"]]) ? $this->messageprops[$this->proptags["duedate"]] : $this->action['start'];
                $dayend = ($this->recur['term'] == 0x23) ? 0x7fffffff : $this->dayStartOf($this->recur["end"]);

                // Fix recur object
                $this->recur['startocc'] = 0;
                $this->recur['endocc'] = 0;

                // Retrieve next occurrence
                $items = $this->getItems($start, $dayend, 1);

                return !empty($items) ? $items[0] : false;
            }
        }

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 118 of file class.baserecurrence.php.

        {
            return $this->recur;
        }
BaseRecurrence::getTimezone ( tz,
date 
) [inherited]

getTimezone gives the timezone offset (in minutes) of the given local date/time according to the given TZ info

Definition at line 1503 of file class.baserecurrence.php.

        {
            // No timezone -> GMT (+0)
            if(!isset($tz["timezone"]))
                return 0;

            $dst = false;
            $gmdate = $this->gmtime($date);

            $dststart = $this->getDateByYearMonthWeekDayHour($gmdate["tm_year"], $tz["dststartmonth"], $tz["dststartweek"], 0, $tz["dststarthour"]);
            $dstend = $this->getDateByYearMonthWeekDayHour($gmdate["tm_year"], $tz["dstendmonth"], $tz["dstendweek"], 0, $tz["dstendhour"]);

            if($dststart <= $dstend) {
                // Northern hemisphere, eg DST is during Mar-Oct
                if($date > $dststart && $date < $dstend) {
                    $dst = true;
                }
            } else {
                // Southern hemisphere, eg DST is during Oct-Mar
                if($date < $dstend || $date > $dststart) {
                    $dst = true;
                }
            }

            if($dst) {
                return $tz["timezone"] + $tz["timezonedst"];
            } else {
                return $tz["timezone"];
            }
        }

Here is the call graph for this function:

Here is the caller graph for this function:

BaseRecurrence::getTimezoneData ( tz) [inherited]

Definition at line 1558 of file class.baserecurrence.php.

        {
            $data = pack("lllllvvllvlvvlv", $tz["timezone"], 0, $tz["timezonedst"], 0, $tz["dstendmonth"], $tz["dstendweek"], $tz["dstendhour"], 0, 0, 0, $tz["dststartmonth"], $tz["dststartweek"], $tz["dststarthour"], 0 ,0);

            return $data;
        }

Here is the caller graph for this function:

BaseRecurrence::GetTZOffset ( ts) [inherited]

gmtime() doesn't exist in standard PHP, so we have to implement it ourselves

Author:
Steve Hardy

Definition at line 1420 of file class.baserecurrence.php.

        {
            $Offset = date("O", $ts);

            $Parity = $Offset < 0 ? -1 : 1;
            $Offset = $Parity * $Offset;
            $Offset = ($Offset - ($Offset % 100)) / 100 * 60 + $Offset % 100;

            return $Parity * $Offset;
        }

Here is the caller graph for this function:

BaseRecurrence::getWeekNr ( date) [inherited]

getWeekNr() returns the week nr of the month (ie first week of february is 1)

Definition at line 1537 of file class.baserecurrence.php.

        {
            $gmdate = gmtime($date);
            $gmdate["tm_mday"] = 0;
            return strftime("%W", $date) - strftime("%W", gmmktime($gmdate)) + 1;
        }

Here is the call graph for this function:

BaseRecurrence::gmtime ( time) [inherited]

gmtime() doesn't exist in standard PHP, so we have to implement it ourselves

Author:
Steve Hardy
Parameters:
Date$time
Returns:
Date GMT Time

Definition at line 1437 of file class.baserecurrence.php.

        {
            $TZOffset = $this->GetTZOffset($time);

            $t_time = $time - $TZOffset * 60; #Counter adjust for localtime()
            $t_arr = localtime($t_time, 1);

            return $t_arr;
        }

Here is the call graph for this function:

Here is the caller graph for this function:

BaseRecurrence::isLeapYear ( year) [inherited]

Definition at line 1447 of file class.baserecurrence.php.

                                   {
            return ( $year % 4 == 0 && ($year % 100 != 0 || $year % 400 == 0) );
        }

Here is the caller graph for this function:

Function which marks existing occurrence to 'Complete'.

Parameters:
array$recurarray action from client
Returns:
array of properties of regenerated task else false

Definition at line 381 of file class.taskrecurrence.php.

        {
            // Fix timezone object
            $this->tz = false;
            $this->action =& $recur;
            $dead_occurrence = isset($this->messageprops[$this->proptags['dead_occurrence']]) ? $this->messageprops[$this->proptags['dead_occurrence']] : false;

            if (!$dead_occurrence) {
                return $this->moveToNextOccurrence();
            }

            return false;
        }

Here is the call graph for this function:

BaseRecurrence::monthOfYear ( minutes) [inherited]

Definition at line 1930 of file class.baserecurrence.php.

                                       {
            $d = gmmktime(0,0,0,1,1,2001); // The year 2001 was a non-leap year, and the minutes provided are always in non-leap-year-minutes

            $d += $minutes*60;

            $dtime = $this->gmtime($d);

            return $dtime["tm_mon"];
        }

Here is the call graph for this function:

Here is the caller graph for this function:

BaseRecurrence::monthStartOf ( date) [inherited]

Function to get timestamp of the beginning of the month of the timestamp given.

Parameters:
date$date
Returns:
date Timestamp referring to same month but on the first day, and at 00:00:00

Definition at line 1623 of file class.baserecurrence.php.

        {
            $time1 = $this->gmtime($date);

            return gmmktime(0, 0, 0, $time1["tm_mon"] + 1, 1, $time1["tm_year"] + 1900);
        }

Here is the call graph for this function:

Here is the caller graph for this function:

Function which creates new task as current occurrence and moves the existing task to next occurrence.

Parameters:
array$recur$action from client
Returns:
boolean if moving to next occurrence succeed then it returns properties of either newly created task or existing task ELSE false because that was last occurrence

Every recurring task should have a 'duedate'. If a recurring task is created with no start/end date then we create first two occurrence separately and for first occurrence recurrence has ended.

Definition at line 179 of file class.taskrecurrence.php.

        {
            $result = false;
            if ((empty($this->action['startdate']) && empty($this->action['duedate']))
                || ($this->action['complete'] == 1) || (isset($this->action['deleteOccurrence']) && $this->action['deleteOccurrence'])){

                $nextOccurrence = $this->getNextOccurrence();
                $result = mapi_getprops($this->message, array(PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID));

                $props = array();
                if ($nextOccurrence) {
                    if (!isset($this->action['deleteOccurrence'])) {
                        // Create current occurrence as separate task
                        $result = $this->regenerateTask($this->action['complete']);
                    }

                    // Set reminder for next occurrence
                    $this->setReminder($nextOccurrence);

                    // Update properties for next occurrence
                    $this->action['duedate'] = $props[$this->proptags['duedate']] = $nextOccurrence[$this->proptags['duedate']];
                    $this->action['commonend'] = $props[$this->proptags['commonend']] = $nextOccurrence[$this->proptags['duedate']];

                    $this->action['startdate'] = $props[$this->proptags['startdate']] = $nextOccurrence[$this->proptags['startdate']];
                    $this->action['commonstart'] = $props[$this->proptags['commonstart']] = $nextOccurrence[$this->proptags['startdate']];

                    // If current task as been mark as 'Complete' then next occurrence should be uncomplete.
                    if (isset($this->action['complete']) && $this->action['complete'] == 1) {
                        $this->action['status'] = $props[$this->proptags["status"]] = olTaskNotStarted;
                        $this->action['complete'] = $props[$this->proptags["complete"]] = false;
                        $this->action['percent_complete'] = $props[$this->proptags["percent_complete"]] = 0;
                    }

                    $props[$this->proptags["dead_occurrence"]] = false;
                } else {
                    if (isset($this->action['deleteOccurrence']) && $this->action['deleteOccurrence'])
                        return false;

                    // Didn't get next occurrence, probably this is the last one, so recurrence ends here
                    $props[$this->proptags["dead_occurrence"]] = true;
                    $props[$this->proptags["datecompleted"]] = $this->action['datecompleted'];
                    $props[$this->proptags["task_f_creator"]] = true;

                    //OL props
                    $props[$this->proptags["side_effects"]] = 1296;
                    $props[$this->proptags["icon_index"]] = 1280;
                }

                mapi_setprops($this->message, $props);
            }

            return $result;
        }

Here is the call graph for this function:

Here is the caller graph for this function:

BaseRecurrence::parseRecurrence ( rdata) [inherited]

Function for parsing the Recurrence value of a Calendar item.

Retrieve it from Named Property 0x8216 as a PT_BINARY and pass the data to this function

Returns a structure containing the data:

type - type of recurrence: day=10, week=11, month=12, year=13 subtype - type of day recurrence: 2=monthday (ie 21st day of month), 3=nday'th weekdays (ie. 2nd Tuesday and Wednesday) start - unix timestamp of first occurrence end - unix timestamp of last occurrence (up to and including), so when start == end -> occurrences = 1 numoccur - occurrences (may be very large when there is no end data)

then, for each type:

Daily: everyn - every [everyn] days in minutes regen - regenerating event (like tasks)

Weekly: everyn - every [everyn] weeks in weeks regen - regenerating event (like tasks) weekdays - bitmask of week days, where each bit is one weekday (weekdays & 1 = Sunday, weekdays & 2 = Monday, etc)

Monthly: everyn - every [everyn] months regen - regenerating event (like tasks)

subtype 2: monthday - on day [monthday] of the month

subtype 3: weekdays - bitmask of week days, where each bit is one weekday (weekdays & 1 = Sunday, weekdays & 2 = Monday, etc) nday - on [nday]'th [weekdays] of the month

Yearly: everyn - every [everyn] months (12, 24, 36, ...) month - in month month regen - regenerating event (like tasks)

subtype 2: monthday - on day [monthday] of the month

subtype 3: weekdays - bitmask of week days, where each bit is one weekday (weekdays & 1 = Sunday, weekdays & 2 = Monday, etc) nday - on [nday]'th [weekdays] of the month [month]

Parameters:
string$rdataBinary string
Returns:
array recurrence data.

We now have $exc_changed, $exc_base_dates and $exc_changed_details We will ignore $exc_changed, as this information is available in $exc_changed_details also. If an item is in $exc_base_dates and NOT in $exc_changed_details, then the item has been deleted.

Definition at line 190 of file class.baserecurrence.php.

        {
            if (strlen($rdata) < 10) {
                return;
            }

            $ret["changed_occurences"] = array();
            $ret["deleted_occurences"] = array();

            $data = unpack("Vconst1/Crtype/Cconst2/Vrtype2", $rdata);

            $ret["type"] = $data["rtype"];
            $ret["subtype"] = $data["rtype2"];
            $rdata = substr($rdata, 10);

            switch ($data["rtype"])
            {
                case 0x0a:
                    // Daily
                    if (strlen($rdata) < 12) {
                        return $ret;
                    }

                    $data = unpack("Vunknown/Veveryn/Vregen", $rdata);
                    $ret["everyn"] = $data["everyn"];
                    $ret["regen"] = $data["regen"];

                    switch($ret["subtype"])
                    {
                        case 0:
                            $rdata = substr($rdata, 12);
                            break;
                        case 1:
                            $rdata = substr($rdata, 16);
                            break;
                    }

                    break;

                case 0x0b:
                    // Weekly
                    if (strlen($rdata) < 16) {
                        return $ret;
                    }

                    $data = unpack("Vconst1/Veveryn/Vregen", $rdata);
                    $rdata = substr($rdata, 12);

                    $ret["everyn"] = $data["everyn"];
                    $ret["regen"] = $data["regen"];
                    $ret["weekdays"] = 0;

                    if ($data["regen"] == 0) {
                        $data = unpack("Vweekdays", $rdata);
                        $rdata = substr($rdata, 4);

                        $ret["weekdays"] = $data["weekdays"];
                    }
                    break;

                case 0x0c:
                    // Monthly
                    if (strlen($rdata) < 16) {
                        return $ret;
                    }

                    $data = unpack("Vconst1/Veveryn/Vregen/Vmonthday", $rdata);

                    $ret["everyn"] = $data["everyn"];
                    $ret["regen"] = $data["regen"];

                    if ($ret["subtype"] == 3) {
                        $ret["weekdays"] = $data["monthday"];
                    } else {
                        $ret["monthday"] = $data["monthday"];
                    }

                    $rdata = substr($rdata, 16);

                    if ($ret["subtype"] == 3) {
                        $data = unpack("Vnday", $rdata);
                        $ret["nday"] = $data["nday"];
                        $rdata = substr($rdata, 4);
                    }
                    break;

                case 0x0d:
                    // Yearly
                    if (strlen($rdata) < 16)
                        return $ret;

                    $data = unpack("Vmonth/Veveryn/Vregen/Vmonthday", $rdata);

                    $ret["month"] = $data["month"];
                    $ret["everyn"] = $data["everyn"];
                    $ret["regen"] = $data["regen"];

                    if ($ret["subtype"] == 3) {
                        $ret["weekdays"] = $data["monthday"];
                    } else {
                        $ret["monthday"] = $data["monthday"];
                    }

                    $rdata = substr($rdata, 16);

                    if ($ret["subtype"] == 3) {
                        $data = unpack("Vnday", $rdata);
                        $ret["nday"] = $data["nday"];
                        $rdata = substr($rdata, 4);
                    }
                    break;
            }

            if (strlen($rdata) < 16) {
                return $ret;
            }

            $data = unpack("Cterm/C3const1/Vnumoccur/Vconst2/Vnumexcept", $rdata);

            $rdata = substr($rdata, 16);

            $ret["term"] = $data["term"];
            $ret["numoccur"] = $data["numoccur"];
            $ret["numexcept"] = $data["numexcept"];

            // exc_base_dates are *all* the base dates that have been either deleted or modified
            $exc_base_dates = array();
            for($i = 0; $i < $ret["numexcept"]; $i++)
            {
                if (strlen($rdata) < 4) {
                    // We shouldn't arrive here, because that implies
                    // numexcept does not match the amount of data
                    // which is available for the exceptions.
                    return $ret;
                }
                $data = unpack("Vbasedate", $rdata);
                $rdata = substr($rdata, 4);
                $exc_base_dates[] = $this->recurDataToUnixData($data["basedate"]);
            }

            if (strlen($rdata) < 4) {
                return $ret;
            }

            $data = unpack("Vnumexceptmod", $rdata);
            $rdata = substr($rdata, 4);

            $ret["numexceptmod"] = $data["numexceptmod"];

            // exc_changed are the base dates of *modified* occurrences. exactly what is modified
            // is in the attachments *and* in the data further down this function.
            $exc_changed = array();
            for($i = 0; $i < $ret["numexceptmod"]; $i++)
            {
                if (strlen($rdata) < 4) {
                    // We shouldn't arrive here, because that implies
                    // numexceptmod does not match the amount of data
                    // which is available for the exceptions.
                    return $ret;
                }
                $data = unpack("Vstartdate", $rdata);
                $rdata = substr($rdata, 4);
                $exc_changed[] = $this->recurDataToUnixData($data["startdate"]);
            }

            if (strlen($rdata) < 8) {
                return $ret;
            }

            $data = unpack("Vstart/Vend", $rdata);
            $rdata = substr($rdata, 8);

            $ret["start"] = $this->recurDataToUnixData($data["start"]);
            $ret["end"] = $this->recurDataToUnixData($data["end"]);

            // this is where task recurrence stop
            if (strlen($rdata) < 16) {
                return $ret;
            }

            $data = unpack("Vreaderversion/Vwriterversion/Vstartmin/Vendmin", $rdata);
            $rdata = substr($rdata, 16);

            $ret["startocc"] = $data["startmin"];
            $ret["endocc"] = $data["endmin"];
            $readerversion = $data["readerversion"];
            $writerversion = $data["writerversion"];

            $data = unpack("vnumber", $rdata);
            $rdata = substr($rdata, 2);

            $nexceptions = $data["number"];
            $exc_changed_details = array();

            // Parse n modified exceptions
            for($i=0;$i<$nexceptions;$i++)
            {
                $item = array();

                // Get exception startdate, enddate and basedate (the date at which the occurrence would have started)
                $data = unpack("Vstartdate/Venddate/Vbasedate", $rdata);
                $rdata = substr($rdata, 12);

                // Convert recurtimestamp to unix timestamp
                $startdate = $this->recurDataToUnixData($data["startdate"]);
                $enddate = $this->recurDataToUnixData($data["enddate"]);
                $basedate = $this->recurDataToUnixData($data["basedate"]);

                // Set the right properties
                $item["basedate"] = $this->dayStartOf($basedate);
                $item["start"] = $startdate;
                $item["end"] = $enddate;

                $data = unpack("vbitmask", $rdata);
                $rdata = substr($rdata, 2);
                $item["bitmask"] = $data["bitmask"]; // save bitmask for extended exceptions

                // Bitmask to verify what properties are changed
                $bitmask = $data["bitmask"];

                // ARO_SUBJECT: 0x0001
                // Look for field: SubjectLength (2b), SubjectLength2 (2b) and Subject
                if(($bitmask &(1 << 0))) {
                    $data = unpack("vnull_length/vlength", $rdata);
                    $rdata = substr($rdata, 4);

                    $length = $data["length"];
                    $item["subject"] = ""; // Normalized subject
                    for($j = 0; $j < $length && strlen($rdata); $j++)
                    {
                        $data = unpack("Cchar", $rdata);
                        $rdata = substr($rdata, 1);

                        $item["subject"] .= chr($data["char"]);
                    }
                }

                // ARO_MEETINGTYPE: 0x0002
                if(($bitmask &(1 << 1))) {
                    $rdata = substr($rdata, 4);
                    // Attendees modified: no data here (only in attachment)
                }

                // ARO_REMINDERDELTA: 0x0004
                // Look for field: ReminderDelta (4b)
                if(($bitmask &(1 << 2))) {
                    $data = unpack("Vremind_before", $rdata);
                    $rdata = substr($rdata, 4);

                    $item["remind_before"] = $data["remind_before"];
                }

                // ARO_REMINDER: 0x0008
                // Look field: ReminderSet (4b)
                if(($bitmask &(1 << 3))) {
                    $data = unpack("Vreminder_set", $rdata);
                    $rdata = substr($rdata, 4);

                    $item["reminder_set"] = $data["reminder_set"];
                }

                // ARO_LOCATION: 0x0010
                // Look for fields: LocationLength (2b), LocationLength2 (2b) and Location
                // Similar to ARO_SUBJECT above.
                if(($bitmask &(1 << 4))) {
                    $data = unpack("vnull_length/vlength", $rdata);
                    $rdata = substr($rdata, 4);

                    $item["location"] = "";

                    $length = $data["length"];
                    $data = substr($rdata, 0, $length);
                    $rdata = substr($rdata, $length);

                    $item["location"] .= $data;
                }

                // ARO_BUSYSTATUS: 0x0020
                // Look for field: BusyStatus (4b)
                if(($bitmask &(1 << 5))) {
                    $data = unpack("Vbusystatus", $rdata);
                    $rdata = substr($rdata, 4);

                    $item["busystatus"] = $data["busystatus"];
                }

                // ARO_ATTACHMENT: 0x0040
                if(($bitmask &(1 << 6))) {
                    // no data: RESERVED
                    $rdata = substr($rdata, 4);
                }

                // ARO_SUBTYPE: 0x0080
                // Look for field: SubType (4b). Determines whether it is an allday event.
                if(($bitmask &(1 << 7))) {
                    $data = unpack("Vallday", $rdata);
                    $rdata = substr($rdata, 4);

                    $item["alldayevent"] = $data["allday"];
                }

                // ARO_APPTCOLOR: 0x0100
                // Look for field: AppointmentColor (4b)
                if(($bitmask &(1 << 8))) {
                    $data = unpack("Vlabel", $rdata);
                    $rdata = substr($rdata, 4);

                    $item["label"] = $data["label"];
                }

                // ARO_EXCEPTIONAL_BODY: 0x0200
                if(($bitmask &(1 << 9))) {
                    // Notes or Attachments modified: no data here (only in attachment)
                }

                array_push($exc_changed_details, $item);
            }

            // Find deleted occurrences
            $deleted_occurences = array();

            foreach($exc_base_dates as $base_date) {
                $found = false;

                foreach($exc_changed_details as $details) {
                    if($details["basedate"] == $base_date) {
                        $found = true;
                        break;
                    }
                }
                if(! $found) {
                    // item was not in exc_changed_details, so it must be deleted
                    $deleted_occurences[] = $base_date;
                }
            }

            $ret["deleted_occurences"] = $deleted_occurences;
            $ret["changed_occurences"] = $exc_changed_details;

            // enough data for normal exception (no extended data)
            if (strlen($rdata) < 16) {
                return $ret;
            }

            $data = unpack("Vreservedsize", $rdata);
            $rdata = substr($rdata, 4 + $data["reservedsize"]);

            for($i=0;$i<$nexceptions;$i++)
            {
                // subject and location in ucs-2 to utf-8
                if ($writerversion >= 0x3009) {
                    $data = unpack("Vsize/Vvalue", $rdata); // size includes sizeof(value)==4
                    $rdata = substr($rdata, 4 + $data["size"]);
                }

                $data = unpack("Vreservedsize", $rdata);
                $rdata = substr($rdata, 4 + $data["reservedsize"]);

                // ARO_SUBJECT(0x01) | ARO_LOCATION(0x10)
                if ($exc_changed_details[$i]["bitmask"] & 0x11) {
                    $data = unpack("Vstart/Vend/Vorig", $rdata);
                    $rdata = substr($rdata, 4 * 3);

                    $exc_changed_details[$i]["ex_start_datetime"] = $data["start"];
                    $exc_changed_details[$i]["ex_end_datetime"] = $data["end"];
                    $exc_changed_details[$i]["ex_orig_date"] = $data["orig"];
                }

                // ARO_SUBJECT
                if ($exc_changed_details[$i]["bitmask"] & 0x01) {
                    // decode ucs2 string to utf-8
                    $data = unpack("vlength", $rdata);
                    $rdata = substr($rdata, 2);
                    $length = $data["length"];
                    $data = substr($rdata, 0, $length * 2);
                    $rdata = substr($rdata, $length * 2);
                    $subject = iconv("UCS-2LE", "UTF-8", $data);
                    // replace subject with unicode subject
                    $exc_changed_details[$i]["subject"] = $subject;
                }

                // ARO_LOCATION
                if ($exc_changed_details[$i]["bitmask"] & 0x10) {
                    // decode ucs2 string to utf-8
                    $data = unpack("vlength", $rdata);
                    $rdata = substr($rdata, 2);
                    $length = $data["length"];
                    $data = substr($rdata, 0, $length * 2);
                    $rdata = substr($rdata, $length * 2);
                    $location = iconv("UCS-2LE", "UTF-8", $data);
                    // replace subject with unicode subject
                    $exc_changed_details[$i]["location"] = $location;
                }

                // ARO_SUBJECT(0x01) | ARO_LOCATION(0x10)
                if ($exc_changed_details[$i]["bitmask"] & 0x11) {
                    $data = unpack("Vreservedsize", $rdata);
                    $rdata = substr($rdata, 4 + $data["reservedsize"]);
                }
            }

            // update with extended data
            $ret["changed_occurences"] = $exc_changed_details;

            return $ret;
        }

Here is the call graph for this function:

Here is the caller graph for this function:

BaseRecurrence::parseTimezone ( data) [inherited]

parseTimezone parses the timezone as specified in named property 0x8233 in Outlook calendar messages.

Returns the timezone in minutes negative offset (GMT +2:00 -> -120)

Definition at line 1549 of file class.baserecurrence.php.

        {
            if(strlen($data) < 48)
                return;

            $tz = unpack("ltimezone/lunk/ltimezonedst/lunk/ldstendmonth/vdstendweek/vdstendhour/lunk/lunk/vunk/ldststartmonth/vdststartweek/vdststarthour/lunk/vunk", $data);
            return $tz;
        }

Here is the caller graph for this function:

TaskRecurrence::processOccurrenceItem ( &$  items,
start,
end,
now 
)

processOccurrenceItem, adds an item to a list of occurrences, but only if the resulting occurrence starts or ends in the interval <$start, $end>

Parameters:
array$itemsreference to the array to be added to
date$startstart of timeframe in GMT TIME
date$endend of timeframe in GMT TIME
date$basedate(hour/sec/min assumed to be 00:00:00) in LOCAL TIME OF THE OCCURRENCE

Definition at line 359 of file class.taskrecurrence.php.

        {
            if ($now > $start) {
                $newItem = array();
                $newItem[$this->proptags['startdate']] = $now;

                // If startdate and enddate are set on task, then slide enddate according to duration
                if (isset($this->messageprops[$this->proptags["startdate"]]) && isset($this->messageprops[$this->proptags["duedate"]])) {
                    $newItem[$this->proptags['duedate']] = $newItem[$this->proptags['startdate']] + ($this->messageprops[$this->proptags["duedate"]] - $this->messageprops[$this->proptags["startdate"]]);
                } else {
                    $newItem[$this->proptags['duedate']] = $newItem[$this->proptags['startdate']];
                }

                $items[] = $newItem;
            }
        }
BaseRecurrence::recurDataToUnixData ( rdate) [inherited]

Function which converts a recurrence date timestamp to an unix date timestamp.

Author:
Steve Hardy
Parameters:
Int$rdatethe date which will be converted
Returns:
Int the converted date

Definition at line 1400 of file class.baserecurrence.php.

        {
            return ($rdate - 194074560) * 60 ;
        }

Here is the caller graph for this function:

TaskRecurrence::regenerateTask ( markComplete)

Function which clones current occurrence and sets appropriate properties.

The original recurring item is moved to next occurrence.

Parameters:
boolean$markCompletetrue if existing occurrence has to be mark complete else false.

Definition at line 266 of file class.taskrecurrence.php.

        {
            // Get all properties
            $taskItemProps = mapi_getprops($this->message);

            if (isset($this->action["subject"])) $taskItemProps[$this->proptags["subject"]] = $this->action["subject"];
            if (isset($this->action["importance"])) $taskItemProps[$this->proptags["importance"]] = $this->action["importance"];
            if (isset($this->action["startdate"])) {
                $taskItemProps[$this->proptags["startdate"]] = $this->action["startdate"];
                $taskItemProps[$this->proptags["commonstart"]] = $this->action["startdate"];
            }
            if (isset($this->action["duedate"])) {
                $taskItemProps[$this->proptags["duedate"]] = $this->action["duedate"];
                $taskItemProps[$this->proptags["commonend"]] = $this->action["duedate"];
            }

            $folder = mapi_msgstore_openentry($this->store, $taskItemProps[PR_PARENT_ENTRYID]);
            $newMessage = mapi_folder_createmessage($folder);

            $taskItemProps[$this->proptags["status"]] = $markComplete ? olTaskComplete : olTaskNotStarted;
            $taskItemProps[$this->proptags["complete"]] = $markComplete;
            $taskItemProps[$this->proptags["percent_complete"]] = $markComplete ? 1 : 0;

            // This occurrence has been marked as 'Complete' so disable reminder
            if ($markComplete) {
                $taskItemProps[$this->proptags["reset_reminder"]] = false;
                $taskItemProps[$this->proptags["reminder"]] = false;
                $taskItemProps[$this->proptags["datecompleted"]] = $this->action["datecompleted"];

                unset($this->action[$this->proptags['datecompleted']]);
            }

            // Recurrence ends for this item
            $taskItemProps[$this->proptags["dead_occurrence"]] = true;
            $taskItemProps[$this->proptags["task_f_creator"]] = true;

            //OL props
            $taskItemProps[$this->proptags["side_effects"]] = 1296;
            $taskItemProps[$this->proptags["icon_index"]] = 1280;

            // Copy recipients
            $recipienttable = mapi_message_getrecipienttable($this->message);
            $recipients = mapi_table_queryallrows($recipienttable, array(PR_ENTRYID, PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_RECIPIENT_ENTRYID, PR_RECIPIENT_TYPE, PR_SEND_INTERNET_ENCODING, PR_SEND_RICH_INFO, PR_RECIPIENT_DISPLAY_NAME, PR_ADDRTYPE, PR_DISPLAY_TYPE, PR_RECIPIENT_TRACKSTATUS, PR_RECIPIENT_TRACKSTATUS_TIME, PR_RECIPIENT_FLAGS, PR_ROWID));

            $copy_to_recipientTable = mapi_message_getrecipienttable($newMessage);
            $copy_to_recipientRows = mapi_table_queryallrows($copy_to_recipientTable, array(PR_ROWID));
            foreach($copy_to_recipientRows as $recipient) {
                mapi_message_modifyrecipients($newMessage, MODRECIP_REMOVE, array($recipient));
            }
            mapi_message_modifyrecipients($newMessage, MODRECIP_ADD, $recipients);

            // Copy attachments
            $attachmentTable = mapi_message_getattachmenttable($this->message);
            if($attachmentTable) {
                $attachments = mapi_table_queryallrows($attachmentTable, array(PR_ATTACH_NUM, PR_ATTACH_SIZE, PR_ATTACH_LONG_FILENAME, PR_ATTACHMENT_HIDDEN, PR_DISPLAY_NAME, PR_ATTACH_METHOD));

                foreach($attachments as $attach_props){
                    $attach_old = mapi_message_openattach($this->message, (int) $attach_props[PR_ATTACH_NUM]);
                    $attach_newResourceMsg = mapi_message_createattach($newMessage);

                    mapi_copyto($attach_old, array(), array(), $attach_newResourceMsg, 0);
                    mapi_savechanges($attach_newResourceMsg);
                }
            }

            mapi_setprops($newMessage, $taskItemProps);
            mapi_savechanges($newMessage);

            // Update body of original message
            $msgbody = mapi_message_openproperty($this->message, PR_BODY);
            $msgbody = trim($this->windows1252_to_utf8($msgbody), "\0");
            $separator = "------------\r\n";

            if (!empty($msgbody) && strrpos($msgbody, $separator) === false) {
                $msgbody = $separator . $msgbody;
                $stream = mapi_openpropertytostream($this->message, PR_BODY, MAPI_CREATE | MAPI_MODIFY);
                mapi_stream_setsize($stream, strlen($msgbody));
                mapi_stream_write($stream, $msgbody);
                mapi_stream_commit($stream);
            }

            // We need these properties to notify client
            return mapi_getprops($newMessage, array(PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID));
        }

Here is the call graph for this function:

Here is the caller graph for this function:

Saves the recurrence data to the recurrence property.

Parameters:
array$propertiesthe recurrence data.
Returns:
string binary string

Definition at line 609 of file class.baserecurrence.php.

        {
            // Only save if a message was passed
            if(!isset($this->message))
                return;

            // Abort if no recurrence was set
            if(!isset($this->recur["type"]) && !isset($this->recur["subtype"])) {
                return;
            }

            if(!isset($this->recur["start"]) && !isset($this->recur["end"])) {
                return;
            }

            if(!isset($this->recur["startocc"]) && !isset($this->recur["endocc"])) {
                return;
            }

            $rdata = pack("CCCCCCV", 0x04, 0x30, 0x04, 0x30, (int) $this->recur["type"], 0x20, (int) $this->recur["subtype"]);

            $weekstart = 1; //monday
            $forwardcount = 0;
            $restocc = 0;
            $dayofweek = (int) gmdate("w", (int) $this->recur["start"]); //0 (for Sunday) through 6 (for Saturday)

            $term = (int) $this->recur["type"];
            switch($term)
            {
                case 0x0A:
                    // Daily
                    if(!isset($this->recur["everyn"])) {
                        return;
                    }

                    if($this->recur["subtype"] == 1) {

                        // Daily every workday
                        $rdata .= pack("VVVV", (6 * 24 * 60), 1, 0, 0x3E);
                    } else {
                        // Daily every N days (everyN in minutes)

                        $everyn =  ((int) $this->recur["everyn"]) / 1440;

                        // Calc first occ
                        $firstocc = $this->unixDataToRecurData($this->recur["start"]) % ((int) $this->recur["everyn"]);

                        $rdata .= pack("VVV", $firstocc, (int) $this->recur["everyn"], $this->recur["regen"] ? 1 : 0);
                    }
                    break;
                case 0x0B:
                    // Weekly
                    if(!isset($this->recur["everyn"])) {
                        return;
                    }

                    if (!$this->recur["regen"] && !isset($this->recur["weekdays"])) {
                        return;
                    }

                    // No need to calculate startdate if sliding flag was set.
                    if (!$this->recur['regen']) {
                        // Calculate start date of recurrence

                        // Find the first day that matches one of the weekdays selected
                        $daycount = 0;
                        $dayskip = -1;
                        for($j = 0; $j < 7; $j++) {
                            if(((int) $this->recur["weekdays"]) & (1<<( ($dayofweek+$j)%7)) ) {
                                if($dayskip == -1)
                                    $dayskip = $j;

                                $daycount++;
                            }
                        }

                        // $dayskip is the number of days to skip from the startdate until the first occurrence
                        // $daycount is the number of days per week that an occurrence occurs

                        $weekskip = 0;
                        if(($dayofweek < $weekstart && $dayskip > 0) || ($dayofweek+$dayskip) > 6)
                            $weekskip = 1;

                        // Check if the recurrence ends after a number of occurences, in that case we must calculate the
                        // remaining occurences based on the start of the recurrence.
                        if (((int) $this->recur["term"]) == 0x22) {
                            // $weekskip is the amount of weeks to skip from the startdate before the first occurence
                            // $forwardcount is the maximum number of week occurrences we can go ahead after the first occurrence that
                            // is still inside the recurrence. We subtract one to make sure that the last week is never forwarded over
                            // (eg when numoccur = 2, and daycount = 1)
                            $forwardcount = floor( (int) ($this->recur["numoccur"] -1 ) / $daycount);

                            // $restocc is the number of occurrences left after $forwardcount whole weeks of occurrences, minus one
                            // for the occurrence on the first day
                            $restocc = ((int) $this->recur["numoccur"]) - ($forwardcount*$daycount) - 1;

                            // $forwardcount is now the number of weeks we can go forward and still be inside the recurrence
                            $forwardcount *= (int) $this->recur["everyn"];
                        }

                        // The real start is start + dayskip + weekskip-1 (since dayskip will already bring us into the next week)
                        $this->recur["start"] = ((int) $this->recur["start"]) + ($dayskip * 24*60*60)+ ($weekskip *(((int) $this->recur["everyn"]) - 1) * 7 * 24*60*60);
                    }

                    // Calc first occ
                    $firstocc = ($this->unixDataToRecurData($this->recur["start"]) ) % ( ((int) $this->recur["everyn"]) * 7 * 24 * 60);

                    $firstocc -= (((int) gmdate("w", (int) $this->recur["start"])) - 1) * 24 * 60;

                    if ($this->recur["regen"])
                        $rdata .= pack("VVV", $firstocc, (int) $this->recur["everyn"], 1);
                    else
                        $rdata .= pack("VVVV", $firstocc, (int) $this->recur["everyn"], 0, (int) $this->recur["weekdays"]);
                    break;
                case 0x0C:
                    // Monthly
                case 0x0D:
                    // Yearly
                    if(!isset($this->recur["everyn"])) {
                        return;
                    }
                    if($term == 0x0D /*yearly*/ && !isset($this->recur["month"])) {
                        return;
                    }

                    if($term == 0x0C /*monthly*/) {
                        $everyn = (int) $this->recur["everyn"];
                    }else {
                        $everyn = $this->recur["regen"] ? ((int) $this->recur["everyn"]) * 12 : 12;
                    }

                    // Get montday/month/year of original start
                    $curmonthday = gmdate("j", (int) $this->recur["start"] );
                    $curyear = gmdate("Y", (int) $this->recur["start"] );
                    $curmonth = gmdate("n", (int) $this->recur["start"] );

                    // Check if the recurrence ends after a number of occurences, in that case we must calculate the
                    // remaining occurences based on the start of the recurrence.
                    if (((int) $this->recur["term"]) == 0x22) {
                        // $forwardcount is the number of occurrences we can skip and still be inside the recurrence range (minus
                        // one to make sure there are always at least one occurrence left)
                        $forwardcount = ((((int) $this->recur["numoccur"])-1) * $everyn );
                    }

                    // Get month for yearly on D'th day of month M
                    if($term == 0x0D /*yearly*/) {
                        $selmonth = floor(((int) $this->recur["month"]) / (24 * 60 *29)) + 1; // 1=jan, 2=feb, eg
                    }

                    switch((int) $this->recur["subtype"])
                    {
                        // on D day of every M month
                        case 2:
                            if(!isset($this->recur["monthday"])) {
                                return;
                            }
                            // Recalc startdate

                            // Set on the right begin day

                            // Go the beginning of the month
                            $this->recur["start"] -= ($curmonthday-1) * 24*60*60;
                            // Go the the correct month day
                            $this->recur["start"] += (((int) $this->recur["monthday"])-1) * 24*60*60;

                            // If the previous calculation gave us a start date *before* the original start date, then we need to skip to the next occurrence
                            if ( ($term == 0x0C /*monthly*/ && ((int) $this->recur["monthday"]) < $curmonthday) ||
                                ($term == 0x0D /*yearly*/ &&( $selmonth < $curmonth || ($selmonth == $curmonth && ((int) $this->recur["monthday"]) < $curmonthday)) ))
                            {
                                if($term == 0x0D /*yearly*/)
                                    $count = ($everyn - ($curmonth - $selmonth)); // Yearly, go to next occurrence in 'everyn' months minus difference in first occurence and original date
                                else
                                    $count = $everyn; // Monthly, go to next occurrence in 'everyn' months

                                // Forward by $count months. This is done by getting the number of days in that month and forwarding that many days
                                for($i=0; $i < $count; $i++) {
                                    $this->recur["start"] += $this->getMonthInSeconds($curyear, $curmonth);

                                    if($curmonth == 12) {
                                        $curyear++;
                                        $curmonth = 0;
                                    }
                                    $curmonth++;
                                }
                            }

                            // "start" is now pointing to the first occurrence, except that it will overshoot if the
                            // month in which it occurs has less days than specified as the day of the month. So 31st
                            // of each month will overshoot in february (29 days). We compensate for that by checking
                            // if the day of the month we got is wrong, and then back up to the last day of the previous
                            // month.
                            if(((int) $this->recur["monthday"]) >=28 && ((int) $this->recur["monthday"]) <= 31 &&
                                gmdate("j", ((int) $this->recur["start"])) < ((int) $this->recur["monthday"]))
                            {
                                $this->recur["start"] -= gmdate("j", ((int) $this->recur["start"])) * 24 * 60 *60;
                            }

                            // "start" is now the first occurrence

                            if($term == 0x0C /*monthly*/) {
                                // Calc first occ
                                $monthIndex = ((((12%$everyn) * ((((int) gmdate("Y", $this->recur["start"])) - 1601)%$everyn)) % $everyn) + (((int) gmdate("n", $this->recur["start"])) - 1))%$everyn;

                                $firstocc = 0;
                                for($i=0; $i < $monthIndex; $i++) {
                                    $firstocc+= $this->getMonthInSeconds(1601 + floor($i/12), ($i%12)+1) / 60;
                                }

                                $rdata .= pack("VVVV", $firstocc, $everyn, $this->recur["regen"], (int) $this->recur["monthday"]);
                            } else{
                                // Calc first occ
                                $firstocc = 0;
                                $monthIndex = (int) gmdate("n", $this->recur["start"]);
                                for($i=1; $i < $monthIndex; $i++) {
                                    $firstocc+= $this->getMonthInSeconds(1601 + floor($i/12), $i) / 60;
                                }

                                $rdata .= pack("VVVV", $firstocc, $everyn, $this->recur["regen"], (int) $this->recur["monthday"]);
                            }
                            break;

                        case 3:
                            // monthly: on Nth weekday of every M month
                            // yearly: on Nth weekday of M month
                            if(!isset($this->recur["weekdays"]) && !isset($this->recur["nday"])) {
                                return;
                            }

                            $weekdays = (int) $this->recur["weekdays"];
                            $nday = (int) $this->recur["nday"];

                            // Calc startdate
                            $monthbegindow = (int) $this->recur["start"];

                            if($nday == 5) {
                                // Set date on the last day of the last month
                                $monthbegindow += (gmdate("t", $monthbegindow ) - gmdate("j", $monthbegindow )) * 24 * 60 * 60;
                            }else {
                                // Set on the first day of the month
                                $monthbegindow -= ((gmdate("j", $monthbegindow )-1) * 24 * 60 * 60);
                            }

                            if($term == 0x0D /*yearly*/) {
                                // Set on right month
                                if($selmonth < $curmonth)
                                    $tmp = 12 - $curmonth + $selmonth;
                                else
                                    $tmp = ($selmonth - $curmonth);

                                for($i=0; $i < $tmp; $i++) {
                                    $monthbegindow += $this->getMonthInSeconds($curyear, $curmonth);

                                    if($curmonth == 12) {
                                        $curyear++;
                                        $curmonth = 0;
                                    }
                                    $curmonth++;
                                }

                            }else {
                                // Check or you exist in the right month

                                for($i = 0; $i < 7; $i++) {
                                    if($nday == 5 && (1<<( (gmdate("w", $monthbegindow)-$i)%7) ) & $weekdays) {
                                        $day = gmdate("j", $monthbegindow) - $i;
                                        break;
                                    }else if($nday != 5 && (1<<( (gmdate("w", $monthbegindow )+$i)%7) ) & $weekdays) {
                                        $day = (($nday-1)*7) + ($i+1);
                                        break;
                                    }
                                }

                                // Goto the next X month
                                if(isset($day) && ($day < gmdate("j", (int) $this->recur["start"])) ) {
                                    if($nday == 5) {
                                        $monthbegindow += 24 * 60 * 60;
                                        if($curmonth == 12) {
                                            $curyear++;
                                            $curmonth = 0;
                                        }
                                        $curmonth++;
                                    }

                                    for($i=0; $i < $everyn; $i++) {
                                        $monthbegindow += $this->getMonthInSeconds($curyear, $curmonth);

                                        if($curmonth == 12) {
                                            $curyear++;
                                            $curmonth = 0;
                                        }
                                        $curmonth++;
                                    }

                                    if($nday == 5) {
                                        $monthbegindow -= 24 * 60 * 60;
                                    }
                                }
                            }

                            //FIXME: weekstart?

                            $day = 0;
                            // Set start on the right day
                            for($i = 0; $i < 7; $i++) {
                                if($nday == 5 && (1<<( (gmdate("w", $monthbegindow )-$i)%7) ) & $weekdays) {
                                    $day = $i;
                                    break;
                                }else if($nday != 5 && (1<<( (gmdate("w", $monthbegindow )+$i)%7) ) & $weekdays) {
                                    $day = ($nday - 1) * 7 + ($i+1);
                                    break;
                                }
                            }
                            if($nday == 5)
                                $monthbegindow -= $day * 24 * 60 *60;
                            else
                                $monthbegindow += ($day-1) * 24 * 60 *60;

                            $firstocc = 0;

                            if($term == 0x0C /*monthly*/) {
                                // Calc first occ
                                $monthIndex = ((((12%$everyn) * (((int) gmdate("Y", $this->recur["start"]) - 1601)%$everyn)) % $everyn) + (((int) gmdate("n", $this->recur["start"])) - 1))%$everyn;

                                for($i=0; $i < $monthIndex; $i++) {
                                    $firstocc+= $this->getMonthInSeconds(1601 + floor($i/12), ($i%12)+1) / 60;
                                }

                                $rdata .= pack("VVVVV", $firstocc, $everyn, 0, $weekdays, $nday);
                            } else {
                                // Calc first occ
                                $monthIndex = (int) gmdate("n", $this->recur["start"]);

                                for($i=1; $i < $monthIndex; $i++) {
                                    $firstocc+= $this->getMonthInSeconds(1601 + floor($i/12), $i) / 60;
                                }

                                $rdata .= pack("VVVVV", $firstocc, $everyn, 0, $weekdays, $nday);
                            }
                            break;
                    }
                    break;



            }

            if(!isset($this->recur["term"])) {
                return;
            }

            // Terminate
            $term = (int) $this->recur["term"];
            $rdata .= pack("CCCC", $term, 0x20, 0x00, 0x00);

            switch($term)
            {
                // After the given enddate
                case 0x21:
                    $rdata .= pack("V", 10);
                    break;
                // After a number of times
                case 0x22:
                    if(!isset($this->recur["numoccur"])) {
                        return;
                    }

                    $rdata .= pack("V", (int) $this->recur["numoccur"]);
                    break;
                // Never ends
                case 0x23:
                    $rdata .= pack("V", 0);
                    break;
            }

            // Strange little thing for the recurrence type "every workday"
            if(((int) $this->recur["type"]) == 0x0B && ((int) $this->recur["subtype"]) == 1) {
                $rdata .= pack("V", 1);
            } else { // Other recurrences
                $rdata .= pack("V", 0);
            }

            // Exception data

            // Get all exceptions
            $deleted_items = $this->recur["deleted_occurences"];
            $changed_items = $this->recur["changed_occurences"];

            // Merge deleted and changed items into one list
            $items = $deleted_items;

            foreach($changed_items as $changed_item)
                array_push($items, $changed_item["basedate"]);

            sort($items);

            // Add the merged list in to the rdata
            $rdata .= pack("V", count($items));
            foreach($items as $item)
                $rdata .= pack("V", $this->unixDataToRecurData($item));

            // Loop through the changed exceptions (not deleted)
            $rdata .= pack("V", count($changed_items));
            $items = array();

            foreach($changed_items as $changed_item)
            {
                $items[] = $this->dayStartOf($changed_item["start"]);
            }

            sort($items);

            // Add the changed items list int the rdata
            foreach($items as $item)
                $rdata .= pack("V", $this->unixDataToRecurData($item));

            // Set start date
            $rdata .= pack("V", $this->unixDataToRecurData((int) $this->recur["start"]));

            // Set enddate
            switch($term)
            {
                // After the given enddate
                case 0x21:
                    $rdata .= pack("V", $this->unixDataToRecurData((int) $this->recur["end"]));
                    break;
                // After a number of times
                case 0x22:
                    // @todo: calculate enddate with intval($this->recur["startocc"]) + intval($this->recur["duration"]) > 24 hour
                    $occenddate = (int) $this->recur["start"];

                    switch((int) $this->recur["type"]) {
                        case 0x0A: //daily

                            if($this->recur["subtype"] == 1) {
                                // Daily every workday
                                $restocc = (int) $this->recur["numoccur"];

                                // Get starting weekday
                                $nowtime = $this->gmtime($occenddate);
                                $j = $nowtime["tm_wday"];

                                while(1)
                                {
                                    if(($j%7) > 0 && ($j%7)<6 ) {
                                        $restocc--;
                                    }

                                    $j++;

                                    if($restocc <= 0)
                                        break;

                                    $occenddate += 24*60*60;
                                }

                            } else {
                                // -1 because the first day already counts (from 1-1-1980 to 1-1-1980 is 1 occurrence)
                                $occenddate += (((int) $this->recur["everyn"]) * 60 * (((int) $this->recur["numoccur"]-1)));
                            }
                            break;
                        case 0x0B: //weekly
                            // Needed values
                            // $forwardcount - number of weeks we can skip forward
                            // $restocc - number of remaning occurrences after the week skip

                            // Add the weeks till the last item
                            $occenddate+=($forwardcount*7*24*60*60);

                            $dayofweek = gmdate("w", $occenddate);

                            // Loop through the last occurrences until we have had them all
                            for($j = 1; $restocc>0; $j++)
                            {
                                // Jump to the next week (which may be N weeks away) when going over the week boundary
                                if((($dayofweek+$j)%7) == $weekstart)
                                    $occenddate += (((int) $this->recur["everyn"])-1) * 7 * 24*60*60;

                                // If this is a matching day, once less occurrence to process
                                if(((int) $this->recur["weekdays"]) & (1<<(($dayofweek+$j)%7)) ) {
                                    $restocc--;
                                }

                                // Next day
                                $occenddate += 24*60*60;
                            }

                            break;
                        case 0x0C: //monthly
                        case 0x0D: //yearly

                            $curyear = gmdate("Y", (int) $this->recur["start"] );
                            $curmonth = gmdate("n", (int) $this->recur["start"] );
                            // $forwardcount = months

                            switch((int) $this->recur["subtype"])
                            {
                                case 2: // on D day of every M month
                                    while($forwardcount > 0)
                                    {
                                        $occenddate += $this->getMonthInSeconds($curyear, $curmonth);

                                        if($curmonth >=12) {
                                            $curmonth = 1;
                                            $curyear++;
                                        } else {
                                            $curmonth++;
                                        }
                                        $forwardcount--;
                                    }

                                    // compensation between 28 and 31
                                    if(((int) $this->recur["monthday"]) >=28 && ((int) $this->recur["monthday"]) <= 31 &&
                                        gmdate("j", $occenddate) < ((int) $this->recur["monthday"]))
                                    {
                                        if(gmdate("j", $occenddate) < 28)
                                            $occenddate -= gmdate("j", $occenddate) * 24 * 60 *60;
                                        else
                                            $occenddate += (gmdate("t", $occenddate) - gmdate("j", $occenddate)) * 24 * 60 *60;
                                    }


                                    break;
                                case 3: // on Nth weekday of every M month
                                    $nday = (int) $this->recur["nday"]; //1 tot 5
                                    $weekdays = (int) $this->recur["weekdays"];


                                    while($forwardcount > 0)
                                    {
                                        $occenddate += $this->getMonthInSeconds($curyear, $curmonth);
                                        if($curmonth >=12) {
                                            $curmonth = 1;
                                            $curyear++;
                                        } else {
                                            $curmonth++;
                                        }

                                        $forwardcount--;
                                    }

                                    if($nday == 5) {
                                        // Set date on the last day of the last month
                                        $occenddate += (gmdate("t", $occenddate ) - gmdate("j", $occenddate )) * 24 * 60 * 60;
                                    }else {
                                        // Set date on the first day of the last month
                                        $occenddate -= (gmdate("j", $occenddate )-1) * 24 * 60 * 60;
                                    }

                                    for($i = 0; $i < 7; $i++) {
                                        if( $nday == 5 && (1<<( (gmdate("w", $occenddate)-$i)%7) ) & $weekdays) {
                                            $occenddate -= $i * 24 * 60 * 60;
                                            break;
                                        }else if($nday != 5 && (1<<( (gmdate("w", $occenddate)+$i)%7) ) & $weekdays) {
                                            $occenddate +=  ($i + (($nday-1) *7)) * 24 * 60 * 60;
                                            break;
                                        }
                                    }

                                break; //case 3:
                                }

                            break;

                    }

                    if (defined("PHP_INT_MAX") && $occenddate > PHP_INT_MAX)
                        $occenddate = PHP_INT_MAX;

                    $this->recur["end"] = $occenddate;

                    $rdata .= pack("V", $this->unixDataToRecurData((int) $this->recur["end"]) );
                    break;
                // Never ends
                case 0x23:
                default:
                    $this->recur["end"] = 0x7fffffff; // max date -> 2038
                    $rdata .= pack("V", 0x5AE980DF);
                    break;
            }

            // UTC date
            $utcstart = $this->toGMT($this->tz, (int) $this->recur["start"]);
            $utcend = $this->toGMT($this->tz, (int) $this->recur["end"]);

            //utc date+time
            $utcfirstoccstartdatetime = (isset($this->recur["startocc"])) ? $utcstart + (((int) $this->recur["startocc"])*60) : $utcstart;
            $utcfirstoccenddatetime = (isset($this->recur["endocc"])) ? $utcstart + (((int) $this->recur["endocc"]) * 60) : $utcstart;

            // update reminder time
            mapi_setprops($this->message, Array($this->proptags["reminder_time"] => $utcfirstoccstartdatetime ));

            // update first occurrence date
            mapi_setprops($this->message, Array($this->proptags["startdate"] => $utcfirstoccstartdatetime ));
            mapi_setprops($this->message, Array($this->proptags["duedate"] => $utcfirstoccenddatetime ));
            mapi_setprops($this->message, Array($this->proptags["commonstart"] => $utcfirstoccstartdatetime ));
            mapi_setprops($this->message, Array($this->proptags["commonend"] => $utcfirstoccenddatetime ));

            // Set Outlook properties, if it is an appointment
            if (isset($this->recur["message_class"]) && $this->recur["message_class"] == "IPM.Appointment") {
                // update real begin and real end date
                mapi_setprops($this->message, Array($this->proptags["startdate_recurring"] => $utcstart));
                mapi_setprops($this->message, Array($this->proptags["enddate_recurring"] => $utcend));

                // recurrencetype
                // Strange enough is the property recurrencetype, (type-0x9) and not the CDO recurrencetype
                mapi_setprops($this->message, Array($this->proptags["recurrencetype"] => ((int) $this->recur["type"]) - 0x9));

                // set named prop 'side_effects' to 369, needed for Outlook to ask for single or total recurrence when deleting
                mapi_setprops($this->message, Array($this->proptags["side_effects"] => 369));
            } else {
                mapi_setprops($this->message, Array($this->proptags["side_effects"] => 3441));
            }

            // FlagDueBy is datetime of the first reminder occurrence. Outlook gives on this time a reminder popup dialog
            // Any change of the recurrence (including changing and deleting exceptions) causes the flagdueby to be reset
            // to the 'next' occurrence; this makes sure that deleting the next ocurrence will correctly set the reminder to
            // the occurrence after that. The 'next' occurrence is defined as being the first occurrence that starts at moment X (server time)
            // with the reminder flag set.
            $reminderprops = mapi_getprops($this->message, array($this->proptags["reminder_minutes"]) );
            if(isset($reminderprops[$this->proptags["reminder_minutes"]]) ) {
                $occ = false;
                $occurrences = $this->getItems(time(), 0x7ff00000, 3, true);

                for($i = 0, $len = count($occurrences) ; $i < $len; $i++) {
                    // This will actually also give us appointments that have already started, but not yet ended. Since we want the next
                    // reminder that occurs after time(), we may have to skip the first few entries. We get 3 entries since that is the maximum
                    // number that would be needed (assuming reminder for item X cannot be before the previous occurrence starts). Worst case:
                    // time() is currently after start but before end of item, but reminder of next item has already passed (reminder for next item
                    // can be DURING the previous item, eg daily allday events). In that case, the first and second items must be skipped.

                    if(($occurrences[$i][$this->proptags["startdate"]] - $reminderprops[$this->proptags["reminder_minutes"]] * 60) > time()) {
                        $occ = $occurrences[$i];
                        break;
                    }
                }

                if($occ) {
                    mapi_setprops($this->message, Array($this->proptags["flagdueby"] => $occ[$this->proptags["startdate"]] - ($reminderprops[$this->proptags["reminder_minutes"]] * 60) ));
                } else {
                    // Last reminder passed, no reminders any more.
                    mapi_setprops($this->message, Array($this->proptags["reminder"] => false, $this->proptags["flagdueby"] => 0x7ff00000));
                }
            }

            // Default data
            // Second item (0x08) indicates the Outlook version (see documentation at the bottom of this file for more information)
            $rdata .= pack("VCCCC", 0x00003006, 0x08, 0x30, 0x00, 0x00);

            if(isset($this->recur["startocc"]) && isset($this->recur["endocc"])) {
                // Set start and endtime in minutes
                $rdata .= pack("VV", (int) $this->recur["startocc"], (int) $this->recur["endocc"]);
            }

            // Detailed exception data

            $changed_items = $this->recur["changed_occurences"];

            $rdata .= pack("v", count($changed_items));

            foreach($changed_items as $changed_item)
            {
                // Set start and end time of exception
                $rdata .= pack("V", $this->unixDataToRecurData($changed_item["start"]));
                $rdata .= pack("V", $this->unixDataToRecurData($changed_item["end"]));
                $rdata .= pack("V", $this->unixDataToRecurData($changed_item["basedate"]));

                //Bitmask
                $bitmask = 0;

                // Check for changed strings
                if(isset($changed_item["subject"]))    {
                    $bitmask |= 1 << 0;
                }

                if(isset($changed_item["remind_before"])) {
                    $bitmask |= 1 << 2;
                }

                if(isset($changed_item["reminder_set"])) {
                    $bitmask |= 1 << 3;
                }

                if(isset($changed_item["location"])) {
                    $bitmask |= 1 << 4;
                }

                if(isset($changed_item["busystatus"])) {
                    $bitmask |= 1 << 5;
                }

                if(isset($changed_item["alldayevent"])) {
                    $bitmask |= 1 << 7;
                }

                if(isset($changed_item["label"])) {
                    $bitmask |= 1 << 8;
                }

                $rdata .= pack("v", $bitmask);

                // Set "subject"
                if(isset($changed_item["subject"])) {
                    // convert utf-8 to non-unicode blob string (us-ascii?)
                    $subject = iconv("UTF-8", "windows-1252//TRANSLIT", $changed_item["subject"]);
                    $length = strlen($subject);
                    $rdata .= pack("vv", $length + 1, $length);
                    $rdata .= pack("a".$length, $subject);
                }

                if(isset($changed_item["remind_before"])) {
                    $rdata .= pack("V", $changed_item["remind_before"]);
                }

                if(isset($changed_item["reminder_set"])) {
                    $rdata .= pack("V", $changed_item["reminder_set"]);
                }

                if(isset($changed_item["location"])) {
                    $location = iconv("UTF-8", "windows-1252//TRANSLIT", $changed_item["location"]);
                    $length = strlen($location);
                    $rdata .= pack("vv", $length + 1, $length);
                    $rdata .= pack("a".$length, $location);
                }

                if(isset($changed_item["busystatus"])) {
                    $rdata .= pack("V", $changed_item["busystatus"]);
                }

                if(isset($changed_item["alldayevent"])) {
                    $rdata .= pack("V", $changed_item["alldayevent"]);
                }

                if(isset($changed_item["label"])) {
                    $rdata .= pack("V", $changed_item["label"]);
                }
            }

            $rdata .= pack("V", 0);

            // write extended data
            foreach($changed_items as $changed_item)
            {
                $rdata .= pack("V", 0);
                if(isset($changed_item["subject"]) || isset($changed_item["location"])) {
                    $rdata .= pack("V", $this->unixDataToRecurData($changed_item["start"]));
                    $rdata .= pack("V", $this->unixDataToRecurData($changed_item["end"]));
                    $rdata .= pack("V", $this->unixDataToRecurData($changed_item["basedate"]));
                }

                if(isset($changed_item["subject"])) {
                    $subject = iconv("UTF-8", "UCS-2LE", $changed_item["subject"]);
                    $length = iconv_strlen($subject, "UCS-2LE");
                    $rdata .= pack("v", $length);
                    $rdata .= pack("a".$length*2, $subject);
                }

                if(isset($changed_item["location"])) {
                    $location = iconv("UTF-8", "UCS-2LE", $changed_item["location"]);
                    $length = iconv_strlen($location, "UCS-2LE");
                    $rdata .= pack("v", $length);
                    $rdata .= pack("a".$length*2, $location);
                }

                if(isset($changed_item["subject"]) || isset($changed_item["location"])) {
                    $rdata .= pack("V", 0);
                }
            }

            $rdata .= pack("V", 0);

            // Set props
            mapi_setprops($this->message, Array($this->proptags["recurring_data"] => $rdata, $this->proptags["recurring"] => true));
            if(isset($this->tz) && $this->tz){
                $timezone = "GMT";
                if ($this->tz["timezone"]!=0){
                    // Create user readable timezone information
                    $timezone = sprintf("(GMT %s%02d:%02d)",    (-$this->tz["timezone"]>0 ? "+" : "-"),
                                                            abs($this->tz["timezone"]/60),
                                                            abs($this->tz["timezone"]%60));
                }
                mapi_setprops($this->message, Array($this->proptags["timezone_data"] => $this->getTimezoneData($this->tz),
                                                    $this->proptags["timezone"] => $timezone));
            }
        }

Here is the call graph for this function:

Here is the caller graph for this function:

Sets task object to first occurrence if startdate/duedate of task object is different from first occurrence.

Definition at line 151 of file class.taskrecurrence.php.

        {
            // Check if it is already the first occurrence
            if($this->action['start'] == $this->recur["start"]){
                return;
            }else{
                $items = $this->getNextOccurrence();

                $props = array();
                $props[$this->proptags['startdate']] = $items[$this->proptags['startdate']];
                $props[$this->proptags['commonstart']] = $items[$this->proptags['startdate']];

                $props[$this->proptags['duedate']] = $items[$this->proptags['duedate']];
                $props[$this->proptags['commonend']] = $items[$this->proptags['duedate']];

                mapi_setprops($this->message, $props);
            }
        }

Here is the call graph for this function:

Here is the caller graph for this function:

Function which saves recurrence and also regenerates task if necessary.

Parameters:
array$recurnew recurrence properties
Returns:
array of properties of regenerated task else false

Definition at line 118 of file class.taskrecurrence.php.

        {
            $this->recur = $recur;
            $this->action =& $recur;

            if(!isset($this->recur["changed_occurences"]))
                $this->recur["changed_occurences"] = Array();

            if(!isset($this->recur["deleted_occurences"]))
                $this->recur["deleted_occurences"] = Array();

            if (!isset($this->recur['startocc'])) $this->recur['startocc'] = 0;
            if (!isset($this->recur['endocc'])) $this->recur['endocc'] = 0;

            // Save recurrence because we need proper startrecurrdate and endrecurrdate
            $this->saveRecurrence();

            // Update $this->recur with proper startrecurrdate and endrecurrdate updated after saveing recurrence
            $msgProps = mapi_getprops($this->message, array($this->proptags['recurring_data']));
            $recurring_data = $this->parseRecurrence($msgProps[$this->proptags['recurring_data']]);
            foreach($recurring_data as $key => $value) {
                $this->recur[$key] = $value;
            }

            $this->setFirstOccurrence();

            // Let's see if next occurrence has to be generated
            return $this->moveToNextOccurrence();
        }

Here is the call graph for this function:

TaskRecurrence::setReminder ( nextOccurrence)

Function which sets reminder on recurring task after existing occurrence has been deleted or marked complete.

Parameters:
array$nextOccurrenceproperties of next occurrence

Definition at line 399 of file class.taskrecurrence.php.

        {
            $props = array();
            if ($nextOccurrence) {
                // Check if reminder is reset. Default is 'false'
                $reset_reminder = isset($this->messageprops[$this->proptags['reset_reminder']]) ? $this->messageprops[$this->proptags['reset_reminder']] : false;
                $reminder = $this->messageprops[$this->proptags['reminder']];

                // Either reminder was already set OR reminder was set but was dismissed bty user
                if ($reminder || $reset_reminder) {
                    // Reminder can be set at any time either before or after the duedate, so get duration between the reminder time and duedate
                    $reminder_time = isset($this->messageprops[$this->proptags['reminder_time']]) ? $this->messageprops[$this->proptags['reminder_time']] : 0;
                    $reminder_difference = isset($this->messageprops[$this->proptags['duedate']]) ? $this->messageprops[$this->proptags['duedate']] : 0;
                    $reminder_difference = $reminder_difference - $reminder_time;

                    // Apply duration to next calculated duedate
                    $next_reminder_time = $nextOccurrence[$this->proptags['duedate']] - $reminder_difference;

                    $props[$this->proptags['reminder_time']] = $next_reminder_time;
                    $props[$this->proptags['flagdueby']] = $next_reminder_time;
                    $this->action['reminder'] = $props[$this->proptags['reminder']] = true;
                }
            } else {
                // Didn't get next occurrence, probably this is the last occurrence
                $props[$this->proptags['reminder']] = false;
                $props[$this->proptags['reset_reminder']] = false;
            }

            if (!empty($props))
                mapi_setprops($this->message, $props);
        }

Here is the caller graph for this function:

BaseRecurrence::sortExceptionStart ( a,
b 
) [inherited]

Definition at line 1940 of file class.baserecurrence.php.

        {
            return $a["start"] == $b["start"] ? 0 : ($a["start"]  > $b["start"] ? 1 : -1 );
        }
BaseRecurrence::sortStarttime ( a,
b 
) [inherited]

Definition at line 1899 of file class.baserecurrence.php.

        {
            $aTime = $a[$this->proptags["startdate"]];
            $bTime = $b[$this->proptags["startdate"]];

            return $aTime==$bTime?0:($aTime>$bTime?1:-1);
        }
TaskRecurrence::TaskRecurrence ( store,
message 
)

Definition at line 61 of file class.taskrecurrence.php.

        {
            $this->store = $store;
            $this->message = $message;

            $properties = array();
            $properties["entryid"] = PR_ENTRYID;
            $properties["parent_entryid"] = PR_PARENT_ENTRYID;
            $properties["icon_index"] = PR_ICON_INDEX;
            $properties["message_class"] = PR_MESSAGE_CLASS;
            $properties["message_flags"] = PR_MESSAGE_FLAGS;
            $properties["subject"] = PR_SUBJECT;
            $properties["importance"] = PR_IMPORTANCE;
            $properties["sensitivity"] = PR_SENSITIVITY;
            $properties["last_modification_time"] = PR_LAST_MODIFICATION_TIME;
            $properties["status"] = "PT_LONG:PSETID_Task:0x8101";
            $properties["percent_complete"] = "PT_DOUBLE:PSETID_Task:0x8102";
            $properties["startdate"] = "PT_SYSTIME:PSETID_Task:0x8104";
            $properties["duedate"] = "PT_SYSTIME:PSETID_Task:0x8105";
            $properties["reset_reminder"] = "PT_BOOLEAN:PSETID_Task:0x8107";
            $properties["dead_occurrence"] = "PT_BOOLEAN:PSETID_Task:0x8109";
            $properties["datecompleted"] = "PT_SYSTIME:PSETID_Task:0x810f";
            $properties["recurring_data"] = "PT_BINARY:PSETID_Task:0x8116";
            $properties["actualwork"] = "PT_LONG:PSETID_Task:0x8110";
            $properties["totalwork"] = "PT_LONG:PSETID_Task:0x8111";
            $properties["complete"] = "PT_BOOLEAN:PSETID_Task:0x811c";
            $properties["task_f_creator"] = "PT_BOOLEAN:PSETID_Task:0x811e";
            $properties["owner"] = "PT_STRING8:PSETID_Task:0x811f";
            $properties["recurring"] = "PT_BOOLEAN:PSETID_Task:0x8126";

            $properties["reminder_minutes"] = "PT_LONG:PSETID_Common:0x8501";
            $properties["reminder_time"] = "PT_SYSTIME:PSETID_Common:0x8502";
            $properties["reminder"] = "PT_BOOLEAN:PSETID_Common:0x8503";

            $properties["private"] = "PT_BOOLEAN:PSETID_Common:0x8506";
            $properties["contacts"] = "PT_MV_STRING8:PSETID_Common:0x853a";
            $properties["contacts_string"] = "PT_STRING8:PSETID_Common:0x8586";
            $properties["categories"] = "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords";

            $properties["commonstart"] = "PT_SYSTIME:PSETID_Common:0x8516";
            $properties["commonend"] = "PT_SYSTIME:PSETID_Common:0x8517";
            $properties["commonassign"] = "PT_LONG:PSETID_Common:0x8518";
            $properties["flagdueby"] = "PT_SYSTIME:PSETID_Common:0x8560";
            $properties["side_effects"] = "PT_LONG:PSETID_Common:0x8510";
            $properties["reminder"] = "PT_BOOLEAN:PSETID_Common:0x8503";
            $properties["reminder_minutes"] = "PT_LONG:PSETID_Common:0x8501";

            $this->proptags = getPropIdsFromStrings($store, $properties);

            parent::BaseRecurrence($store, $message, $properties);
        }

Here is the call graph for this function:

BaseRecurrence::toGMT ( tz,
date 
) [inherited]

toGMT returns a timestamp in GMT time for the time and timezone given

Definition at line 1589 of file class.baserecurrence.php.

                                   {
            if(!isset($tz['timezone']))
                return $date;
            $offset = $this->getTimezone($tz, $date);

            return $date + $offset * 60;
        }

Here is the call graph for this function:

Here is the caller graph for this function:

BaseRecurrence::unixDataToRecurData ( date) [inherited]

Function which converts an unix date timestamp to recurrence date timestamp.

Author:
Johnny Biemans
Parameters:
Date$datethe date which will be converted
Returns:
Int the converted date in minutes

Definition at line 1411 of file class.baserecurrence.php.

        {
            return ($date / 60) + 194074560;
        }

Here is the caller graph for this function:

Convert from windows-1252 encoded string to UTF-8 string.

The same conversion rules as utf8_to_windows1252 apply.

Parameters:
string$stringthe Windows-1252 string to convert
Returns:
string UTF-8 representation of the string

Definition at line 455 of file class.taskrecurrence.php.

        {
            if (function_exists("iconv")){
                return iconv("Windows-1252", "UTF-8//TRANSLIT", $string);
            }else{
                return utf8_encode($string); // no euro support here
            }
        }

Here is the caller graph for this function:

BaseRecurrence::yearStartOf ( date) [inherited]

Function to get timestamp of the beginning of the year of the timestamp given.

Parameters:
date$date
Returns:
date Timestamp referring to the same year but on Jan 01, at 00:00:00

Definition at line 1635 of file class.baserecurrence.php.

        {
            $time1 = $this->gmtime($date);

            return gmmktime(0, 0, 0, 1, 1, $time1["tm_year"] + 1900);
        }

Here is the call graph for this function:

Here is the caller graph for this function:


Member Data Documentation

Definition at line 66 of file class.baserecurrence.php.

Definition at line 71 of file class.baserecurrence.php.

Definition at line 76 of file class.baserecurrence.php.

Definition at line 81 of file class.baserecurrence.php.

Definition at line 61 of file class.baserecurrence.php.

Timezone info which is always false for task.

Reimplemented from BaseRecurrence.

Definition at line 59 of file class.taskrecurrence.php.


The documentation for this class was generated from the following file: