Issues (752)

server/includes/modules/class.taskitemmodule.php (6 issues)

1
<?php
2
3
/**
4
 * Task ItemModule
5
 * Module which opens, creates, saves and deletes an item. It
6
 * extends the Module class.
7
 */
8
class TaskItemModule extends ItemModule {
9
	/**
10
	 * Constructor.
11
	 *
12
	 * @param int   $id   unique id
13
	 * @param array $data list of all actions
14
	 */
15
	public function __construct($id, $data) {
16
		parent::__construct($id, $data);
17
18
		$this->properties = $GLOBALS["properties"]->getTaskProperties();
19
20
		$this->plaintext = true;
21
	}
22
23
	#[Override]
24
	public function open($store, $entryid, $action) {
25
		$data = [];
26
27
		$message = $GLOBALS['operations']->openMessage($store, $entryid);
28
29
		if (empty($message)) {
30
			return;
31
		}
32
33
		// Open embedded message if requested
34
		$attachNum = !empty($action['attach_num']) ? $action['attach_num'] : false;
35
		if ($attachNum) {
36
			// get message props of sub message
37
			$parentMessage = $message;
38
			$message = $GLOBALS['operations']->openMessage($store, $entryid, $attachNum);
39
40
			if (empty($message)) {
41
				return;
42
			}
43
44
			$data['item'] = $GLOBALS['operations']->getEmbeddedMessageProps($store, $message, $this->properties, $parentMessage, $attachNum);
45
		}
46
		else {
47
			$data['item'] = $GLOBALS['operations']->getMessageProps($store, $message, $this->properties, $this->plaintext);
48
49
			$tr = new TaskRequest($store, $message, $GLOBALS['mapisession']->getSession());
0 ignored issues
show
The type TaskRequest 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...
50
			if ($tr->isTaskRequest() || $tr->isTaskRequestResponse()) {
51
				$tr->isTaskRequest() ? $tr->processTaskRequest() : $tr->processTaskResponse();
52
				$task = $tr->getAssociatedTask(false);
53
				$action["message_action"]["open_task"] = true;
54
				$data = $this->getMessageProps($store, $entryid, $action, $task);
55
				$data['item']['props']['task_not_found'] = ($task === false);
56
			}
57
		}
58
59
		$this->addActionData('item', $data);
60
		$GLOBALS['bus']->addData($this->getResponseData());
61
	}
62
63
	/**
64
	 * Function which used to open and get the all properties of the message. if message_action
65
	 * "open_task" is true then it will open the associated task of task request and return its data item
66
	 * else return the task request data item.
67
	 *
68
	 * @param object $store   MAPI Message Store Object
69
	 * @param string $entryid entryid of the message
70
	 * @param object $task    associated task of task request
71
	 * @param mixed  $action
72
	 *
73
	 * @return array $data item properties of given message
74
	 */
75
	public function getMessageProps($store, $entryid, $action, $task) {
76
		if (isset($action["message_action"]["open_task"]) && $action["message_action"]["open_task"] && $task !== false) {
77
			$taskProps = mapi_getprops($task, [PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID]);
78
			$message = $GLOBALS['operations']->openMessage($store, $taskProps[PR_ENTRYID]);
79
		}
80
		else {
81
			$message = $GLOBALS['operations']->openMessage($store, $entryid);
82
		}
83
		$data['item'] = $GLOBALS['operations']->getMessageProps($store, $message, $this->properties, $this->plaintext);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.
Loading history...
84
85
		return $data;
86
	}
87
88
	/**
89
	 * Function which saves an item.
90
	 *
91
	 * @param object $store         MAPI Message Store Object
92
	 * @param string $parententryid parent entryid of the message
93
	 * @param array  $action        the action data, sent by the client
94
	 * @param mixed  $entryid
95
	 */
96
	#[Override]
97
	public function save($store, $parententryid, $entryid, $action) {
98
		if (isset($action["props"])) {
99
			if (!$store && !$parententryid) {
0 ignored issues
show
$store is of type object, thus it always evaluated to true.
Loading history...
100
				if (isset($action["props"]["message_class"])) {
101
					$store = $GLOBALS["mapisession"]->getDefaultMessageStore();
102
					$parententryid = $this->getDefaultFolderEntryID($store, $action["props"]["message_class"]);
103
				}
104
			}
105
106
			if ($store && $parententryid) {
107
				// Set the message flag for the item
108
				if (isset($action['props']['message_flags']) && $entryid) {
109
					$GLOBALS['operations']->setMessageFlag($store, $entryid, $action['props']['message_flags']);
110
				}
111
112
				$messageProps = $this->saveTask($store, $parententryid, $entryid, $action);
113
114
				if ($messageProps) {
115
					$send = $action["message_action"]["send"] ?? false;
116
					$message = $GLOBALS['operations']->openMessage($store, $messageProps[PR_ENTRYID]);
117
					$data = $GLOBALS['operations']->getMessageProps($store, $message, $this->properties, $this->plaintext);
118
119
					// taskupdates property true if assigner as checked "Keep copy of task in task list" checkbox.
120
					// if it is not checked then it means user don't want assigned task copy in his task list.
121
					if ($send && isset($data["props"]['taskupdates']) && $data["props"]['taskupdates'] === false) {
122
						// Hard delete the task if taskupdates property is not true.
123
						$folder = mapi_msgstore_openentry($store, $parententryid);
124
						mapi_folder_deletemessages($folder, [$messageProps[PR_ENTRYID]], DELETE_HARD_DELETE);
125
126
						$data = Conversion::mapMAPI2XML($this->properties, $messageProps);
127
						$GLOBALS["bus"]->notify(bin2hex($parententryid), TABLE_DELETE, $messageProps);
128
					}
129
					else {
130
						$GLOBALS["bus"]->notify(bin2hex($parententryid), TABLE_SAVE, $messageProps);
131
					}
132
					// Notify To-Do list folder as new task item was created.
133
					$GLOBALS["bus"]->notify(bin2hex(TodoList::getEntryId()), OBJECT_SAVE, $messageProps);
134
					$this->addActionData("update", ["item" => $data]);
135
					$GLOBALS["bus"]->addData($this->getResponseData());
136
				}
137
			}
138
		}
139
	}
140
141
	/**
142
	 * Function which deletes an item.
143
	 *
144
	 * @param object $store         MAPI Message Store Object
145
	 * @param string $parententryid parent entryid of the message
146
	 * @param mixed  $entryids
147
	 * @param array  $action        the action data, sent by the client
148
	 */
149
	#[Override]
150
	public function delete($store, $parententryid, $entryids, $action) {
151
		if ($store && $parententryid) {
152
			$props = [];
153
			$props[PR_PARENT_ENTRYID] = $parententryid;
154
			$props[PR_ENTRYID] = $entryids;
155
156
			$storeprops = mapi_getprops($store, [PR_ENTRYID]);
157
			$props[PR_STORE_ENTRYID] = $storeprops[PR_ENTRYID];
158
159
			$result = $this->deleteTask($store, $parententryid, $entryids, $action);
160
161
			if ($result) {
162
				if (isset($result['occurrenceDeleted']) && $result['occurrenceDeleted']) {
163
					// Occurrence deleted, update item
164
					$GLOBALS["bus"]->notify(bin2hex($parententryid), TABLE_SAVE, $props);
165
				}
166
				else {
167
					$GLOBALS["bus"]->notify(bin2hex($parententryid), TABLE_DELETE, $props);
168
				}
169
				$this->sendFeedback(true);
170
			}
171
		}
172
	}
173
174
	/**
175
	 * Deletes a task.
176
	 *
177
	 * deletes occurrence if task is a recurring item.
178
	 *
179
	 * @param mapistore $store         MAPI Message Store Object
0 ignored issues
show
The type mapistore 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...
180
	 * @param string    $parententryid parent entryid of the messages to be deleted
181
	 * @param array     $entryids      a list of entryids which will be deleted
182
	 * @param mixed     $action
183
	 *
184
	 * @return bool true if action succeeded, false if not
185
	 */
186
	public function deleteTask($store, $parententryid, $entryids, $action) {
187
		$result = false;
188
		$message = mapi_msgstore_openentry($store, $entryids);
189
		$messageAction = $action["message_action"]["action_type"] ?? false;
190
		// If user wants to delete only occurrence then delete this occurrence
191
		if (!is_array($entryids) && $messageAction) {
0 ignored issues
show
The condition is_array($entryids) is always true.
Loading history...
192
			if ($message) {
193
				if ($messageAction == 'occurrence') {
194
					$recur = new TaskRecurrence($store, $message);
0 ignored issues
show
The type TaskRecurrence 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...
195
					$occurrenceDeleted = $recur->deleteOccurrence($action);
196
				}
197
				elseif ($messageAction == 'declineAndDelete' || $messageAction == 'completeAndDelete') {
198
					$taskReq = new TaskRequest($store, $message, $GLOBALS["mapisession"]->getSession());
199
					if ($messageAction == 'declineAndDelete') {
200
						$taskReq->doDecline();
201
					}
202
					elseif ($messageAction == 'completeAndDelete') {
203
						$taskReq->sendCompleteUpdate();
204
					}
205
				}
206
			}
207
		}
208
209
		// Deleting occurrence failed, maybe that was its last occurrence, so now we delete whole series.
210
		if (!isset($occurrenceDeleted) || !$occurrenceDeleted) {
211
			$properties = $GLOBALS["properties"]->getTaskProperties();
212
			$goid = mapi_getprops($message, [$properties["task_goid"]]);
213
			// If task is assigned task to assignee and user is trying to delete the task.
214
			// then we have to remove respective task request(IPM.TaskRequest.Accept/Decline/Update)
215
			// notification mail from inbox.
216
			if (isset($goid[$properties["task_goid"]]) && !empty($goid[$properties["task_goid"]])) {
217
				$taskReq = new TaskRequest($store, $message, $GLOBALS["mapisession"]->getSession());
218
				$result = $taskReq->deleteReceivedTR();
219
				if ($result) {
220
					$GLOBALS["bus"]->notify(bin2hex((string) $result[PR_PARENT_ENTRYID]), TABLE_DELETE, $result);
221
				}
222
			}
223
224
			// If softdelete is set then set it in softDelete variable and pass it for deleting message.
225
			$softDelete = $action['message_action']['soft_delete'] ?? false;
226
			$result = $GLOBALS["operations"]->deleteMessages($store, $parententryid, $entryids, $softDelete);
227
		}
228
		else {
229
			$result = ['occurrenceDeleted' => true];
230
		}
231
232
		return $result;
233
	}
234
235
	/**
236
	 * Save task item.
237
	 *
238
	 * just one step more before saving task message that to support recurrence and task request here. We need
239
	 * to regenerate task if it is recurring and client has changed either set as complete or delete or
240
	 * given new start or end date.
241
	 *
242
	 * @param mapistore $store         MAPI store of the message
243
	 * @param string    $parententryid Parent entryid of the message (folder entryid, NOT message entryid)
244
	 * @param array     $action        Action array containing XML request
245
	 * @param mixed     $entryid
246
	 *
247
	 * @return array of PR_ENTRYID, PR_PARENT_ENTRYID and PR_STORE_ENTRYID properties of modified item
248
	 */
249
	public function saveTask($store, $parententryid, $entryid, $action) {
250
		$properties = $this->properties;
251
		$messageProps = [];
252
		$send = $action["message_action"]["send"] ?? false;
253
254
		if ($store && $parententryid) {
255
			if (isset($action["props"])) {
256
				if (isset($action["entryid"]) && empty($action["entryid"])) {
257
					$GLOBALS["operations"]->setSenderAddress($store, $action);
258
				}
259
260
				$props = $action["props"];
261
				$recips = [];
262
				if (isset($action["recipients"]) && is_array($action["recipients"])) {
263
					$recips = $action["recipients"];
264
				}
265
266
				if (isset($action["entryid"]) && !empty($action["entryid"])) {
267
					$message = mapi_msgstore_openentry($store, hex2bin((string) $action["entryid"]));
268
					if ($message) {
269
						$messageProps = mapi_getprops($message, [PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID, $properties['recurring']]);
270
271
						if ((isset($messageProps[$properties['recurring']]) && $messageProps[$properties['recurring']]) ||
272
							(isset($props['recurring']) && $props['recurring'])) {
273
							$recur = new TaskRecurrence($store, $message);
274
275
							if (isset($props['recurring_reset']) && $props['recurring_reset'] == 1) {
276
								$msgProps = $recur->setRecurrence($props);
277
							}
278
							elseif (isset($props['complete']) && $props['complete'] == 1) {
279
								$msgProps = $recur->markOccurrenceComplete($props);
280
							}
281
						}
282
						mapi_savechanges($message);
283
284
						$messageProps = Conversion::mapXML2MAPI($properties, $props);
285
286
						$message = $GLOBALS["operations"]->saveMessage($store, $entryid, $parententryid, $messageProps, $messageProps, $recips, $action['attachments'] ?? [], []);
287
288
						if (isset($msgProps) && $msgProps) {
289
							$messageProps = $msgProps;
290
						}
291
					}
292
				}
293
				else {
294
					$messageProps = Conversion::mapXML2MAPI($properties, $props);
295
					// New message
296
297
					$copyAttachments = false;
298
					$copyFromMessage = false;
299
300
					// we need to copy the original attachments when create task from message.
301
					if (isset($action['message_action'], $action['message_action']['source_entryid'])) {
302
						$copyFromMessage = hex2bin($action['message_action']['source_entryid']);
303
						$copyFromStore = hex2bin((string) $action['message_action']['source_store_entryid']);
304
						$copyAttachments = true;
305
306
						// get resources of store and message
307
						$copyFromStore = $GLOBALS['mapisession']->openMessageStore($copyFromStore);
308
						$copyFromMessage = $GLOBALS['operations']->openMessage($copyFromStore, $copyFromMessage);
309
					}
310
311
					$message = $GLOBALS["operations"]->saveMessage($store, $entryid, $parententryid, $messageProps, $messageProps, $recips, $action['attachments'] ?? [], [], $copyFromMessage, $copyAttachments, false, false, false);
312
313
					// Set recurrence
314
					if (isset($action['props']['recurring']) && $action['props']['recurring'] == 1) {
315
						$recur = new TaskRecurrence($store, $message);
316
						$recur->setRecurrence($props);
317
					}
318
				}
319
320
				if ($message) {
321
					$tr = new TaskRequest($store, $message, $GLOBALS["mapisession"]->getSession());
322
					if ($send) {
323
						$tr->sendTaskRequest(_("Task Request:") . " ");
324
325
						return $messageProps;
326
					}
327
					if (isset($action["message_action"]["response_type"]) && $action["message_action"]["response_type"] === tdmtTaskUpd) {
328
						$tr->doUpdate();
329
330
						return $messageProps;
331
					}
332
					if (isset($action["message_action"]["action_type"]) && $action["message_action"]["action_type"] === "restoreToTaskList") {
333
						$deleteTRProps = $tr->deleteReceivedTR();
334
						if ($deleteTRProps) {
335
							$GLOBALS["bus"]->notify(bin2hex((string) $deleteTRProps[PR_PARENT_ENTRYID]), TABLE_DELETE, $deleteTRProps);
336
						}
337
338
						return $messageProps;
339
					}
340
				}
341
342
				mapi_savechanges($message);
343
			}
344
		}
345
346
		// Return message properties that can be sent to the bus to notify changes
347
		return $messageProps;
348
	}
349
}
350