|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/* |
|
4
|
|
|
* SPDX-License-Identifier: AGPL-3.0-only |
|
5
|
|
|
* SPDX-FileCopyrightText: Copyright 2007-2016 Zarafa Deutschland GmbH |
|
6
|
|
|
* SPDX-FileCopyrightText: Copyright 2020-2024 grommunio GmbH |
|
7
|
|
|
* |
|
8
|
|
|
* WBXML appointment entities that can be parsed directly (as a stream) from |
|
9
|
|
|
* WBXML. It is automatically decoded according to $mapping and the Sync |
|
10
|
|
|
* WBXML mappings. |
|
11
|
|
|
*/ |
|
12
|
|
|
|
|
13
|
|
|
class SyncAppointment extends SyncObject { |
|
14
|
|
|
public $timezone; |
|
15
|
|
|
public $dtstamp; |
|
16
|
|
|
public $starttime; |
|
17
|
|
|
public $subject; |
|
18
|
|
|
public $uid; |
|
19
|
|
|
public $organizername; |
|
20
|
|
|
public $organizeremail; |
|
21
|
|
|
public $location; |
|
22
|
|
|
public $location2; // AS 16: SyncLocation object |
|
23
|
|
|
public $endtime; |
|
24
|
|
|
public $recurrence; |
|
25
|
|
|
public $sensitivity; |
|
26
|
|
|
public $busystatus; |
|
27
|
|
|
public $alldayevent; |
|
28
|
|
|
public $reminder; |
|
29
|
|
|
public $rtf; |
|
30
|
|
|
public $meetingstatus; |
|
31
|
|
|
public $attendees; |
|
32
|
|
|
public $body; |
|
33
|
|
|
public $bodytruncated; |
|
34
|
|
|
public $exceptions; |
|
35
|
|
|
public $deleted; |
|
36
|
|
|
public $exceptionstarttime; |
|
37
|
|
|
public $categories; |
|
38
|
|
|
|
|
39
|
|
|
// AS 12.0 props |
|
40
|
|
|
public $asbody; |
|
41
|
|
|
public $nativebodytype; |
|
42
|
|
|
|
|
43
|
|
|
// AS 14.0 props |
|
44
|
|
|
public $disallownewtimeprop; |
|
45
|
|
|
public $responsetype; |
|
46
|
|
|
public $responserequested; |
|
47
|
|
|
|
|
48
|
|
|
// AS 14.1 props |
|
49
|
|
|
public $onlineMeetingConfLink; |
|
50
|
|
|
public $onlineMeetingExternalLink; |
|
51
|
|
|
|
|
52
|
|
|
// AS 16.0 props |
|
53
|
|
|
public $asattachments; |
|
54
|
|
|
public $clientuid; |
|
55
|
|
|
public $instanceid; |
|
56
|
|
|
public $instanceiddelete; |
|
57
|
|
|
|
|
58
|
|
|
public function __construct() { |
|
59
|
|
|
$mapping = [ |
|
60
|
|
|
SYNC_POOMCAL_TIMEZONE => [ |
|
61
|
|
|
self::STREAMER_VAR => "timezone", |
|
62
|
|
|
self::STREAMER_RONOTIFY => true, |
|
63
|
|
|
], |
|
64
|
|
|
SYNC_POOMCAL_DTSTAMP => [ |
|
65
|
|
|
self::STREAMER_VAR => "dtstamp", |
|
66
|
|
|
self::STREAMER_TYPE => self::STREAMER_TYPE_DATE, |
|
67
|
|
|
self::STREAMER_CHECKS => [self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETZERO], |
|
68
|
|
|
], |
|
69
|
|
|
SYNC_POOMCAL_STARTTIME => [ |
|
70
|
|
|
self::STREAMER_VAR => "starttime", |
|
71
|
|
|
self::STREAMER_TYPE => self::STREAMER_TYPE_DATE, |
|
72
|
|
|
self::STREAMER_CHECKS => [self::STREAMER_CHECK_CMPLOWER => SYNC_POOMCAL_ENDTIME], |
|
73
|
|
|
self::STREAMER_RONOTIFY => true, |
|
74
|
|
|
], |
|
75
|
|
|
SYNC_POOMCAL_SUBJECT => [ |
|
76
|
|
|
self::STREAMER_VAR => "subject", |
|
77
|
|
|
self::STREAMER_CHECKS => [self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETEMPTY], |
|
78
|
|
|
self::STREAMER_RONOTIFY => true, |
|
79
|
|
|
self::STREAMER_PRIVATE => self::STRIP_PRIVATE_SUBSTITUTE, |
|
80
|
|
|
], |
|
81
|
|
|
SYNC_POOMCAL_UID => [self::STREAMER_VAR => "uid"], |
|
82
|
|
|
SYNC_POOMCAL_ORGANIZERNAME => [ |
|
83
|
|
|
self::STREAMER_VAR => "organizername", // verified below |
|
84
|
|
|
self::STREAMER_PRIVATE => 'Undisclosed Organizer', |
|
85
|
|
|
], |
|
86
|
|
|
SYNC_POOMCAL_ORGANIZEREMAIL => [ |
|
87
|
|
|
self::STREAMER_VAR => "organizeremail", // verified below |
|
88
|
|
|
self::STREAMER_PRIVATE => 'undisclosed@localhost', |
|
89
|
|
|
], |
|
90
|
|
|
SYNC_POOMCAL_LOCATION => [ |
|
91
|
|
|
self::STREAMER_VAR => "location", |
|
92
|
|
|
self::STREAMER_RONOTIFY => true, |
|
93
|
|
|
self::STREAMER_PRIVATE => true, |
|
94
|
|
|
], |
|
95
|
|
|
SYNC_POOMCAL_ENDTIME => [ |
|
96
|
|
|
self::STREAMER_VAR => "endtime", |
|
97
|
|
|
self::STREAMER_TYPE => self::STREAMER_TYPE_DATE, |
|
98
|
|
|
self::STREAMER_CHECKS => [self::STREAMER_CHECK_CMPHIGHER => SYNC_POOMCAL_STARTTIME], |
|
99
|
|
|
self::STREAMER_RONOTIFY => true, |
|
100
|
|
|
], |
|
101
|
|
|
SYNC_POOMCAL_RECURRENCE => [ |
|
102
|
|
|
self::STREAMER_VAR => "recurrence", |
|
103
|
|
|
self::STREAMER_TYPE => "SyncRecurrence", |
|
104
|
|
|
self::STREAMER_RONOTIFY => true, |
|
105
|
|
|
], |
|
106
|
|
|
// Sensitivity values |
|
107
|
|
|
// 0 = Normal |
|
108
|
|
|
// 1 = Personal |
|
109
|
|
|
// 2 = Private |
|
110
|
|
|
// 3 = Confident |
|
111
|
|
|
SYNC_POOMCAL_SENSITIVITY => [ |
|
112
|
|
|
self::STREAMER_VAR => "sensitivity", |
|
113
|
|
|
self::STREAMER_CHECKS => [self::STREAMER_CHECK_ONEVALUEOF => [0, 1, 2, 3]], |
|
114
|
|
|
self::STREAMER_RONOTIFY => true, |
|
115
|
|
|
self::STREAMER_VALUEMAP => [0 => "Normal", |
|
116
|
|
|
1 => "Personal", |
|
117
|
|
|
2 => "Private", |
|
118
|
|
|
3 => "Confident", |
|
119
|
|
|
], |
|
120
|
|
|
], |
|
121
|
|
|
// Busystatus values |
|
122
|
|
|
// 0 = Free |
|
123
|
|
|
// 1 = Tentative |
|
124
|
|
|
// 2 = Busy |
|
125
|
|
|
// 3 = Out of office |
|
126
|
|
|
// 4 = Working Elsewhere |
|
127
|
|
|
SYNC_POOMCAL_BUSYSTATUS => [ |
|
128
|
|
|
self::STREAMER_VAR => "busystatus", |
|
129
|
|
|
self::STREAMER_CHECKS => [ |
|
130
|
|
|
self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETTWO, |
|
131
|
|
|
self::STREAMER_CHECK_ONEVALUEOF => [0, 1, 2, 3, 4], |
|
132
|
|
|
], |
|
133
|
|
|
self::STREAMER_RONOTIFY => true, |
|
134
|
|
|
// if private is stripped, value will be set to 2 (busy) |
|
135
|
|
|
self::STREAMER_PRIVATE => 2, |
|
136
|
|
|
self::STREAMER_VALUEMAP => [ |
|
137
|
|
|
0 => "Free", |
|
138
|
|
|
1 => "Tentative", |
|
139
|
|
|
2 => "Busy", |
|
140
|
|
|
3 => "Out of office", |
|
141
|
|
|
4 => "Working Elsewhere", |
|
142
|
|
|
], |
|
143
|
|
|
], |
|
144
|
|
|
SYNC_POOMCAL_ALLDAYEVENT => [ |
|
145
|
|
|
self::STREAMER_VAR => "alldayevent", |
|
146
|
|
|
self::STREAMER_CHECKS => [self::STREAMER_CHECK_ZEROORONE => self::STREAMER_CHECK_SETZERO], |
|
147
|
|
|
self::STREAMER_RONOTIFY => true, |
|
148
|
|
|
self::STREAMER_VALUEMAP => [ |
|
149
|
|
|
0 => "No", |
|
150
|
|
|
1 => "Yes", |
|
151
|
|
|
], |
|
152
|
|
|
], |
|
153
|
|
|
SYNC_POOMCAL_REMINDER => [ |
|
154
|
|
|
self::STREAMER_VAR => "reminder", |
|
155
|
|
|
self::STREAMER_CHECKS => [self::STREAMER_CHECK_CMPHIGHER => -1], |
|
156
|
|
|
self::STREAMER_RONOTIFY => true, |
|
157
|
|
|
// if private is stripped, value will be unset (no reminder) |
|
158
|
|
|
self::STREAMER_PRIVATE => true, |
|
159
|
|
|
], |
|
160
|
|
|
// Meetingstatus values |
|
161
|
|
|
// 0 = is not a meeting |
|
162
|
|
|
// 1 = is a meeting |
|
163
|
|
|
// 3 = Meeting received |
|
164
|
|
|
// 5 = Meeting is canceled |
|
165
|
|
|
// 7 = Meeting is canceled and received |
|
166
|
|
|
// 9 = as 1 |
|
167
|
|
|
// 11 = as 3 |
|
168
|
|
|
// 13 = as 5 |
|
169
|
|
|
// 15 = as 7 |
|
170
|
|
|
SYNC_POOMCAL_MEETINGSTATUS => [ |
|
171
|
|
|
self::STREAMER_VAR => "meetingstatus", |
|
172
|
|
|
self::STREAMER_CHECKS => [self::STREAMER_CHECK_ONEVALUEOF => [0, 1, 3, 5, 7, 9, 11, 13, 15]], |
|
173
|
|
|
self::STREAMER_RONOTIFY => true, |
|
174
|
|
|
self::STREAMER_VALUEMAP => [ |
|
175
|
|
|
0 => "Not a meeting", |
|
176
|
|
|
1 => "Meeting", |
|
177
|
|
|
3 => "Meeting received", |
|
178
|
|
|
5 => "Meeting canceled", |
|
179
|
|
|
7 => "Meeting canceled and received", |
|
180
|
|
|
9 => "Meeting", |
|
181
|
|
|
11 => "Meeting received", |
|
182
|
|
|
13 => "Meeting canceled", |
|
183
|
|
|
15 => "Meeting canceled and received", |
|
184
|
|
|
], |
|
185
|
|
|
], |
|
186
|
|
|
SYNC_POOMCAL_ATTENDEES => [ |
|
187
|
|
|
self::STREAMER_VAR => "attendees", |
|
188
|
|
|
self::STREAMER_TYPE => "SyncAttendee", |
|
189
|
|
|
self::STREAMER_ARRAY => SYNC_POOMCAL_ATTENDEE, |
|
190
|
|
|
self::STREAMER_RONOTIFY => true, |
|
191
|
|
|
self::STREAMER_PRIVATE => true, |
|
192
|
|
|
], |
|
193
|
|
|
SYNC_POOMCAL_BODY => [ |
|
194
|
|
|
self::STREAMER_VAR => "body", |
|
195
|
|
|
self::STREAMER_RONOTIFY => true, |
|
196
|
|
|
self::STREAMER_PRIVATE => true, |
|
197
|
|
|
], |
|
198
|
|
|
SYNC_POOMCAL_BODYTRUNCATED => [ |
|
199
|
|
|
self::STREAMER_VAR => "bodytruncated", |
|
200
|
|
|
self::STREAMER_PRIVATE => true, |
|
201
|
|
|
], |
|
202
|
|
|
SYNC_POOMCAL_EXCEPTIONS => [ |
|
203
|
|
|
self::STREAMER_VAR => "exceptions", |
|
204
|
|
|
self::STREAMER_TYPE => "SyncAppointmentException", |
|
205
|
|
|
self::STREAMER_ARRAY => SYNC_POOMCAL_EXCEPTION, |
|
206
|
|
|
self::STREAMER_RONOTIFY => true, |
|
207
|
|
|
], |
|
208
|
|
|
SYNC_POOMCAL_CATEGORIES => [ |
|
209
|
|
|
self::STREAMER_VAR => "categories", |
|
210
|
|
|
self::STREAMER_ARRAY => SYNC_POOMCAL_CATEGORY, |
|
211
|
|
|
self::STREAMER_RONOTIFY => true, |
|
212
|
|
|
self::STREAMER_PRIVATE => true, |
|
213
|
|
|
], |
|
214
|
|
|
]; |
|
215
|
|
|
|
|
216
|
|
|
if (Request::GetProtocolVersion() >= 12.0) { |
|
217
|
|
|
$mapping[SYNC_AIRSYNCBASE_BODY] = [ |
|
218
|
|
|
self::STREAMER_VAR => "asbody", |
|
219
|
|
|
self::STREAMER_TYPE => "SyncBaseBody", |
|
220
|
|
|
self::STREAMER_RONOTIFY => true, |
|
221
|
|
|
self::STREAMER_PRIVATE => true, |
|
222
|
|
|
]; |
|
223
|
|
|
|
|
224
|
|
|
$mapping[SYNC_AIRSYNCBASE_NATIVEBODYTYPE] = [self::STREAMER_VAR => "nativebodytype"]; |
|
225
|
|
|
|
|
226
|
|
|
// unset these properties because airsyncbase body and attachments will be used instead |
|
227
|
|
|
unset($mapping[SYNC_POOMCAL_BODY], $mapping[SYNC_POOMCAL_BODYTRUNCATED]); |
|
228
|
|
|
} |
|
229
|
|
|
|
|
230
|
|
|
if (Request::GetProtocolVersion() >= 14.0) { |
|
231
|
|
|
$mapping[SYNC_POOMCAL_DISALLOWNEWTIMEPROPOSAL] = [ |
|
232
|
|
|
self::STREAMER_VAR => "disallownewtimeprop", |
|
233
|
|
|
self::STREAMER_RONOTIFY => true, |
|
234
|
|
|
self::STREAMER_PRIVATE => 1, |
|
235
|
|
|
]; // don't permit new time proposal |
|
236
|
|
|
$mapping[SYNC_POOMCAL_RESPONSEREQUESTED] = [ |
|
237
|
|
|
self::STREAMER_VAR => "responserequested", |
|
238
|
|
|
self::STREAMER_RONOTIFY => true, |
|
239
|
|
|
]; |
|
240
|
|
|
$mapping[SYNC_POOMCAL_RESPONSETYPE] = [ |
|
241
|
|
|
self::STREAMER_VAR => "responsetype", |
|
242
|
|
|
self::STREAMER_RONOTIFY => true, |
|
243
|
|
|
]; |
|
244
|
|
|
} |
|
245
|
|
|
|
|
246
|
|
|
if (Request::GetProtocolVersion() >= 14.1) { |
|
247
|
|
|
$mapping[SYNC_POOMCAL_ONLINEMEETINGCONFLINK] = [ |
|
248
|
|
|
self::STREAMER_VAR => "onlineMeetingConfLink", |
|
249
|
|
|
self::STREAMER_RONOTIFY => true, |
|
250
|
|
|
]; |
|
251
|
|
|
$mapping[SYNC_POOMCAL_ONLINEMEETINGEXTERNALLINK] = [ |
|
252
|
|
|
self::STREAMER_VAR => "onlineMeetingExternalLink", |
|
253
|
|
|
self::STREAMER_RONOTIFY => true, |
|
254
|
|
|
]; |
|
255
|
|
|
} |
|
256
|
|
|
|
|
257
|
|
|
if (Request::GetProtocolVersion() >= 16.0) { |
|
258
|
|
|
$mapping[SYNC_AIRSYNCBASE_ATTACHMENTS] = [ |
|
259
|
|
|
self::STREAMER_VAR => "asattachments", |
|
260
|
|
|
// Different tags can be used to encapsulate the SyncBaseAttachmentSubtypes depending on its usecase |
|
261
|
|
|
self::STREAMER_ARRAY => [ |
|
262
|
|
|
SYNC_AIRSYNCBASE_ATTACHMENT => "SyncBaseAttachment", |
|
263
|
|
|
SYNC_AIRSYNCBASE_ADD => "SyncBaseAttachmentAdd", |
|
264
|
|
|
SYNC_AIRSYNCBASE_DELETE => "SyncBaseAttachmentDelete", |
|
265
|
|
|
], |
|
266
|
|
|
]; |
|
267
|
|
|
$mapping[SYNC_AIRSYNCBASE_LOCATION] = [ |
|
268
|
|
|
self::STREAMER_VAR => "location2", |
|
269
|
|
|
self::STREAMER_TYPE => "SyncLocation", |
|
270
|
|
|
self::STREAMER_RONOTIFY => true, |
|
271
|
|
|
]; |
|
272
|
|
|
$mapping[SYNC_POOMCAL_CLIENTUID] = [ |
|
273
|
|
|
self::STREAMER_VAR => "clientuid", |
|
274
|
|
|
self::STREAMER_RONOTIFY => true, |
|
275
|
|
|
]; |
|
276
|
|
|
// Placeholder for the InstanceId (recurrence exceptions) and its deletion request |
|
277
|
|
|
$mapping[SYNC_AIRSYNCBASE_INSTANCEID] = [ |
|
278
|
|
|
self::STREAMER_VAR => "instanceid", |
|
279
|
|
|
self::STREAMER_TYPE => self::STREAMER_TYPE_IGNORE, |
|
280
|
|
|
]; |
|
281
|
|
|
$mapping[SYNC_AIRSYNCBASE_INSTANCEID_DELETE] = [ |
|
282
|
|
|
self::STREAMER_VAR => "instanceiddelete", |
|
283
|
|
|
self::STREAMER_TYPE => self::STREAMER_TYPE_IGNORE, |
|
284
|
|
|
]; |
|
285
|
|
|
|
|
286
|
|
|
// unset these properties because airsyncbase location will be used instead |
|
287
|
|
|
unset($mapping[SYNC_POOMCAL_LOCATION]); |
|
288
|
|
|
} |
|
289
|
|
|
|
|
290
|
|
|
parent::__construct($mapping); |
|
291
|
|
|
|
|
292
|
|
|
// Indicates that this SyncObject supports the private flag and stripping of private data. |
|
293
|
|
|
$this->supportsPrivateStripping = true; |
|
294
|
|
|
} |
|
295
|
|
|
|
|
296
|
|
|
/** |
|
297
|
|
|
* Method checks if the object has the minimum of required parameters |
|
298
|
|
|
* and fulfills semantic dependencies. |
|
299
|
|
|
* |
|
300
|
|
|
* This overloads the general check() with special checks to be executed |
|
301
|
|
|
* Checks if SYNC_POOMCAL_ORGANIZERNAME and SYNC_POOMCAL_ORGANIZEREMAIL are correctly set |
|
302
|
|
|
* |
|
303
|
|
|
* @param bool $logAsDebug (opt) default is false, so messages are logged in WARN log level |
|
304
|
|
|
* |
|
305
|
|
|
* @return bool |
|
306
|
|
|
*/ |
|
307
|
|
|
#[Override] |
|
308
|
|
|
public function Check($logAsDebug = false) { |
|
309
|
|
|
// Fix starttime and endtime if they are not set on NEW appointments. |
|
310
|
|
|
if ($this->flags === SYNC_NEWMESSAGE) { |
|
|
|
|
|
|
311
|
|
|
$time = time(); |
|
312
|
|
|
$calcstart = $time + 1800 - ($time % 1800); // round up to the next half hour |
|
313
|
|
|
|
|
314
|
|
|
// Check error cases first |
|
315
|
|
|
// Case 2: starttime not set, endtime in the past |
|
316
|
|
|
if (!isset($this->starttime) && isset($this->endtime) && $this->endtime < $time) { |
|
317
|
|
|
SLog::Write(LOGLEVEL_WARN, "SyncAppointment->Check(): Parameter 'starttime' not set while 'endtime' is in the past (case 2). Aborting."); |
|
318
|
|
|
|
|
319
|
|
|
return false; |
|
320
|
|
|
} |
|
321
|
|
|
// Case 3b: starttime not set, endtime in the future (3) but before the calculated starttime (3b) |
|
322
|
|
|
if (!isset($this->starttime) && isset($this->endtime) && $this->endtime > $time && $this->endtime < $calcstart) { |
|
323
|
|
|
SLog::Write(LOGLEVEL_WARN, "SyncAppointment->Check(): Parameter 'starttime' not set while 'endtime' is in the future but before the calculated starttime (case 3b). Aborting."); |
|
324
|
|
|
|
|
325
|
|
|
return false; |
|
326
|
|
|
} |
|
327
|
|
|
// Case 5: starttime in the future but no endtime set |
|
328
|
|
|
if (isset($this->starttime) && $this->starttime > $time && !isset($this->endtime)) { |
|
329
|
|
|
SLog::Write(LOGLEVEL_WARN, "SyncAppointment->Check(): Parameter 'starttime' is in the future but 'endtime' is not set (case 5). Aborting."); |
|
330
|
|
|
|
|
331
|
|
|
return false; |
|
332
|
|
|
} |
|
333
|
|
|
|
|
334
|
|
|
// Set starttime to the rounded up next half hour |
|
335
|
|
|
// Case 1, 3a (endtime won't be changed as it's set) |
|
336
|
|
|
if (!isset($this->starttime)) { |
|
337
|
|
|
$this->starttime = $calcstart; |
|
338
|
|
|
SLog::Write(LOGLEVEL_WBXML, sprintf("SyncAppointment->Check(): Parameter 'starttime' was not set, setting it to %d (%s).", $this->starttime, Utils::FormatDate($this->starttime))); |
|
339
|
|
|
} |
|
340
|
|
|
// Case 1, 4 |
|
341
|
|
|
if (!isset($this->endtime)) { |
|
342
|
|
|
$this->endtime = $calcstart + 1800; // 30 min after calcstart |
|
343
|
|
|
SLog::Write(LOGLEVEL_WBXML, sprintf("SyncAppointment->Check(): Parameter 'endtime' was not set, setting it to %d (%s).", $this->endtime, Utils::FormatDate($this->endtime))); |
|
344
|
|
|
} |
|
345
|
|
|
} |
|
346
|
|
|
|
|
347
|
|
|
$ret = parent::Check($logAsDebug); |
|
348
|
|
|
|
|
349
|
|
|
// semantic checks general "turn off switch" |
|
350
|
|
|
if (defined("DO_SEMANTIC_CHECKS") && DO_SEMANTIC_CHECKS === false) { |
|
|
|
|
|
|
351
|
|
|
return $ret; |
|
352
|
|
|
} |
|
353
|
|
|
|
|
354
|
|
|
if (!$ret) { |
|
355
|
|
|
return false; |
|
356
|
|
|
} |
|
357
|
|
|
|
|
358
|
|
|
if ($this->meetingstatus > 0) { |
|
359
|
|
|
if (!isset($this->organizername) || !isset($this->organizeremail)) { |
|
360
|
|
|
SLog::Write(LOGLEVEL_INFO, "SyncAppointment->Check(): Parameter 'organizername' and 'organizeremail' should be set for a meeting request"); |
|
361
|
|
|
} |
|
362
|
|
|
} |
|
363
|
|
|
|
|
364
|
|
|
// do not sync a recurrent appointment without a timezone (except all day events) |
|
365
|
|
|
if (isset($this->recurrence) && !isset($this->timezone) && empty($this->alldayevent)) { |
|
366
|
|
|
SLog::Write(LOGLEVEL_INFO, "SyncAppointment->Check(): timezone for a recurring appointment is not set."); |
|
367
|
|
|
|
|
368
|
|
|
return false; |
|
369
|
|
|
} |
|
370
|
|
|
|
|
371
|
|
|
if (isset($this->busystatus) && $this->busystatus == 0xFFFFFFFF) { |
|
372
|
|
|
SLog::Write(LOGLEVEL_INFO, "SyncAppointment->Check(): rewriting busystatus -1 (0xFFFFFFFF) to fbBusy (2)."); |
|
373
|
|
|
$this->busystatus = fbBusy; |
|
|
|
|
|
|
374
|
|
|
} |
|
375
|
|
|
|
|
376
|
|
|
return true; |
|
377
|
|
|
} |
|
378
|
|
|
} |
|
379
|
|
|
|
|
380
|
|
|
class SyncAppointmentResponse extends SyncAppointment { |
|
381
|
|
|
use ResponseTrait; |
|
382
|
|
|
} |
|
383
|
|
|
|