source: trunk/zpush/backend/zarafa/mapi/class.taskrequest.php @ 7589

Revision 7589, 48.5 KB checked in by douglas, 11 years ago (diff)

Ticket #3209 - Integrar módulo de sincronização Z-push ao Expresso

Line 
1<?php
2/*
3 * Copyright 2005 - 2012  Zarafa B.V.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License, version 3,
7 * as published by the Free Software Foundation with the following additional
8 * term according to sec. 7:
9 *
10 * According to sec. 7 of the GNU Affero General Public License, version
11 * 3, the terms of the AGPL are supplemented with the following terms:
12 *
13 * "Zarafa" is a registered trademark of Zarafa B.V. The licensing of
14 * the Program under the AGPL does not imply a trademark license.
15 * Therefore any rights, title and interest in our trademarks remain
16 * entirely with us.
17 *
18 * However, if you propagate an unmodified version of the Program you are
19 * allowed to use the term "Zarafa" to indicate that you distribute the
20 * Program. Furthermore you may use our trademarks where it is necessary
21 * to indicate the intended purpose of a product or service provided you
22 * use it in accordance with honest practices in industrial or commercial
23 * matters.  If you want to propagate modified versions of the Program
24 * under the name "Zarafa" or "Zarafa Server", you may only do so if you
25 * have a written permission by Zarafa B.V. (to acquire a permission
26 * please contact Zarafa at trademark@zarafa.com).
27 *
28 * The interactive user interface of the software displays an attribution
29 * notice containing the term "Zarafa" and/or the logo of Zarafa.
30 * Interactive user interfaces of unmodified and modified versions must
31 * display Appropriate Legal Notices according to sec. 5 of the GNU
32 * Affero General Public License, version 3, when you propagate
33 * unmodified or modified versions of the Program. In accordance with
34 * sec. 7 b) of the GNU Affero General Public License, version 3, these
35 * Appropriate Legal Notices must retain the logo of Zarafa or display
36 * the words "Initial Development by Zarafa" if the display of the logo
37 * is not reasonably feasible for technical reasons. The use of the logo
38 * of Zarafa in Legal Notices is allowed for unmodified and modified
39 * versions of the software.
40 *
41 * This program is distributed in the hope that it will be useful,
42 * but WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
44 * GNU Affero General Public License for more details.
45 *
46 * You should have received a copy of the GNU Affero General Public License
47 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
48 *
49 */
50
51
52    /*
53    * In general
54    *
55    * This class never actually modifies a task item unless we receive a task request update. This means
56    * that setting all the properties to make the task item itself behave like a task request is up to the
57    * caller.
58    *
59    * The only exception to this is the generation of the TaskGlobalObjId, the unique identifier identifying
60    * this task request to both the organizer and the assignee. The globalobjectid is generated when the
61    * task request is sent via sendTaskRequest.
62    */
63
64    /* The TaskMode value is only used for the IPM.TaskRequest items. It must 0 (tdmtNothing) on IPM.Task items.
65    *
66    * It is used to indicate the type of change that is being carried in the IPM.TaskRequest item (although this
67    * information seems redundant due to that information already being available in PR_MESSAGE_CLASS).
68    */
69    define('tdmtNothing', 0);            // Value in IPM.Task items
70    define('tdmtTaskReq', 1);            // Assigner -> Assignee
71    define('tdmtTaskAcc', 2);            // Assignee -> Assigner
72    define('tdmtTaskDec', 3);            // Assignee -> Assigner
73    define('tdmtTaskUpd', 4);            // Assignee -> Assigner
74    define('tdmtTaskSELF', 5);            // Assigner -> Assigner (?)
75
76    /* The TaskHistory is used to show the last action on the task on both the assigner and the assignee's side.
77    *
78    * It is used in combination with 'AssignedTime' and 'tasklastdelegate' or 'tasklastuser' to show the information
79    * at the top of the task request in the format 'Accepted by <user> on 01-01-2010 11:00'.
80    */
81    define('thNone', 0);
82    define('thAccepted', 1);            // Set by assignee
83    define('thDeclined', 2);            // Set by assignee
84    define('thUpdated', 3);                // Set by assignee
85    define('thDueDateChanged', 4);
86    define('thAssigned', 5);            // Set by assigner
87
88    /* The TaskState value is used to differentiate the version of a task in the assigner's folder and the version in the
89    * assignee's folder. The buttons shown depend on this and the 'taskaccepted' boolean (for the assignee)
90    */
91    define('tdsNOM', 0);        // Got a response to a deleted task, and re-created the task for the assigner
92    define('tdsOWNNEW', 1);        // Not assigned
93    define('tdsOWN', 2);        // Assignee version
94    define('tdsACC', 3);        // Assigner version
95    define('tdsDEC', 4);        // Assigner version, but assignee declined
96
97    /* The delegationstate is used for the assigner to indicate state
98    */
99    define('olTaskNotDelegated', 0);
100    define('olTaskDelegationUnknown', 1); // After sending req
101    define('olTaskDelegationAccepted', 2); // After receiving accept
102    define('olTaskDelegationDeclined', 3); // After receiving decline
103
104    /* The task ownership indicates the role of the current user relative to the task.
105    */
106    define('olNewTask', 0);
107    define('olDelegatedTask', 1);    // Task has been assigned
108    define('olOwnTask', 2);            // Task owned
109
110    /* taskmultrecips indicates whether the task request sent or received has multiple assignees or not.
111    */
112    define('tmrNone', 0);
113    define('tmrSent', 1);        // Task has been sent to multiple assignee
114    define('tmrReceived', 2);    // Task Request received has multiple assignee
115
116    class TaskRequest {
117
118        // All recipient properties
119        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);
120
121        /* Constructor
122         *
123         * Constructs a TaskRequest object for the specified message. This can be either the task request
124         * message itself (in the inbox) or the task in the tasks folder, depending on the action to be performed.
125         *
126         * As a general rule, the object message passed is the object 'in view' when the user performs one of the
127         * actions in this class.
128         *
129         * @param $store store MAPI Store in which $message resides. This is also the store where the tasks folder is assumed to be in
130         * @param $message message MAPI Message to which the task request referes (can be an email or a task)
131         * @param $session session MAPI Session which is used to open tasks folders for delegated task requests or responses
132         */
133        function TaskRequest($store, $message, $session) {
134            $this->store = $store;
135            $this->message = $message;
136            $this->session = $session;
137
138            $properties["owner"] = "PT_STRING8:PSETID_Task:0x811f";
139            $properties["updatecount"] = "PT_LONG:PSETID_Task:0x8112";
140            $properties["taskstate"] = "PT_LONG:PSETID_Task:0x8113";
141            $properties["taskmultrecips"] = "PT_LONG:PSETID_Task:0x8120";
142            $properties["taskupdates"] = "PT_BOOLEAN:PSETID_Task:0x811b";
143            $properties["tasksoc"] = "PT_BOOLEAN:PSETID_Task:0x8119";
144            $properties["taskhistory"] = "PT_LONG:PSETID_Task:0x811a";
145            $properties["taskmode"] = "PT_LONG:PSETID_Common:0x8518";
146            $properties["taskglobalobjid"] = "PT_BINARY:PSETID_Common:0x8519";
147            $properties["complete"] = "PT_BOOLEAN:PSETID_Common:0x811c";
148            $properties["assignedtime"] = "PT_SYSTIME:PSETID_Task:0x8115";
149            $properties["taskfcreator"] = "PT_BOOLEAN:PSETID_Task:0x0x811e";
150            $properties["tasklastuser"] = "PT_STRING8:PSETID_Task:0x8122";
151            $properties["tasklastdelegate"] = "PT_STRING8:PSETID_Task:0x8125";
152            $properties["taskaccepted"] = "PT_BOOLEAN:PSETID_Task:0x8108";
153            $properties["delegationstate"] = "PT_LONG:PSETID_Task:0x812a";
154            $properties["ownership"] = "PT_LONG:PSETID_Task:0x8129";
155
156            $properties["complete"] = "PT_BOOLEAN:PSETID_Task:0x811c";
157            $properties["datecompleted"] = "PT_SYSTIME:PSETID_Task:0x810f";
158            $properties["recurring"] = "PT_BOOLEAN:PSETID_Task:0x8126";
159            $properties["startdate"] = "PT_SYSTIME:PSETID_Task:0x8104";
160            $properties["duedate"] = "PT_SYSTIME:PSETID_Task:0x8105";
161            $properties["status"] = "PT_LONG:PSETID_Task:0x8101";
162            $properties["percent_complete"] = "PT_DOUBLE:PSETID_Task:0x8102";
163            $properties["totalwork"] = "PT_LONG:PSETID_Task:0x8111";
164            $properties["actualwork"] = "PT_LONG:PSETID_Task:0x8110";
165            $properties["categories"] = "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords";
166            $properties["companies"] = "PT_MV_STRING8:PSETID_Common:0x8539";
167            $properties["mileage"] = "PT_STRING8:PSETID_Common:0x8534";
168            $properties["billinginformation"] = "PT_STRING8:PSETID_Common:0x8535";
169
170            $this->props = getPropIdsFromStrings($store, $properties);
171        }
172
173        // General functions
174
175        /* Return TRUE if the item is a task request message
176         */
177        function isTaskRequest()
178        {
179            $props = mapi_getprops($this->message, Array(PR_MESSAGE_CLASS));
180
181            if(isset($props[PR_MESSAGE_CLASS]) && $props[PR_MESSAGE_CLASS] == "IPM.TaskRequest") {
182                return true;
183            }
184        }
185
186        /* Return TRUE if the item is a task response message
187         */
188        function isTaskRequestResponse() {
189            $props = mapi_getprops($this->message, Array(PR_MESSAGE_CLASS));
190
191            if(isset($props[PR_MESSAGE_CLASS]) && strpos($props[PR_MESSAGE_CLASS], "IPM.TaskRequest.") === 0) {
192                return true;
193            }
194        }
195
196        /*
197         * Gets the task associated with an IPM.TaskRequest message
198         *
199         * If the task does not exist yet, it is created, using the attachment object in the
200         * task request item.
201         */
202        function getAssociatedTask($create)
203        {
204            $props = mapi_getprops($this->message, array(PR_MESSAGE_CLASS, $this->props['taskglobalobjid']));
205
206            if($props[PR_MESSAGE_CLASS] == "IPM.Task")
207                return $this->message; // Message itself is task, so return that
208
209            $tfolder = $this->getDefaultTasksFolder();
210            $globalobjid = $props[$this->props['taskglobalobjid']];
211
212            // Find the task by looking for the taskglobalobjid
213            $restriction = array(RES_PROPERTY, array(RELOP => RELOP_EQ, ULPROPTAG => $this->props['taskglobalobjid'], VALUE => $globalobjid));
214
215            $contents = mapi_folder_getcontentstable($tfolder);
216
217            $rows = mapi_table_queryallrows($contents, array(PR_ENTRYID), $restriction);
218
219            if(empty($rows)) {
220                // None found, create one if possible
221                if(!$create)
222                    return false;
223
224                $task = mapi_folder_createmessage($tfolder);
225
226                $sub = $this->getEmbeddedTask($this->message);
227                mapi_copyto($sub, array(), array(), $task);
228
229                // Copy sender information from the e-mail
230                $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));
231                mapi_setprops($task, $senderprops);
232
233                $senderprops = mapi_getprops($this->message, array(PR_SENDER_NAME, PR_SENDER_EMAIL_ADDRESS, PR_SENDER_ENTRYID, PR_SENDER_ADDRTYPE, PR_SENDER_SEARCH_KEY));
234                mapi_setprops($task, $senderprops);
235
236            } else {
237                // If there are multiple, just use the first
238                $entryid = $rows[0][PR_ENTRYID];
239
240                $store = $this->getTaskFolderStore();
241                $task = mapi_msgstore_openentry($store, $entryid);
242            }
243
244            return $task;
245        }
246
247
248
249        // Organizer functions (called by the organizer)
250
251        /* Processes a task request response, which can be any of the following:
252         * - Task accept (task history is marked as accepted)
253         * - Task decline (task history is marked as declined)
254         * - Task update (updates completion %, etc)
255         */
256        function processTaskResponse() {
257            $messageprops = mapi_getprops($this->message, array(PR_PROCESSED));
258            if(isset($messageprops[PR_PROCESSED]) && $messageprops[PR_PROCESSED])
259                return true;
260
261            // Get the task for this response
262            $task = $this->getAssociatedTask(false);
263
264            if(!$task) {
265                // Got a response for a task that has been deleted, create a new one and mark it as such
266                $task = $this->getAssociatedTask(true);
267
268                // tdsNOM indicates a task request that had gone missing
269                mapi_setprops($task, array($this->props['taskstate'] => tdsNOM ));
270            }
271
272            // Get the embedded task information and copy it into our task
273            $sub = $this->getEmbeddedTask($this->message);
274            mapi_copyto($sub, array(), array($this->props['taskstate'], $this->props['taskhistory'], $this->props['taskmode'], $this->props['taskfcreator']), $task);
275
276            $props = mapi_getprops($this->message, array(PR_MESSAGE_CLASS));
277
278            // Set correct taskmode and taskhistory depending on response type
279            switch($props[PR_MESSAGE_CLASS]) {
280                case 'IPM.TaskRequest.Accept':
281                    $taskhistory = thAccepted;
282                    $taskstate = tdsACC;
283                    $delegationstate =  olTaskDelegationAccepted;
284                    break;
285                case 'IPM.TaskRequest.Decline':
286                    $taskhistory = thDeclined;
287                    $taskstate = tdsDEC;
288                    $delegationstate =  olTaskDelegationDeclined;
289                    break;
290                case 'IPM.TaskRequest.Update':
291                    $taskhistory = thUpdated;
292                    $taskstate = tdsACC; // Doesn't actually change anything
293                    $delegationstate =  olTaskDelegationAccepted;
294                    break;
295            }
296
297            // Update taskstate (what the task looks like) and task history (last action done by the assignee)
298            mapi_setprops($task, array($this->props['taskhistory'] => $taskhistory, $this->props['taskstate'] => $taskstate, $this->props['delegationstate'] => $delegationstate, $this->props['ownership'] => olDelegatedTask));
299
300            mapi_setprops($this->message, array(PR_PROCESSED => true));
301            mapi_savechanges($task);
302
303            return true;
304        }
305
306        /* Create a new message in the current user's outbox and submit it
307         *
308         * Takes the task passed in the constructor as the task to be sent; recipient should
309         * be pre-existing. The task request will be sent to all recipients.
310         */
311        function sendTaskRequest($prefix) {
312            // Generate a TaskGlobalObjectId
313            $taskid = $this->createTGOID();
314            $messageprops = mapi_getprops($this->message, array(PR_SUBJECT));
315
316            // Set properties on Task Request
317            mapi_setprops($this->message, array(
318                $this->props['taskglobalobjid'] => $taskid, /* our new taskglobalobjid */
319                $this->props['taskstate'] => tdsACC,         /* state for our outgoing request */
320                $this->props['taskmode'] => tdmtNothing,     /* we're not sending a change */
321                $this->props['updatecount'] => 2,            /* version 2 (no idea) */
322                $this->props['delegationstate'] => olTaskDelegationUnknown, /* no reply yet */
323                $this->props['ownership'] => olDelegatedTask, /* Task has been assigned */
324                $this->props['taskhistory'] => thAssigned,    /* Task has been assigned */
325                PR_ICON_INDEX => 1283                        /* Task request icon*/
326            ));
327            $this->setLastUser();
328            $this->setOwnerForAssignor();
329            mapi_savechanges($this->message);
330
331            // Create outgoing task request message
332            $outgoing = $this->createOutgoingMessage();
333            // No need to copy attachments as task will be attached as embedded message.
334            mapi_copyto($this->message, array(), array(PR_MESSAGE_ATTACHMENTS), $outgoing);
335
336            // Make it a task request, and put it in sent items after it is sent
337            mapi_setprops($outgoing, array(
338                PR_MESSAGE_CLASS => "IPM.TaskRequest",         /* class is task request */
339                $this->props['taskstate'] => tdsOWNNEW,     /* for the recipient the task is new */
340                $this->props['taskmode'] => tdmtTaskReq,    /* for the recipient it's a request */
341                $this->props['updatecount'] => 1,            /* version 2 is in the attachment */
342                PR_SUBJECT => $prefix . $messageprops[PR_SUBJECT],
343                PR_ICON_INDEX => 0xFFFFFFFF,                /* show assigned icon */
344            ));
345
346            // Set Body
347            $body = $this->getBody();
348            $stream = mapi_openpropertytostream($outgoing, PR_BODY, MAPI_CREATE | MAPI_MODIFY);
349            mapi_stream_setsize($stream, strlen($body));
350            mapi_stream_write($stream, $body);
351            mapi_stream_commit($stream);
352
353            $attach = mapi_message_createattach($outgoing);
354            mapi_setprops($attach, array(PR_ATTACH_METHOD => ATTACH_EMBEDDED_MSG, PR_DISPLAY_NAME => $messageprops[PR_SUBJECT]));
355
356            $sub = mapi_attach_openproperty($attach, PR_ATTACH_DATA_OBJ, IID_IMessage, 0, MAPI_MODIFY | MAPI_CREATE);
357
358            mapi_copyto($this->message, array(), array(), $sub);
359            mapi_savechanges($sub);
360
361            mapi_savechanges($attach);
362
363            mapi_savechanges($outgoing);
364            mapi_message_submitmessage($outgoing);
365            return true;
366        }
367
368        // Assignee functions (called by the assignee)
369
370        /* Update task version counter
371         *
372         * Must be called before each update to increase counter
373         */
374        function updateTaskRequest() {
375            $messageprops = mapi_getprops($this->message, array($this->props['updatecount']));
376
377            if(isset($messageprops)) {
378                $messageprops[$this->props['updatecount']]++;
379            } else {
380                $messageprops[$this->props['updatecount']] = 1;
381            }
382
383            mapi_setprops($this->message, $messageprops);
384        }
385
386        /* Process a task request
387         *
388         * Message passed should be an IPM.TaskRequest message. The task request is then processed to create
389         * the task in the tasks folder if needed.
390         */
391        function processTaskRequest() {
392            if(!$this->isTaskRequest())
393                return false;
394
395            $messageprops = mapi_getprops($this->message, array(PR_PROCESSED));
396            if (isset($messageprops[PR_PROCESSED]) && $messageprops[PR_PROCESSED])
397                return true;
398
399            $task = $this->getAssociatedTask(true);
400            $taskProps = mapi_getprops($task, array($this->props['taskmultrecips']));
401
402            // 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
403            $taskProps[$this->props["taskstate"]] = tdsOWN;
404            $taskProps[$this->props["taskhistory"]] = thAssigned;
405            $taskProps[$this->props["taskmode"]] = tdmtNothing;
406            $taskProps[$this->props["taskaccepted"]] = false;
407            $taskProps[$this->props["taskfcreator"]] = false;
408            $taskProps[$this->props["ownership"]] = olOwnTask;
409            $taskProps[$this->props["delegationstate"]] = olTaskNotDelegated;
410            $taskProps[PR_ICON_INDEX] = 1282;
411
412            // This task was assigned to multiple recips, so set this user as owner
413            if (isset($taskProps[$this->props['taskmultrecips']]) && $taskProps[$this->props['taskmultrecips']] == tmrSent) {
414                $loginUserData = $this->retrieveUserData();
415
416                if ($loginUserData) {
417                    $taskProps[$this->props['owner']] = $loginUserData[PR_DISPLAY_NAME];
418                    $taskProps[$this->props['taskmultrecips']] = tmrReceived;
419                }
420            }
421            mapi_setprops($task, $taskProps);
422
423            $this->setAssignorInRecipients($task);
424
425            mapi_savechanges($task);
426
427            $taskprops = mapi_getprops($task, array(PR_ENTRYID));
428
429            mapi_setprops($this->message, array(PR_PROCESSED => true));
430            mapi_savechanges($this->message);
431
432            return $taskprops[PR_ENTRYID];
433        }
434
435        /* Accept a task request and send the response.
436         *
437         * Message passed should be an IPM.Task (eg the task from getAssociatedTask())
438         *
439         * Copies the task to the user's task folder, sets it to accepted, and sends the acceptation
440         * message back to the organizer. The caller is responsible for removing the message.
441         *
442         * @return entryid EntryID of the accepted task
443         */
444        function doAccept($prefix) {
445            $messageprops = mapi_getprops($this->message, array($this->props['taskstate']));
446
447            if(!isset($messageprops[$this->props['taskstate']]) || $messageprops[$this->props['taskstate']] != tdsOWN)
448                return false; // Can only accept assignee task
449
450            $this->setLastUser();
451            $this->updateTaskRequest();
452
453            // Set as accepted
454            mapi_setprops($this->message, array($this->props['taskhistory'] => thAccepted, $this->props['assignedtime'] => time(), $this->props['taskaccepted'] => true,  $this->props['delegationstate'] => olTaskNotDelegated));
455
456            mapi_savechanges($this->message);
457
458            $this->sendResponse(tdmtTaskAcc, $prefix);
459
460            //@TODO: delete received task request from Inbox
461            return $this->deleteReceivedTR();
462        }
463
464        /* Decline a task request and send the response.
465         *
466         * Passed message must be a task request message, ie isTaskRequest() must return TRUE.
467         *
468         * Sends the decline message back to the organizer. The caller is responsible for removing the message.
469         *
470         * @return boolean TRUE on success, FALSE on failure
471         */
472        function doDecline($prefix) {
473            $messageprops = mapi_getprops($this->message, array($this->props['taskstate']));
474
475            if(!isset($messageprops[$this->props['taskstate']]) || $messageprops[$this->props['taskstate']] != tdsOWN)
476                return false; // Can only decline assignee task
477
478            $this->setLastUser();
479            $this->updateTaskRequest();
480
481            // Set as declined
482            mapi_setprops($this->message, array($this->props['taskhistory'] => thDeclined,  $this->props['delegationstate'] => olTaskDelegationDeclined));
483
484            mapi_savechanges($this->message);
485
486            $this->sendResponse(tdmtTaskDec, $prefix);
487
488            return $this->deleteReceivedTR();
489        }
490
491        /* Send an update of the task if requested, and send the Status-On-Completion report if complete and requested
492         *
493         * If no updates were requested from the organizer, this function does nothing.
494         *
495         * @return boolean TRUE if the update succeeded, FALSE otherwise.
496         */
497        function doUpdate($prefix, $prefixComplete) {
498            $messageprops = mapi_getprops($this->message, array($this->props['taskstate'], PR_SUBJECT));
499
500            if(!isset($messageprops[$this->props['taskstate']]) || $messageprops[$this->props['taskstate']] != tdsOWN)
501                return false; // Can only update assignee task
502
503            $this->setLastUser();
504            $this->updateTaskRequest();
505
506            // Set as updated
507            mapi_setprops($this->message, array($this->props['taskhistory'] => thUpdated));
508
509            mapi_savechanges($this->message);
510
511            $props = mapi_getprops($this->message, array($this->props['taskupdates'], $this->props['tasksoc'], $this->props['recurring'], $this->props['complete']));
512            if ($props[$this->props['taskupdates']] && !(isset($props[$this->props['recurring']]) && $props[$this->props['recurring']]))
513                $this->sendResponse(tdmtTaskUpd, $prefix);
514
515            if($props[$this->props['tasksoc']]  && $props[$this->props['complete']] ) {
516                $outgoing = $this->createOutgoingMessage();
517
518                mapi_setprops($outgoing, array(PR_SUBJECT => $prefixComplete . $messageprops[PR_SUBJECT]));
519
520                $this->setRecipientsForResponse($outgoing, tdmtTaskUpd, true);
521                $body = $this->getBody();
522                $stream = mapi_openpropertytostream($outgoing, PR_BODY, MAPI_CREATE | MAPI_MODIFY);
523                mapi_stream_setsize($stream, strlen($body));
524                mapi_stream_write($stream, $body);
525                mapi_stream_commit($stream);
526
527                mapi_savechanges($outgoing);
528                mapi_message_submitmessage($outgoing);
529            }
530        }
531
532        // Internal functions
533
534        /* Get the store associated with the task
535         *
536         * Normally this will just open the store that the processed message is in. However, if the message is opened
537         * by a delegate, this function opens the store that the message was delegated from.
538         */
539        function getTaskFolderStore()
540        {
541            $ownerentryid = false;
542
543            $rcvdprops = mapi_getprops($this->message, array(PR_RCVD_REPRESENTING_ENTRYID));
544            if(isset($rcvdprops[PR_RCVD_REPRESENTING_ENTRYID])) {
545                $ownerentryid = $rcvdprops;
546            }
547
548            if(!$ownerentryid) {
549                $store = $this->store;
550            } else {
551                $ab = mapi_openaddressbook($this->session); // seb changed from $session to $this->session
552                if(!$ab) return false; // manni $ before ab was missing
553
554                $mailuser = mapi_ab_openentry($ab, $ownerentryid);
555                if(!$mailuser) return false;
556
557                $mailuserprops = mapi_getprops($mailuser, array(PR_EMAIL_ADDRESS));
558                if(!isset($mailuserprops[PR_EMAIL_ADDRESS])) return false;
559
560                $storeid = mapi_msgstore_createentryid($this->store, $mailuserprops[PR_EMAIL_ADDRESS]);
561
562                $store = mapi_openmsgstore($this->session, $storeid);
563
564            }
565            return $store;
566        }
567
568        /* Open the default task folder for the current user, or the specified user if passed
569         *
570         * @param $ownerentryid (Optional)EntryID of user for which we are opening the task folder
571         */
572        function getDefaultTasksFolder()
573        {
574            $store = $this->getTaskFolderStore();
575
576            $inbox = mapi_msgstore_getreceivefolder($store);
577            $inboxprops = mapi_getprops($inbox, Array(PR_IPM_TASK_ENTRYID));
578            if(!isset($inboxprops[PR_IPM_TASK_ENTRYID]))
579                return false;
580
581            return mapi_msgstore_openentry($store, $inboxprops[PR_IPM_TASK_ENTRYID]);
582        }
583
584        function getSentReprProps($store)
585        {
586            $storeprops = mapi_getprops($store, array(PR_MAILBOX_OWNER_ENTRYID));
587            if(!isset($storeprops[PR_MAILBOX_OWNER_ENTRYID])) return false;
588
589            $ab = mapi_openaddressbook($this->session);
590            $mailuser = mapi_ab_openentry($ab, $storeprops[PR_MAILBOX_OWNER_ENTRYID]);
591            $mailuserprops = mapi_getprops($mailuser, array(PR_ADDRTYPE, PR_EMAIL_ADDRESS, PR_DISPLAY_NAME, PR_SEARCH_KEY, PR_ENTRYID));
592
593            $props = array();
594            $props[PR_SENT_REPRESENTING_ADDRTYPE] = $mailuserprops[PR_ADDRTYPE];
595            $props[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $mailuserprops[PR_EMAIL_ADDRESS];
596            $props[PR_SENT_REPRESENTING_NAME] = $mailuserprops[PR_DISPLAY_NAME];
597            $props[PR_SENT_REPRESENTING_SEARCH_KEY] = $mailuserprops[PR_SEARCH_KEY];
598            $props[PR_SENT_REPRESENTING_ENTRYID] = $mailuserprops[PR_ENTRYID];
599
600            return $props;
601        }
602
603        /*
604         * Creates an outgoing message based on the passed message - will set delegate information
605         * and sentmail folder
606         */
607        function createOutgoingMessage()
608        {
609            // Open our default store for this user (that's the only store we can submit in)
610            $store = $this->getDefaultStore();
611            $storeprops = mapi_getprops($store, array(PR_IPM_OUTBOX_ENTRYID, PR_IPM_SENTMAIL_ENTRYID));
612
613            $outbox = mapi_msgstore_openentry($store, $storeprops[PR_IPM_OUTBOX_ENTRYID]);
614            if(!$outbox) return false;
615
616            $outgoing = mapi_folder_createmessage($outbox);
617            if(!$outgoing) return false;
618
619            // Set SENT_REPRESENTING in case we're sending as a delegate
620            $ownerstore = $this->getTaskFolderStore();
621            $sentreprprops = $this->getSentReprProps($ownerstore);
622            mapi_setprops($outgoing, $sentreprprops);
623
624            mapi_setprops($outgoing, array(PR_SENTMAIL_ENTRYID => $storeprops[PR_IPM_SENTMAIL_ENTRYID]));
625
626            return $outgoing;
627        }
628
629        /*
630         * Send a response message (from assignee back to organizer).
631         *
632         * @param $type int Type of response (tdmtTaskAcc, tdmtTaskDec, tdmtTaskUpd);
633         * @return boolean TRUE on success
634         */
635        function sendResponse($type, $prefix)
636        {
637            // Create a message in our outbox
638            $outgoing = $this->createOutgoingMessage();
639
640            $messageprops = mapi_getprops($this->message, array(PR_SUBJECT));
641
642            $attach = mapi_message_createattach($outgoing);
643            mapi_setprops($attach, array(PR_ATTACH_METHOD => ATTACH_EMBEDDED_MSG, PR_DISPLAY_NAME => $messageprops[PR_SUBJECT], PR_ATTACHMENT_HIDDEN => true));
644            $sub = mapi_attach_openproperty($attach, PR_ATTACH_DATA_OBJ, IID_IMessage, 0, MAPI_CREATE | MAPI_MODIFY);
645
646            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);
647            mapi_copyto($this->message, array(), array(), $sub);
648
649            if (!$this->setRecipientsForResponse($outgoing, $type)) return false;
650
651            switch($type) {
652                case tdmtTaskAcc:
653                    $messageclass = "IPM.TaskRequest.Accept";
654                    break;
655                case tdmtTaskDec:
656                    $messageclass = "IPM.TaskRequest.Decline";
657                    break;
658                case tdmtTaskUpd:
659                    $messageclass = "IPM.TaskRequest.Update";
660                    break;
661            };
662
663            mapi_savechanges($sub);
664            mapi_savechanges($attach);
665
666            // Set Body
667            $body = $this->getBody();
668            $stream = mapi_openpropertytostream($outgoing, PR_BODY, MAPI_CREATE | MAPI_MODIFY);
669            mapi_stream_setsize($stream, strlen($body));
670            mapi_stream_write($stream, $body);
671            mapi_stream_commit($stream);
672
673            // Set subject, taskmode, message class, icon index, response time
674            mapi_setprops($outgoing, array(PR_SUBJECT => $prefix . $messageprops[PR_SUBJECT],
675                                            $this->props['taskmode'] => $type,
676                                            PR_MESSAGE_CLASS => $messageclass,
677                                            PR_ICON_INDEX => 0xFFFFFFFF,
678                                            $this->props['assignedtime'] => time()));
679
680            mapi_savechanges($outgoing);
681            mapi_message_submitmessage($outgoing);
682
683            return true;
684        }
685
686        function getDefaultStore()
687        {
688            $table = mapi_getmsgstorestable($this->session);
689            $rows = mapi_table_queryallrows($table, array(PR_DEFAULT_STORE, PR_ENTRYID));
690
691            foreach($rows as $row) {
692                if($row[PR_DEFAULT_STORE])
693                    return mapi_openmsgstore($this->session, $row[PR_ENTRYID]);
694            }
695
696            return false;
697        }
698
699        /* Creates a new TaskGlobalObjId
700         *
701         * Just 16 bytes of random data
702         */
703        function createTGOID()
704        {
705            $goid = "";
706            for($i=0;$i<16;$i++) {
707                $goid .= chr(rand(0, 255));
708            }
709            return $goid;
710        }
711
712        function getEmbeddedTask($message) {
713            $table = mapi_message_getattachmenttable($message);
714            $rows = mapi_table_queryallrows($table, array(PR_ATTACH_NUM));
715
716            // Assume only one attachment
717            if(empty($rows))
718                return false;
719
720            $attach = mapi_message_openattach($message, $rows[0][PR_ATTACH_NUM]);
721            $message = mapi_openproperty($attach, PR_ATTACH_DATA_OBJ, IID_IMessage, 0, 0);
722
723            return $message;
724        }
725
726        function setLastUser() {
727            $delegatestore = $this->getDefaultStore();
728            $taskstore = $this->getTaskFolderStore();
729
730            $delegateprops = mapi_getprops($delegatestore, array(PR_MAILBOX_OWNER_NAME));
731            $taskprops = mapi_getprops($taskstore, array(PR_MAILBOX_OWNER_NAME));
732
733            // The owner of the task
734            $username = $delegateprops[PR_MAILBOX_OWNER_NAME];
735            // This is me (the one calling the script)
736            $delegate = $taskprops[PR_MAILBOX_OWNER_NAME];
737
738            mapi_setprops($this->message, array($this->props["tasklastuser"] => $username, $this->props["tasklastdelegate"] => $delegate, $this->props['assignedtime'] => time()));
739        }
740
741        /** Assignee becomes the owner when a user/assignor assigns any task to someone. Also there can be more than one assignee.
742         * This function sets assignee as owner in the assignor's copy of task.
743         */
744        function setOwnerForAssignor()
745        {
746            $recipTable = mapi_message_getrecipienttable($this->message);
747            $recips = mapi_table_queryallrows($recipTable, array(PR_DISPLAY_NAME));
748
749            if (!empty($recips)) {
750                $owner = array();
751                foreach ($recips as $value) {
752                    $owner[] = $value[PR_DISPLAY_NAME];
753                }
754
755                $props = array($this->props['owner'] => implode("; ", $owner));
756                mapi_setprops($this->message, $props);
757            }
758        }
759
760        /** Sets assignor as recipients in assignee's copy of task.
761         *
762         * If assignor has requested task updates then the assignor is added as recipient type MAPI_CC.
763         *
764         * Also if assignor has request SOC then the assignor is also add as recipient type MAPI_BCC
765         *
766         * @param $task message MAPI message which assignee's copy of task
767         */
768        function setAssignorInRecipients($task)
769        {
770            $recipTable = mapi_message_getrecipienttable($task);
771
772            // Delete all MAPI_TO recipients
773            $recips = mapi_table_queryallrows($recipTable, array(PR_ROWID), array(RES_PROPERTY,
774                                                                                array(    RELOP => RELOP_EQ,
775                                                                                        ULPROPTAG => PR_RECIPIENT_TYPE,
776                                                                                        VALUE => MAPI_TO
777                                                                                )));
778            foreach($recips as $recip)
779                mapi_message_modifyrecipients($task, MODRECIP_REMOVE, array($recip));
780
781            $recips = array();
782            $taskReqProps = mapi_getprops($this->message, array(PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_EMAIL_ADDRESS, PR_SENT_REPRESENTING_ENTRYID, PR_SENT_REPRESENTING_ADDRTYPE));
783            $associatedTaskProps = mapi_getprops($task, array($this->props['taskupdates'], $this->props['tasksoc'], $this->props['taskmultrecips']));
784
785            // Build assignor info
786            $assignor = array(    PR_ENTRYID => $taskReqProps[PR_SENT_REPRESENTING_ENTRYID],
787                                PR_DISPLAY_NAME => $taskReqProps[PR_SENT_REPRESENTING_NAME],
788                                PR_EMAIL_ADDRESS => $taskReqProps[PR_SENT_REPRESENTING_EMAIL_ADDRESS],
789                                PR_RECIPIENT_DISPLAY_NAME => $taskReqProps[PR_SENT_REPRESENTING_NAME],
790                                PR_ADDRTYPE => empty($taskReqProps[PR_SENT_REPRESENTING_ADDRTYPE]) ? 'SMTP' : $taskReqProps[PR_SENT_REPRESENTING_ADDRTYPE],
791                                PR_RECIPIENT_FLAGS => recipSendable
792                        );
793
794            // Assignor has requested task updates, so set him/her as MAPI_CC in recipienttable.
795            if ((isset($associatedTaskProps[$this->props['taskupdates']]) && $associatedTaskProps[$this->props['taskupdates']])
796                && !(isset($associatedTaskProps[$this->props['taskmultrecips']]) && $associatedTaskProps[$this->props['taskmultrecips']] == tmrReceived)) {
797                $assignor[PR_RECIPIENT_TYPE] = MAPI_CC;
798                $recips[] = $assignor;
799            }
800
801            // Assignor wants to receive an email report when task is mark as 'Complete', so in recipients as MAPI_BCC
802            if (isset($associatedTaskProps[$this->props['taskupdates']]) && $associatedTaskProps[$this->props['tasksoc']]) {
803                $assignor[PR_RECIPIENT_TYPE] = MAPI_BCC;
804                $recips[] = $assignor;
805            }
806
807            if (!empty($recips))
808                mapi_message_modifyrecipients($task, MODRECIP_ADD, $recips);
809        }
810
811        /** Returns user information who has task request
812         */
813        function retrieveUserData()
814        {
815            // get user entryid
816            $storeProps = mapi_getprops($this->store, array(PR_USER_ENTRYID));
817            if (!$storeProps[PR_USER_ENTRYID])    return false;
818
819            $ab = mapi_openaddressbook($this->session);
820            // open the user entry
821            $user = mapi_ab_openentry($ab, $storeProps[PR_USER_ENTRYID]);
822            if (!$user) return false;
823
824            // receive userdata
825            $userProps = mapi_getprops($user, array(PR_DISPLAY_NAME));
826            if (!$userProps[PR_DISPLAY_NAME]) return false;
827
828            return $userProps;
829        }
830
831        /** Deletes incoming task request from Inbox
832         *
833         * @returns array returns PR_ENTRYID, PR_STORE_ENTRYID and PR_PARENT_ENTRYID of the deleted task request
834         */
835        function deleteReceivedTR()
836        {
837            $store = $this->getTaskFolderStore();
838            $inbox = mapi_msgstore_getreceivefolder($store);
839
840            $storeProps = mapi_getprops($store, array(PR_IPM_WASTEBASKET_ENTRYID));
841            $props = mapi_getprops($this->message, array($this->props['taskglobalobjid']));
842            $globalobjid = $props[$this->props['taskglobalobjid']];
843
844            // Find the task by looking for the taskglobalobjid
845            $restriction = array(RES_PROPERTY, array(RELOP => RELOP_EQ, ULPROPTAG => $this->props['taskglobalobjid'], VALUE => $globalobjid));
846
847            $contents = mapi_folder_getcontentstable($inbox);
848
849            $rows = mapi_table_queryallrows($contents, array(PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID), $restriction);
850
851            $taskrequest = false;
852            if(!empty($rows)) {
853                // If there are multiple, just use the first
854                $entryid = $rows[0][PR_ENTRYID];
855                $wastebasket = mapi_msgstore_openentry($store, $storeProps[PR_IPM_WASTEBASKET_ENTRYID]);
856                mapi_folder_copymessages($inbox, Array($entryid), $wastebasket, MESSAGE_MOVE);
857
858                return array(PR_ENTRYID => $entryid, PR_PARENT_ENTRYID => $rows[0][PR_PARENT_ENTRYID], PR_STORE_ENTRYID => $rows[0][PR_STORE_ENTRYID]);
859            }
860
861            return false;
862        }
863
864        /** Converts already sent task request to normal task
865         */
866        function createUnassignedCopy()
867        {
868            mapi_deleteprops($this->message, array($this->props['taskglobalobjid']));
869            mapi_setprops($this->message, array($this->props['updatecount'] => 1));
870
871            // Remove all recipents
872            $this->deleteAllRecipients($this->message);
873        }
874
875        /** Sets recipients for the outgoing message according to type of the response.
876         *
877         * If it is a task update, then only recipient type MAPI_CC are taken from the task message.
878         *
879         * If it is accept/decline response, then PR_SENT_REPRESENTATING_XXXX are taken as recipient.
880         *
881         *@param $outgoing MAPI_message outgoing mapi message
882         *@param $responseType String response type
883         *@param $sendSOC Boolean true if sending complete response else false.
884         */
885        function setRecipientsForResponse($outgoing, $responseType, $sendSOC = false)
886        {
887            // Clear recipients from outgoing msg
888            $this->deleteAllRecipients($outgoing);
889
890            // If it is a task update then get MAPI_CC recipients which are assignors who has asked for task update.
891            if ($responseType == tdmtTaskUpd) {
892                $recipTable = mapi_message_getrecipienttable($this->message);
893                $recips = mapi_table_queryallrows($recipTable, $this->recipprops, array(RES_PROPERTY,
894                                                                                        array(    RELOP => RELOP_EQ,
895                                                                                                ULPROPTAG => PR_RECIPIENT_TYPE,
896                                                                                                VALUE => ($sendSOC ? MAPI_BCC : MAPI_CC)
897                                                                                        )
898                                                ));
899
900                // No recipients found, return error
901                if (empty($recips))
902                    return false;
903
904                foreach($recips as $recip) {
905                    $recip[PR_RECIPIENT_TYPE] = MAPI_TO;    // Change recipient type to MAPI_TO
906                    mapi_message_modifyrecipients($outgoing, MODRECIP_ADD, array($recip));
907                }
908                return true;
909            }
910
911            $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));
912            $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);
913
914            mapi_message_modifyrecipients($outgoing, MODRECIP_ADD, array($recip));
915
916            return true;
917        }
918
919        /** Adds task details to message body and returns body.
920         *
921         *@return string contructed body with task details.
922         */
923        function getBody()
924        {
925            //@TODO: Fix translations
926
927            $msgProps = mapi_getprops($this->message);
928            $body = "";
929
930            if (isset($msgProps[PR_SUBJECT])) $body .= "\n" . _("Subject") . ":\t". $msgProps[PR_SUBJECT];
931            if (isset($msgProps[$this->props['startdate']])) $body .= "\n" . _("Start Date") . ":\t". strftime(_("%A, %B %d, %Y"),$msgProps[$this->props['startdate']]);
932            if (isset($msgProps[$this->props['duedate']])) $body .= "\n" . _("Due Date") . ":\t". strftime(_("%A, %B %d, %Y"),$msgProps[$this->props['duedate']]);
933            $body .= "\n";
934
935            if (isset($msgProps[$this->props['status']])) {
936                $body .= "\n" . _("Status") . ":\t";
937                if ($msgProps[$this->props['status']] == 0) $body .= _("Not Started");
938                else if ($msgProps[$this->props['status']] == 1) $body .= _("In Progress");
939                else if ($msgProps[$this->props['status']] == 2) $body .= _("Complete");
940                else if ($msgProps[$this->props['status']] == 3) $body .= _("Wait for other person");
941                else if ($msgProps[$this->props['status']] == 4) $body .= _("Deferred");
942            }
943
944            if (isset($msgProps[$this->props['percent_complete']])) {
945                $body .= "\n" . _("Percent Complete") . ":\t". ($msgProps[$this->props['percent_complete']] * 100).'%';
946
947                if ($msgProps[$this->props['percent_complete']] == 1 && isset($msgProps[$this->props['datecompleted']]))
948                    $body .= "\n" . _("Date Completed") . ":\t". strftime("%A, %B %d, %Y",$msgProps[$this->props['datecompleted']]);
949            }
950            $body .= "\n";
951
952            if (isset($msgProps[$this->props['totalwork']])) $body .= "\n" . _("Total Work") . ":\t". ($msgProps[$this->props['totalwork']]/60) ." " . _("hours");
953            if (isset($msgProps[$this->props['actualwork']])) $body .= "\n" . _("Actual Work") . ":\t". ($msgProps[$this->props['actualwork']]/60) ." " . _("hours");
954            $body .="\n";
955
956            if (isset($msgProps[$this->props['owner']])) $body .= "\n" . _("Owner") . ":\t". $msgProps[$this->props['owner']];
957            $body .="\n";
958
959            if (isset($msgProps[$this->props['categories']]) && !empty($msgProps[$this->props['categories']])) $body .= "\nCategories:\t". implode(', ', $msgProps[$this->props['categories']]);
960            if (isset($msgProps[$this->props['companies']]) && !empty($msgProps[$this->props['companies']])) $body .= "\nCompany:\t". implode(', ', $msgProps[$this->props['companies']]);
961            if (isset($msgProps[$this->props['billinginformation']])) $body .= "\n" . _("Billing Information") . ":\t". $msgProps[$this->props['billinginformation']];
962            if (isset($msgProps[$this->props['mileage']])) $body .= "\n" . _("Mileage") . ":\t". $msgProps[$this->props['mileage']];
963            $body .="\n";
964
965            $content = mapi_message_openproperty($this->message, PR_BODY);
966            $body .= "\n". trim($content, "\0");
967
968            return $body;
969        }
970
971        /**
972        * Convert from windows-1252 encoded string to UTF-8 string
973        *
974        * The same conversion rules as utf8_to_windows1252 apply.
975        *
976        * @see Conversion::utf8_to_windows1252()
977        *
978        * @param string $string the Windows-1252 string to convert
979        * @return string UTF-8 representation of the string
980        */
981        function windows1252_to_utf8($string)
982        {
983            if (function_exists("iconv")){
984                return iconv("Windows-1252", "UTF-8//TRANSLIT", $string);
985            }else{
986                return utf8_encode($string); // no euro support here
987            }
988        }
989
990        /** Reclaims ownership of a decline task
991         *
992         * Deletes taskrequest properties and recipients from the task message.
993         */
994        function reclaimownership()
995        {
996            // Delete task request properties
997            mapi_deleteprops($this->message, array($this->props['taskglobalobjid'],
998                                                    $this->props['tasklastuser'],
999                                                    $this->props['tasklastdelegate']));
1000
1001            mapi_setprops($this->message, array($this->props['updatecount'] => 2,
1002                                                $this->props['taskfcreator'] => true));
1003
1004            // Delete recipients
1005            $this->deleteAllRecipients($this->message);
1006        }
1007
1008        /** Deletes all recipients from given message object
1009         *
1010         *@param $message MAPI message from which recipients are to be removed.
1011         */
1012        function deleteAllRecipients($message)
1013        {
1014            $recipTable = mapi_message_getrecipienttable($message);
1015            $recipRows = mapi_table_queryallrows($recipTable, array(PR_ROWID));
1016
1017            foreach($recipRows as $recipient)
1018                mapi_message_modifyrecipients($message, MODRECIP_REMOVE, array($recipient));
1019        }
1020
1021        function sendCompleteUpdate($prefix, $action, $prefixComplete)
1022        {
1023            $messageprops = mapi_getprops($this->message, array($this->props['taskstate']));
1024
1025            if(!isset($messageprops[$this->props['taskstate']]) || $messageprops[$this->props['taskstate']] != tdsOWN)
1026                return false; // Can only decline assignee task
1027
1028            mapi_setprops($this->message, array($this->props['complete'] => true,
1029                                                $this->props['datecompleted'] => $action["dateCompleted"],
1030                                                $this->props['status'] => 2,
1031                                                $this->props['percent_complete'] => 1));
1032
1033            $this->doUpdate($prefix, $prefixComplete);
1034        }
1035    }
1036?>
Note: See TracBrowser for help on using the repository browser.