. * */ require_once("backend/zarafa/mapi/class.baserecurrence.php"); class TaskRecurrence extends BaseRecurrence { /** * Timezone info which is always false for task */ var $tz = false; function TaskRecurrence($store, $message) { $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); } /** * Function which saves recurrence and also regenerates task if necessary. *@param array $recur new recurrence properties *@return array of properties of regenerated task else false */ function setRecurrence(&$recur) { $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(); } /** * Sets task object to first occurrence if startdate/duedate of task object is different from first occurrence */ function setFirstOccurrence() { // 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); } } /** * Function which creates new task as current occurrence and moves the * existing task to next occurrence. * *@param array $recur $action from client *@return 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 */ function moveToNextOccurrence() { $result = false; /** * 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. */ 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; } /** * Function which return properties of next occurrence *@return array startdate/enddate of next occurrence */ function getNextOccurrence() { 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; } } /** * Function which clones current occurrence and sets appropriate properties. * The original recurring item is moved to next occurrence. *@param boolean $markComplete true if existing occurrence has to be mark complete else false. */ function regenerateTask($markComplete) { // 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)); } /** * processOccurrenceItem, adds an item to a list of occurrences, but only if the * resulting occurrence starts or ends in the interval <$start, $end> * @param array $items reference to the array to be added to * @param date $start start of timeframe in GMT TIME * @param date $end end of timeframe in GMT TIME * @param date $basedate (hour/sec/min assumed to be 00:00:00) in LOCAL TIME OF THE OCCURRENCE */ function processOccurrenceItem(&$items, $start, $end, $now) { 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; } } /** * Function which marks existing occurrence to 'Complete' *@param array $recur array action from client *@return array of properties of regenerated task else false */ function markOccurrenceComplete(&$recur) { // 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; } /** * Function which sets reminder on recurring task after existing occurrence has been deleted or marked complete. *@param array $nextOccurrence properties of next occurrence */ function setReminder($nextOccurrence) { $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); } /** * Function which recurring task to next occurrence. * It simply doesn't regenerate task @param array $action */ function deleteOccurrence($action) { $this->tz = false; $this->action = $action; $result = $this->moveToNextOccurrence(); mapi_savechanges($this->message); return $result; } /** * Convert from windows-1252 encoded string to UTF-8 string * * The same conversion rules as utf8_to_windows1252 apply. * * @param string $string the Windows-1252 string to convert * @return string UTF-8 representation of the string */ function windows1252_to_utf8($string) { if (function_exists("iconv")){ return iconv("Windows-1252", "UTF-8//TRANSLIT", $string); }else{ return utf8_encode($string); // no euro support here } } } ?>