Test Failed
Push — master ( 647c72...cd42b5 )
by
unknown
10:25
created

TaskRequest   F

Complexity

Total Complexity 142

Size/Duplication

Total Lines 1185
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 593
dl 0
loc 1185
rs 2
c 0
b 0
f 0
wmc 142

32 Methods

Rating   Name   Duplication   Size   Complexity  
A getDefaultTasksFolder() 0 10 2
A doAccept() 0 39 5
A createTGOID() 0 7 2
A createOutgoingMessage() 0 20 3
A sendCompleteUpdate() 0 14 3
B doUpdate() 0 20 8
A updateTaskRequest() 0 10 2
A __construct() 0 39 1
A getDefaultStore() 0 11 3
A isReceivedItem() 0 3 2
A getSentReprProps() 0 19 2
B setAssignorInRecipients() 0 44 9
B sendResponse() 0 64 8
A updateSentTaskRequest() 0 37 4
A deleteReceivedTR() 0 34 3
A getSubProperties() 0 19 1
F processTaskResponse() 0 114 21
A setTaskCommentsInfo() 0 3 1
A doDecline() 0 29 4
A getTaskFolderStore() 0 27 6
A setLastUser() 0 19 2
A getTaskCommentsInfo() 0 3 1
A getEmbeddedTask() 0 32 5
A getAssociatedTask() 0 58 4
B isTaskRequestUpdated() 0 38 8
B processTaskRequest() 0 50 11
A isTaskRequest() 0 12 5
A sendTaskRequest() 0 57 1
A deleteAllRecipients() 0 7 2
A setRecipientsForResponse() 0 41 5
A isTaskRequestResponse() 0 12 5
A setOwnerForAssignor() 0 13 3

How to fix   Complexity   

Complex Class

Complex classes like TaskRequest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TaskRequest, and based on these observations, apply Extract Interface, too.

1
<?php
2
	/*
3
	* In general
4
	*
5
	* This class never actually modifies a task item unless we receive a task request update. This means
6
	* that setting all the properties to make the task item itself behave like a task request is up to the
7
	* caller.
8
	*
9
	* The only exception to this is the generation of the TaskGlobalObjId, the unique identifier identifying
10
	* this task request to both the organizer and the assignee. The globalobjectid is generated when the
11
	* task request is sent via sendTaskRequest.
12
	*/
13
14
	/* The TaskMode value is only used for the IPM.TaskRequest items.
15
	 * It must 0 (tdmtNothing) on IPM.Task items.
16
	 *
17
	 * It is used to indicate the type of change that is being
18
	 * carried in the IPM.TaskRequest item (although this information seems
19
	 * redundant due to that information already being available in PR_MESSAGE_CLASS).
20
	 */
21
	define('tdmtNothing', 0);			// Value in IPM.Task items
22
	define('tdmtTaskReq', 1);			// Assigner -> Assignee
23
	define('tdmtTaskAcc', 2);			// Assignee -> Assigner
24
	define('tdmtTaskDec', 3);			// Assignee -> Assigner
25
	define('tdmtTaskUpd', 4);			// Assignee -> Assigner
26
	define('tdmtTaskSELF', 5);			// Assigner -> Assigner (?)
27
28
	/* The TaskHistory is used to show the last action on the task
29
	 * on both the assigner and the assignee's side.
30
	 *
31
	 * It is used in combination with 'task_assigned_time' and 'tasklastdelegate'
32
	 * or 'tasklastuser' to show the information at the top of the task request in
33
	 * the format 'Accepted by <user> on 01-01-2010 11:00'.
34
	 */
35
	define('thNone', 0);
36
	define('thAccepted', 1);			// Set by assignee
37
	define('thDeclined', 2);			// Set by assignee
38
	define('thUpdated', 3);				// Set by assignee
39
	define('thDueDateChanged', 4);
40
	define('thAssigned', 5);			// Set by assigner
41
42
	/* The TaskState value is used to differentiate the version of a task
43
	 * in the assigner's folder and the version in the
44
	 * assignee's folder. The buttons shown depend on this and
45
	 * the 'taskaccepted' boolean (for the assignee)
46
	 */
47
	define('tdsNOM', 0);		// Got a response to a deleted task, and re-created the task for the assigner
48
	define('tdsOWNNEW', 1);		// Not assigned
49
	define('tdsOWN', 2);		// Assignee version
50
	define('tdsACC', 3);		// Assigner version
51
	define('tdsDEC', 4);		// Assigner version, but assignee declined
52
53
	/* The TaskAcceptanceState is used for the assigner to indicate state */
54
	define('olTaskNotDelegated', 0);
55
	define('olTaskDelegationUnknown', 1); // After sending req
56
	define('olTaskDelegationAccepted', 2); // After receiving accept
57
	define('olTaskDelegationDeclined', 3); // After receiving decline
58
59
	/* The task ownership indicates the role of the current user relative to the task. */
60
	define('olNewTask', 0);
61
	define('olDelegatedTask', 1);	// Task has been assigned
62
	define('olOwnTask', 2);			// Task owned
63
64
	/* taskmultrecips indicates whether the task request sent or received has multiple assignees or not. */
65
	define('tmrNone', 0);
66
	define('tmrSent', 1);		// Task has been sent to multiple assignee
67
	define('tmrReceived', 2);	// Task Request received has multiple assignee
68
69
	//Task icon index.
70
	define('ICON_TASK_ASSIGNEE', 0x00000502);
71
	define('ICON_TASK_DECLINE', 0x00000506);
72
	define('ICON_TASK_ASSIGNER', 0x00000503);
73
74
	class TaskRequest {
75
76
		// All recipient properties
77
		var $recipProps = Array(
78
			PR_ENTRYID,
79
			PR_DISPLAY_NAME,
80
			PR_EMAIL_ADDRESS,
81
			PR_RECIPIENT_ENTRYID,
82
			PR_RECIPIENT_TYPE,
83
			PR_SEND_INTERNET_ENCODING,
84
			PR_SEND_RICH_INFO,
85
			PR_RECIPIENT_DISPLAY_NAME,
86
			PR_ADDRTYPE,
87
			PR_DISPLAY_TYPE,
88
			PR_RECIPIENT_TRACKSTATUS,
89
			PR_RECIPIENT_TRACKSTATUS_TIME,
90
			PR_RECIPIENT_FLAGS,
91
			PR_ROWID,
92
			PR_SEARCH_KEY
93
		);
94
95
		/* Constructor
96
		 *
97
		 * Constructs a TaskRequest object for the specified message. This can be either the task request
98
		 * message itself (in the inbox) or the task in the tasks folder, depending on the action to be performed.
99
		 *
100
		 * As a general rule, the object message passed is the object 'in view' when the user performs one of the
101
		 * actions in this class.
102
		 *
103
		 * @param $store store MAPI Store in which $message resides. This is also the store where the tasks folder is assumed to be in
0 ignored issues
show
Bug introduced by
The type store was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
104
		 * @param $message message MAPI Message to which the task request refers (can be an email or a task)
105
		 * @param $session session MAPI Session which is used to open tasks folders for delegated task requests or responses
106
		 */
107
		function __construct($store, $message, $session) {
108
			$this->store = $store;
0 ignored issues
show
Bug Best Practice introduced by
The property store does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
109
			$this->message = $message;
0 ignored issues
show
Bug Best Practice introduced by
The property message does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
110
			$this->session = $session;
0 ignored issues
show
Bug Best Practice introduced by
The property session does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
111
			$this->taskCommentsInfo = false;
0 ignored issues
show
Bug Best Practice introduced by
The property taskCommentsInfo does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
112
113
			$properties["owner"] = "PT_STRING8:PSETID_Task:0x811f";
0 ignored issues
show
Comprehensibility Best Practice introduced by
$properties was never initialized. Although not strictly required by PHP, it is generally a good practice to add $properties = array(); before regardless.
Loading history...
114
			$properties["updatecount"] = "PT_LONG:PSETID_Task:0x8112";
115
			$properties["taskstate"] = "PT_LONG:PSETID_Task:0x8113";
116
			$properties["taskmultrecips"] = "PT_LONG:PSETID_Task:0x8120";
117
			$properties["taskupdates"] = "PT_BOOLEAN:PSETID_Task:0x811b";
118
			$properties["tasksoc"] = "PT_BOOLEAN:PSETID_Task:0x8119";
119
			$properties["taskhistory"] = "PT_LONG:PSETID_Task:0x811a";
120
			$properties["taskmode"] = "PT_LONG:PSETID_Common:0x8518";
121
			$properties["task_goid"] = "PT_BINARY:PSETID_Common:0x8519";
122
			$properties["complete"] = "PT_BOOLEAN:PSETID_Common:0x811c";
123
			$properties["task_assigned_time"] = "PT_SYSTIME:PSETID_Task:0x8115";
124
			$properties["taskfcreator"] = "PT_BOOLEAN:PSETID_Task:0x0x811e";
125
			$properties["tasklastuser"] = "PT_STRING8:PSETID_Task:0x8122";
126
			$properties["tasklastdelegate"] = "PT_STRING8:PSETID_Task:0x8125";
127
			$properties["taskaccepted"] = "PT_BOOLEAN:PSETID_Task:0x8108";
128
			$properties["task_acceptance_state"] = "PT_LONG:PSETID_Task:0x812a";
129
			$properties["ownership"] = "PT_LONG:PSETID_Task:0x8129";
130
131
			$properties["complete"] = "PT_BOOLEAN:PSETID_Task:0x811c";
132
			$properties["datecompleted"] = "PT_SYSTIME:PSETID_Task:0x810f";
133
			$properties["recurring"] = "PT_BOOLEAN:PSETID_Task:0x8126";
134
			$properties["startdate"] = "PT_SYSTIME:PSETID_Task:0x8104";
135
			$properties["duedate"] = "PT_SYSTIME:PSETID_Task:0x8105";
136
			$properties["status"] = "PT_LONG:PSETID_Task:0x8101";
137
			$properties["percent_complete"] = "PT_DOUBLE:PSETID_Task:0x8102";
138
			$properties["totalwork"] = "PT_LONG:PSETID_Task:0x8111";
139
			$properties["actualwork"] = "PT_LONG:PSETID_Task:0x8110";
140
			$properties["categories"] = "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords";
141
			$properties["companies"] = "PT_MV_STRING8:PSETID_Common:0x8539";
142
			$properties["mileage"] = "PT_STRING8:PSETID_Common:0x8534";
143
			$properties["billinginformation"] = "PT_STRING8:PSETID_Common:0x8535";
144
145
			$this->props = getPropIdsFromStrings($store, $properties);
0 ignored issues
show
Bug Best Practice introduced by
The property props does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
146
		}
147
148
		// General functions
149
150
		/**
151
		 * Returns TRUE if the message pointed to is an incoming task request and should
152
		 * therefore be replied to with doAccept or doDecline().
153
		 * @param String $messageClass message class to use for checking.
154
		 * @return Boolean Returns true if this is a task request else false.
155
		 */
156
		function isTaskRequest($messageClass = false)
157
		{
158
			if($messageClass === false) {
159
				$props = mapi_getprops($this->message, Array(PR_MESSAGE_CLASS));
160
				$messageClass = isset($props[PR_MESSAGE_CLASS]) ? $props[PR_MESSAGE_CLASS] : false;
161
			}
162
163
			if($messageClass !== false &&  $messageClass === "IPM.TaskRequest") {
164
				return true;
165
			}
166
167
			return false;
168
		}
169
170
		/**
171
		 * Returns TRUE if the message pointed to is a returning task request response.
172
		 * @param String $messageClass message class to use for checking.
173
		 * @return Boolean Returns true if this is a task request else false.
174
		 */
175
		function isTaskRequestResponse($messageClass = false)
176
		{
177
			if($messageClass === false) {
178
				$props = mapi_getprops($this->message, Array(PR_MESSAGE_CLASS));
179
				$messageClass = isset($props[PR_MESSAGE_CLASS]) ? $props[PR_MESSAGE_CLASS] : false;
180
			}
181
182
			if($messageClass !== false &&  strpos($messageClass, "IPM.TaskRequest.") === 0) {
183
				return true;
184
			}
185
186
			return false;
187
		}
188
189
		/**
190
		 * Returns TRUE if the message pointed to is an incoming task request/response.
191
		 *
192
		 * @param array $props The MAPI properties to check message is an incoming task request/response
193
		 * @return Boolean Returns true if this is an incoming task request/response. else false.
194
		 */
195
		function isReceivedItem($props)
196
		{
197
			return isset($props[PR_MESSAGE_TO_ME]) ? $props[PR_MESSAGE_TO_ME] : false;
198
		}
199
200
		/**
201
		 * Gets the task associated with an IPM.TaskRequest message
202
		 *
203
		 * If the task does not exist yet, it is created, using the attachment object in the
204
		 * task request item.
205
		 *
206
		 * @param boolean $create false to find the associated task in user's task folder. true to
207
		 * create task in user's task folder if task is not exist in task folder.
208
		 *
209
		 * @return MAPIMessage|false Return associated task of task request else false
0 ignored issues
show
Bug introduced by
The type MAPIMessage was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
210
		 */
211
		function getAssociatedTask($create)
212
		{
213
			$props = mapi_getprops($this->message, array(PR_MESSAGE_CLASS, $this->props['task_goid']));
214
215
			if($props[PR_MESSAGE_CLASS] == "IPM.Task") {
216
				// Message itself is task, so return that
217
				return $this->message;
218
			}
219
220
			$taskFolder = $this->getDefaultTasksFolder();
221
			$goid = $props[$this->props['task_goid']];
222
223
			// Find the task by looking for the task_goid
224
			$restriction = array(RES_PROPERTY, array(RELOP => RELOP_EQ,
225
									ULPROPTAG => $this->props['task_goid'],
226
									VALUE => $goid)
227
								);
228
229
			$contents = mapi_folder_getcontentstable($taskFolder);
230
231
			$rows = mapi_table_queryallrows($contents, array(PR_ENTRYID), $restriction);
232
233
			if(empty($rows)) {
234
				// None found, create one if possible
235
				if(!$create) {
236
					return false;
237
				}
238
239
				$task = mapi_folder_createmessage($taskFolder);
240
241
				$sub = $this->getEmbeddedTask($this->message);
242
				mapi_copyto($sub, array(), array($this->props['categories']), $task);
243
244
				$senderProps = array(
245
					PR_SENT_REPRESENTING_NAME,
246
					PR_SENT_REPRESENTING_EMAIL_ADDRESS,
247
					PR_SENT_REPRESENTING_ENTRYID,
248
					PR_SENT_REPRESENTING_ADDRTYPE,
249
					PR_SENT_REPRESENTING_SEARCH_KEY,
250
					PR_SENDER_NAME,
251
					PR_SENDER_EMAIL_ADDRESS,
252
					PR_SENDER_ENTRYID,
253
					PR_SENDER_ADDRTYPE,
254
					PR_SENDER_SEARCH_KEY);
255
256
				// Copy sender information from the e-mail
257
				$props = mapi_getprops($this->message, $senderProps);
258
				$props[PR_MESSAGE_CLASS] = 'IPM.Task';
259
				mapi_setprops($task, $props);
260
			} else {
261
				// If there are multiple, just use the first
262
				$entryid = $rows[0][PR_ENTRYID];
263
264
				$store = $this->getTaskFolderStore();
265
				$task = mapi_msgstore_openentry($store, $entryid);
266
			}
267
268
			return $task;
269
		}
270
271
		/**
272
		 * Function which checks that if we have received a task request/response
273
		 * for an already updated task in task folder.
274
		 *
275
		 * @return Boolean true if task request is updated later.
276
		 */
277
		function isTaskRequestUpdated() {
278
			$props = mapi_getprops($this->message, array(PR_MESSAGE_CLASS, $this->props['task_goid'], $this->props['updatecount']));
279
			$result = false;
280
			$associatedTask = $this->getAssociatedTask(false);
281
			if ($this->isTaskRequest($props[PR_MESSAGE_CLASS])) {
282
				if($associatedTask) {
283
					return true;
284
				} else {
285
					$folder = $this->getDefaultTasksFolder();
286
					$goid = $props[$this->props['task_goid']];
287
288
					// Find the task by looking for the task_goid
289
					$restriction = array(RES_PROPERTY, array(RELOP => RELOP_EQ,
290
						ULPROPTAG => $this->props['task_goid'],
291
						VALUE => $goid)
292
					);
293
294
					$table = mapi_folder_getcontentstable($folder, MAPI_DEFERRED_ERRORS | SHOW_SOFT_DELETES);
295
					$softDeletedItems = mapi_table_queryallrows($table, array(PR_ENTRYID), $restriction);
296
					if (!empty($softDeletedItems)) {
297
						return true;
298
					}
299
				}
300
			}
301
302
			if ($associatedTask !== false) {
303
				$taskItemProps = mapi_getprops($associatedTask, array($this->props['updatecount']));
304
				/*
305
				 * if(message_counter < task_counter) task object is newer then task response (task is updated)
306
				 * if(message_counter >= task_counter) task is not updated, do normal processing
307
				 */
308
				if (isset($taskItemProps[$this->props['updatecount']]) && isset($props[$this->props['updatecount']])) {
309
					if($props[$this->props['updatecount']] < $taskItemProps[$this->props['updatecount']]) {
310
						$result = true;
311
					}
312
				}
313
			}
314
			return $result;
315
		}
316
317
		// Organizer functions (called by the organizer)
318
319
		/**
320
		 * Processes a task request response, which can be any of the following:
321
		 * - Task accept (task history is marked as accepted)
322
		 * - Task decline (task history is marked as declined)
323
		 * - Task update (updates completion %, etc)
324
		 */
325
		function processTaskResponse() {
326
			$messageProps = mapi_getprops($this->message, array(PR_PROCESSED, $this->props["taskupdates"], PR_MESSAGE_TO_ME));
327
			if (isset($messageProps[PR_PROCESSED]) && $messageProps[PR_PROCESSED]) {
328
				return true;
329
			} else {
330
				mapi_setprops($this->message, Array(PR_PROCESSED => true));
331
				mapi_savechanges($this->message);
332
			}
333
334
			// Get the embedded task information.
335
			$sub = $this->getEmbeddedTask($this->message);
336
			// OL saves the task related properties in the embedded message
337
			$subProps = mapi_getprops($sub, array($this->props["taskupdates"]));
338
339
			// If task is updated in task folder then we don't need to process
340
			// old response
341
			if($this->isTaskRequestUpdated()) {
342
				return true;
343
			}
344
345
			$isReceivedItem = $this->isReceivedItem($messageProps);
346
347
			$isCreateAssociatedTask = false;
348
			$isAllowUpdateAssociatedTask = $subProps[$this->props["taskupdates"]];
349
			$props = mapi_getprops($this->message, array(PR_MESSAGE_CLASS));
350
			// Set correct taskmode and taskhistory depending on response type
351
			switch ($props[PR_MESSAGE_CLASS]) {
352
				case 'IPM.TaskRequest.Accept':
353
					$taskHistory = thAccepted;
354
					$taskState = $isReceivedItem ? tdsACC : tdsOWN;
355
					$taskOwner = $isReceivedItem ? olDelegatedTask : olOwnTask;
356
					$taskAcceptanceState = $isReceivedItem ? olTaskDelegationAccepted : olTaskNotDelegated;
357
					break;
358
				case 'IPM.TaskRequest.Decline':
359
					$isCreateAssociatedTask = $isReceivedItem;
360
					$isAllowUpdateAssociatedTask = $isReceivedItem;
361
					$taskHistory = thDeclined;
362
					$taskState = $isReceivedItem ? tdsDEC : tdsACC;
363
					$taskOwner = $isReceivedItem ? olOwnTask : olDelegatedTask;
364
					$taskAcceptanceState = $isReceivedItem ? olTaskDelegationDeclined : olTaskDelegationUnknown;
365
					break;
366
				case 'IPM.TaskRequest.Update':
367
				case 'IPM.TaskRequest.Complete':
368
					$taskHistory = thUpdated;
369
					$taskState = $isReceivedItem ? tdsACC : tdsOWN;
370
					$taskAcceptanceState = olTaskNotDelegated;
371
					$taskOwner = $isReceivedItem ? olDelegatedTask : olOwnTask;
372
					break;
373
			}
374
375
			$props =  array($this->props['taskhistory'] => $taskHistory,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $taskHistory does not seem to be defined for all execution paths leading up to this point.
Loading history...
376
				$this->props['taskstate'] => $taskState,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $taskState does not seem to be defined for all execution paths leading up to this point.
Loading history...
377
				$this->props['task_acceptance_state'] => $taskAcceptanceState,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $taskAcceptanceState does not seem to be defined for all execution paths leading up to this point.
Loading history...
378
				$this->props['ownership'] => $taskOwner);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $taskOwner does not seem to be defined for all execution paths leading up to this point.
Loading history...
379
380
			// Get the task for this response
381
			$task = $this->getAssociatedTask($isCreateAssociatedTask);
382
			if ($task && $isAllowUpdateAssociatedTask) {
383
				// To avoid duplication of attachments in associated task. we simple remove the
384
				// all attachments from associated task.
385
				$taskAttachTable = mapi_message_getattachmenttable($task);
386
				$taskAttachments = mapi_table_queryallrows($taskAttachTable, array(PR_ATTACH_NUM));
387
				foreach($taskAttachments as $taskAttach) {
388
					mapi_message_deleteattach($task, $taskAttach[PR_ATTACH_NUM]);
389
				}
390
391
				$ignoreProps = array(
392
					$this->props['taskstate'],
393
					$this->props['taskhistory'],
394
					$this->props['taskmode'],
395
					$this->props['taskfcreator']
396
				);
397
				// Ignore PR_ICON_INDEX when task request response
398
				// is not received item.
399
				if ($isReceivedItem === false) {
400
					$ignoreProps[] = PR_ICON_INDEX;
401
				}
402
403
				// We copy all properties except taskstate, taskhistory, taskmode and taskfcreator properties
404
				// from $sub message to $task even also we copy all attachments from $sub to $task message.
405
				mapi_copyto($sub, array(), $ignoreProps, $task);
406
				$senderProps = mapi_getprops($this->message, array(
407
					PR_SENDER_NAME,
408
					PR_SENDER_EMAIL_ADDRESS,
409
					PR_SENDER_ENTRYID,
410
					PR_SENDER_ADDRTYPE,
411
					PR_SENDER_SEARCH_KEY,
412
					PR_MESSAGE_DELIVERY_TIME,
413
					PR_SENT_REPRESENTING_NAME,
414
					PR_SENT_REPRESENTING_EMAIL_ADDRESS,
415
					PR_SENT_REPRESENTING_ADDRTYPE,
416
					PR_SENT_REPRESENTING_ENTRYID,
417
					PR_SENT_REPRESENTING_SEARCH_KEY));
418
419
				mapi_setprops($task, $senderProps);
420
421
				// Update taskstate and task history (last action done by the assignee)
422
				mapi_setprops($task,$props);
423
424
				// Copy missing properties from embedded task
425
				$subProperties = $this->getSubProperties();
426
				$subprops = mapi_getprops($sub, $subProperties);
427
				mapi_setprops($task, $subprops);
428
429
				mapi_savechanges($task);
430
			}
431
432
			mapi_setprops($this->message, $props);
433
			mapi_savechanges($this->message);
434
435
			if($isReceivedItem) {
436
				$this->updateSentTaskRequest();
437
			}
438
			return true;
439
		}
440
441
		/**
442
		 * Update the sent task request in sent items folder.
443
		 * @return bool
444
		 */
445
		function updateSentTaskRequest() {
446
			$props = mapi_getprops($this->message, array(
447
				$this->props['taskhistory'],
448
				$this->props["taskstate"],
449
				$this->props["ownership"],
450
				$this->props['task_goid'],
451
				$this->props['task_acceptance_state'],
452
				$this->props["tasklastuser"],
453
				$this->props["tasklastdelegate"]));
454
455
			$store = $this->getDefaultStore();
456
			$storeProps = mapi_getprops($store, array(PR_IPM_SENTMAIL_ENTRYID));
457
458
			$sentFolder = mapi_msgstore_openentry($store, $storeProps[PR_IPM_SENTMAIL_ENTRYID]);
459
			if(!$sentFolder) {
460
				return false;
461
			}
462
463
			// Find the task by looking for the task_goid
464
			$restriction = array(RES_PROPERTY, array(RELOP => RELOP_EQ,
465
				ULPROPTAG => $this->props['task_goid'],
466
				VALUE => $props[$this->props['task_goid']])
467
			);
468
469
			$contentsTable = mapi_folder_getcontentstable($sentFolder);
470
471
			$rows = mapi_table_queryallrows($contentsTable, array(PR_ENTRYID), $restriction);
472
473
			if(!empty($rows)) {
474
				foreach ($rows as $row) {
475
					$sentTaskRequest = mapi_msgstore_openentry($store, $row[PR_ENTRYID]);
476
					mapi_setprops($sentTaskRequest, $props);
477
					mapi_setprops($sentTaskRequest, array(PR_PROCESSED => true));
478
					mapi_savechanges($sentTaskRequest);
479
				}
480
			}
481
			return true;
482
		}
483
484
		/* Create a new message in the current user's outbox and submit it
485
		 *
486
		 * Takes the task passed in the constructor as the task to be sent; recipient should
487
		 * be pre-existing. The task request will be sent to all recipients.
488
		 */
489
		function sendTaskRequest($prefix) {
490
			// Generate a TaskGlobalObjectId
491
			$taskid = $this->createTGOID();
492
			$messageprops = mapi_getprops($this->message, array(PR_SUBJECT));
493
494
			// Set properties on Task Request
495
			mapi_setprops($this->message, array(
496
				$this->props['task_goid'] => $taskid, /* our new task_goid */
497
				$this->props['taskstate'] => tdsACC, 		/* state for our outgoing request */
498
				$this->props['taskmode'] => tdmtNothing, 	/* we're not sending a change */
499
				$this->props['updatecount'] => 2,			/* version 2 (no idea) */
500
				$this->props['task_acceptance_state'] => olTaskDelegationUnknown, /* no reply yet */
501
				$this->props['ownership'] => olDelegatedTask, /* Task has been assigned */
502
				$this->props['taskhistory'] => thAssigned,	/* Task has been assigned */
503
				PR_CONVERSATION_TOPIC => $messageprops[PR_SUBJECT],
504
				PR_ICON_INDEX => ICON_TASK_ASSIGNER
505
			));
506
			$this->setLastUser();
507
			$this->setOwnerForAssignor();
508
			mapi_savechanges($this->message);
509
510
			// Create outgoing task request message
511
			$outgoing = $this->createOutgoingMessage();
512
513
514
			// No need to copy PR_ICON_INDEX and  PR_SENT_* information in to outgoing message.
515
			$ignoreProps = array(PR_ICON_INDEX, PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_EMAIL_ADDRESS, PR_SENT_REPRESENTING_ADDRTYPE, PR_SENT_REPRESENTING_ENTRYID, PR_SENT_REPRESENTING_SEARCH_KEY);
516
			mapi_copyto($this->message, array(), $ignoreProps, $outgoing);
517
518
			// Make it a task request, and put it in sent items after it is sent
519
			mapi_setprops($outgoing, array(
520
				PR_MESSAGE_CLASS => "IPM.TaskRequest", 		/* class is task request */
521
				$this->props['taskstate'] => tdsOWN, 	    /* for the recipient he is the task owner */
522
				$this->props['taskmode'] => tdmtTaskReq,	/* for the recipient it's a request */
523
				$this->props['updatecount'] => 1,			/* version 2 is in the attachment */
524
				PR_SUBJECT_PREFIX => $prefix,
525
				PR_SUBJECT => $prefix . $messageprops[PR_SUBJECT]
526
			));
527
528
			$attach = mapi_message_createattach($outgoing);
529
			mapi_setprops($attach, array(
530
					PR_ATTACH_METHOD => ATTACH_EMBEDDED_MSG,
531
					PR_ATTACHMENT_HIDDEN => true,
532
					PR_DISPLAY_NAME => $messageprops[PR_SUBJECT]));
533
534
			$sub = mapi_attach_openproperty($attach, PR_ATTACH_DATA_OBJ, IID_IMessage, 0, MAPI_MODIFY | MAPI_CREATE);
535
536
			mapi_copyto($this->message, array(), array(), $sub);
537
			mapi_setprops($sub, array(PR_MESSAGE_CLASS => 'IPM.Task'));
538
539
			mapi_savechanges($sub);
540
541
			mapi_savechanges($attach);
542
543
			mapi_savechanges($outgoing);
544
			mapi_message_submitmessage($outgoing);
545
			return true;
546
		}
547
548
		// Assignee functions (called by the assignee)
549
550
		/* Update task version counter
551
		 *
552
		 * Must be called before each update to increase counter
553
		 */
554
		function updateTaskRequest() {
555
			$messageprops = mapi_getprops($this->message, array($this->props['updatecount']));
556
557
			if(isset($messageprops)) {
558
				$messageprops[$this->props['updatecount']]++;
559
			} else {
560
				$messageprops[$this->props['updatecount']] = 1;
561
			}
562
563
			mapi_setprops($this->message, $messageprops);
564
		}
565
566
		/* Process a task request
567
		 *
568
		 * Message passed should be an IPM.TaskRequest message. The task request is then processed to create
569
		 * the task in the tasks folder if needed.
570
		 */
571
		function processTaskRequest() {
572
			if (!$this->isTaskRequest()) {
573
				return false;
574
			}
575
			$messageProps = mapi_getprops($this->message, array(PR_PROCESSED, $this->props["taskupdates"], PR_MESSAGE_TO_ME));
576
			if (isset($messageProps[PR_PROCESSED]) && $messageProps[PR_PROCESSED]) {
577
				return true;
578
			}
579
580
			// if task is updated in task folder then we don't need to process
581
			// old request.
582
			if($this->isTaskRequestUpdated()) {
583
				return true;
584
			}
585
586
			$isReceivedItem = $this->isReceivedItem($messageProps);
587
588
			$props = array();
589
			$props[PR_PROCESSED] = true;
590
			$props[$this->props["taskstate"]] = $isReceivedItem ? tdsOWN : tdsACC;
591
			$props[$this->props["ownership"]] = $isReceivedItem ? olOwnTask : olDelegatedTask;
592
593
			mapi_setprops($this->message, $props);
594
			mapi_savechanges($this->message);
595
596
			// Don't create associated task in task folder if "taskupdates" is not true.
597
			if (!$isReceivedItem && !$messageProps[$this->props["taskupdates"]]) {
598
				return true;
599
			} else {
600
				// create an associated task in task folder while
601
				// reading/loading task request on client side.
602
				$task = $this->getAssociatedTask(true);
603
604
				$taskProps = mapi_getprops($task, array($this->props['taskmultrecips']));
605
				$taskProps[$this->props["taskstate"]] = $isReceivedItem ? tdsOWN : tdsACC;
606
				$taskProps[$this->props["taskhistory"]] = thAssigned;
607
				$taskProps[$this->props["taskmode"]] = tdmtNothing;
608
				$taskProps[$this->props["taskaccepted"]] = false;
609
				$taskProps[$this->props["taskfcreator"]] = false;
610
				$taskProps[$this->props["ownership"]] = $isReceivedItem ? olOwnTask : olDelegatedTask;
611
				$taskProps[$this->props["task_acceptance_state"]] = olTaskNotDelegated;
612
				$taskProps[PR_ICON_INDEX] = ICON_TASK_ASSIGNEE;
613
614
				mapi_setprops($task, $taskProps);
615
				$this->setAssignorInRecipients($task);
616
617
				mapi_savechanges($task);
618
			}
619
620
			return true;
621
		}
622
623
		/**
624
		 * Accept a task request and send the response.
625
		 *
626
		 * Message passed should be an IPM.Task (eg the task from getAssociatedTask())
627
		 *
628
		 * Copies the task to the user's task folder, sets it to accepted, and sends the acceptation
629
		 * message back to the organizer. The caller is responsible for removing the message.
630
		 *
631
		 * @return entryid EntryID of the accepted task
632
		 */
633
		function doAccept() {
634
			$prefix = _("Task Accepted:") . " ";
635
			$messageProps = mapi_getprops($this->message, array(PR_MESSAGE_CLASS, $this->props['taskstate']));
636
637
			if(!isset($messageProps[$this->props['taskstate']]) || $messageProps[$this->props['taskstate']] != tdsOWN) {
638
				// Can only accept assignee task
639
				return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type entryid.
Loading history...
640
			}
641
642
			$this->setLastUser();
643
			$this->updateTaskRequest();
644
645
			$props = array(
646
				$this->props['taskhistory'] => thAccepted,
647
				$this->props['task_assigned_time'] => time(),
648
				$this->props['taskaccepted'] => true,
649
				$this->props['task_acceptance_state'] => olTaskNotDelegated);
650
651
			// Message is TaskRequest then update the associated task as well.
652
			if ($this->isTaskRequest($messageProps[PR_MESSAGE_CLASS])) {
653
				$task = $this->getAssociatedTask(false);
654
				if ($task) {
655
					mapi_setprops($task, $props);
656
					mapi_savechanges($task);
657
				}
658
			}
659
660
			// Set as accepted
661
			mapi_setprops($this->message, $props);
662
663
			// As we copy the all properties from received message we need to remove following
664
			// properties from accept response.
665
			mapi_deleteprops($this->message, array(PR_MESSAGE_RECIP_ME, PR_MESSAGE_TO_ME, PR_MESSAGE_CC_ME, PR_PROCESSED));
666
667
			mapi_savechanges($this->message);
668
669
			$this->sendResponse(tdmtTaskAcc, $prefix);
670
671
			return $this->deleteReceivedTR();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->deleteReceivedTR() returns the type array|false which is incompatible with the documented return type entryid.
Loading history...
672
		}
673
674
		/* Decline a task request and send the response.
675
		 *
676
		 * Passed message must be a task request message, ie isTaskRequest() must return TRUE.
677
		 *
678
		 * Sends the decline message back to the organizer. The caller is responsible for removing the message.
679
		 *
680
		 * @return boolean TRUE on success, FALSE on failure
681
		 */
682
		function doDecline() {
683
			$prefix = _("Task Declined:") . " ";
684
			$messageProps = mapi_getprops($this->message, array($this->props['taskstate']));
685
686
			if(!isset($messageProps[$this->props['taskstate']]) || $messageProps[$this->props['taskstate']] != tdsOWN) {
687
				return false; // Can only decline assignee task
688
			}
689
690
			$this->setLastUser();
691
			$this->updateTaskRequest();
692
693
			// Set as declined
694
			mapi_setprops($this->message, array(
695
					$this->props['taskhistory'] => thDeclined,
696
					$this->props['task_acceptance_state'] => olTaskDelegationDeclined
697
				));
698
			mapi_deleteprops($this->message, array(PR_MESSAGE_RECIP_ME, PR_MESSAGE_TO_ME, PR_MESSAGE_CC_ME, PR_PROCESSED));
699
			mapi_savechanges($this->message);
700
701
			$this->sendResponse(tdmtTaskDec, $prefix);
702
703
			// Delete the associated task when task request is declined by the assignee.
704
			$task = $this->getAssociatedTask(false);
705
			if ($task) {
706
				$taskFolder = $this->getDefaultTasksFolder();
707
				$props = mapi_getprops($task, array(PR_ENTRYID));
708
				mapi_folder_deletemessages($taskFolder, array($props[PR_ENTRYID]));
709
			}
710
			return $this->deleteReceivedTR();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->deleteReceivedTR() also could return the type array which is incompatible with the documented return type boolean.
Loading history...
711
		}
712
713
		/**
714
		 * Send an update of the task if requested, and send the Status-On-Completion report if complete and requested
715
		 *
716
		 * If no updates were requested from the organizer, this function does nothing.
717
		 *
718
		 * @return boolean TRUE if the update succeeded, FALSE otherwise.
719
		 */
720
		function doUpdate() {
721
			$messageProps = mapi_getprops($this->message, array($this->props['taskstate'], PR_SUBJECT));
722
723
			if(!isset($messageProps[$this->props['taskstate']]) || $messageProps[$this->props['taskstate']] != tdsOWN) {
724
				return false; // Can only update assignee task
725
			}
726
727
			$this->setLastUser();
728
			$this->updateTaskRequest();
729
730
			// Set as updated
731
			mapi_setprops($this->message, array($this->props['taskhistory'] => thUpdated));
732
733
			mapi_savechanges($this->message);
734
735
			$props = mapi_getprops($this->message, array($this->props['taskupdates'], $this->props['tasksoc'], $this->props['recurring'], $this->props['complete']));
736
			if (!$props[$this->props['complete']] && $props[$this->props['taskupdates']] && !(isset($props[$this->props['recurring']]) && $props[$this->props['recurring']])) {
737
				$this->sendResponse(tdmtTaskUpd, _("Task Updated:") . " ");
738
			} else if($props[$this->props['complete']]) {
739
				$this->sendResponse(tdmtTaskUpd, _("Task Completed:") . " ");
740
			}
741
		}
742
743
		/**
744
		 * Get the store associated with the task
745
		 *
746
		 * Normally this will just open the store that the processed message is in. However, if the message is opened
747
		 * by a delegate, this function opens the store that the message was delegated from.
748
		 */
749
		function getTaskFolderStore()
750
		{
751
			$ownerentryid = false;
752
753
			$rcvdprops = mapi_getprops($this->message, array(PR_RCVD_REPRESENTING_ENTRYID));
754
			if(isset($rcvdprops[PR_RCVD_REPRESENTING_ENTRYID])) {
755
				$ownerentryid = $rcvdprops[PR_RCVD_REPRESENTING_ENTRYID];
756
			}
757
758
			if(!$ownerentryid) {
759
				$store = $this->store;
760
			} else {
761
				$ab = mapi_openaddressbook($this->session);
762
				if(!$ab) return false;
763
764
				$mailuser = mapi_ab_openentry($ab, $ownerentryid);
765
				if(!$mailuser) return false;
766
767
				$mailuserprops = mapi_getprops($mailuser, array(PR_EMAIL_ADDRESS));
768
				if(!isset($mailuserprops[PR_EMAIL_ADDRESS])) return false;
769
770
				$storeid = mapi_msgstore_createentryid($this->store, $mailuserprops[PR_EMAIL_ADDRESS]);
771
772
				$store = mapi_openmsgstore($this->session, $storeid);
773
774
			}
775
			return $store;
776
		}
777
778
		/**
779
		 * Open the default task folder for the current user, or the specified user if passed
780
		 */
781
		function getDefaultTasksFolder()
782
		{
783
			$store = $this->getTaskFolderStore();
784
785
			$inbox = mapi_msgstore_getreceivefolder($store);
786
			$inboxprops = mapi_getprops($inbox, Array(PR_IPM_TASK_ENTRYID));
787
			if(!isset($inboxprops[PR_IPM_TASK_ENTRYID]))
788
				return false;
789
790
			return mapi_msgstore_openentry($store, $inboxprops[PR_IPM_TASK_ENTRYID]);
791
		}
792
793
		/**
794
		 * Function prepare the sent representing properties from given MAPI store.
795
		 * @param $store MAPI store object
796
		 * @return array|bool if store is not mail box owner entryid then
797
		 * return false else prepare the sent representing props and return it.
798
		 */
799
		function getSentReprProps($store)
800
		{
801
			$storeprops = mapi_getprops($store, array(PR_MAILBOX_OWNER_ENTRYID));
802
			if (!isset($storeprops[PR_MAILBOX_OWNER_ENTRYID])) {
803
				return false;
804
			}
805
806
			$ab = mapi_openaddressbook($this->session);
807
			$mailuser = mapi_ab_openentry($ab, $storeprops[PR_MAILBOX_OWNER_ENTRYID]);
808
			$mailuserprops = mapi_getprops($mailuser, array(PR_ADDRTYPE, PR_EMAIL_ADDRESS, PR_DISPLAY_NAME, PR_SEARCH_KEY, PR_ENTRYID));
809
810
			$props = array();
811
			$props[PR_SENT_REPRESENTING_ADDRTYPE] = $mailuserprops[PR_ADDRTYPE];
812
			$props[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $mailuserprops[PR_EMAIL_ADDRESS];
813
			$props[PR_SENT_REPRESENTING_NAME] = $mailuserprops[PR_DISPLAY_NAME];
814
			$props[PR_SENT_REPRESENTING_SEARCH_KEY] = $mailuserprops[PR_SEARCH_KEY];
815
			$props[PR_SENT_REPRESENTING_ENTRYID] = $mailuserprops[PR_ENTRYID];
816
817
			return $props;
818
		}
819
820
		/**
821
		 * Creates an outgoing message based on the passed message - will set delegate information
822
		 * and sent mail folder
823
		 */
824
		function createOutgoingMessage()
825
		{
826
			// Open our default store for this user (that's the only store we can submit in)
827
			$store = $this->getDefaultStore();
828
			$storeprops = mapi_getprops($store, array(PR_IPM_OUTBOX_ENTRYID, PR_IPM_SENTMAIL_ENTRYID));
829
830
			$outbox = mapi_msgstore_openentry($store, $storeprops[PR_IPM_OUTBOX_ENTRYID]);
831
			if(!$outbox) return false;
832
833
			$outgoing = mapi_folder_createmessage($outbox);
834
			if(!$outgoing) return false;
835
836
			// Set SENT_REPRESENTING in case we're sending as a delegate
837
			$ownerstore = $this->getTaskFolderStore();
838
			$sentreprprops = $this->getSentReprProps($ownerstore);
839
			mapi_setprops($outgoing, $sentreprprops);
840
841
			mapi_setprops($outgoing, array(PR_SENTMAIL_ENTRYID => $storeprops[PR_IPM_SENTMAIL_ENTRYID]));
842
843
			return $outgoing;
844
		}
845
846
		/**
847
		 * Send a response message (from assignee back to organizer).
848
		 *
849
		 * @param $type int Type of response (tdmtTaskAcc, tdmtTaskDec, tdmtTaskUpd);
850
		 * @return boolean TRUE on success
851
		 */
852
		function sendResponse($type, $prefix)
853
		{
854
			// Create a message in our outbox
855
			$outgoing = $this->createOutgoingMessage();
856
			$messageprops = mapi_getprops($this->message, array(PR_CONVERSATION_TOPIC, PR_MESSAGE_CLASS, $this->props['complete']));
857
858
			$attach = mapi_message_createattach($outgoing);
859
			mapi_setprops($attach, array(PR_ATTACH_METHOD => ATTACH_EMBEDDED_MSG, PR_DISPLAY_NAME => $messageprops[PR_CONVERSATION_TOPIC], PR_ATTACHMENT_HIDDEN => true));
860
			$sub = mapi_attach_openproperty($attach, PR_ATTACH_DATA_OBJ, IID_IMessage, 0, MAPI_CREATE | MAPI_MODIFY);
861
862
			$message = !$this->isTaskRequest() ? $this->message : $this->getAssociatedTask(false);
863
864
			$ignoreProps = array(PR_ICON_INDEX, $this->props["categories"], PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_EMAIL_ADDRESS, PR_SENT_REPRESENTING_ADDRTYPE, PR_SENT_REPRESENTING_ENTRYID, PR_SENT_REPRESENTING_SEARCH_KEY);
865
866
			mapi_copyto($message, array(), $ignoreProps, $outgoing);
867
			mapi_copyto($message, array(), array(), $sub);
868
869
			if (!$this->setRecipientsForResponse($outgoing, $type)) {
870
				return false;
871
			}
872
873
			$props = array();
874
			switch($type) {
875
				case tdmtTaskAcc:
876
					$props[PR_MESSAGE_CLASS] = "IPM.TaskRequest.Accept";
877
					mapi_setprops($sub,array(PR_ICON_INDEX => ICON_TASK_ASSIGNER));
878
					break;
879
				case tdmtTaskDec:
880
					$props[PR_MESSAGE_CLASS] = "IPM.TaskRequest.Decline";
881
					mapi_setprops($sub,array(PR_ICON_INDEX => ICON_TASK_DECLINE));
882
					break;
883
				case tdmtTaskUpd:
884
					mapi_setprops($sub,array(PR_ICON_INDEX => ICON_TASK_ASSIGNER));
885
					if($messageprops[$this->props['complete']]) {
886
						$props[PR_MESSAGE_CLASS] = "IPM.TaskRequest.Complete";
887
					} else {
888
						$props[PR_MESSAGE_CLASS] = "IPM.TaskRequest.Update";
889
					}
890
891
					break;
892
			};
893
894
			mapi_savechanges($sub);
895
			mapi_savechanges($attach);
896
897
			$props[PR_SUBJECT] = $prefix . $messageprops[PR_CONVERSATION_TOPIC];
898
			$props[$this->props['taskmode']] = $type;
899
			$props[$this->props['task_assigned_time']] = time();
900
901
			mapi_setprops($outgoing, $props);
902
903
			// taskCommentsInfo contains some comments which added by assignee while
904
			// edit response before sending task response.
905
			if ($this->taskCommentsInfo) {
906
				$comments = $this->getTaskCommentsInfo();
907
				$stream = mapi_openproperty($outgoing, PR_BODY, IID_IStream, 0, MAPI_CREATE | MAPI_MODIFY);
908
				mapi_stream_setsize($stream, strlen($comments));
909
				mapi_stream_write($stream, $comments);
910
				mapi_stream_commit($stream);
911
			}
912
913
			mapi_savechanges($outgoing);
914
			mapi_message_submitmessage($outgoing);
915
			return true;
916
		}
917
918
		function getDefaultStore()
919
		{
920
			$table = mapi_getmsgstorestable($this->session);
921
			$rows = mapi_table_queryallrows($table, array(PR_DEFAULT_STORE, PR_ENTRYID));
922
923
			foreach($rows as $row) {
924
				if($row[PR_DEFAULT_STORE])
925
					return mapi_openmsgstore($this->session, $row[PR_ENTRYID]);
926
			}
927
928
			return false;
929
		}
930
931
		/**
932
		 * Creates a new TaskGlobalObjId
933
		 *
934
		 * Just 16 bytes of random data
935
		 */
936
		function createTGOID()
937
		{
938
			$goid = "";
939
			for($i=0;$i<16;$i++) {
940
				$goid .= chr(rand(0, 255));
941
			}
942
			return $goid;
943
		}
944
945
		/**
946
		 * Function used to get the embedded task of task request. Which further used to
947
		 * Create/Update associated task of assigner/assignee.
948
		 *
949
		 * @param object $message which contains embedded task.
950
		 * @return object|false $task if found embedded task else false
951
		 */
952
		function getEmbeddedTask($message) {
953
			$task = false;
954
			$goid = mapi_getprops($this->message, array($this->props["task_goid"]));
955
			$attachmentTable = mapi_message_getattachmenttable($this->message);
956
			$restriction = array(RES_PROPERTY,
957
								array(RELOP => RELOP_EQ,
958
									ULPROPTAG => PR_ATTACH_METHOD,
959
									VALUE => ATTACH_EMBEDDED_MSG)
960
							);
961
			$rows = mapi_table_queryallrows($attachmentTable, array(PR_ATTACH_NUM), $restriction);
962
963
			if(empty($rows)) {
964
				return $task;
965
			}
966
967
			foreach ($rows as $row) {
968
				try {
969
					$attach = mapi_message_openattach($this->message, $row[PR_ATTACH_NUM]);
970
					$task = mapi_attach_openobj($attach);
971
				} catch (MAPIException $e) {
972
					continue;
973
				}
974
975
				$taskGoid = mapi_getprops($task, array($this->props["task_goid"]));
976
				if($goid[$this->props["task_goid"]] === $taskGoid[$this->props["task_goid"]]) {
977
					mapi_setprops($attach, array(PR_ATTACHMENT_HIDDEN => true));
978
					mapi_savechanges($attach);
979
					mapi_savechanges($this->message);
980
					break;
981
				}
982
			}
983
			return $task;
984
		}
985
986
		/**
987
		 * Function was used to set the user name who has last used this task also it was
988
		 * update the  tasklastdelegate and task_assigned_time.
989
		 */
990
		function setLastUser()
991
		{
992
			$delegatestore = $this->getDefaultStore();
993
			$taskstore = $this->getTaskFolderStore();
994
995
			$delegateprops = mapi_getprops($delegatestore, array(PR_MAILBOX_OWNER_NAME));
996
			$taskprops = mapi_getprops($taskstore, array(PR_MAILBOX_OWNER_NAME));
997
998
			// The owner of the task
999
			$username = $delegateprops[PR_MAILBOX_OWNER_NAME];
1000
			// This is me (the one calling the script)
1001
			$delegate = $taskprops[PR_MAILBOX_OWNER_NAME];
1002
1003
			if ($this->isTaskRequest()) {
1004
				$task = $this->getAssociatedTask(false);
1005
				mapi_setprops($task, array($this->props["tasklastuser"] => $username, $this->props["tasklastdelegate"] => $delegate, $this->props['task_assigned_time'] => time()));
1006
				mapi_savechanges($task);
1007
			}
1008
			mapi_setprops($this->message, array($this->props["tasklastuser"] => $username, $this->props["tasklastdelegate"] => $delegate, $this->props['task_assigned_time'] => time()));
1009
		}
1010
1011
		/**
1012
		 * Assignee becomes the owner when a user/assignor assigns any task to someone. Also there can be more than one assignee.
1013
		 * This function sets assignee as owner in the assignor's copy of task.
1014
		 */
1015
		function setOwnerForAssignor()
1016
		{
1017
			$recipTable = mapi_message_getrecipienttable($this->message);
1018
			$recips = mapi_table_queryallrows($recipTable, array(PR_DISPLAY_NAME));
1019
1020
			if (!empty($recips)) {
1021
				$owner = array();
1022
				foreach ($recips as $value) {
1023
					$owner[] = $value[PR_DISPLAY_NAME];
1024
				}
1025
1026
				$props = array($this->props['owner'] => implode("; ", $owner));
1027
				mapi_setprops($this->message, $props);
1028
			}
1029
		}
1030
1031
		/**
1032
		 * Sets assignor as recipients in assignee's copy of task.
1033
		 *
1034
		 * If assignor has requested task updates then the assignor is added as recipient type MAPI_CC.
1035
		 *
1036
		 * Also if assignor has request SOC then the assignor is also add as recipient type MAPI_BCC
1037
		 *
1038
		 * @param $task message MAPI message which assignee's copy of task
1039
		 */
1040
		function setAssignorInRecipients($task)
1041
		{
1042
			$recipTable = mapi_message_getrecipienttable($task);
1043
1044
			// Delete all MAPI_TO recipients
1045
			$recips = mapi_table_queryallrows($recipTable, array(PR_ROWID), array(RES_PROPERTY,
1046
																				array(	RELOP => RELOP_EQ,
1047
																						ULPROPTAG => PR_RECIPIENT_TYPE,
1048
																						VALUE => MAPI_TO
1049
																				)));
1050
			foreach($recips as $recip) {
1051
				mapi_message_modifyrecipients($task, MODRECIP_REMOVE, array($recip));
1052
			}
1053
1054
			$recips = array();
1055
			$taskReqProps = 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));
1056
			$associatedTaskProps = mapi_getprops($task, array($this->props['taskupdates'], $this->props['tasksoc'], $this->props['taskmultrecips']));
1057
1058
			// Build assignor info
1059
			$assignor = array(
1060
				PR_ENTRYID => $taskReqProps[PR_SENT_REPRESENTING_ENTRYID],
1061
				PR_DISPLAY_NAME => $taskReqProps[PR_SENT_REPRESENTING_NAME],
1062
				PR_EMAIL_ADDRESS => $taskReqProps[PR_SENT_REPRESENTING_EMAIL_ADDRESS],
1063
				PR_RECIPIENT_DISPLAY_NAME => $taskReqProps[PR_SENT_REPRESENTING_NAME],
1064
				PR_ADDRTYPE => empty($taskReqProps[PR_SENT_REPRESENTING_ADDRTYPE]) ? 'SMTP' : $taskReqProps[PR_SENT_REPRESENTING_ADDRTYPE],
1065
				PR_RECIPIENT_FLAGS => recipSendable,
1066
				PR_SEARCH_KEY => $taskReqProps[PR_SENT_REPRESENTING_SEARCH_KEY]
1067
			);
1068
1069
			// Assignor has requested task updates, so set him/her as MAPI_CC in recipienttable.
1070
			if ((isset($associatedTaskProps[$this->props['taskupdates']]) && $associatedTaskProps[$this->props['taskupdates']])
1071
				&& !(isset($associatedTaskProps[$this->props['taskmultrecips']]) && $associatedTaskProps[$this->props['taskmultrecips']] == tmrReceived)) {
1072
				$assignor[PR_RECIPIENT_TYPE] = MAPI_CC;
1073
				$recips[] = $assignor;
1074
			}
1075
1076
			// Assignor wants to receive an email report when task is mark as 'Complete', so in recipients as MAPI_BCC
1077
			if ($associatedTaskProps[$this->props['tasksoc']]) {
1078
				$assignor[PR_RECIPIENT_TYPE] = MAPI_BCC;
1079
				$recips[] = $assignor;
1080
			}
1081
1082
			if (!empty($recips)) {
1083
				mapi_message_modifyrecipients($task, MODRECIP_ADD, $recips);
1084
			}
1085
		}
1086
1087
		/**
1088
		 * Deletes incoming task request from Inbox
1089
		 *
1090
		 * @returns array returns PR_ENTRYID, PR_STORE_ENTRYID and PR_PARENT_ENTRYID of the deleted task request
1091
		 */
1092
		function deleteReceivedTR()
1093
		{
1094
			$store = $this->getTaskFolderStore();
1095
			$storeType = mapi_getprops($store, array(PR_MDB_PROVIDER));
1096
			if ($storeType[PR_MDB_PROVIDER] === ZARAFA_STORE_PUBLIC_GUID) {
1097
				$store = $this->getDefaultStore();
1098
			}
1099
			$inbox = mapi_msgstore_getreceivefolder($store);
1100
1101
			$storeProps = mapi_getprops($store, array(PR_IPM_WASTEBASKET_ENTRYID));
1102
			$props = mapi_getprops($this->message, array($this->props['task_goid']));
1103
			$goid = $props[$this->props['task_goid']];
1104
1105
			// Find the task by looking for the task_goid
1106
			$restriction = array(RES_PROPERTY,
1107
							array(RELOP => RELOP_EQ,
1108
								ULPROPTAG => $this->props['task_goid'],
1109
								VALUE => $goid)
1110
						);
1111
1112
			$contents = mapi_folder_getcontentstable($inbox);
1113
1114
			$rows = mapi_table_queryallrows($contents, array(PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID), $restriction);
1115
1116
			if(!empty($rows)) {
1117
				// If there are multiple, just use the first
1118
				$entryid = $rows[0][PR_ENTRYID];
1119
				$wastebasket = mapi_msgstore_openentry($store, $storeProps[PR_IPM_WASTEBASKET_ENTRYID]);
1120
				mapi_folder_copymessages($inbox, Array($entryid), $wastebasket, MESSAGE_MOVE);
1121
1122
				return array(PR_ENTRYID => $entryid, PR_PARENT_ENTRYID => $rows[0][PR_PARENT_ENTRYID], PR_STORE_ENTRYID => $rows[0][PR_STORE_ENTRYID]);
1123
			}
1124
1125
			return false;
1126
		}
1127
1128
		/**
1129
		 * Sets recipients for the outgoing message according to type of the response.
1130
		 *
1131
		 * If it is a task update, then only recipient type MAPI_CC are taken from the task message.
1132
		 *
1133
		 * If it is accept/decline response, then PR_SENT_REPRESENTATION_XXXX are taken as recipient.
1134
		 *
1135
		 *@param $outgoing MAPI_message outgoing mapi message
0 ignored issues
show
Bug introduced by
The type MAPI_message was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1136
		 *@param $responseType String response type
1137
		 */
1138
		function setRecipientsForResponse($outgoing, $responseType)
1139
		{
1140
			// Clear recipients from outgoing msg
1141
			$this->deleteAllRecipients($outgoing);
1142
1143
			// If it is a task update then get MAPI_CC recipients which are assignors who has asked for task update.
1144
			if ($responseType == tdmtTaskUpd) {
1145
				$props = mapi_getprops($this->message, array($this->props['complete']));
1146
				$isComplete = $props[$this->props['complete']];
1147
1148
				$recipTable = mapi_message_getrecipienttable($this->message);
1149
				$recips = mapi_table_queryallrows($recipTable, $this->recipProps, array(RES_PROPERTY,
1150
																						array(	RELOP => RELOP_EQ,
1151
																								ULPROPTAG => PR_RECIPIENT_TYPE,
1152
																								VALUE => ($isComplete ? MAPI_BCC : MAPI_CC)
1153
																						)
1154
												));
1155
1156
				// No recipients found, return error
1157
				if (empty($recips)) {
1158
					return false;
1159
				}
1160
1161
				foreach($recips as $recip) {
1162
					$recip[PR_RECIPIENT_TYPE] = MAPI_TO;	// Change recipient type to MAPI_TO
1163
					mapi_message_modifyrecipients($outgoing, MODRECIP_ADD, array($recip));
1164
				}
1165
				return true;
1166
			}
1167
1168
			$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));
1169
			$recip = array(
1170
					PR_DISPLAY_NAME => $orgprops[PR_SENT_REPRESENTING_NAME],
1171
					PR_EMAIL_ADDRESS => $orgprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS],
1172
					PR_ADDRTYPE => $orgprops[PR_SENT_REPRESENTING_ADDRTYPE],
1173
					PR_ENTRYID => $orgprops[PR_SENT_REPRESENTING_ENTRYID],
1174
					PR_RECIPIENT_TYPE => MAPI_TO);
1175
1176
			mapi_message_modifyrecipients($outgoing, MODRECIP_ADD, array($recip));
1177
1178
			return true;
1179
		}
1180
1181
		/**
1182
		 * Deletes all recipients from given message object
1183
		 *
1184
		 * @param $message MAPI message from which recipients are to be removed.
1185
		 */
1186
		function deleteAllRecipients($message)
1187
		{
1188
			$recipTable = mapi_message_getrecipienttable($message);
1189
			$recipRows = mapi_table_queryallrows($recipTable, array(PR_ROWID));
1190
1191
			foreach($recipRows as $recipient) {
1192
				mapi_message_modifyrecipients($message, MODRECIP_REMOVE, array($recipient));
1193
			}
1194
		}
1195
1196
		/**
1197
		 * Function used to mark the record to complete and send complete update
1198
		 * notification to assigner.
1199
		 *
1200
		 * @return boolean TRUE if the update succeeded, FALSE otherwise.
1201
		 */
1202
		function sendCompleteUpdate()
1203
		{
1204
			$messageprops = mapi_getprops($this->message, array($this->props['taskstate']));
1205
1206
			if(!isset($messageprops[$this->props['taskstate']]) || $messageprops[$this->props['taskstate']] != tdsOWN) {
1207
				return false; // Can only decline assignee task
1208
			}
1209
1210
			mapi_setprops($this->message, array($this->props['complete'] => true,
1211
												$this->props['datecompleted'] => time(),
1212
												$this->props['status'] => 2,
1213
												$this->props['percent_complete'] => 1));
1214
1215
			$this->doUpdate();
1216
		}
1217
1218
		/**
1219
		 * Function returns extra info about task request comments along with message body
1220
		 * which will be included in body while sending task request/response.
1221
		 *
1222
		 * @return string info about task request comments along with message body.
1223
		 */
1224
		function getTaskCommentsInfo()
1225
		{
1226
			return $this->taskCommentsInfo;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->taskCommentsInfo returns the type boolean which is incompatible with the documented return type string.
Loading history...
1227
		}
1228
1229
		/**
1230
		 * Function sets extra info about task request comments along with message body
1231
		 * which will be included in body while sending task request/response.
1232
		 *
1233
		 * @param string $taskCommentsInfo info about task request comments along with message body.
1234
		 */
1235
		function setTaskCommentsInfo($taskCommentsInfo)
1236
		{
1237
			$this->taskCommentsInfo = $taskCommentsInfo;
0 ignored issues
show
Bug Best Practice introduced by
The property taskCommentsInfo does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1238
		}
1239
1240
		function getSubProperties() {
1241
			$subProperties = array();
1242
			$subProperties["subject"] = PR_SUBJECT;
1243
			$subProperties["convtopic"] = PR_CONVERSATION_TOPIC;
1244
			$subProperties["complete"] = "PT_BOOLEAN:PSETID_Task:0x811c";
1245
			$subProperties["datecompleted"] = "PT_SYSTIME:PSETID_Task:0x810f";
1246
			$subProperties["recurring"] = "PT_BOOLEAN:PSETID_Task:0x8126";
1247
			$subProperties["startdate"] = "PT_SYSTIME:PSETID_Task:0x8104";
1248
			$subProperties["duedate"] = "PT_SYSTIME:PSETID_Task:0x8105";
1249
			$subProperties["status"] = "PT_LONG:PSETID_Task:0x8101";
1250
			$subProperties["percent_complete"] = "PT_DOUBLE:PSETID_Task:0x8102";
1251
			$subProperties["totalwork"] = "PT_LONG:PSETID_Task:0x8111";
1252
			$subProperties["actualwork"] = "PT_LONG:PSETID_Task:0x8110";
1253
			$subProperties["categories"] = "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords";
1254
			$subProperties["companies"] = "PT_MV_STRING8:PSETID_Common:0x8539";
1255
			$subProperties["mileage"] = "PT_STRING8:PSETID_Common:0x8534";
1256
			$subProperties["billinginformation"] = "PT_STRING8:PSETID_Common:0x8535";
1257
1258
			return getPropIdsFromStrings($this->store, $subProperties);
1259
		}
1260
	}
1261
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...
1262