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
![]() |
|||||||
242 | $newMessage = mapi_folder_createmessage($folder); |
||||||
0 ignored issues
–
show
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
![]() |
|||||||
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
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
![]() |
|||||||
268 | |||||||
269 | $copy_to_recipientTable = mapi_message_getrecipienttable($newMessage); |
||||||
0 ignored issues
–
show
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
![]() |
|||||||
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
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
![]() |
|||||||
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
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
![]() |
|||||||
284 | |||||||
285 | mapi_copyto($attach_old, [], [], $attach_newResourceMsg, 0); |
||||||
0 ignored issues
–
show
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
![]() 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
![]() |
|||||||
286 | mapi_savechanges($attach_newResourceMsg); |
||||||
0 ignored issues
–
show
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
![]() |
|||||||
287 | } |
||||||
288 | } |
||||||
289 | |||||||
290 | mapi_setprops($newMessage, $taskItemProps); |
||||||
0 ignored issues
–
show
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
![]() |
|||||||
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
$msgbody of type Resource|false 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
![]() |
|||||||
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
Are you sure the assignment to
$stream is correct as mapi_openproperty($this-...am, STGM_TRANSACTED, 0) seems to always return null .
This check looks for function or method calls that always return null and whose return value is assigned to a variable. class A
{
function getObject()
{
return null;
}
}
$a = new A();
$object = $a->getObject();
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() 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
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. ![]() |
|||||||
301 | mapi_stream_setsize($stream, strlen($msgbody)); |
||||||
0 ignored issues
–
show
$stream of type null 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
![]() |
|||||||
302 | mapi_stream_write($stream, $msgbody); |
||||||
0 ignored issues
–
show
$stream of type null 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
![]() |
|||||||
303 | mapi_stream_commit($stream); |
||||||
0 ignored issues
–
show
$stream of type null 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
![]() |
|||||||
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
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
![]() |
|||||||
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 |