Back to index

d-push  2.0
class.taskrecurrence.php
Go to the documentation of this file.
00001 <?php
00002 /*
00003  * Copyright 2005 - 2012  Zarafa B.V.
00004  *
00005  * This program is free software: you can redistribute it and/or modify
00006  * it under the terms of the GNU Affero General Public License, version 3,
00007  * as published by the Free Software Foundation with the following additional
00008  * term according to sec. 7:
00009  *
00010  * According to sec. 7 of the GNU Affero General Public License, version
00011  * 3, the terms of the AGPL are supplemented with the following terms:
00012  *
00013  * "Zarafa" is a registered trademark of Zarafa B.V. The licensing of
00014  * the Program under the AGPL does not imply a trademark license.
00015  * Therefore any rights, title and interest in our trademarks remain
00016  * entirely with us.
00017  *
00018  * However, if you propagate an unmodified version of the Program you are
00019  * allowed to use the term "Zarafa" to indicate that you distribute the
00020  * Program. Furthermore you may use our trademarks where it is necessary
00021  * to indicate the intended purpose of a product or service provided you
00022  * use it in accordance with honest practices in industrial or commercial
00023  * matters.  If you want to propagate modified versions of the Program
00024  * under the name "Zarafa" or "Zarafa Server", you may only do so if you
00025  * have a written permission by Zarafa B.V. (to acquire a permission
00026  * please contact Zarafa at trademark@zarafa.com).
00027  *
00028  * The interactive user interface of the software displays an attribution
00029  * notice containing the term "Zarafa" and/or the logo of Zarafa.
00030  * Interactive user interfaces of unmodified and modified versions must
00031  * display Appropriate Legal Notices according to sec. 5 of the GNU
00032  * Affero General Public License, version 3, when you propagate
00033  * unmodified or modified versions of the Program. In accordance with
00034  * sec. 7 b) of the GNU Affero General Public License, version 3, these
00035  * Appropriate Legal Notices must retain the logo of Zarafa or display
00036  * the words "Initial Development by Zarafa" if the display of the logo
00037  * is not reasonably feasible for technical reasons. The use of the logo
00038  * of Zarafa in Legal Notices is allowed for unmodified and modified
00039  * versions of the software.
00040  *
00041  * This program is distributed in the hope that it will be useful,
00042  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00043  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00044  * GNU Affero General Public License for more details.
00045  *
00046  * You should have received a copy of the GNU Affero General Public License
00047  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00048  *
00049  */
00050 
00051 
00052     require_once("backend/zarafa/mapi/class.baserecurrence.php");
00053 
00054     class TaskRecurrence extends BaseRecurrence
00055     {
00059         var $tz = false;
00060 
00061         function TaskRecurrence($store, $message)
00062         {
00063             $this->store = $store;
00064             $this->message = $message;
00065 
00066             $properties = array();
00067             $properties["entryid"] = PR_ENTRYID;
00068             $properties["parent_entryid"] = PR_PARENT_ENTRYID;
00069             $properties["icon_index"] = PR_ICON_INDEX;
00070             $properties["message_class"] = PR_MESSAGE_CLASS;
00071             $properties["message_flags"] = PR_MESSAGE_FLAGS;
00072             $properties["subject"] = PR_SUBJECT;
00073             $properties["importance"] = PR_IMPORTANCE;
00074             $properties["sensitivity"] = PR_SENSITIVITY;
00075             $properties["last_modification_time"] = PR_LAST_MODIFICATION_TIME;
00076             $properties["status"] = "PT_LONG:PSETID_Task:0x8101";
00077             $properties["percent_complete"] = "PT_DOUBLE:PSETID_Task:0x8102";
00078             $properties["startdate"] = "PT_SYSTIME:PSETID_Task:0x8104";
00079             $properties["duedate"] = "PT_SYSTIME:PSETID_Task:0x8105";
00080             $properties["reset_reminder"] = "PT_BOOLEAN:PSETID_Task:0x8107";
00081             $properties["dead_occurrence"] = "PT_BOOLEAN:PSETID_Task:0x8109";
00082             $properties["datecompleted"] = "PT_SYSTIME:PSETID_Task:0x810f";
00083             $properties["recurring_data"] = "PT_BINARY:PSETID_Task:0x8116";
00084             $properties["actualwork"] = "PT_LONG:PSETID_Task:0x8110";
00085             $properties["totalwork"] = "PT_LONG:PSETID_Task:0x8111";
00086             $properties["complete"] = "PT_BOOLEAN:PSETID_Task:0x811c";
00087             $properties["task_f_creator"] = "PT_BOOLEAN:PSETID_Task:0x811e";
00088             $properties["owner"] = "PT_STRING8:PSETID_Task:0x811f";
00089             $properties["recurring"] = "PT_BOOLEAN:PSETID_Task:0x8126";
00090 
00091             $properties["reminder_minutes"] = "PT_LONG:PSETID_Common:0x8501";
00092             $properties["reminder_time"] = "PT_SYSTIME:PSETID_Common:0x8502";
00093             $properties["reminder"] = "PT_BOOLEAN:PSETID_Common:0x8503";
00094 
00095             $properties["private"] = "PT_BOOLEAN:PSETID_Common:0x8506";
00096             $properties["contacts"] = "PT_MV_STRING8:PSETID_Common:0x853a";
00097             $properties["contacts_string"] = "PT_STRING8:PSETID_Common:0x8586";
00098             $properties["categories"] = "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords";
00099 
00100             $properties["commonstart"] = "PT_SYSTIME:PSETID_Common:0x8516";
00101             $properties["commonend"] = "PT_SYSTIME:PSETID_Common:0x8517";
00102             $properties["commonassign"] = "PT_LONG:PSETID_Common:0x8518";
00103             $properties["flagdueby"] = "PT_SYSTIME:PSETID_Common:0x8560";
00104             $properties["side_effects"] = "PT_LONG:PSETID_Common:0x8510";
00105             $properties["reminder"] = "PT_BOOLEAN:PSETID_Common:0x8503";
00106             $properties["reminder_minutes"] = "PT_LONG:PSETID_Common:0x8501";
00107 
00108             $this->proptags = getPropIdsFromStrings($store, $properties);
00109 
00110             parent::BaseRecurrence($store, $message, $properties);
00111         }
00112 
00118         function setRecurrence(&$recur)
00119         {
00120             $this->recur = $recur;
00121             $this->action =& $recur;
00122 
00123             if(!isset($this->recur["changed_occurences"]))
00124                 $this->recur["changed_occurences"] = Array();
00125 
00126             if(!isset($this->recur["deleted_occurences"]))
00127                 $this->recur["deleted_occurences"] = Array();
00128 
00129             if (!isset($this->recur['startocc'])) $this->recur['startocc'] = 0;
00130             if (!isset($this->recur['endocc'])) $this->recur['endocc'] = 0;
00131 
00132             // Save recurrence because we need proper startrecurrdate and endrecurrdate
00133             $this->saveRecurrence();
00134 
00135             // Update $this->recur with proper startrecurrdate and endrecurrdate updated after saveing recurrence
00136             $msgProps = mapi_getprops($this->message, array($this->proptags['recurring_data']));
00137             $recurring_data = $this->parseRecurrence($msgProps[$this->proptags['recurring_data']]);
00138             foreach($recurring_data as $key => $value) {
00139                 $this->recur[$key] = $value;
00140             }
00141 
00142             $this->setFirstOccurrence();
00143 
00144             // Let's see if next occurrence has to be generated
00145             return $this->moveToNextOccurrence();
00146         }
00147 
00151         function setFirstOccurrence()
00152         {
00153             // Check if it is already the first occurrence
00154             if($this->action['start'] == $this->recur["start"]){
00155                 return;
00156             }else{
00157                 $items = $this->getNextOccurrence();
00158 
00159                 $props = array();
00160                 $props[$this->proptags['startdate']] = $items[$this->proptags['startdate']];
00161                 $props[$this->proptags['commonstart']] = $items[$this->proptags['startdate']];
00162 
00163                 $props[$this->proptags['duedate']] = $items[$this->proptags['duedate']];
00164                 $props[$this->proptags['commonend']] = $items[$this->proptags['duedate']];
00165 
00166                 mapi_setprops($this->message, $props);
00167             }
00168         }
00169 
00179         function moveToNextOccurrence()
00180         {
00181             $result = false;
00186             if ((empty($this->action['startdate']) && empty($this->action['duedate']))
00187                 || ($this->action['complete'] == 1) || (isset($this->action['deleteOccurrence']) && $this->action['deleteOccurrence'])){
00188 
00189                 $nextOccurrence = $this->getNextOccurrence();
00190                 $result = mapi_getprops($this->message, array(PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID));
00191 
00192                 $props = array();
00193                 if ($nextOccurrence) {
00194                     if (!isset($this->action['deleteOccurrence'])) {
00195                         // Create current occurrence as separate task
00196                         $result = $this->regenerateTask($this->action['complete']);
00197                     }
00198 
00199                     // Set reminder for next occurrence
00200                     $this->setReminder($nextOccurrence);
00201 
00202                     // Update properties for next occurrence
00203                     $this->action['duedate'] = $props[$this->proptags['duedate']] = $nextOccurrence[$this->proptags['duedate']];
00204                     $this->action['commonend'] = $props[$this->proptags['commonend']] = $nextOccurrence[$this->proptags['duedate']];
00205 
00206                     $this->action['startdate'] = $props[$this->proptags['startdate']] = $nextOccurrence[$this->proptags['startdate']];
00207                     $this->action['commonstart'] = $props[$this->proptags['commonstart']] = $nextOccurrence[$this->proptags['startdate']];
00208 
00209                     // If current task as been mark as 'Complete' then next occurrence should be uncomplete.
00210                     if (isset($this->action['complete']) && $this->action['complete'] == 1) {
00211                         $this->action['status'] = $props[$this->proptags["status"]] = olTaskNotStarted;
00212                         $this->action['complete'] = $props[$this->proptags["complete"]] = false;
00213                         $this->action['percent_complete'] = $props[$this->proptags["percent_complete"]] = 0;
00214                     }
00215 
00216                     $props[$this->proptags["dead_occurrence"]] = false;
00217                 } else {
00218                     if (isset($this->action['deleteOccurrence']) && $this->action['deleteOccurrence'])
00219                         return false;
00220 
00221                     // Didn't get next occurrence, probably this is the last one, so recurrence ends here
00222                     $props[$this->proptags["dead_occurrence"]] = true;
00223                     $props[$this->proptags["datecompleted"]] = $this->action['datecompleted'];
00224                     $props[$this->proptags["task_f_creator"]] = true;
00225 
00226                     //OL props
00227                     $props[$this->proptags["side_effects"]] = 1296;
00228                     $props[$this->proptags["icon_index"]] = 1280;
00229                 }
00230 
00231                 mapi_setprops($this->message, $props);
00232             }
00233 
00234             return $result;
00235         }
00236 
00241         function getNextOccurrence()
00242         {
00243             if ($this->recur) {
00244                 $items = array();
00245 
00246                 //@TODO: fix start of range
00247                 $start = isset($this->messageprops[$this->proptags["duedate"]]) ? $this->messageprops[$this->proptags["duedate"]] : $this->action['start'];
00248                 $dayend = ($this->recur['term'] == 0x23) ? 0x7fffffff : $this->dayStartOf($this->recur["end"]);
00249 
00250                 // Fix recur object
00251                 $this->recur['startocc'] = 0;
00252                 $this->recur['endocc'] = 0;
00253 
00254                 // Retrieve next occurrence
00255                 $items = $this->getItems($start, $dayend, 1);
00256 
00257                 return !empty($items) ? $items[0] : false;
00258             }
00259         }
00260 
00266         function regenerateTask($markComplete)
00267         {
00268             // Get all properties
00269             $taskItemProps = mapi_getprops($this->message);
00270 
00271             if (isset($this->action["subject"])) $taskItemProps[$this->proptags["subject"]] = $this->action["subject"];
00272             if (isset($this->action["importance"])) $taskItemProps[$this->proptags["importance"]] = $this->action["importance"];
00273             if (isset($this->action["startdate"])) {
00274                 $taskItemProps[$this->proptags["startdate"]] = $this->action["startdate"];
00275                 $taskItemProps[$this->proptags["commonstart"]] = $this->action["startdate"];
00276             }
00277             if (isset($this->action["duedate"])) {
00278                 $taskItemProps[$this->proptags["duedate"]] = $this->action["duedate"];
00279                 $taskItemProps[$this->proptags["commonend"]] = $this->action["duedate"];
00280             }
00281 
00282             $folder = mapi_msgstore_openentry($this->store, $taskItemProps[PR_PARENT_ENTRYID]);
00283             $newMessage = mapi_folder_createmessage($folder);
00284 
00285             $taskItemProps[$this->proptags["status"]] = $markComplete ? olTaskComplete : olTaskNotStarted;
00286             $taskItemProps[$this->proptags["complete"]] = $markComplete;
00287             $taskItemProps[$this->proptags["percent_complete"]] = $markComplete ? 1 : 0;
00288 
00289             // This occurrence has been marked as 'Complete' so disable reminder
00290             if ($markComplete) {
00291                 $taskItemProps[$this->proptags["reset_reminder"]] = false;
00292                 $taskItemProps[$this->proptags["reminder"]] = false;
00293                 $taskItemProps[$this->proptags["datecompleted"]] = $this->action["datecompleted"];
00294 
00295                 unset($this->action[$this->proptags['datecompleted']]);
00296             }
00297 
00298             // Recurrence ends for this item
00299             $taskItemProps[$this->proptags["dead_occurrence"]] = true;
00300             $taskItemProps[$this->proptags["task_f_creator"]] = true;
00301 
00302             //OL props
00303             $taskItemProps[$this->proptags["side_effects"]] = 1296;
00304             $taskItemProps[$this->proptags["icon_index"]] = 1280;
00305 
00306             // Copy recipients
00307             $recipienttable = mapi_message_getrecipienttable($this->message);
00308             $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));
00309 
00310             $copy_to_recipientTable = mapi_message_getrecipienttable($newMessage);
00311             $copy_to_recipientRows = mapi_table_queryallrows($copy_to_recipientTable, array(PR_ROWID));
00312             foreach($copy_to_recipientRows as $recipient) {
00313                 mapi_message_modifyrecipients($newMessage, MODRECIP_REMOVE, array($recipient));
00314             }
00315             mapi_message_modifyrecipients($newMessage, MODRECIP_ADD, $recipients);
00316 
00317             // Copy attachments
00318             $attachmentTable = mapi_message_getattachmenttable($this->message);
00319             if($attachmentTable) {
00320                 $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));
00321 
00322                 foreach($attachments as $attach_props){
00323                     $attach_old = mapi_message_openattach($this->message, (int) $attach_props[PR_ATTACH_NUM]);
00324                     $attach_newResourceMsg = mapi_message_createattach($newMessage);
00325 
00326                     mapi_copyto($attach_old, array(), array(), $attach_newResourceMsg, 0);
00327                     mapi_savechanges($attach_newResourceMsg);
00328                 }
00329             }
00330 
00331             mapi_setprops($newMessage, $taskItemProps);
00332             mapi_savechanges($newMessage);
00333 
00334             // Update body of original message
00335             $msgbody = mapi_message_openproperty($this->message, PR_BODY);
00336             $msgbody = trim($this->windows1252_to_utf8($msgbody), "\0");
00337             $separator = "------------\r\n";
00338 
00339             if (!empty($msgbody) && strrpos($msgbody, $separator) === false) {
00340                 $msgbody = $separator . $msgbody;
00341                 $stream = mapi_openpropertytostream($this->message, PR_BODY, MAPI_CREATE | MAPI_MODIFY);
00342                 mapi_stream_setsize($stream, strlen($msgbody));
00343                 mapi_stream_write($stream, $msgbody);
00344                 mapi_stream_commit($stream);
00345             }
00346 
00347             // We need these properties to notify client
00348             return mapi_getprops($newMessage, array(PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID));
00349         }
00350 
00359         function processOccurrenceItem(&$items, $start, $end, $now)
00360         {
00361             if ($now > $start) {
00362                 $newItem = array();
00363                 $newItem[$this->proptags['startdate']] = $now;
00364 
00365                 // If startdate and enddate are set on task, then slide enddate according to duration
00366                 if (isset($this->messageprops[$this->proptags["startdate"]]) && isset($this->messageprops[$this->proptags["duedate"]])) {
00367                     $newItem[$this->proptags['duedate']] = $newItem[$this->proptags['startdate']] + ($this->messageprops[$this->proptags["duedate"]] - $this->messageprops[$this->proptags["startdate"]]);
00368                 } else {
00369                     $newItem[$this->proptags['duedate']] = $newItem[$this->proptags['startdate']];
00370                 }
00371 
00372                 $items[] = $newItem;
00373             }
00374         }
00375 
00381         function markOccurrenceComplete(&$recur)
00382         {
00383             // Fix timezone object
00384             $this->tz = false;
00385             $this->action =& $recur;
00386             $dead_occurrence = isset($this->messageprops[$this->proptags['dead_occurrence']]) ? $this->messageprops[$this->proptags['dead_occurrence']] : false;
00387 
00388             if (!$dead_occurrence) {
00389                 return $this->moveToNextOccurrence();
00390             }
00391 
00392             return false;
00393         }
00394 
00399         function setReminder($nextOccurrence)
00400         {
00401             $props = array();
00402             if ($nextOccurrence) {
00403                 // Check if reminder is reset. Default is 'false'
00404                 $reset_reminder = isset($this->messageprops[$this->proptags['reset_reminder']]) ? $this->messageprops[$this->proptags['reset_reminder']] : false;
00405                 $reminder = $this->messageprops[$this->proptags['reminder']];
00406 
00407                 // Either reminder was already set OR reminder was set but was dismissed bty user
00408                 if ($reminder || $reset_reminder) {
00409                     // Reminder can be set at any time either before or after the duedate, so get duration between the reminder time and duedate
00410                     $reminder_time = isset($this->messageprops[$this->proptags['reminder_time']]) ? $this->messageprops[$this->proptags['reminder_time']] : 0;
00411                     $reminder_difference = isset($this->messageprops[$this->proptags['duedate']]) ? $this->messageprops[$this->proptags['duedate']] : 0;
00412                     $reminder_difference = $reminder_difference - $reminder_time;
00413 
00414                     // Apply duration to next calculated duedate
00415                     $next_reminder_time = $nextOccurrence[$this->proptags['duedate']] - $reminder_difference;
00416 
00417                     $props[$this->proptags['reminder_time']] = $next_reminder_time;
00418                     $props[$this->proptags['flagdueby']] = $next_reminder_time;
00419                     $this->action['reminder'] = $props[$this->proptags['reminder']] = true;
00420                 }
00421             } else {
00422                 // Didn't get next occurrence, probably this is the last occurrence
00423                 $props[$this->proptags['reminder']] = false;
00424                 $props[$this->proptags['reset_reminder']] = false;
00425             }
00426 
00427             if (!empty($props))
00428                 mapi_setprops($this->message, $props);
00429         }
00430 
00436         function deleteOccurrence($action)
00437         {
00438             $this->tz = false;
00439             $this->action = $action;
00440             $result = $this->moveToNextOccurrence();
00441 
00442             mapi_savechanges($this->message);
00443 
00444             return $result;
00445         }
00446 
00455         function windows1252_to_utf8($string)
00456         {
00457             if (function_exists("iconv")){
00458                 return iconv("Windows-1252", "UTF-8//TRANSLIT", $string);
00459             }else{
00460                 return utf8_encode($string); // no euro support here
00461             }
00462         }
00463     }
00464 ?>