Passed
Push — master ( d7fd99...ee5080 )
by
unknown
02:37 queued 13s
created

TaskRecurrence::processOccurrenceItem()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 8
c 1
b 0
f 0
nc 3
nop 8
dl 0
loc 14
rs 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/*
3
 * SPDX-License-Identifier: AGPL-3.0-only
4
 * SPDX-FileCopyrightText: Copyright 2005-2016 Zarafa Deutschland GmbH
5
 * SPDX-FileCopyrightText: Copyright 2020-2022 grommunio GmbH
6
 */
7
8
class TaskRecurrence extends BaseRecurrence {
9
	/**
10
	 * Timezone info which is always false for task.
11
	 *
12
	 * @var false
13
	 */
14
	public $tz = false;
15
16
	private $action;
17
18
	public function __construct($store, $message) {
19
		$this->store = $store;
20
		$this->message = $message;
21
22
		$properties = [];
23
		$properties["entryid"] = PR_ENTRYID;
24
		$properties["parent_entryid"] = PR_PARENT_ENTRYID;
25
		$properties["icon_index"] = PR_ICON_INDEX;
26
		$properties["message_class"] = PR_MESSAGE_CLASS;
27
		$properties["message_flags"] = PR_MESSAGE_FLAGS;
28
		$properties["subject"] = PR_SUBJECT;
29
		$properties["importance"] = PR_IMPORTANCE;
30
		$properties["sensitivity"] = PR_SENSITIVITY;
31
		$properties["last_modification_time"] = PR_LAST_MODIFICATION_TIME;
32
		$properties["status"] = "PT_LONG:PSETID_Task:" . PidLidTaskStatus;
33
		$properties["percent_complete"] = "PT_DOUBLE:PSETID_Task:" . PidLidPercentComplete;
34
		$properties["startdate"] = "PT_SYSTIME:PSETID_Task:" . PidLidTaskStartDate;
35
		$properties["duedate"] = "PT_SYSTIME:PSETID_Task:" . PidLidTaskDueDate;
36
		$properties["reset_reminder"] = "PT_BOOLEAN:PSETID_Task:0x8107";
37
		$properties["dead_occurrence"] = "PT_BOOLEAN:PSETID_Task:0x8109";
38
		$properties["datecompleted"] = "PT_SYSTIME:PSETID_Task:" . PidLidTaskDateCompleted;
39
		$properties["recurring_data"] = "PT_BINARY:PSETID_Task:0x8116";
40
		$properties["actualwork"] = "PT_LONG:PSETID_Task:0x8110";
41
		$properties["totalwork"] = "PT_LONG:PSETID_Task:0x8111";
42
		$properties["complete"] = "PT_BOOLEAN:PSETID_Task:" . PidLidTaskComplete;
43
		$properties["task_f_creator"] = "PT_BOOLEAN:PSETID_Task:0x811e";
44
		$properties["owner"] = "PT_STRING8:PSETID_Task:0x811f";
45
		$properties["recurring"] = "PT_BOOLEAN:PSETID_Task:0x8126";
46
47
		$properties["reminder_minutes"] = "PT_LONG:PSETID_Common:" . PidLidReminderDelta;
48
		$properties["reminder_time"] = "PT_SYSTIME:PSETID_Common:" . PidLidReminderTime;
49
		$properties["reminder"] = "PT_BOOLEAN:PSETID_Common:" . PidLidReminderSet;
50
51
		$properties["private"] = "PT_BOOLEAN:PSETID_Common:" . PidLidPrivate;
52
		$properties["contacts"] = "PT_MV_STRING8:PSETID_Common:0x853a";
53
		$properties["contacts_string"] = "PT_STRING8:PSETID_Common:0x8586";
54
		$properties["categories"] = "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords";
55
56
		$properties["commonstart"] = "PT_SYSTIME:PSETID_Common:0x8516";
57
		$properties["commonend"] = "PT_SYSTIME:PSETID_Common:0x8517";
58
		$properties["commonassign"] = "PT_LONG:PSETID_Common:0x8518";
59
		$properties["flagdueby"] = "PT_SYSTIME:PSETID_Common:" . PidLidReminderSignalTime;
60
		$properties["side_effects"] = "PT_LONG:PSETID_Common:0x8510";
61
62
		$this->proptags = getPropIdsFromStrings($store, $properties);
63
64
		parent::__construct($store, $message);
65
	}
66
67
	/**
68
	 * Function which saves recurrence and also regenerates task if necessary.
69
	 *
70
	 * @param mixed $recur new recurrence properties
71
	 *
72
	 * @return array|bool of properties of regenerated task else false
73
	 */
74
	public function setRecurrence(&$recur) {
75
		$this->recur = $recur;
76
		$this->action = &$recur;
77
78
		if (!isset($this->recur["changed_occurrences"])) {
79
			$this->recur["changed_occurrences"] = [];
80
		}
81
82
		if (!isset($this->recur["deleted_occurrences"])) {
83
			$this->recur["deleted_occurrences"] = [];
84
		}
85
86
		if (!isset($this->recur['startocc'])) {
87
			$this->recur['startocc'] = 0;
88
		}
89
		if (!isset($this->recur['endocc'])) {
90
			$this->recur['endocc'] = 0;
91
		}
92
93
		// Save recurrence because we need proper startrecurrdate and endrecurrdate
94
		$this->saveRecurrence();
95
96
		// Update $this->recur with proper startrecurrdate and endrecurrdate updated after saving recurrence
97
		$msgProps = mapi_getprops($this->message, [$this->proptags['recurring_data']]);
98
		$recurring_data = $this->parseRecurrence($msgProps[$this->proptags['recurring_data']]);
99
		foreach ($recurring_data as $key => $value) {
100
			$this->recur[$key] = $value;
101
		}
102
103
		$this->setFirstOccurrence();
104
105
		// Let's see if next occurrence has to be generated
106
		return $this->moveToNextOccurrence();
107
	}
108
109
	/**
110
	 * Sets task object to first occurrence if startdate/duedate of task object is different from first occurrence.
111
	 */
112
	public function setFirstOccurrence() {
113
		// Check if it is already the first occurrence
114
		if ($this->action['start'] == $this->recur["start"]) {
115
			return;
116
		}
117
		$items = $this->getNextOccurrence();
118
119
		$props = [];
120
		$props[$this->proptags['startdate']] = $items[$this->proptags['startdate']];
121
		$props[$this->proptags['commonstart']] = $items[$this->proptags['startdate']];
122
123
		$props[$this->proptags['duedate']] = $items[$this->proptags['duedate']];
124
		$props[$this->proptags['commonend']] = $items[$this->proptags['duedate']];
125
126
		mapi_setprops($this->message, $props);
127
	}
128
129
	/**
130
	 * Function which creates new task as current occurrence and moves the
131
	 * existing task to next occurrence.
132
	 *
133
	 * @return array|bool properties of newly created task if moving to next occurrence succeeds
134
	 *                    false if that was last occurrence
135
	 */
136
	public function moveToNextOccurrence() {
137
		$result = false;
138
		/*
139
		 * Every recurring task should have a 'duedate'. If a recurring task is created with no start/end date
140
		 * then we create first two occurrence separately and for first occurrence recurrence has ended.
141
		 */
142
		if ((empty($this->action['startdate']) && empty($this->action['duedate'])) ||
143
			($this->action['complete'] == 1) || (isset($this->action['deleteOccurrence']) && $this->action['deleteOccurrence'])) {
144
			$nextOccurrence = $this->getNextOccurrence();
145
			$result = mapi_getprops($this->message, [PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID]);
146
147
			$props = [];
148
			if (!empty($nextOccurrence)) {
149
				if (!isset($this->action['deleteOccurrence'])) {
150
					// Create current occurrence as separate task
151
					$result = $this->regenerateTask($this->action['complete']);
152
				}
153
154
				// Set reminder for next occurrence
155
				$this->setReminder($nextOccurrence);
156
157
				// Update properties for next occurrence
158
				$this->action['duedate'] = $props[$this->proptags['duedate']] = $nextOccurrence[$this->proptags['duedate']];
159
				$this->action['commonend'] = $props[$this->proptags['commonend']] = $nextOccurrence[$this->proptags['duedate']];
160
161
				$this->action['startdate'] = $props[$this->proptags['startdate']] = $nextOccurrence[$this->proptags['startdate']];
162
				$this->action['commonstart'] = $props[$this->proptags['commonstart']] = $nextOccurrence[$this->proptags['startdate']];
163
164
				// If current task as been mark as 'Complete' then next occurrence should be incomplete.
165
				if (isset($this->action['complete']) && $this->action['complete'] == 1) {
166
					$this->action['status'] = $props[$this->proptags["status"]] = olTaskNotStarted;
167
					$this->action['complete'] = $props[$this->proptags["complete"]] = false;
168
					$this->action['percent_complete'] = $props[$this->proptags["percent_complete"]] = 0;
169
				}
170
171
				$props[$this->proptags["dead_occurrence"]] = false;
172
			}
173
			else {
174
				if (isset($this->action['deleteOccurrence']) && $this->action['deleteOccurrence']) {
175
					return false;
176
				}
177
178
				// Didn't get next occurrence, probably this is the last one, so recurrence ends here
179
				$props[$this->proptags["dead_occurrence"]] = true;
180
				$props[$this->proptags["datecompleted"]] = $this->action['datecompleted'];
181
				$props[$this->proptags["task_f_creator"]] = true;
182
183
				// OL props
184
				$props[$this->proptags["side_effects"]] = 1296;
185
				$props[$this->proptags["icon_index"]] = 1280;
186
			}
187
188
			mapi_setprops($this->message, $props);
189
		}
190
191
		return $result;
192
	}
193
194
	/**
195
	 * Function which return properties of next occurrence.
196
	 *
197
	 * @return null|array|false|T startdate/enddate of next occurrence
198
	 */
199
	public function getNextOccurrence() {
200
		if ($this->recur) {
201
			// @TODO: fix start of range
202
			$start = isset($this->messageprops[$this->proptags["duedate"]]) ? $this->messageprops[$this->proptags["duedate"]] : $this->action['start'];
203
			$dayend = ($this->recur['term'] == 0x23) ? 0x7FFFFFFF : $this->dayStartOf($this->recur["end"]);
204
205
			// Fix recur object
206
			$this->recur['startocc'] = 0;
207
			$this->recur['endocc'] = 0;
208
209
			// Retrieve next occurrence
210
			$items = $this->getItems($start, $dayend, 1);
211
212
			return !empty($items) ? $items[0] : false;
213
		}
214
	}
215
216
	/**
217
	 * Function which clones current occurrence and sets appropriate properties.
218
	 * The original recurring item is moved to next occurrence.
219
	 *
220
	 * @param bool $markComplete true if existing occurrence has to be marked complete
221
	 */
222
	public function regenerateTask($markComplete) {
223
		// Get all properties
224
		$taskItemProps = mapi_getprops($this->message);
225
226
		if (isset($this->action["subject"])) {
227
			$taskItemProps[$this->proptags["subject"]] = $this->action["subject"];
228
		}
229
		if (isset($this->action["importance"])) {
230
			$taskItemProps[$this->proptags["importance"]] = $this->action["importance"];
231
		}
232
		if (isset($this->action["startdate"])) {
233
			$taskItemProps[$this->proptags["startdate"]] = $this->action["startdate"];
234
			$taskItemProps[$this->proptags["commonstart"]] = $this->action["startdate"];
235
		}
236
		if (isset($this->action["duedate"])) {
237
			$taskItemProps[$this->proptags["duedate"]] = $this->action["duedate"];
238
			$taskItemProps[$this->proptags["commonend"]] = $this->action["duedate"];
239
		}
240
241
		$folder = mapi_msgstore_openentry($this->store, $taskItemProps[PR_PARENT_ENTRYID]);
0 ignored issues
show
Bug introduced by
$this->store of type resource is incompatible with the type resource expected by parameter $store of mapi_msgstore_openentry(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

241
		$folder = mapi_msgstore_openentry(/** @scrutinizer ignore-type */ $this->store, $taskItemProps[PR_PARENT_ENTRYID]);
Loading history...
242
		$newMessage = mapi_folder_createmessage($folder);
0 ignored issues
show
Bug introduced by
It seems like $folder can also be of type false; however, parameter $fld of mapi_folder_createmessage() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

242
		$newMessage = mapi_folder_createmessage(/** @scrutinizer ignore-type */ $folder);
Loading history...
243
244
		$taskItemProps[$this->proptags["status"]] = $markComplete ? olTaskComplete : olTaskNotStarted;
245
		$taskItemProps[$this->proptags["complete"]] = $markComplete;
246
		$taskItemProps[$this->proptags["percent_complete"]] = $markComplete ? 1 : 0;
247
248
		// This occurrence has been marked as 'Complete' so disable reminder
249
		if ($markComplete) {
250
			$taskItemProps[$this->proptags["reset_reminder"]] = false;
251
			$taskItemProps[$this->proptags["reminder"]] = false;
252
			$taskItemProps[$this->proptags["datecompleted"]] = $this->action["datecompleted"];
253
254
			unset($this->action[$this->proptags['datecompleted']]);
255
		}
256
257
		// Recurrence ends for this item
258
		$taskItemProps[$this->proptags["dead_occurrence"]] = true;
259
		$taskItemProps[$this->proptags["task_f_creator"]] = true;
260
261
		// OL props
262
		$taskItemProps[$this->proptags["side_effects"]] = 1296;
263
		$taskItemProps[$this->proptags["icon_index"]] = 1280;
264
265
		// Copy recipients
266
		$recipienttable = mapi_message_getrecipienttable($this->message);
267
		$recipients = mapi_table_queryallrows($recipienttable, [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]);
0 ignored issues
show
Bug introduced by
It seems like $recipienttable can also be of type false; however, parameter $table of mapi_table_queryallrows() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

267
		$recipients = mapi_table_queryallrows(/** @scrutinizer ignore-type */ $recipienttable, [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]);
Loading history...
268
269
		$copy_to_recipientTable = mapi_message_getrecipienttable($newMessage);
0 ignored issues
show
Bug introduced by
It seems like $newMessage can also be of type false; however, parameter $msg of mapi_message_getrecipienttable() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

269
		$copy_to_recipientTable = mapi_message_getrecipienttable(/** @scrutinizer ignore-type */ $newMessage);
Loading history...
270
		$copy_to_recipientRows = mapi_table_queryallrows($copy_to_recipientTable, [PR_ROWID]);
271
		foreach ($copy_to_recipientRows as $recipient) {
272
			mapi_message_modifyrecipients($newMessage, MODRECIP_REMOVE, [$recipient]);
0 ignored issues
show
Bug introduced by
It seems like $newMessage can also be of type false; however, parameter $msg of mapi_message_modifyrecipients() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

272
			mapi_message_modifyrecipients(/** @scrutinizer ignore-type */ $newMessage, MODRECIP_REMOVE, [$recipient]);
Loading history...
273
		}
274
		mapi_message_modifyrecipients($newMessage, MODRECIP_ADD, $recipients);
275
276
		// Copy attachments
277
		$attachmentTable = mapi_message_getattachmenttable($this->message);
278
		if ($attachmentTable) {
279
			$attachments = mapi_table_queryallrows($attachmentTable, [PR_ATTACH_NUM, PR_ATTACH_SIZE, PR_ATTACH_LONG_FILENAME, PR_ATTACHMENT_HIDDEN, PR_DISPLAY_NAME, PR_ATTACH_METHOD]);
280
281
			foreach ($attachments as $attach_props) {
282
				$attach_old = mapi_message_openattach($this->message, (int) $attach_props[PR_ATTACH_NUM]);
283
				$attach_newResourceMsg = mapi_message_createattach($newMessage);
0 ignored issues
show
Bug introduced by
It seems like $newMessage can also be of type false; however, parameter $msg of mapi_message_createattach() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

283
				$attach_newResourceMsg = mapi_message_createattach(/** @scrutinizer ignore-type */ $newMessage);
Loading history...
284
285
				mapi_copyto($attach_old, [], [], $attach_newResourceMsg, 0);
0 ignored issues
show
Bug introduced by
It seems like $attach_old can also be of type false; however, parameter $src of mapi_copyto() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

285
				mapi_copyto(/** @scrutinizer ignore-type */ $attach_old, [], [], $attach_newResourceMsg, 0);
Loading history...
Bug introduced by
It seems like $attach_newResourceMsg can also be of type false; however, parameter $dst of mapi_copyto() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

285
				mapi_copyto($attach_old, [], [], /** @scrutinizer ignore-type */ $attach_newResourceMsg, 0);
Loading history...
286
				mapi_savechanges($attach_newResourceMsg);
0 ignored issues
show
Bug introduced by
It seems like $attach_newResourceMsg can also be of type false; however, parameter $any of mapi_savechanges() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

286
				mapi_savechanges(/** @scrutinizer ignore-type */ $attach_newResourceMsg);
Loading history...
287
			}
288
		}
289
290
		mapi_setprops($newMessage, $taskItemProps);
0 ignored issues
show
Bug introduced by
It seems like $newMessage can also be of type false; however, parameter $any of mapi_setprops() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

290
		mapi_setprops(/** @scrutinizer ignore-type */ $newMessage, $taskItemProps);
Loading history...
291
		mapi_savechanges($newMessage);
292
293
		// Update body of original message
294
		$msgbody = mapi_openproperty($this->message, PR_BODY);
295
		$msgbody = trim($msgbody, "\0");
0 ignored issues
show
Bug introduced by
$msgbody of type false|resource is incompatible with the type string expected by parameter $string of trim(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

295
		$msgbody = trim(/** @scrutinizer ignore-type */ $msgbody, "\0");
Loading history...
296
		$separator = "------------\r\n";
297
298
		if (!empty($msgbody) && strrpos($msgbody, $separator) === false) {
299
			$msgbody = $separator . $msgbody;
300
			$stream = mapi_openproperty($this->message, PR_BODY, IID_IStream, STGM_TRANSACTED, 0);
0 ignored issues
show
Unused Code introduced by
The call to mapi_openproperty() has too many arguments starting with IID_IStream. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

300
			$stream = /** @scrutinizer ignore-call */ mapi_openproperty($this->message, PR_BODY, IID_IStream, STGM_TRANSACTED, 0);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
301
			mapi_stream_setsize($stream, strlen($msgbody));
0 ignored issues
show
Bug introduced by
$stream of type resource is incompatible with the type resource expected by parameter $stream of mapi_stream_setsize(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

301
			mapi_stream_setsize(/** @scrutinizer ignore-type */ $stream, strlen($msgbody));
Loading history...
302
			mapi_stream_write($stream, $msgbody);
0 ignored issues
show
Bug introduced by
$stream of type resource is incompatible with the type resource expected by parameter $stream of mapi_stream_write(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

302
			mapi_stream_write(/** @scrutinizer ignore-type */ $stream, $msgbody);
Loading history...
303
			mapi_stream_commit($stream);
0 ignored issues
show
Bug introduced by
$stream of type resource is incompatible with the type resource expected by parameter $stream of mapi_stream_commit(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

303
			mapi_stream_commit(/** @scrutinizer ignore-type */ $stream);
Loading history...
304
		}
305
306
		// We need these properties to notify client
307
		return mapi_getprops($newMessage, [PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID]);
0 ignored issues
show
Bug introduced by
It seems like $newMessage can also be of type false; however, parameter $any of mapi_getprops() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

307
		return mapi_getprops(/** @scrutinizer ignore-type */ $newMessage, [PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID]);
Loading history...
308
	}
309
310
	/**
311
	 * processOccurrenceItem, adds an item to a list of occurrences, but only if the
312
	 * resulting occurrence starts or ends in the interval <$start, $end>.
313
	 *
314
	 * @param array $items        reference to the array to be added to
315
	 * @param int   $start        start of timeframe in GMT TIME
316
	 * @param int   $end          end of timeframe in GMT TIME
317
	 * @param int   $basedate     (hour/sec/min assumed to be 00:00:00) in LOCAL TIME OF THE OCCURRENCE
318
	 * @param int   $startocc     start of occurrence since beginning of day in minutes
319
	 * @param int   $endocc       end of occurrence since beginning of day in minutes
320
	 * @param int   $tz           the timezone info for this occurrence ( applied to $basedate / $startocc / $endocc )
321
	 * @param bool  $reminderonly If TRUE, only add the item if the reminder is set
322
	 */
323
	public function processOccurrenceItem(&$items, $start, $end, $basedate, $startocc, $endocc, $tz, $reminderonly) {
324
		if ($basedate > $start) {
325
			$newItem = [];
326
			$newItem[$this->proptags['startdate']] = $basedate;
327
328
			// If startdate and enddate are set on task, then slide enddate according to duration
329
			if (isset($this->messageprops[$this->proptags["startdate"]], $this->messageprops[$this->proptags["duedate"]])) {
330
				$newItem[$this->proptags['duedate']] = $newItem[$this->proptags['startdate']] + ($this->messageprops[$this->proptags["duedate"]] - $this->messageprops[$this->proptags["startdate"]]);
331
			}
332
			else {
333
				$newItem[$this->proptags['duedate']] = $newItem[$this->proptags['startdate']];
334
			}
335
336
			$items[] = $newItem;
337
		}
338
	}
339
340
	/**
341
	 * Function which marks existing occurrence to 'Complete'.
342
	 *
343
	 * @param array $recur array action from client
344
	 *
345
	 * @return array|bool of properties of regenerated task else false
346
	 */
347
	public function markOccurrenceComplete(&$recur) {
348
		// Fix timezone object
349
		$this->tz = false;
350
		$this->action = &$recur;
351
		$dead_occurrence = isset($this->messageprops[$this->proptags['dead_occurrence']]) ? $this->messageprops[$this->proptags['dead_occurrence']] : false;
352
353
		if (!$dead_occurrence) {
354
			return $this->moveToNextOccurrence();
355
		}
356
357
		return false;
358
	}
359
360
	/**
361
	 * Function which sets reminder on recurring task after existing occurrence has been deleted or marked complete.
362
	 *
363
	 * @param mixed $nextOccurrence properties of next occurrence
364
	 */
365
	public function setReminder($nextOccurrence): void {
366
		$props = [];
367
		if (!empty($nextOccurrence)) {
368
			// Check if reminder is reset. Default is 'false'
369
			$reset_reminder = isset($this->messageprops[$this->proptags['reset_reminder']]) ? $this->messageprops[$this->proptags['reset_reminder']] : false;
370
			$reminder = $this->messageprops[$this->proptags['reminder']];
371
372
			// Either reminder was already set OR reminder was set but was dismissed bty user
373
			if ($reminder || $reset_reminder) {
374
				// Reminder can be set at any time either before or after the duedate, so get duration between the reminder time and duedate
375
				$reminder_time = isset($this->messageprops[$this->proptags['reminder_time']]) ? $this->messageprops[$this->proptags['reminder_time']] : 0;
376
				$reminder_difference = isset($this->messageprops[$this->proptags['duedate']]) ? $this->messageprops[$this->proptags['duedate']] : 0;
377
				$reminder_difference = $reminder_difference - $reminder_time;
378
379
				// Apply duration to next calculated duedate
380
				$next_reminder_time = $nextOccurrence[$this->proptags['duedate']] - $reminder_difference;
381
382
				$props[$this->proptags['reminder_time']] = $next_reminder_time;
383
				$props[$this->proptags['flagdueby']] = $next_reminder_time;
384
				$this->action['reminder'] = $props[$this->proptags['reminder']] = true;
385
			}
386
		}
387
		else {
388
			// Didn't get next occurrence, probably this is the last occurrence
389
			$props[$this->proptags['reminder']] = false;
390
			$props[$this->proptags['reset_reminder']] = false;
391
		}
392
393
		if (!empty($props)) {
394
			mapi_setprops($this->message, $props);
395
		}
396
	}
397
398
	/**
399
	 * Function which recurring task to next occurrence.
400
	 * It simply doesn't regenerate task.
401
	 *
402
	 * @param array $action
403
	 *
404
	 * @return array|bool
405
	 */
406
	public function deleteOccurrence($action) {
407
		$this->tz = false;
408
		$this->action = $action;
409
		$result = $this->moveToNextOccurrence();
410
411
		mapi_savechanges($this->message);
412
413
		return $result;
414
	}
415
}
416