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

AppointmentItemModule::handleException()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 14
nc 5
nop 6
dl 0
loc 23
rs 9.4888
c 0
b 0
f 0
1
<?php
2
	require_once(BASE_PATH . 'server/includes/mapi/class.recurrence.php');
3
4
	/**
5
	 * Appointment ItemModule
6
	 * Module which openes, creates, saves and deletes an item. It
7
	 * extends the Module class.
8
	 */
9
	class AppointmentItemModule extends ItemModule
10
	{
11
		/**
12
		 * Constructor
13
		 * @param int $id unique id.
14
		 * @param array $data list of all actions.
15
		 */
16
		function __construct($id, $data)
17
		{
18
			parent::__construct($id, $data);
19
20
			$this->properties = $GLOBALS['properties']->getAppointmentProperties();
0 ignored issues
show
Bug Best Practice introduced by
The property properties does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
21
22
			$this->plaintext = true;
23
			$this->skipCopyProperties = array(
24
				$this->properties['goid'],
25
				$this->properties['goid2'],
26
				$this->properties['request_sent'],
27
				PR_OWNER_APPT_ID
28
			);
29
		}
30
31
		function open($store, $entryid, $action)
32
		{
33
			if($store && $entryid) {
34
				$data = array();
35
36
				$message = $GLOBALS['operations']->openMessage($store, $entryid);
37
38
				if(empty($message)) {
39
					return;
40
				}
41
42
				// Open embedded message if requested
43
				$attachNum = !empty($action['attach_num']) ? $action['attach_num'] : false;
44
				if($attachNum) {
45
					// get message props of sub message
46
					$parentMessage = $message;
47
					$message = $GLOBALS['operations']->openMessage($store, $entryid, $attachNum);
48
49
					if(empty($message)) {
50
						return;
51
					}
52
53
					$data['item'] = $GLOBALS['operations']->getEmbeddedMessageProps($store, $message, $this->properties, $parentMessage, $attachNum);
54
				} else {
55
					// add all standard properties from the series/normal message
56
					$data['item'] = $GLOBALS['operations']->getMessageProps($store, $message, $this->properties, $this->plaintext);
57
				}
58
59
				// if appointment is recurring then only we should get properties of occurrence if basedate is supplied
60
				if($data['item']['props']['recurring'] === true) {
61
					if(!empty($action['basedate'])) {
62
						// check for occurrence/exception
63
						$basedate = $action['basedate'];
64
65
						$recur = new Recurrence($store, $message);
66
67
						$exceptionatt = $recur->getExceptionAttachment($basedate);
68
69
						// Single occurrences are never recurring
70
						$data['item']['props']['recurring'] = false;
71
72
						if($exceptionatt) {
73
							// Existing exception (open existing item, which includes basedate)
74
							$exceptionattProps = mapi_getprops($exceptionatt, array(PR_ATTACH_NUM));
75
							$exception = mapi_attach_openobj($exceptionatt, 0);
76
77
							// overwrite properties with the ones from the exception
78
							$exceptionProps = $GLOBALS['operations']->getMessageProps($store, $exception, $this->properties, $this->plaintext);
79
80
							/**
81
							 * If recurring item has set reminder to true then
82
							 * all occurrences before the 'flagdueby' value(of recurring item)
83
							 * should not show that reminder is set.
84
							 */
85
							if (isset($exceptionProps['props']['reminder']) && $data['item']['props']['reminder'] == true) {
86
								$flagDueByDay = $recur->dayStartOf($data['item']['props']['flagdueby']);
87
88
								if ($flagDueByDay > $basedate) {
89
									$exceptionProps['props']['reminder'] = false;
90
								}
91
							}
92
93
							// The properties must be merged, if the recipients or attachments are present in the exception
94
							// then that list should be used. Otherwise the list from the series must be applied (this
95
							// corresponds with OL2007).
96
							// @FIXME getMessageProps should not return empty string if exception doesn't contain body
97
							// by this change we can handle a situation where user has set empty string in the body explicitly
98
							if (!empty($exceptionProps['props']['body']) || !empty($exceptionProps['props']['html_body'])) {
99
								if(!empty($exceptionProps['props']['body'])) {
100
									$data['item']['props']['body'] = $exceptionProps['props']['body'];
101
								}
102
103
								if(!empty($exceptionProps['props']['html_body'])) {
104
									$data['item']['props']['html_body'] = $exceptionProps['props']['html_body'];
105
								}
106
107
								$data['item']['props']['isHTML'] = $exceptionProps['props']['isHTML'];
108
							}
109
							// remove properties from $exceptionProps so array_merge will not overwrite it
110
							unset($exceptionProps['props']['html_body']);
111
							unset($exceptionProps['props']['body']);
112
							unset($exceptionProps['props']['isHTML']);
113
114
							$data['item']['props'] = array_merge($data['item']['props'], $exceptionProps['props']);
115
							if (isset($exceptionProps['recipients'])) {
116
								$data['item']['recipients'] = $exceptionProps['recipients'];
117
							}
118
119
							if (isset($exceptionProps['attachments'])) {
120
								$data['item']['attachments'] = $exceptionProps['attachments'];
121
							}
122
123
							// Make sure we are using the passed basedate and not something wrong in the opened item
124
							$data['item']['props']['basedate'] = $basedate;
125
							$data['item']['attach_num'] = array($exceptionattProps[PR_ATTACH_NUM]);
126
						} else if($recur->isDeleteException($basedate)) {
127
							// Exception is deleted, should not happen, but if it the case then give error
128
							$this->sendFeedback(false,
129
								array(
130
									'type' => ERROR_ZARAFA,
131
									'info' => array(
132
										'original_message' => _('Could not open occurrence.'),
133
										'display_message' => _('Could not open occurrence, specific occurrence is probably deleted.')
134
									)
135
								)
136
							);
137
							return;
138
						} else {
139
							// opening an occurrence of a recurring series (same as normal open, but add basedate, startdate and enddate)
140
							$data['item']['props']['basedate'] = $basedate;
141
							$data['item']['props']['startdate'] = $recur->getOccurrenceStart($basedate);
142
							$data['item']['props']['duedate'] = $recur->getOccurrenceEnd($basedate);
143
							$data['item']['props']['commonstart'] = $data['item']['props']['startdate'];
144
							$data['item']['props']['commonend'] = $data['item']['props']['duedate'];
145
							unset($data['item']['props']['reminder_time']);
146
147
							/**
148
							 * If recurring item has set reminder to true then
149
							 * all occurrences before the 'flagdueby' value(of recurring item)
150
							 * should not show that reminder is set.
151
							 */
152
							if (isset($exceptionProps['props']['reminder']) && $data['item']['props']['reminder'] == true) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $exceptionProps does not exist. Did you maybe mean $exception?
Loading history...
153
								$flagDueByDay = $recur->dayStartOf($data['item']['props']['flagdueby']);
154
155
								if ($flagDueByDay > $basedate) {
156
									$exceptionProps['props']['reminder'] = false;
157
								}
158
							}
159
						}
160
					} else {
161
						// Opening a recurring series, get the recurrence information
162
						$recur = new Recurrence($store, $message);
163
						$recurpattern = $recur->getRecurrence();
164
						$tz = $recur->tz; // no function to do this at the moment
165
166
						// Add the recurrence pattern to the data
167
						if(isset($recurpattern) && is_array($recurpattern)) {
0 ignored issues
show
introduced by
The condition is_array($recurpattern) is always false.
Loading history...
168
							$data['item']['props'] += $recurpattern;
169
						}
170
171
						// Add the timezone information to the data
172
						if(isset($tz) && is_array($tz)) {
0 ignored issues
show
introduced by
The condition is_array($tz) is always false.
Loading history...
173
							$data['item']['props'] += $tz;
174
						}
175
					}
176
				}
177
178
				// Send the data
179
				$this->addActionData('item', $data);
180
				$GLOBALS['bus']->addData($this->getResponseData());
181
			}
182
		}
183
184
		/**
185
		 * Function does customization of exception based on module data.
186
		 * like, here it will generate display message based on actionType
187
		 * for particular exception.
188
		 *
189
		 * @param object $e Exception object
190
		 * @param string $actionType the action type, sent by the client
191
		 * @param MAPIobject $store Store object of message.
0 ignored issues
show
Bug introduced by
The type MAPIobject 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...
192
		 * @param string $parententryid parent entryid of the message.
193
		 * @param string $entryid entryid of the message.
194
		 * @param array $action the action data, sent by the client
195
		 */
196
		function handleException(&$e, $actionType = null, $store = null, $parententryid = null, $entryid = null, $action = null)
197
		{
198
			if (is_null($e->displayMessage)) {
199
				switch ($actionType) {
200
					case "save":
201
						if ($e->getCode() == MAPI_E_NO_ACCESS) {
202
							$message = mapi_msgstore_openentry($store, $entryid);
203
							$messageProps = mapi_getprops($message, array(PR_MESSAGE_CLASS, PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID));
204
							$messageClass = $messageProps[PR_MESSAGE_CLASS];
205
206
							$text = $messageClass !== "IPM.Appointment" ? _('a meeting request') : _('an appointment');
207
							$msg = _('You have insufficient privileges to move ' . $text .' in this calendar. The calendar owner can set these using the \'permissions\'-tab of the folder properties (right click the calendar folder > properties > permissions)');
208
209
							$e->setDisplayMessage($msg);
210
							$e->setTitle(_('Insufficient privileges'));
211
212
							// Need this notification to refresh the calendar.
213
							$GLOBALS['bus']->notify(bin2hex($parententryid), TABLE_DELETE, $messageProps);
0 ignored issues
show
Bug introduced by
It seems like $parententryid can also be of type null; however, parameter $string of bin2hex() does only seem to accept string, 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

213
							$GLOBALS['bus']->notify(bin2hex(/** @scrutinizer ignore-type */ $parententryid), TABLE_DELETE, $messageProps);
Loading history...
214
						}
215
						break;
216
				}
217
			}
218
			parent::handleException($e, $actionType, $store, $parententryid, $entryid, $action);
219
		}
220
221
222
		/**
223
		 * Save the give appointment or meeting request to the calendar.
224
		 *
225
		 * @param mapistore $store MAPI store of the message
0 ignored issues
show
Bug introduced by
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...
226
		 * @param string $parententryid Parent entryid of the message (folder entryid, NOT message entryid)
227
		 * @param string $entryid entryid of the message
228
		 * @param array $action Action array containing json request
229
		 * @param string $actionType The action type which triggered this action
230
		 */
231
		function save($store, $parententryid, $entryid, $action, $actionType = 'save')
232
		{
233
			$result = false;
234
235
			// Save appointment (saveAppointment takes care of creating/modifying exceptions to recurring
236
			// items if necessary)
237
			$messageProps = $GLOBALS['operations']->saveAppointment($store, $entryid, $parententryid, $action, $actionType, $this->directBookingMeetingRequest);
238
239
			// Notify the bus if the save was OK
240
			if($messageProps && !(is_array($messageProps) && isset($messageProps['error'])) && !isset($messageProps['remindertimeerror']) ){
241
				$GLOBALS['bus']->notify(bin2hex($parententryid), TABLE_SAVE, $messageProps);
242
				$result = true;
243
			}
244
245
			$errorMsg = false;
246
			if(!$result && isset($messageProps['remindertimeerror']) && !$messageProps['remindertimeerror']){
247
				$errorMsg = _('Cannot set a reminder to appear before the previous occurrence. Reset reminder to save the change');
248
			} else if (isset($messageProps['isexceptionallowed']) && $messageProps['isexceptionallowed'] === false){
249
				$errorMsg = _('Two occurrences cannot occur on the same day');
250
			} elseif(is_array($messageProps) && isset($messageProps['error'])){
251
				switch($messageProps['error']){
252
					case 1:
253
						$errorMsg = sprintf(_('You marked \'%s\' as a resource. You cannot schedule a meeting with \'%s\' because you do not have the appropriate permissions for that account. Either enter the name as a required or optional attendee or talk to your administrator about giving you permission to schedule \'%s\'.'), $messageProps['displayname'], $messageProps['displayname'], $messageProps['displayname']);
254
						break;
255
					case 2:
256
						$errorMsg = sprintf(_('\'%s\' has declined your meeting because \'%s\' does not automatically accept meeting requests.'), $messageProps['displayname'], $messageProps['displayname']);
257
						break;
258
					case 3:
259
						$errorMsg = sprintf(_('\'%s\' has declined your meeting because it is recurring. You must book each meeting separately with this resource.'), $messageProps['displayname']);
260
						break;
261
					case 4:
262
						$errorMsg = sprintf(_('\'%s\' is already booked for this specified time. You must use another time or find another resource.'), $messageProps['displayname']);
263
						break;
264
					default:
265
						$errorMsg = _('Meeting was not scheduled.');
266
						break;
267
				}
268
			} else {
269
				// Recurring but non-existing exception (same as normal open, but add basedate, startdate and enddate)
270
				$data = array();
271
				if ($result) {
272
					$data = Conversion::mapMAPI2XML($this->properties, $messageProps);
273
274
					// Get recipient information from the saved appointment to update client side
275
					// according to the latest recipient related changes only if changes requested from client.
276
					$savedAppointment = $GLOBALS['operations']->openMessage($store, $messageProps[PR_ENTRYID]);
277
					if(!empty($action['recipients'])) {
278
						$recipients = $GLOBALS["operations"]->getRecipientsInfo($savedAppointment);
279
						if(!empty($recipients)) {
280
							$data["recipients"] = array(
281
								"item" => $recipients
282
							);
283
						}
284
					}
285
286
					// Get attachments information from the saved appointment to update client side
287
					// according to the latest attachments related changes only if changes requested from client.
288
					if (!empty($action['attachments'])) {
289
						$attachments = $GLOBALS["operations"]->getAttachmentsInfo($savedAppointment);
290
						if (!empty($attachments)) {
291
							$data["attachments"] = array(
292
								"item" => $attachments
293
							);
294
						}
295
					}
296
297
					$data['action_response'] = Array(
298
						'resources_booked' => $this->directBookingMeetingRequest
299
					);
300
301
					if(isset($action['message_action']) && isset($action['message_action']['paste'])) {
302
						$data['action_response']['resources_pasted'] = true;
303
					}
304
305
				} else {
306
					if(!empty($action['message_action']['send'])){
307
						$errorMsg = _('Meeting could not be sent.');
308
					} else {
309
						$errorMsg = _('Meeting could not be saved.');
310
					}
311
				}
312
			}
313
314
			if($errorMsg===false){
315
316
				$this->addActionData('update', array('item' => $data));
317
				$GLOBALS['bus']->addData($this->getResponseData());
318
			} else {
319
				$this->sendFeedback(false, array(
320
					'type' => ERROR_ZARAFA,
321
					'info' => array(
322
						'display_message' => $errorMsg,
323
					)
324
				));
325
			}
326
		}
327
	}
328
?>
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...
329