Back to index

d-push  2.0
class.taskrequest.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     /*
00053     * In general
00054     *
00055     * This class never actually modifies a task item unless we receive a task request update. This means
00056     * that setting all the properties to make the task item itself behave like a task request is up to the
00057     * caller.
00058     *
00059     * The only exception to this is the generation of the TaskGlobalObjId, the unique identifier identifying
00060     * this task request to both the organizer and the assignee. The globalobjectid is generated when the
00061     * task request is sent via sendTaskRequest.
00062     */
00063 
00064     /* The TaskMode value is only used for the IPM.TaskRequest items. It must 0 (tdmtNothing) on IPM.Task items.
00065     *
00066     * It is used to indicate the type of change that is being carried in the IPM.TaskRequest item (although this
00067     * information seems redundant due to that information already being available in PR_MESSAGE_CLASS).
00068     */
00069     define('tdmtNothing', 0);            // Value in IPM.Task items
00070     define('tdmtTaskReq', 1);            // Assigner -> Assignee
00071     define('tdmtTaskAcc', 2);            // Assignee -> Assigner
00072     define('tdmtTaskDec', 3);            // Assignee -> Assigner
00073     define('tdmtTaskUpd', 4);            // Assignee -> Assigner
00074     define('tdmtTaskSELF', 5);            // Assigner -> Assigner (?)
00075 
00076     /* The TaskHistory is used to show the last action on the task on both the assigner and the assignee's side.
00077     *
00078     * It is used in combination with 'AssignedTime' and 'tasklastdelegate' or 'tasklastuser' to show the information
00079     * at the top of the task request in the format 'Accepted by <user> on 01-01-2010 11:00'.
00080     */
00081     define('thNone', 0);
00082     define('thAccepted', 1);            // Set by assignee
00083     define('thDeclined', 2);            // Set by assignee
00084     define('thUpdated', 3);                // Set by assignee
00085     define('thDueDateChanged', 4);
00086     define('thAssigned', 5);            // Set by assigner
00087 
00088     /* The TaskState value is used to differentiate the version of a task in the assigner's folder and the version in the
00089     * assignee's folder. The buttons shown depend on this and the 'taskaccepted' boolean (for the assignee)
00090     */
00091     define('tdsNOM', 0);        // Got a response to a deleted task, and re-created the task for the assigner
00092     define('tdsOWNNEW', 1);        // Not assigned
00093     define('tdsOWN', 2);        // Assignee version
00094     define('tdsACC', 3);        // Assigner version
00095     define('tdsDEC', 4);        // Assigner version, but assignee declined
00096 
00097     /* The delegationstate is used for the assigner to indicate state
00098     */
00099     define('olTaskNotDelegated', 0);
00100     define('olTaskDelegationUnknown', 1); // After sending req
00101     define('olTaskDelegationAccepted', 2); // After receiving accept
00102     define('olTaskDelegationDeclined', 3); // After receiving decline
00103 
00104     /* The task ownership indicates the role of the current user relative to the task.
00105     */
00106     define('olNewTask', 0);
00107     define('olDelegatedTask', 1);    // Task has been assigned
00108     define('olOwnTask', 2);            // Task owned
00109 
00110     /* taskmultrecips indicates whether the task request sent or received has multiple assignees or not.
00111     */
00112     define('tmrNone', 0);
00113     define('tmrSent', 1);        // Task has been sent to multiple assignee
00114     define('tmrReceived', 2);    // Task Request received has multiple assignee
00115 
00116     class TaskRequest {
00117 
00118         // All recipient properties
00119         var $recipprops = 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, PR_SEARCH_KEY);
00120 
00121         /* Constructor
00122          *
00123          * Constructs a TaskRequest object for the specified message. This can be either the task request
00124          * message itself (in the inbox) or the task in the tasks folder, depending on the action to be performed.
00125          *
00126          * As a general rule, the object message passed is the object 'in view' when the user performs one of the
00127          * actions in this class.
00128          *
00129          * @param $store store MAPI Store in which $message resides. This is also the store where the tasks folder is assumed to be in
00130          * @param $message message MAPI Message to which the task request referes (can be an email or a task)
00131          * @param $session session MAPI Session which is used to open tasks folders for delegated task requests or responses
00132          */
00133         function TaskRequest($store, $message, $session) {
00134             $this->store = $store;
00135             $this->message = $message;
00136             $this->session = $session;
00137 
00138             $properties["owner"] = "PT_STRING8:PSETID_Task:0x811f";
00139             $properties["updatecount"] = "PT_LONG:PSETID_Task:0x8112";
00140             $properties["taskstate"] = "PT_LONG:PSETID_Task:0x8113";
00141             $properties["taskmultrecips"] = "PT_LONG:PSETID_Task:0x8120";
00142             $properties["taskupdates"] = "PT_BOOLEAN:PSETID_Task:0x811b";
00143             $properties["tasksoc"] = "PT_BOOLEAN:PSETID_Task:0x8119";
00144             $properties["taskhistory"] = "PT_LONG:PSETID_Task:0x811a";
00145             $properties["taskmode"] = "PT_LONG:PSETID_Common:0x8518";
00146             $properties["taskglobalobjid"] = "PT_BINARY:PSETID_Common:0x8519";
00147             $properties["complete"] = "PT_BOOLEAN:PSETID_Common:0x811c";
00148             $properties["assignedtime"] = "PT_SYSTIME:PSETID_Task:0x8115";
00149             $properties["taskfcreator"] = "PT_BOOLEAN:PSETID_Task:0x0x811e";
00150             $properties["tasklastuser"] = "PT_STRING8:PSETID_Task:0x8122";
00151             $properties["tasklastdelegate"] = "PT_STRING8:PSETID_Task:0x8125";
00152             $properties["taskaccepted"] = "PT_BOOLEAN:PSETID_Task:0x8108";
00153             $properties["delegationstate"] = "PT_LONG:PSETID_Task:0x812a";
00154             $properties["ownership"] = "PT_LONG:PSETID_Task:0x8129";
00155 
00156             $properties["complete"] = "PT_BOOLEAN:PSETID_Task:0x811c";
00157             $properties["datecompleted"] = "PT_SYSTIME:PSETID_Task:0x810f";
00158             $properties["recurring"] = "PT_BOOLEAN:PSETID_Task:0x8126";
00159             $properties["startdate"] = "PT_SYSTIME:PSETID_Task:0x8104";
00160             $properties["duedate"] = "PT_SYSTIME:PSETID_Task:0x8105";
00161             $properties["status"] = "PT_LONG:PSETID_Task:0x8101";
00162             $properties["percent_complete"] = "PT_DOUBLE:PSETID_Task:0x8102";
00163             $properties["totalwork"] = "PT_LONG:PSETID_Task:0x8111";
00164             $properties["actualwork"] = "PT_LONG:PSETID_Task:0x8110";
00165             $properties["categories"] = "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords";
00166             $properties["companies"] = "PT_MV_STRING8:PSETID_Common:0x8539";
00167             $properties["mileage"] = "PT_STRING8:PSETID_Common:0x8534";
00168             $properties["billinginformation"] = "PT_STRING8:PSETID_Common:0x8535";
00169 
00170             $this->props = getPropIdsFromStrings($store, $properties);
00171         }
00172 
00173         // General functions
00174 
00175         /* Return TRUE if the item is a task request message
00176          */
00177         function isTaskRequest()
00178         {
00179             $props = mapi_getprops($this->message, Array(PR_MESSAGE_CLASS));
00180 
00181             if(isset($props[PR_MESSAGE_CLASS]) && $props[PR_MESSAGE_CLASS] == "IPM.TaskRequest") {
00182                 return true;
00183             }
00184         }
00185 
00186         /* Return TRUE if the item is a task response message
00187          */
00188         function isTaskRequestResponse() {
00189             $props = mapi_getprops($this->message, Array(PR_MESSAGE_CLASS));
00190 
00191             if(isset($props[PR_MESSAGE_CLASS]) && strpos($props[PR_MESSAGE_CLASS], "IPM.TaskRequest.") === 0) {
00192                 return true;
00193             }
00194         }
00195 
00196         /*
00197          * Gets the task associated with an IPM.TaskRequest message
00198          *
00199          * If the task does not exist yet, it is created, using the attachment object in the
00200          * task request item.
00201          */
00202         function getAssociatedTask($create)
00203         {
00204             $props = mapi_getprops($this->message, array(PR_MESSAGE_CLASS, $this->props['taskglobalobjid']));
00205 
00206             if($props[PR_MESSAGE_CLASS] == "IPM.Task")
00207                 return $this->message; // Message itself is task, so return that
00208 
00209             $tfolder = $this->getDefaultTasksFolder();
00210             $globalobjid = $props[$this->props['taskglobalobjid']];
00211 
00212             // Find the task by looking for the taskglobalobjid
00213             $restriction = array(RES_PROPERTY, array(RELOP => RELOP_EQ, ULPROPTAG => $this->props['taskglobalobjid'], VALUE => $globalobjid));
00214 
00215             $contents = mapi_folder_getcontentstable($tfolder);
00216 
00217             $rows = mapi_table_queryallrows($contents, array(PR_ENTRYID), $restriction);
00218 
00219             if(empty($rows)) {
00220                 // None found, create one if possible
00221                 if(!$create)
00222                     return false;
00223 
00224                 $task = mapi_folder_createmessage($tfolder);
00225 
00226                 $sub = $this->getEmbeddedTask($this->message);
00227                 mapi_copyto($sub, array(), array(), $task);
00228 
00229                 // Copy sender information from the e-mail
00230                 $senderprops = mapi_getprops($this->message, array(PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_EMAIL_ADDRESS, PR_SENT_REPRESENTING_ENTRYID, PR_SENT_REPRESENTING_ADDRTYPE, PR_SENT_REPRESENTING_SEARCH_KEY));
00231                 mapi_setprops($task, $senderprops);
00232 
00233                 $senderprops = mapi_getprops($this->message, array(PR_SENDER_NAME, PR_SENDER_EMAIL_ADDRESS, PR_SENDER_ENTRYID, PR_SENDER_ADDRTYPE, PR_SENDER_SEARCH_KEY));
00234                 mapi_setprops($task, $senderprops);
00235 
00236             } else {
00237                 // If there are multiple, just use the first
00238                 $entryid = $rows[0][PR_ENTRYID];
00239 
00240                 $store = $this->getTaskFolderStore();
00241                 $task = mapi_msgstore_openentry($store, $entryid);
00242             }
00243 
00244             return $task;
00245         }
00246 
00247 
00248 
00249         // Organizer functions (called by the organizer)
00250 
00251         /* Processes a task request response, which can be any of the following:
00252          * - Task accept (task history is marked as accepted)
00253          * - Task decline (task history is marked as declined)
00254          * - Task update (updates completion %, etc)
00255          */
00256         function processTaskResponse() {
00257             $messageprops = mapi_getprops($this->message, array(PR_PROCESSED));
00258             if(isset($messageprops[PR_PROCESSED]) && $messageprops[PR_PROCESSED])
00259                 return true;
00260 
00261             // Get the task for this response
00262             $task = $this->getAssociatedTask(false);
00263 
00264             if(!$task) {
00265                 // Got a response for a task that has been deleted, create a new one and mark it as such
00266                 $task = $this->getAssociatedTask(true);
00267 
00268                 // tdsNOM indicates a task request that had gone missing
00269                 mapi_setprops($task, array($this->props['taskstate'] => tdsNOM ));
00270             }
00271 
00272             // Get the embedded task information and copy it into our task
00273             $sub = $this->getEmbeddedTask($this->message);
00274             mapi_copyto($sub, array(), array($this->props['taskstate'], $this->props['taskhistory'], $this->props['taskmode'], $this->props['taskfcreator']), $task);
00275 
00276             $props = mapi_getprops($this->message, array(PR_MESSAGE_CLASS));
00277 
00278             // Set correct taskmode and taskhistory depending on response type
00279             switch($props[PR_MESSAGE_CLASS]) {
00280                 case 'IPM.TaskRequest.Accept':
00281                     $taskhistory = thAccepted;
00282                     $taskstate = tdsACC;
00283                     $delegationstate =  olTaskDelegationAccepted;
00284                     break;
00285                 case 'IPM.TaskRequest.Decline':
00286                     $taskhistory = thDeclined;
00287                     $taskstate = tdsDEC;
00288                     $delegationstate =  olTaskDelegationDeclined;
00289                     break;
00290                 case 'IPM.TaskRequest.Update':
00291                     $taskhistory = thUpdated;
00292                     $taskstate = tdsACC; // Doesn't actually change anything
00293                     $delegationstate =  olTaskDelegationAccepted;
00294                     break;
00295             }
00296 
00297             // Update taskstate (what the task looks like) and task history (last action done by the assignee)
00298             mapi_setprops($task, array($this->props['taskhistory'] => $taskhistory, $this->props['taskstate'] => $taskstate, $this->props['delegationstate'] => $delegationstate, $this->props['ownership'] => olDelegatedTask));
00299 
00300             mapi_setprops($this->message, array(PR_PROCESSED => true));
00301             mapi_savechanges($task);
00302 
00303             return true;
00304         }
00305 
00306         /* Create a new message in the current user's outbox and submit it
00307          *
00308          * Takes the task passed in the constructor as the task to be sent; recipient should
00309          * be pre-existing. The task request will be sent to all recipients.
00310          */
00311         function sendTaskRequest($prefix) {
00312             // Generate a TaskGlobalObjectId
00313             $taskid = $this->createTGOID();
00314             $messageprops = mapi_getprops($this->message, array(PR_SUBJECT));
00315 
00316             // Set properties on Task Request
00317             mapi_setprops($this->message, array(
00318                 $this->props['taskglobalobjid'] => $taskid, /* our new taskglobalobjid */
00319                 $this->props['taskstate'] => tdsACC,         /* state for our outgoing request */
00320                 $this->props['taskmode'] => tdmtNothing,     /* we're not sending a change */
00321                 $this->props['updatecount'] => 2,            /* version 2 (no idea) */
00322                 $this->props['delegationstate'] => olTaskDelegationUnknown, /* no reply yet */
00323                 $this->props['ownership'] => olDelegatedTask, /* Task has been assigned */
00324                 $this->props['taskhistory'] => thAssigned,    /* Task has been assigned */
00325                 PR_ICON_INDEX => 1283                        /* Task request icon*/
00326             ));
00327             $this->setLastUser();
00328             $this->setOwnerForAssignor();
00329             mapi_savechanges($this->message);
00330 
00331             // Create outgoing task request message
00332             $outgoing = $this->createOutgoingMessage();
00333             // No need to copy attachments as task will be attached as embedded message.
00334             mapi_copyto($this->message, array(), array(PR_MESSAGE_ATTACHMENTS), $outgoing);
00335 
00336             // Make it a task request, and put it in sent items after it is sent
00337             mapi_setprops($outgoing, array(
00338                 PR_MESSAGE_CLASS => "IPM.TaskRequest",         /* class is task request */
00339                 $this->props['taskstate'] => tdsOWNNEW,     /* for the recipient the task is new */
00340                 $this->props['taskmode'] => tdmtTaskReq,    /* for the recipient it's a request */
00341                 $this->props['updatecount'] => 1,            /* version 2 is in the attachment */
00342                 PR_SUBJECT => $prefix . $messageprops[PR_SUBJECT],
00343                 PR_ICON_INDEX => 0xFFFFFFFF,                /* show assigned icon */
00344             ));
00345 
00346             // Set Body
00347             $body = $this->getBody();
00348             $stream = mapi_openpropertytostream($outgoing, PR_BODY, MAPI_CREATE | MAPI_MODIFY);
00349             mapi_stream_setsize($stream, strlen($body));
00350             mapi_stream_write($stream, $body);
00351             mapi_stream_commit($stream);
00352 
00353             $attach = mapi_message_createattach($outgoing);
00354             mapi_setprops($attach, array(PR_ATTACH_METHOD => ATTACH_EMBEDDED_MSG, PR_DISPLAY_NAME => $messageprops[PR_SUBJECT]));
00355 
00356             $sub = mapi_attach_openproperty($attach, PR_ATTACH_DATA_OBJ, IID_IMessage, 0, MAPI_MODIFY | MAPI_CREATE);
00357 
00358             mapi_copyto($this->message, array(), array(), $sub);
00359             mapi_savechanges($sub);
00360 
00361             mapi_savechanges($attach);
00362 
00363             mapi_savechanges($outgoing);
00364             mapi_message_submitmessage($outgoing);
00365             return true;
00366         }
00367 
00368         // Assignee functions (called by the assignee)
00369 
00370         /* Update task version counter
00371          *
00372          * Must be called before each update to increase counter
00373          */
00374         function updateTaskRequest() {
00375             $messageprops = mapi_getprops($this->message, array($this->props['updatecount']));
00376 
00377             if(isset($messageprops)) {
00378                 $messageprops[$this->props['updatecount']]++;
00379             } else {
00380                 $messageprops[$this->props['updatecount']] = 1;
00381             }
00382 
00383             mapi_setprops($this->message, $messageprops);
00384         }
00385 
00386         /* Process a task request
00387          *
00388          * Message passed should be an IPM.TaskRequest message. The task request is then processed to create
00389          * the task in the tasks folder if needed.
00390          */
00391         function processTaskRequest() {
00392             if(!$this->isTaskRequest())
00393                 return false;
00394 
00395             $messageprops = mapi_getprops($this->message, array(PR_PROCESSED));
00396             if (isset($messageprops[PR_PROCESSED]) && $messageprops[PR_PROCESSED])
00397                 return true;
00398 
00399             $task = $this->getAssociatedTask(true);
00400             $taskProps = mapi_getprops($task, array($this->props['taskmultrecips']));
00401 
00402             // Set the task state to say that we're the attendee receiving the message, that we have not yet responded and that this message represents no change
00403             $taskProps[$this->props["taskstate"]] = tdsOWN;
00404             $taskProps[$this->props["taskhistory"]] = thAssigned;
00405             $taskProps[$this->props["taskmode"]] = tdmtNothing;
00406             $taskProps[$this->props["taskaccepted"]] = false;
00407             $taskProps[$this->props["taskfcreator"]] = false;
00408             $taskProps[$this->props["ownership"]] = olOwnTask;
00409             $taskProps[$this->props["delegationstate"]] = olTaskNotDelegated;
00410             $taskProps[PR_ICON_INDEX] = 1282;
00411 
00412             // This task was assigned to multiple recips, so set this user as owner
00413             if (isset($taskProps[$this->props['taskmultrecips']]) && $taskProps[$this->props['taskmultrecips']] == tmrSent) {
00414                 $loginUserData = $this->retrieveUserData();
00415 
00416                 if ($loginUserData) {
00417                     $taskProps[$this->props['owner']] = $loginUserData[PR_DISPLAY_NAME];
00418                     $taskProps[$this->props['taskmultrecips']] = tmrReceived;
00419                 }
00420             }
00421             mapi_setprops($task, $taskProps);
00422 
00423             $this->setAssignorInRecipients($task);
00424 
00425             mapi_savechanges($task);
00426 
00427             $taskprops = mapi_getprops($task, array(PR_ENTRYID));
00428 
00429             mapi_setprops($this->message, array(PR_PROCESSED => true));
00430             mapi_savechanges($this->message);
00431 
00432             return $taskprops[PR_ENTRYID];
00433         }
00434 
00435         /* Accept a task request and send the response.
00436          *
00437          * Message passed should be an IPM.Task (eg the task from getAssociatedTask())
00438          *
00439          * Copies the task to the user's task folder, sets it to accepted, and sends the acceptation
00440          * message back to the organizer. The caller is responsible for removing the message.
00441          *
00442          * @return entryid EntryID of the accepted task
00443          */
00444         function doAccept($prefix) {
00445             $messageprops = mapi_getprops($this->message, array($this->props['taskstate']));
00446 
00447             if(!isset($messageprops[$this->props['taskstate']]) || $messageprops[$this->props['taskstate']] != tdsOWN)
00448                 return false; // Can only accept assignee task
00449 
00450             $this->setLastUser();
00451             $this->updateTaskRequest();
00452 
00453             // Set as accepted
00454             mapi_setprops($this->message, array($this->props['taskhistory'] => thAccepted, $this->props['assignedtime'] => time(), $this->props['taskaccepted'] => true,  $this->props['delegationstate'] => olTaskNotDelegated));
00455 
00456             mapi_savechanges($this->message);
00457 
00458             $this->sendResponse(tdmtTaskAcc, $prefix);
00459 
00460             //@TODO: delete received task request from Inbox
00461             return $this->deleteReceivedTR();
00462         }
00463 
00464         /* Decline a task request and send the response.
00465          *
00466          * Passed message must be a task request message, ie isTaskRequest() must return TRUE.
00467          *
00468          * Sends the decline message back to the organizer. The caller is responsible for removing the message.
00469          *
00470          * @return boolean TRUE on success, FALSE on failure
00471          */
00472         function doDecline($prefix) {
00473             $messageprops = mapi_getprops($this->message, array($this->props['taskstate']));
00474 
00475             if(!isset($messageprops[$this->props['taskstate']]) || $messageprops[$this->props['taskstate']] != tdsOWN)
00476                 return false; // Can only decline assignee task
00477 
00478             $this->setLastUser();
00479             $this->updateTaskRequest();
00480 
00481             // Set as declined
00482             mapi_setprops($this->message, array($this->props['taskhistory'] => thDeclined,  $this->props['delegationstate'] => olTaskDelegationDeclined));
00483 
00484             mapi_savechanges($this->message);
00485 
00486             $this->sendResponse(tdmtTaskDec, $prefix);
00487 
00488             return $this->deleteReceivedTR();
00489         }
00490 
00491         /* Send an update of the task if requested, and send the Status-On-Completion report if complete and requested
00492          *
00493          * If no updates were requested from the organizer, this function does nothing.
00494          *
00495          * @return boolean TRUE if the update succeeded, FALSE otherwise.
00496          */
00497         function doUpdate($prefix, $prefixComplete) {
00498             $messageprops = mapi_getprops($this->message, array($this->props['taskstate'], PR_SUBJECT));
00499 
00500             if(!isset($messageprops[$this->props['taskstate']]) || $messageprops[$this->props['taskstate']] != tdsOWN)
00501                 return false; // Can only update assignee task
00502 
00503             $this->setLastUser();
00504             $this->updateTaskRequest();
00505 
00506             // Set as updated
00507             mapi_setprops($this->message, array($this->props['taskhistory'] => thUpdated));
00508 
00509             mapi_savechanges($this->message);
00510 
00511             $props = mapi_getprops($this->message, array($this->props['taskupdates'], $this->props['tasksoc'], $this->props['recurring'], $this->props['complete']));
00512             if ($props[$this->props['taskupdates']] && !(isset($props[$this->props['recurring']]) && $props[$this->props['recurring']]))
00513                 $this->sendResponse(tdmtTaskUpd, $prefix);
00514 
00515             if($props[$this->props['tasksoc']]  && $props[$this->props['complete']] ) {
00516                 $outgoing = $this->createOutgoingMessage();
00517 
00518                 mapi_setprops($outgoing, array(PR_SUBJECT => $prefixComplete . $messageprops[PR_SUBJECT]));
00519 
00520                 $this->setRecipientsForResponse($outgoing, tdmtTaskUpd, true);
00521                 $body = $this->getBody();
00522                 $stream = mapi_openpropertytostream($outgoing, PR_BODY, MAPI_CREATE | MAPI_MODIFY);
00523                 mapi_stream_setsize($stream, strlen($body));
00524                 mapi_stream_write($stream, $body);
00525                 mapi_stream_commit($stream);
00526 
00527                 mapi_savechanges($outgoing);
00528                 mapi_message_submitmessage($outgoing);
00529             }
00530         }
00531 
00532         // Internal functions
00533 
00534         /* Get the store associated with the task
00535          *
00536          * Normally this will just open the store that the processed message is in. However, if the message is opened
00537          * by a delegate, this function opens the store that the message was delegated from.
00538          */
00539         function getTaskFolderStore()
00540         {
00541             $ownerentryid = false;
00542 
00543             $rcvdprops = mapi_getprops($this->message, array(PR_RCVD_REPRESENTING_ENTRYID));
00544             if(isset($rcvdprops[PR_RCVD_REPRESENTING_ENTRYID])) {
00545                 $ownerentryid = $rcvdprops;
00546             }
00547 
00548             if(!$ownerentryid) {
00549                 $store = $this->store;
00550             } else {
00551                 $ab = mapi_openaddressbook($this->session); // seb changed from $session to $this->session
00552                 if(!$ab) return false; // manni $ before ab was missing
00553 
00554                 $mailuser = mapi_ab_openentry($ab, $ownerentryid);
00555                 if(!$mailuser) return false;
00556 
00557                 $mailuserprops = mapi_getprops($mailuser, array(PR_EMAIL_ADDRESS));
00558                 if(!isset($mailuserprops[PR_EMAIL_ADDRESS])) return false;
00559 
00560                 $storeid = mapi_msgstore_createentryid($this->store, $mailuserprops[PR_EMAIL_ADDRESS]);
00561 
00562                 $store = mapi_openmsgstore($this->session, $storeid);
00563 
00564             }
00565             return $store;
00566         }
00567 
00568         /* Open the default task folder for the current user, or the specified user if passed
00569          *
00570          * @param $ownerentryid (Optional)EntryID of user for which we are opening the task folder
00571          */
00572         function getDefaultTasksFolder()
00573         {
00574             $store = $this->getTaskFolderStore();
00575 
00576             $inbox = mapi_msgstore_getreceivefolder($store);
00577             $inboxprops = mapi_getprops($inbox, Array(PR_IPM_TASK_ENTRYID));
00578             if(!isset($inboxprops[PR_IPM_TASK_ENTRYID]))
00579                 return false;
00580 
00581             return mapi_msgstore_openentry($store, $inboxprops[PR_IPM_TASK_ENTRYID]);
00582         }
00583 
00584         function getSentReprProps($store)
00585         {
00586             $storeprops = mapi_getprops($store, array(PR_MAILBOX_OWNER_ENTRYID));
00587             if(!isset($storeprops[PR_MAILBOX_OWNER_ENTRYID])) return false;
00588 
00589             $ab = mapi_openaddressbook($this->session);
00590             $mailuser = mapi_ab_openentry($ab, $storeprops[PR_MAILBOX_OWNER_ENTRYID]);
00591             $mailuserprops = mapi_getprops($mailuser, array(PR_ADDRTYPE, PR_EMAIL_ADDRESS, PR_DISPLAY_NAME, PR_SEARCH_KEY, PR_ENTRYID));
00592 
00593             $props = array();
00594             $props[PR_SENT_REPRESENTING_ADDRTYPE] = $mailuserprops[PR_ADDRTYPE];
00595             $props[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $mailuserprops[PR_EMAIL_ADDRESS];
00596             $props[PR_SENT_REPRESENTING_NAME] = $mailuserprops[PR_DISPLAY_NAME];
00597             $props[PR_SENT_REPRESENTING_SEARCH_KEY] = $mailuserprops[PR_SEARCH_KEY];
00598             $props[PR_SENT_REPRESENTING_ENTRYID] = $mailuserprops[PR_ENTRYID];
00599 
00600             return $props;
00601         }
00602 
00603         /*
00604          * Creates an outgoing message based on the passed message - will set delegate information
00605          * and sentmail folder
00606          */
00607         function createOutgoingMessage()
00608         {
00609             // Open our default store for this user (that's the only store we can submit in)
00610             $store = $this->getDefaultStore();
00611             $storeprops = mapi_getprops($store, array(PR_IPM_OUTBOX_ENTRYID, PR_IPM_SENTMAIL_ENTRYID));
00612 
00613             $outbox = mapi_msgstore_openentry($store, $storeprops[PR_IPM_OUTBOX_ENTRYID]);
00614             if(!$outbox) return false;
00615 
00616             $outgoing = mapi_folder_createmessage($outbox);
00617             if(!$outgoing) return false;
00618 
00619             // Set SENT_REPRESENTING in case we're sending as a delegate
00620             $ownerstore = $this->getTaskFolderStore();
00621             $sentreprprops = $this->getSentReprProps($ownerstore);
00622             mapi_setprops($outgoing, $sentreprprops);
00623 
00624             mapi_setprops($outgoing, array(PR_SENTMAIL_ENTRYID => $storeprops[PR_IPM_SENTMAIL_ENTRYID]));
00625 
00626             return $outgoing;
00627         }
00628 
00629         /*
00630          * Send a response message (from assignee back to organizer).
00631          *
00632          * @param $type int Type of response (tdmtTaskAcc, tdmtTaskDec, tdmtTaskUpd);
00633          * @return boolean TRUE on success
00634          */
00635         function sendResponse($type, $prefix)
00636         {
00637             // Create a message in our outbox
00638             $outgoing = $this->createOutgoingMessage();
00639 
00640             $messageprops = mapi_getprops($this->message, array(PR_SUBJECT));
00641 
00642             $attach = mapi_message_createattach($outgoing);
00643             mapi_setprops($attach, array(PR_ATTACH_METHOD => ATTACH_EMBEDDED_MSG, PR_DISPLAY_NAME => $messageprops[PR_SUBJECT], PR_ATTACHMENT_HIDDEN => true));
00644             $sub = mapi_attach_openproperty($attach, PR_ATTACH_DATA_OBJ, IID_IMessage, 0, MAPI_CREATE | MAPI_MODIFY);
00645 
00646             mapi_copyto($this->message, array(), array(PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_EMAIL_ADDRESS, PR_SENT_REPRESENTING_ADDRTYPE, PR_SENT_REPRESENTING_ENTRYID, PR_SENT_REPRESENTING_SEARCH_KEY), $outgoing);
00647             mapi_copyto($this->message, array(), array(), $sub);
00648 
00649             if (!$this->setRecipientsForResponse($outgoing, $type)) return false;
00650 
00651             switch($type) {
00652                 case tdmtTaskAcc:
00653                     $messageclass = "IPM.TaskRequest.Accept";
00654                     break;
00655                 case tdmtTaskDec:
00656                     $messageclass = "IPM.TaskRequest.Decline";
00657                     break;
00658                 case tdmtTaskUpd:
00659                     $messageclass = "IPM.TaskRequest.Update";
00660                     break;
00661             };
00662 
00663             mapi_savechanges($sub);
00664             mapi_savechanges($attach);
00665 
00666             // Set Body
00667             $body = $this->getBody();
00668             $stream = mapi_openpropertytostream($outgoing, PR_BODY, MAPI_CREATE | MAPI_MODIFY);
00669             mapi_stream_setsize($stream, strlen($body));
00670             mapi_stream_write($stream, $body);
00671             mapi_stream_commit($stream);
00672 
00673             // Set subject, taskmode, message class, icon index, response time
00674             mapi_setprops($outgoing, array(PR_SUBJECT => $prefix . $messageprops[PR_SUBJECT],
00675                                             $this->props['taskmode'] => $type,
00676                                             PR_MESSAGE_CLASS => $messageclass,
00677                                             PR_ICON_INDEX => 0xFFFFFFFF,
00678                                             $this->props['assignedtime'] => time()));
00679 
00680             mapi_savechanges($outgoing);
00681             mapi_message_submitmessage($outgoing);
00682 
00683             return true;
00684         }
00685 
00686         function getDefaultStore()
00687         {
00688             $table = mapi_getmsgstorestable($this->session);
00689             $rows = mapi_table_queryallrows($table, array(PR_DEFAULT_STORE, PR_ENTRYID));
00690 
00691             foreach($rows as $row) {
00692                 if($row[PR_DEFAULT_STORE])
00693                     return mapi_openmsgstore($this->session, $row[PR_ENTRYID]);
00694             }
00695 
00696             return false;
00697         }
00698 
00699         /* Creates a new TaskGlobalObjId
00700          *
00701          * Just 16 bytes of random data
00702          */
00703         function createTGOID()
00704         {
00705             $goid = "";
00706             for($i=0;$i<16;$i++) {
00707                 $goid .= chr(rand(0, 255));
00708             }
00709             return $goid;
00710         }
00711 
00712         function getEmbeddedTask($message) {
00713             $table = mapi_message_getattachmenttable($message);
00714             $rows = mapi_table_queryallrows($table, array(PR_ATTACH_NUM));
00715 
00716             // Assume only one attachment
00717             if(empty($rows))
00718                 return false;
00719 
00720             $attach = mapi_message_openattach($message, $rows[0][PR_ATTACH_NUM]);
00721             $message = mapi_openproperty($attach, PR_ATTACH_DATA_OBJ, IID_IMessage, 0, 0);
00722 
00723             return $message;
00724         }
00725 
00726         function setLastUser() {
00727             $delegatestore = $this->getDefaultStore();
00728             $taskstore = $this->getTaskFolderStore();
00729 
00730             $delegateprops = mapi_getprops($delegatestore, array(PR_MAILBOX_OWNER_NAME));
00731             $taskprops = mapi_getprops($taskstore, array(PR_MAILBOX_OWNER_NAME));
00732 
00733             // The owner of the task
00734             $username = $delegateprops[PR_MAILBOX_OWNER_NAME];
00735             // This is me (the one calling the script)
00736             $delegate = $taskprops[PR_MAILBOX_OWNER_NAME];
00737 
00738             mapi_setprops($this->message, array($this->props["tasklastuser"] => $username, $this->props["tasklastdelegate"] => $delegate, $this->props['assignedtime'] => time()));
00739         }
00740 
00744         function setOwnerForAssignor()
00745         {
00746             $recipTable = mapi_message_getrecipienttable($this->message);
00747             $recips = mapi_table_queryallrows($recipTable, array(PR_DISPLAY_NAME));
00748 
00749             if (!empty($recips)) {
00750                 $owner = array();
00751                 foreach ($recips as $value) {
00752                     $owner[] = $value[PR_DISPLAY_NAME];
00753                 }
00754 
00755                 $props = array($this->props['owner'] => implode("; ", $owner));
00756                 mapi_setprops($this->message, $props);
00757             }
00758         }
00759 
00768         function setAssignorInRecipients($task)
00769         {
00770             $recipTable = mapi_message_getrecipienttable($task);
00771 
00772             // Delete all MAPI_TO recipients
00773             $recips = mapi_table_queryallrows($recipTable, array(PR_ROWID), array(RES_PROPERTY,
00774                                                                                 array(    RELOP => RELOP_EQ,
00775                                                                                         ULPROPTAG => PR_RECIPIENT_TYPE,
00776                                                                                         VALUE => MAPI_TO
00777                                                                                 )));
00778             foreach($recips as $recip)
00779                 mapi_message_modifyrecipients($task, MODRECIP_REMOVE, array($recip));
00780 
00781             $recips = array();
00782             $taskReqProps = mapi_getprops($this->message, array(PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_EMAIL_ADDRESS, PR_SENT_REPRESENTING_ENTRYID, PR_SENT_REPRESENTING_ADDRTYPE));
00783             $associatedTaskProps = mapi_getprops($task, array($this->props['taskupdates'], $this->props['tasksoc'], $this->props['taskmultrecips']));
00784 
00785             // Build assignor info
00786             $assignor = array(    PR_ENTRYID => $taskReqProps[PR_SENT_REPRESENTING_ENTRYID],
00787                                 PR_DISPLAY_NAME => $taskReqProps[PR_SENT_REPRESENTING_NAME],
00788                                 PR_EMAIL_ADDRESS => $taskReqProps[PR_SENT_REPRESENTING_EMAIL_ADDRESS],
00789                                 PR_RECIPIENT_DISPLAY_NAME => $taskReqProps[PR_SENT_REPRESENTING_NAME],
00790                                 PR_ADDRTYPE => empty($taskReqProps[PR_SENT_REPRESENTING_ADDRTYPE]) ? 'SMTP' : $taskReqProps[PR_SENT_REPRESENTING_ADDRTYPE],
00791                                 PR_RECIPIENT_FLAGS => recipSendable
00792                         );
00793 
00794             // Assignor has requested task updates, so set him/her as MAPI_CC in recipienttable.
00795             if ((isset($associatedTaskProps[$this->props['taskupdates']]) && $associatedTaskProps[$this->props['taskupdates']])
00796                 && !(isset($associatedTaskProps[$this->props['taskmultrecips']]) && $associatedTaskProps[$this->props['taskmultrecips']] == tmrReceived)) {
00797                 $assignor[PR_RECIPIENT_TYPE] = MAPI_CC;
00798                 $recips[] = $assignor;
00799             }
00800 
00801             // Assignor wants to receive an email report when task is mark as 'Complete', so in recipients as MAPI_BCC
00802             if (isset($associatedTaskProps[$this->props['taskupdates']]) && $associatedTaskProps[$this->props['tasksoc']]) {
00803                 $assignor[PR_RECIPIENT_TYPE] = MAPI_BCC;
00804                 $recips[] = $assignor;
00805             }
00806 
00807             if (!empty($recips))
00808                 mapi_message_modifyrecipients($task, MODRECIP_ADD, $recips);
00809         }
00810 
00813         function retrieveUserData()
00814         {
00815             // get user entryid
00816             $storeProps = mapi_getprops($this->store, array(PR_USER_ENTRYID));
00817             if (!$storeProps[PR_USER_ENTRYID])    return false;
00818 
00819             $ab = mapi_openaddressbook($this->session);
00820             // open the user entry
00821             $user = mapi_ab_openentry($ab, $storeProps[PR_USER_ENTRYID]);
00822             if (!$user) return false;
00823 
00824             // receive userdata
00825             $userProps = mapi_getprops($user, array(PR_DISPLAY_NAME));
00826             if (!$userProps[PR_DISPLAY_NAME]) return false;
00827 
00828             return $userProps;
00829         }
00830 
00835         function deleteReceivedTR()
00836         {
00837             $store = $this->getTaskFolderStore();
00838             $inbox = mapi_msgstore_getreceivefolder($store);
00839 
00840             $storeProps = mapi_getprops($store, array(PR_IPM_WASTEBASKET_ENTRYID));
00841             $props = mapi_getprops($this->message, array($this->props['taskglobalobjid']));
00842             $globalobjid = $props[$this->props['taskglobalobjid']];
00843 
00844             // Find the task by looking for the taskglobalobjid
00845             $restriction = array(RES_PROPERTY, array(RELOP => RELOP_EQ, ULPROPTAG => $this->props['taskglobalobjid'], VALUE => $globalobjid));
00846 
00847             $contents = mapi_folder_getcontentstable($inbox);
00848 
00849             $rows = mapi_table_queryallrows($contents, array(PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID), $restriction);
00850 
00851             $taskrequest = false;
00852             if(!empty($rows)) {
00853                 // If there are multiple, just use the first
00854                 $entryid = $rows[0][PR_ENTRYID];
00855                 $wastebasket = mapi_msgstore_openentry($store, $storeProps[PR_IPM_WASTEBASKET_ENTRYID]);
00856                 mapi_folder_copymessages($inbox, Array($entryid), $wastebasket, MESSAGE_MOVE);
00857 
00858                 return array(PR_ENTRYID => $entryid, PR_PARENT_ENTRYID => $rows[0][PR_PARENT_ENTRYID], PR_STORE_ENTRYID => $rows[0][PR_STORE_ENTRYID]);
00859             }
00860 
00861             return false;
00862         }
00863 
00866         function createUnassignedCopy()
00867         {
00868             mapi_deleteprops($this->message, array($this->props['taskglobalobjid']));
00869             mapi_setprops($this->message, array($this->props['updatecount'] => 1));
00870 
00871             // Remove all recipents
00872             $this->deleteAllRecipients($this->message);
00873         }
00874 
00885         function setRecipientsForResponse($outgoing, $responseType, $sendSOC = false)
00886         {
00887             // Clear recipients from outgoing msg
00888             $this->deleteAllRecipients($outgoing);
00889 
00890             // If it is a task update then get MAPI_CC recipients which are assignors who has asked for task update.
00891             if ($responseType == tdmtTaskUpd) {
00892                 $recipTable = mapi_message_getrecipienttable($this->message);
00893                 $recips = mapi_table_queryallrows($recipTable, $this->recipprops, array(RES_PROPERTY,
00894                                                                                         array(    RELOP => RELOP_EQ,
00895                                                                                                 ULPROPTAG => PR_RECIPIENT_TYPE,
00896                                                                                                 VALUE => ($sendSOC ? MAPI_BCC : MAPI_CC)
00897                                                                                         )
00898                                                 ));
00899 
00900                 // No recipients found, return error
00901                 if (empty($recips))
00902                     return false;
00903 
00904                 foreach($recips as $recip) {
00905                     $recip[PR_RECIPIENT_TYPE] = MAPI_TO;    // Change recipient type to MAPI_TO
00906                     mapi_message_modifyrecipients($outgoing, MODRECIP_ADD, array($recip));
00907                 }
00908                 return true;
00909             }
00910 
00911             $orgprops = mapi_getprops($this->message, array(PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_EMAIL_ADDRESS, PR_SENT_REPRESENTING_ADDRTYPE, PR_SENT_REPRESENTING_ENTRYID, PR_SUBJECT));
00912             $recip = array(PR_DISPLAY_NAME => $orgprops[PR_SENT_REPRESENTING_NAME], PR_EMAIL_ADDRESS => $orgprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS], PR_ADDRTYPE => $orgprops[PR_SENT_REPRESENTING_ADDRTYPE], PR_ENTRYID => $orgprops[PR_SENT_REPRESENTING_ENTRYID], PR_RECIPIENT_TYPE => MAPI_TO);
00913 
00914             mapi_message_modifyrecipients($outgoing, MODRECIP_ADD, array($recip));
00915 
00916             return true;
00917         }
00918 
00923         function getBody()
00924         {
00925             //@TODO: Fix translations
00926 
00927             $msgProps = mapi_getprops($this->message);
00928             $body = "";
00929 
00930             if (isset($msgProps[PR_SUBJECT])) $body .= "\n" . _("Subject") . ":\t". $msgProps[PR_SUBJECT];
00931             if (isset($msgProps[$this->props['startdate']])) $body .= "\n" . _("Start Date") . ":\t". strftime(_("%A, %B %d, %Y"),$msgProps[$this->props['startdate']]);
00932             if (isset($msgProps[$this->props['duedate']])) $body .= "\n" . _("Due Date") . ":\t". strftime(_("%A, %B %d, %Y"),$msgProps[$this->props['duedate']]);
00933             $body .= "\n";
00934 
00935             if (isset($msgProps[$this->props['status']])) {
00936                 $body .= "\n" . _("Status") . ":\t";
00937                 if ($msgProps[$this->props['status']] == 0) $body .= _("Not Started");
00938                 else if ($msgProps[$this->props['status']] == 1) $body .= _("In Progress");
00939                 else if ($msgProps[$this->props['status']] == 2) $body .= _("Complete");
00940                 else if ($msgProps[$this->props['status']] == 3) $body .= _("Wait for other person");
00941                 else if ($msgProps[$this->props['status']] == 4) $body .= _("Deferred");
00942             }
00943 
00944             if (isset($msgProps[$this->props['percent_complete']])) {
00945                 $body .= "\n" . _("Percent Complete") . ":\t". ($msgProps[$this->props['percent_complete']] * 100).'%';
00946 
00947                 if ($msgProps[$this->props['percent_complete']] == 1 && isset($msgProps[$this->props['datecompleted']]))
00948                     $body .= "\n" . _("Date Completed") . ":\t". strftime("%A, %B %d, %Y",$msgProps[$this->props['datecompleted']]);
00949             }
00950             $body .= "\n";
00951 
00952             if (isset($msgProps[$this->props['totalwork']])) $body .= "\n" . _("Total Work") . ":\t". ($msgProps[$this->props['totalwork']]/60) ." " . _("hours");
00953             if (isset($msgProps[$this->props['actualwork']])) $body .= "\n" . _("Actual Work") . ":\t". ($msgProps[$this->props['actualwork']]/60) ." " . _("hours");
00954             $body .="\n";
00955 
00956             if (isset($msgProps[$this->props['owner']])) $body .= "\n" . _("Owner") . ":\t". $msgProps[$this->props['owner']];
00957             $body .="\n";
00958 
00959             if (isset($msgProps[$this->props['categories']]) && !empty($msgProps[$this->props['categories']])) $body .= "\nCategories:\t". implode(', ', $msgProps[$this->props['categories']]);
00960             if (isset($msgProps[$this->props['companies']]) && !empty($msgProps[$this->props['companies']])) $body .= "\nCompany:\t". implode(', ', $msgProps[$this->props['companies']]);
00961             if (isset($msgProps[$this->props['billinginformation']])) $body .= "\n" . _("Billing Information") . ":\t". $msgProps[$this->props['billinginformation']];
00962             if (isset($msgProps[$this->props['mileage']])) $body .= "\n" . _("Mileage") . ":\t". $msgProps[$this->props['mileage']];
00963             $body .="\n";
00964 
00965             $content = mapi_message_openproperty($this->message, PR_BODY);
00966             $body .= "\n". trim($content, "\0");
00967 
00968             return $body;
00969         }
00970 
00981         function windows1252_to_utf8($string)
00982         {
00983             if (function_exists("iconv")){
00984                 return iconv("Windows-1252", "UTF-8//TRANSLIT", $string);
00985             }else{
00986                 return utf8_encode($string); // no euro support here
00987             }
00988         }
00989 
00994         function reclaimownership()
00995         {
00996             // Delete task request properties
00997             mapi_deleteprops($this->message, array($this->props['taskglobalobjid'],
00998                                                     $this->props['tasklastuser'],
00999                                                     $this->props['tasklastdelegate']));
01000 
01001             mapi_setprops($this->message, array($this->props['updatecount'] => 2,
01002                                                 $this->props['taskfcreator'] => true));
01003 
01004             // Delete recipients
01005             $this->deleteAllRecipients($this->message);
01006         }
01007 
01012         function deleteAllRecipients($message)
01013         {
01014             $recipTable = mapi_message_getrecipienttable($message);
01015             $recipRows = mapi_table_queryallrows($recipTable, array(PR_ROWID));
01016 
01017             foreach($recipRows as $recipient)
01018                 mapi_message_modifyrecipients($message, MODRECIP_REMOVE, array($recipient));
01019         }
01020 
01021         function sendCompleteUpdate($prefix, $action, $prefixComplete)
01022         {
01023             $messageprops = mapi_getprops($this->message, array($this->props['taskstate']));
01024 
01025             if(!isset($messageprops[$this->props['taskstate']]) || $messageprops[$this->props['taskstate']] != tdsOWN)
01026                 return false; // Can only decline assignee task
01027 
01028             mapi_setprops($this->message, array($this->props['complete'] => true,
01029                                                 $this->props['datecompleted'] => $action["dateCompleted"],
01030                                                 $this->props['status'] => 2,
01031                                                 $this->props['percent_complete'] => 1));
01032 
01033             $this->doUpdate($prefix, $prefixComplete);
01034         }
01035     }
01036 ?>