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

server/includes/mapi/class.taskrecurrence.php (1 issue)

Severity
1
<?php
2
	require_once(BASE_PATH . 'server/includes/mapi/class.baserecurrence.php');
3
4
	class TaskRecurrence extends BaseRecurrence
5
	{
6
		/**
7
		 * Timezone info which is always false for task
8
		 */
9
		var $tz = false;
10
11
		function __construct($store, $message)
12
		{
13
			$this->store = $store;
14
			$this->message = $message;
15
16
			$properties = array();
17
			$properties["entryid"] = PR_ENTRYID;
18
			$properties["parent_entryid"] = PR_PARENT_ENTRYID;
19
			$properties["icon_index"] = PR_ICON_INDEX;
20
			$properties["message_class"] = PR_MESSAGE_CLASS;
21
			$properties["message_flags"] = PR_MESSAGE_FLAGS;
22
			$properties["subject"] = PR_SUBJECT;
23
			$properties["importance"] = PR_IMPORTANCE;
24
			$properties["sensitivity"] = PR_SENSITIVITY;
25
			$properties["last_modification_time"] = PR_LAST_MODIFICATION_TIME;
26
			$properties["status"] = "PT_LONG:PSETID_Task:0x8101";
27
			$properties["percent_complete"] = "PT_DOUBLE:PSETID_Task:0x8102";
28
			$properties["startdate"] = "PT_SYSTIME:PSETID_Task:0x8104";
29
			$properties["duedate"] = "PT_SYSTIME:PSETID_Task:0x8105";
30
			$properties["reset_reminder"] = "PT_BOOLEAN:PSETID_Task:0x8107";
31
			$properties["dead_occurrence"] = "PT_BOOLEAN:PSETID_Task:0x8109";
32
			$properties["datecompleted"] = "PT_SYSTIME:PSETID_Task:0x810f";
33
			$properties["recurring_data"] = "PT_BINARY:PSETID_Task:0x8116";
34
			$properties["actualwork"] = "PT_LONG:PSETID_Task:0x8110";
35
			$properties["totalwork"] = "PT_LONG:PSETID_Task:0x8111";
36
			$properties["complete"] = "PT_BOOLEAN:PSETID_Task:0x811c";
37
			$properties["task_f_creator"] = "PT_BOOLEAN:PSETID_Task:0x811e";
38
			$properties["owner"] = "PT_STRING8:PSETID_Task:0x811f";
39
			$properties["recurring"] = "PT_BOOLEAN:PSETID_Task:0x8126";
40
41
			$properties["reminder_minutes"] = "PT_LONG:PSETID_Common:0x8501";
42
			$properties["reminder_time"] = "PT_SYSTIME:PSETID_Common:0x8502";
43
			$properties["reminder"] = "PT_BOOLEAN:PSETID_Common:0x8503";
44
45
			$properties["private"] = "PT_BOOLEAN:PSETID_Common:0x8506";
46
			$properties["contacts"] = "PT_MV_STRING8:PSETID_Common:0x853a";
47
			$properties["contacts_string"] = "PT_STRING8:PSETID_Common:0x8586";
48
			$properties["categories"] = "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords";
49
50
			$properties["commonstart"] = "PT_SYSTIME:PSETID_Common:0x8516";
51
			$properties["commonend"] = "PT_SYSTIME:PSETID_Common:0x8517";
52
			$properties["commonassign"] = "PT_LONG:PSETID_Common:0x8518";
53
			$properties["flagdueby"] = "PT_SYSTIME:PSETID_Common:0x8560";
54
			$properties["side_effects"] = "PT_LONG:PSETID_Common:0x8510";
55
56
			$this->proptags = getPropIdsFromStrings($store, $properties);
57
58
			parent::__construct($store, $message, $properties);
59
		}
60
61
		/**
62
		 * Function which saves recurrence and also regenerates task if necessary.
63
		 *@param array $recur new recurrence properties
64
		 *@return array of properties of regenerated task else false
65
		 */
66
		function setRecurrence(&$recur)
67
		{
68
			$this->recur = $recur;
69
			$this->action =& $recur;
70
71
			if(!isset($this->recur["changed_occurrences"]))
72
				$this->recur["changed_occurrences"] = Array();
73
74
			if(!isset($this->recur["deleted_occurrences"]))
75
				$this->recur["deleted_occurrences"] = Array();
76
77
			if (!isset($this->recur['startocc'])) $this->recur['startocc'] = 0;
78
			if (!isset($this->recur['endocc'])) $this->recur['endocc'] = 0;
79
80
			// Save recurrence because we need proper startrecurrdate and endrecurrdate
81
			$this->saveRecurrence();
82
83
			// Update $this->recur with proper startrecurrdate and endrecurrdate updated after saving recurrence
84
			$msgProps = mapi_getprops($this->message, array($this->proptags['recurring_data']));
85
			$recurring_data = $this->parseRecurrence($msgProps[$this->proptags['recurring_data']]);
86
			foreach($recurring_data as $key => $value) {
87
				$this->recur[$key] = $value;
88
			}
89
90
			$this->setFirstOccurrence();
91
92
			// Let's see if next occurrence has to be generated
93
			return $this->moveToNextOccurrence();
94
		}
95
96
		/**
97
		 * Sets task object to first occurrence if startdate/duedate of task object is different from first occurrence
98
		 */
99
		function setFirstOccurrence()
100
		{
101
			// Check if it is already the first occurrence
102
			if($this->action['start'] == $this->recur["start"]){
103
				return;
104
			} else {
105
				$items = $this->getNextOccurrence();
106
107
				$props = array();
108
				$props[$this->proptags['startdate']] = $items[$this->proptags['startdate']];
109
				$props[$this->proptags['commonstart']] = $items[$this->proptags['startdate']];
110
111
				$props[$this->proptags['duedate']] = $items[$this->proptags['duedate']];
112
				$props[$this->proptags['commonend']] = $items[$this->proptags['duedate']];
113
114
				mapi_setprops($this->message, $props);
115
			}
116
		}
117
118
		/**
119
		 * Function which creates new task as current occurrence and moves the
120
		 * existing task to next occurrence.
121
		 *
122
		 *@param array $recur $action from client
123
		 *@return boolean if moving to next occurrence succeed then it returns
124
		 *		properties of either newly created task or existing task ELSE
125
		 *		false because that was last occurrence
126
		 */
127
		function moveToNextOccurrence()
128
		{
129
			$result = false;
130
			/**
131
			 * Every recurring task should have a 'duedate'. If a recurring task is created with no start/end date
132
			 * then we create first two occurrence separately and for first occurrence recurrence has ended.
133
			 */
134
			if ((empty($this->action['startdate']) && empty($this->action['duedate']))
135
				|| ($this->action['complete'] == 1) || (isset($this->action['deleteOccurrence']) && $this->action['deleteOccurrence'])){
136
137
				$nextOccurrence = $this->getNextOccurrence();
138
				$result = mapi_getprops($this->message, array(PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID));
139
140
				$props = array();
141
				if ($nextOccurrence) {
142
					if (!isset($this->action['deleteOccurrence'])) {
143
						// Create current occurrence as separate task
144
						$result = $this->regenerateTask($this->action['complete']);
145
					}
146
147
					// Set reminder for next occurrence
148
					$this->setReminder($nextOccurrence);
149
150
					// Update properties for next occurrence
151
					$this->action['duedate'] = $props[$this->proptags['duedate']] = $nextOccurrence[$this->proptags['duedate']];
152
					$this->action['commonend'] = $props[$this->proptags['commonend']] = $nextOccurrence[$this->proptags['duedate']];
153
154
					$this->action['startdate'] = $props[$this->proptags['startdate']] = $nextOccurrence[$this->proptags['startdate']];
155
					$this->action['commonstart'] = $props[$this->proptags['commonstart']] = $nextOccurrence[$this->proptags['startdate']];
156
157
					// If current task as been mark as 'Complete' then next occurrence should be incomplete.
158
					if (isset($this->action['complete']) && $this->action['complete'] == 1) {
159
						$this->action['status'] = $props[$this->proptags["status"]] = olTaskNotStarted;
160
						$this->action['complete'] = $props[$this->proptags["complete"]] = false;
161
						$this->action['percent_complete'] = $props[$this->proptags["percent_complete"]] = 0;
162
					}
163
164
					$props[$this->proptags["dead_occurrence"]] = false;
165
				} else {
166
					if (isset($this->action['deleteOccurrence']) && $this->action['deleteOccurrence'])
167
						return false;
168
169
					// Didn't get next occurrence, probably this is the last one, so recurrence ends here
170
					$props[$this->proptags["dead_occurrence"]] = true;
171
					$props[$this->proptags["datecompleted"]] = $this->action['datecompleted'];
172
					$props[$this->proptags["task_f_creator"]] = true;
173
174
					//OL props
175
					$props[$this->proptags["side_effects"]] = 1296;
176
					$props[$this->proptags["icon_index"]] = 1280;
177
				}
178
179
				mapi_setprops($this->message, $props);
180
			}
181
182
			return $result;
183
		}
184
185
		/**
186
		 * Function which return properties of next occurrence
187
		 *@return array startdate/enddate of next occurrence
188
		 */
189
		function getNextOccurrence()
190
		{
191
			if ($this->recur) {
192
				$items = array();
193
194
				//@TODO: fix start of range
195
				$start = isset($this->messageprops[$this->proptags["duedate"]]) ? $this->messageprops[$this->proptags["duedate"]] : $this->action['start'];
196
				$dayend = ($this->recur['term'] == 0x23) ? 0x7fffffff : $this->dayStartOf($this->recur["end"]);
197
198
				// Fix recur object
199
				$this->recur['startocc'] = 0;
200
				$this->recur['endocc'] = 0;
201
202
				// Retrieve next occurrence
203
				$items = $this->getItems($start, $dayend, 1);
204
205
				return !empty($items) ? $items[0] : false;
206
			}
207
		}
208
209
		/**
210
		 * Function which clones current occurrence and sets appropriate properties.
211
		 * The original recurring item is moved to next occurrence.
212
		 *@param boolean $markComplete true if existing occurrence has to be mark complete else false.
213
		 */
214
		function regenerateTask($markComplete)
215
		{
216
			// Get all properties
217
			$taskItemProps = mapi_getprops($this->message);
218
219
			if (isset($this->action["subject"])) $taskItemProps[$this->proptags["subject"]] = $this->action["subject"];
220
			if (isset($this->action["importance"])) $taskItemProps[$this->proptags["importance"]] = $this->action["importance"];
221
			if (isset($this->action["startdate"])) {
222
				$taskItemProps[$this->proptags["startdate"]] = $this->action["startdate"];
223
				$taskItemProps[$this->proptags["commonstart"]] = $this->action["startdate"];
224
			}
225
			if (isset($this->action["duedate"])) {
226
				$taskItemProps[$this->proptags["duedate"]] = $this->action["duedate"];
227
				$taskItemProps[$this->proptags["commonend"]] = $this->action["duedate"];
228
			}
229
230
			$folder = mapi_msgstore_openentry($this->store, $taskItemProps[PR_PARENT_ENTRYID]);
231
			$newMessage = mapi_folder_createmessage($folder);
232
233
			$taskItemProps[$this->proptags["status"]] = $markComplete ? olTaskComplete : olTaskNotStarted;
234
			$taskItemProps[$this->proptags["complete"]] = $markComplete;
235
			$taskItemProps[$this->proptags["percent_complete"]] = $markComplete ? 1 : 0;
236
237
			// This occurrence has been marked as 'Complete' so disable reminder
238
			if ($markComplete) {
239
				$taskItemProps[$this->proptags["reset_reminder"]] = false;
240
				$taskItemProps[$this->proptags["reminder"]] = false;
241
				$taskItemProps[$this->proptags["datecompleted"]] = $this->action["datecompleted"];
242
243
				unset($this->action[$this->proptags['datecompleted']]);
244
			}
245
246
			// Recurrence ends for this item
247
			$taskItemProps[$this->proptags["dead_occurrence"]] = true;
248
			$taskItemProps[$this->proptags["task_f_creator"]] = true;
249
250
			//OL props
251
			$taskItemProps[$this->proptags["side_effects"]] = 1296;
252
			$taskItemProps[$this->proptags["icon_index"]] = 1280;
253
254
			// Copy recipients
255
			$recipienttable = mapi_message_getrecipienttable($this->message);
256
			$recipients = mapi_table_queryallrows($recipienttable, array(PR_ENTRYID, PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_RECIPIENT_ENTRYID, PR_RECIPIENT_TYPE, PR_SEND_INTERNET_ENCODING, PR_SEND_RICH_INFO, PR_RECIPIENT_DISPLAY_NAME, PR_ADDRTYPE, PR_DISPLAY_TYPE, PR_RECIPIENT_TRACKSTATUS, PR_RECIPIENT_TRACKSTATUS_TIME, PR_RECIPIENT_FLAGS, PR_ROWID));
257
258
			$copy_to_recipientTable = mapi_message_getrecipienttable($newMessage);
259
			$copy_to_recipientRows = mapi_table_queryallrows($copy_to_recipientTable, array(PR_ROWID));
260
			foreach($copy_to_recipientRows as $recipient) {
261
				mapi_message_modifyrecipients($newMessage, MODRECIP_REMOVE, array($recipient));
262
			}
263
			mapi_message_modifyrecipients($newMessage, MODRECIP_ADD, $recipients);
264
265
			// Copy attachments
266
			$attachmentTable = mapi_message_getattachmenttable($this->message);
267
			if($attachmentTable) {
268
				$attachments = mapi_table_queryallrows($attachmentTable, array(PR_ATTACH_NUM, PR_ATTACH_SIZE, PR_ATTACH_LONG_FILENAME, PR_ATTACHMENT_HIDDEN, PR_DISPLAY_NAME, PR_ATTACH_METHOD));
269
270
				foreach($attachments as $attach_props){
271
					$attach_old = mapi_message_openattach($this->message, (int) $attach_props[PR_ATTACH_NUM]);
272
					$attach_newResourceMsg = mapi_message_createattach($newMessage);
273
274
					mapi_copyto($attach_old, array(), array(), $attach_newResourceMsg, 0);
275
					mapi_savechanges($attach_newResourceMsg);
276
				}
277
			}
278
279
			mapi_setprops($newMessage, $taskItemProps);
280
			mapi_savechanges($newMessage);
281
282
			// Update body of original message
283
			$msgbody = mapi_message_openproperty($this->message, PR_BODY);
284
			$msgbody = trim($msgbody, "\0");
285
			$separator = "------------\r\n";
286
287
			if (!empty($msgbody) && strrpos($msgbody, $separator) === false) {
288
				$msgbody = $separator . $msgbody;
289
				$stream = mapi_openproperty($this->message, PR_BODY, IID_IStream, 0, 0);
290
				mapi_stream_setsize($stream, strlen($msgbody));
291
				mapi_stream_write($stream, $msgbody);
292
				mapi_stream_commit($stream);
293
			}
294
295
			// We need these properties to notify client
296
			return mapi_getprops($newMessage, array(PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID));
297
		}
298
299
		/**
300
		 * processOccurrenceItem, adds an item to a list of occurrences, but only if the
301
		 * resulting occurrence starts or ends in the interval <$start, $end>
302
		 * @param array $items reference to the array to be added to
303
		 * @param date $start start of timeframe in GMT TIME
304
		 * @param date $end end of timeframe in GMT TIME
305
		 * @param date $basedate (hour/sec/min assumed to be 00:00:00) in LOCAL TIME OF THE OCCURRENCE
306
		 */
307
		function processOccurrenceItem(&$items, $start, $end, $now)
308
		{
309
			if ($now > $start) {
310
				$newItem = array();
311
				$newItem[$this->proptags['startdate']] = $now;
312
313
				// If startdate and enddate are set on task, then slide enddate according to duration
314
				if (isset($this->messageprops[$this->proptags["startdate"]]) && isset($this->messageprops[$this->proptags["duedate"]])) {
315
					$newItem[$this->proptags['duedate']] = $newItem[$this->proptags['startdate']] + ($this->messageprops[$this->proptags["duedate"]] - $this->messageprops[$this->proptags["startdate"]]);
316
				} else {
317
					$newItem[$this->proptags['duedate']] = $newItem[$this->proptags['startdate']];
318
				}
319
320
				$items[] = $newItem;
321
			}
322
		}
323
324
		/**
325
		 * Function which marks existing occurrence to 'Complete'
326
		 *@param array $recur array action from client
327
		 *@return array of properties of regenerated task else false
328
		 */
329
		function markOccurrenceComplete(&$recur)
330
		{
331
			// Fix timezone object
332
			$this->tz = false;
333
			$this->action =& $recur;
334
			$dead_occurrence = isset($this->messageprops[$this->proptags['dead_occurrence']]) ? $this->messageprops[$this->proptags['dead_occurrence']] : false;
335
336
			if (!$dead_occurrence) {
337
				return $this->moveToNextOccurrence();
338
			}
339
340
			return false;
341
		}
342
343
		/**
344
		 * Function which sets reminder on recurring task after existing occurrence has been deleted or marked complete.
345
		 *@param array $nextOccurrence properties of next occurrence
346
		 */
347
		function setReminder($nextOccurrence)
348
		{
349
			$props = array();
350
			if ($nextOccurrence) {
351
				// Check if reminder is reset. Default is 'false'
352
				$reset_reminder = isset($this->messageprops[$this->proptags['reset_reminder']]) ? $this->messageprops[$this->proptags['reset_reminder']] : false;
353
				$reminder = $this->messageprops[$this->proptags['reminder']];
354
355
				// Either reminder was already set OR reminder was set but was dismissed bty user
356
				if ($reminder || $reset_reminder) {
357
					// Reminder can be set at any time either before or after the duedate, so get duration between the reminder time and duedate
358
					$reminder_time = isset($this->messageprops[$this->proptags['reminder_time']]) ? $this->messageprops[$this->proptags['reminder_time']] : 0;
359
					$reminder_difference = isset($this->messageprops[$this->proptags['duedate']]) ? $this->messageprops[$this->proptags['duedate']] : 0;
360
					$reminder_difference = $reminder_difference - $reminder_time;
361
362
					// Apply duration to next calculated duedate
363
					$next_reminder_time = $nextOccurrence[$this->proptags['duedate']] - $reminder_difference;
364
365
					$props[$this->proptags['reminder_time']] = $next_reminder_time;
366
					$props[$this->proptags['flagdueby']] = $next_reminder_time;
367
					$this->action['reminder'] = $props[$this->proptags['reminder']] = true;
368
				}
369
			} else {
370
				// Didn't get next occurrence, probably this is the last occurrence
371
				$props[$this->proptags['reminder']] = false;
372
				$props[$this->proptags['reset_reminder']] = false;
373
			}
374
375
			if (!empty($props))
376
				mapi_setprops($this->message, $props);
377
		}
378
379
		/**
380
		 * Function which recurring task to next occurrence.
381
		 * It simply doesn't regenerate task
382
		 @param array $action
383
		 */
384
		function deleteOccurrence($action)
385
		{
386
			$this->tz = false;
387
			$this->action = $action;
388
			$result = $this->moveToNextOccurrence();
389
390
			mapi_savechanges($this->message);
391
392
			return $result;
393
		}
394
	}
395
?>
0 ignored issues
show
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...
396