d3902933b9f35ab7e9d6855401cec904b444c0d3
[tine20] / tests / tine20 / ActiveSync / Controller / CalendarTests.php
1 <?php
2 /**
3  * Tine 2.0 - http://www.tine20.org
4  * 
5  * @package     ActiveSync
6  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
7  * @copyright   Copyright (c) 2010-2014 Metaways Infosystems GmbH (http://www.metaways.de)
8  * @author      Cornelius Weiss <c.weiss@metaways.de>
9  */
10
11 /**
12  * Test class for Calendar_Controller_Event
13  * 
14  * @package     ActiveSync
15  */
16 class ActiveSync_Controller_CalendarTests extends ActiveSync_Controller_ControllerTest
17 {
18     /**
19      * name of the application
20      * 
21      * @var string
22      */
23     protected $_applicationName = 'Calendar';
24     
25     protected $_controllerName = 'ActiveSync_Controller_Calendar';
26     
27     protected $_class = Syncroton_Data_Factory::CLASS_CALENDAR;
28     
29     protected $_prefs;
30     
31     protected $_defaultCalendar;
32     
33     protected $_defaultHasChanged = false;
34     
35     /**
36      * @var array test objects
37      */
38     protected $objects = array();
39     
40     protected $_testXMLInput = '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
41     <Sync xmlns="uri:AirSync" xmlns:Calendar="uri:Calendar">
42         <Collections>
43             <Collection>
44                 <Class>Calendar</Class>
45                 <SyncKey>9</SyncKey>
46                 <CollectionId>41</CollectionId>
47                 <DeletesAsMoves/>
48                 <GetChanges/>
49                 <WindowSize>50</WindowSize>
50                 <Options><FilterType>5</FilterType></Options>
51                 <Commands>
52                     <Change>
53                         <ServerId>6de7cb687964dc6eea109cd81750177979362217</ServerId>
54                         <ApplicationData>
55                             <Calendar:Timezone>xP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAFAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAIAAAAAAAAAxP///w==</Calendar:Timezone>
56                             <Calendar:AllDayEvent>0</Calendar:AllDayEvent>
57                             <Calendar:BusyStatus>2</Calendar:BusyStatus>
58                             <Calendar:DtStamp>20121125T150537Z</Calendar:DtStamp>
59                             <Calendar:EndTime>20121123T160000Z</Calendar:EndTime>
60                             <Calendar:Sensitivity>0</Calendar:Sensitivity>
61                             <Calendar:Subject>Repeat</Calendar:Subject>
62                             <Calendar:StartTime>20121123T130000Z</Calendar:StartTime>
63                             <Calendar:UID>6de7cb687964dc6eea109cd81750177979362217</Calendar:UID>
64                             <Calendar:MeetingStatus>1</Calendar:MeetingStatus>
65                             <Calendar:Attendees>
66                                 <Calendar:Attendee>
67                                     <Calendar:Name>Lars Kneschke</Calendar:Name>
68                                     <Calendar:Email>lars@kneschke.de</Calendar:Email>
69                                 </Calendar:Attendee>
70                             </Calendar:Attendees>
71                             <Calendar:Recurrence>
72                                 <Calendar:Type>0</Calendar:Type><Calendar:Interval>1</Calendar:Interval><Calendar:Until>20121128T225959Z</Calendar:Until>
73                             </Calendar:Recurrence>
74                             <Calendar:Exceptions>
75                                 <Calendar:Exception>
76                                     <Calendar:Deleted>0</Calendar:Deleted><Calendar:ExceptionStartTime>20121125T130000Z</Calendar:ExceptionStartTime><Calendar:StartTime>20121125T140000Z</Calendar:StartTime><Calendar:EndTime>20121125T170000Z</Calendar:EndTime><Calendar:Subject>Repeat mal anders</Calendar:Subject><Calendar:BusyStatus>2</Calendar:BusyStatus><Calendar:AllDayEvent>0</Calendar:AllDayEvent>
77                                 </Calendar:Exception>
78                                 <Calendar:Exception>
79                                     <Calendar:Deleted>1</Calendar:Deleted><Calendar:ExceptionStartTime>20121124T130000Z</Calendar:ExceptionStartTime></Calendar:Exception>
80                                 </Calendar:Exceptions>
81                             <Calendar:Reminder>15</Calendar:Reminder>
82                             <Body xmlns="uri:AirSyncBase"><Type>1</Type><Data>Hello</Data></Body>
83                         </ApplicationData>
84                     </Change>
85                 </Commands>
86             </Collection>
87         </Collections>
88     </Sync>';
89     
90     protected $_testXMLInput_palmPreV12 = '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
91     <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase" xmlns:Calendar="uri:Calendar">
92         <Collections>
93             <Collection>
94                 <Class>Calendar</Class>
95                 <SyncKey>345</SyncKey>
96                 <CollectionId>calendar-root</CollectionId>
97                 <DeletesAsMoves/>
98                 <GetChanges/>
99                 <WindowSize>50</WindowSize>
100                 <Options>
101                     <FilterType>4</FilterType><AirSyncBase:BodyPreference><AirSyncBase:Type>1</AirSyncBase:Type></AirSyncBase:BodyPreference>
102                 </Options>
103                 <Commands>
104                     <Change>
105                         <ServerId>3452c1dd3f21d1c12589e517f0c6a928137113a4</ServerId>
106                         <ApplicationData>
107                             <AirSyncBase:Body>
108                                 <AirSyncBase:Type>1</AirSyncBase:Type>
109                                 <AirSyncBase:Data>test beschreibung zeile 1&#13;
110 Zeile 2&#13;
111 Zeile 3</AirSyncBase:Data>
112                             </AirSyncBase:Body>
113                             <Calendar:Timezone>xP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAFAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAIAAAAAAAAAxP///w==</Calendar:Timezone>
114                             <Calendar:UID>3452c1dd3f21d1c12589e517f0c6a928137113a4</Calendar:UID>
115                             <Calendar:DtStamp>20101104T070652Z</Calendar:DtStamp>
116                             <Calendar:Subject>GM straussberg2</Calendar:Subject>
117                             <Calendar:MeetingStatus>1</Calendar:MeetingStatus>
118                             <Calendar:OrganizerName>Nadine Blau</Calendar:OrganizerName>
119                             <Calendar:OrganizerEmail>meine@mail.com</Calendar:OrganizerEmail>
120                             <Calendar:Attendees><Calendar:Attendee><Calendar:Name>Nadine Blau</Calendar:Name><Calendar:Email>meine@mail.com</Calendar:Email></Calendar:Attendee></Calendar:Attendees>
121                             <Calendar:BusyStatus>0</Calendar:BusyStatus>
122                             <Calendar:AllDayEvent>1</Calendar:AllDayEvent>
123                             <Calendar:StartTime>20101103T230000Z</Calendar:StartTime>
124                             <Calendar:EndTime>20101106T230000Z</Calendar:EndTime>
125                             <Calendar:Sensitivity>0</Calendar:Sensitivity>
126                         </ApplicationData>
127                     </Change>
128                 </Commands>
129             </Collection>
130         </Collections>
131     </Sync>';
132     
133     protected $_testXMLInput_DailyRepeatingEvent = '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
134     <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase" xmlns:Calendar="uri:Calendar">
135         <Collections>
136             <Collection>
137                 <Class>Calendar</Class>
138                 <SyncKey>8</SyncKey>
139                 <CollectionId>calendar-root</CollectionId>
140                 <DeletesAsMoves/><GetChanges/>
141                 <WindowSize>100</WindowSize>
142                 <Options><FilterType>4</FilterType><AirSyncBase:BodyPreference><AirSyncBase:Type>1</AirSyncBase:Type><AirSyncBase:TruncationSize>5120</AirSyncBase:TruncationSize></AirSyncBase:BodyPreference><AirSyncBase:BodyPreference><AirSyncBase:Type>3</AirSyncBase:Type><AirSyncBase:TruncationSize>5120</AirSyncBase:TruncationSize><AirSyncBase:AllOrNone>1</AirSyncBase:AllOrNone></AirSyncBase:BodyPreference><Conflict>1</Conflict></Options>
143                 <Commands>
144                     <Add>
145                         <ClientId>1073741902</ClientId>
146                         <ApplicationData><Calendar:Timezone>xP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAFAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAIAAAAAAAAAxP///w==</Calendar:Timezone>
147                             <Calendar:AllDayEvent>0</Calendar:AllDayEvent>
148                             <Calendar:BusyStatus>2</Calendar:BusyStatus>
149                             <Calendar:DtStamp>20101224T082738Z</Calendar:DtStamp>
150                             <Calendar:EndTime>20101220T100000Z</Calendar:EndTime>
151                             <Calendar:MeetingStatus>0</Calendar:MeetingStatus>
152                             <Calendar:Reminder>15</Calendar:Reminder>
153                             <Calendar:Sensitivity>0</Calendar:Sensitivity>
154                             <Calendar:Subject>Tdfffdd</Calendar:Subject>
155                             <Calendar:StartTime>20101220T090000Z</Calendar:StartTime>
156                             <Calendar:UID>040000008200E00074C5B7101A82E00800000000DCA959CF1C69F280D15448CF43450B301000000019B5FB15984956377D4EBEFE125A8EF6</Calendar:UID>
157                             <Calendar:Recurrence>
158                                 <Calendar:Until>20101222T230000Z</Calendar:Until>
159                                 <Calendar:Interval>1</Calendar:Interval>
160                                 <Calendar:Type>0</Calendar:Type>
161                             </Calendar:Recurrence>
162                         </ApplicationData>
163                     </Add>
164                 </Commands>
165             </Collection>
166         </Collections>
167     </Sync>';
168
169     protected $_testXMLInput_SamsungGalaxyStatus = '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
170     <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase" xmlns:Calendar="uri:Calendar">
171         <Collections>
172             <Collection>
173                 <Class>Calendar</Class>
174                 <SyncKey>20</SyncKey>
175                 <CollectionId>calendar-root</CollectionId>
176                 <DeletesAsMoves/>
177                 <GetChanges/>
178                 <WindowSize>5</WindowSize>
179                 <Options><FilterType>4</FilterType><BodyPreference xmlns="uri:AirSyncBase"><Type>1</Type><TruncationSize>400000</TruncationSize></BodyPreference></Options>
180                 <Commands>
181                     <Change>
182                         <ServerId>b2b0593bc21c89e5d07edb7b5caa56ff7e243e92</ServerId>
183                         <ApplicationData>
184                             <Timezone xmlns="uri:Calendar">xP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAEAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAIAAAAAAAAAxP///w==</Timezone>
185                             <AllDayEvent xmlns="uri:Calendar">0</AllDayEvent>
186                             <StartTime xmlns="uri:Calendar">20120229T120000Z</StartTime>
187                             <EndTime xmlns="uri:Calendar">20120229T140000Z</EndTime>
188                             <DtStamp xmlns="uri:Calendar">20120228T093153Z</DtStamp>
189                             <Subject xmlns="uri:Calendar">testtermin</Subject>
190                             <Sensitivity xmlns="uri:Calendar">0</Sensitivity>
191                             <OrganizerEmail xmlns="uri:Calendar">user2@example.com</OrganizerEmail>
192                             <Reminder xmlns="uri:Calendar">30</Reminder>
193                             <UID xmlns="uri:Calendar">2b4a047d3f71b4d47d2d3907f4a27d70f650cd30</UID>
194                             <Attendees xmlns="uri:Calendar">
195                                 <Attendee><Name>user1@example.com</Name><Email>user1@example.com</Email><AttendeeType>1</AttendeeType></Attendee>
196                                 <Attendee><Name>user2@example.com</Name><Email>user2@example.com</Email><AttendeeType>1</AttendeeType></Attendee>
197                                 <Attendee><Name>user3@example.com</Name><Email>user3@example.com</Email><AttendeeType>1</AttendeeType></Attendee>
198                             </Attendees>
199                             <BusyStatus xmlns="uri:Calendar">0</BusyStatus>
200                             <MeetingStatus xmlns="uri:Calendar">3</MeetingStatus>
201                         </ApplicationData>
202                     </Change>
203                 </Commands>
204             </Collection>
205         </Collections>
206     </Sync>';
207     
208     protected $_testXMLInputOutlook13 = '<?xml version="1.0" encoding="utf-8"?>
209     <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
210     <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase" xmlns:Calendar="uri:Calendar">
211       <Collections>
212         <Collection>
213           <SyncKey>5</SyncKey>
214           <CollectionId>calendar-root</CollectionId>
215           <DeletesAsMoves>0</DeletesAsMoves>
216           <GetChanges>0</GetChanges>
217           <WindowSize>512</WindowSize>
218           <Options>
219             <FilterType>0</FilterType>
220             <BodyPreference xmlns="uri:AirSyncBase">
221               <Type>2</Type>
222               <AllOrNone>1</AllOrNone>
223             </BodyPreference>
224           </Options>
225           <Commands>
226             <Change>
227               <ServerId>d8a9cecb073736aa78c95a249f383123cc03365b</ServerId>
228               <ApplicationData>
229                 <Timezone xmlns="uri:Calendar">xP///1cALgAgAEUAdQByAG8AcABlACAAUwB0AGEAbgBkAGEAcgBkACAAVABpAG0AZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAFAAMAAAAAAAAAAAAAAFcALgAgAEUAdQByAG8AcABlACAARABhAHkAbABpAGcAaAB0ACAAVABpAG0AZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAIAAAAAAAAAxP///w==</Timezone>
230                 <DtStamp xmlns="uri:Calendar">20120914T125503Z</DtStamp>
231                 <StartTime xmlns="uri:Calendar">20120914T140000Z</StartTime>
232                 <Subject xmlns="uri:Calendar">TEST9</Subject>
233                 <UID xmlns="uri:Calendar">fd4b68a5871aa0082284e9bc5664a35da207b75b</UID>
234                 <OrganizerName xmlns="uri:Calendar">unittest@tine20.org</OrganizerName>
235                 <OrganizerEmail xmlns="uri:Calendar">unittest@tine20.org</OrganizerEmail>
236                 <Attendees xmlns="uri:Calendar">
237                   <Attendee>
238                     <Email>unittest@tine20.org</Email>
239                     <Name>Admin Account, Tine 2.0</Name>
240                     <AttendeeStatus>3</AttendeeStatus>
241                     <AttendeeType>1</AttendeeType>
242                   </Attendee>
243                   <Attendee>
244                     <Email>pwulf@tine20.org</Email>
245                     <Name>Wulf, Paul</Name>
246                     <AttendeeStatus>0</AttendeeStatus>
247                     <AttendeeType>1</AttendeeType>
248                   </Attendee>
249                 </Attendees>
250                 <EndTime xmlns="uri:Calendar">20120914T143000Z</EndTime>
251                 <Recurrence xmlns="uri:Calendar">
252                   <Type>0</Type>
253                   <Interval>1</Interval>
254                   <Occurrences>3</Occurrences>
255                 </Recurrence>
256                 <Exceptions xmlns="uri:Calendar">
257                   <Exception>
258                     <ExceptionStartTime>20120916T140000Z</ExceptionStartTime>
259                     <StartTime>20120916T140000Z</StartTime>
260                     <DtStamp>20120914T125503Z</DtStamp>
261                     <EndTime>20120916T143000Z</EndTime>
262                     <Sensitivity>0</Sensitivity>
263                     <BusyStatus>2</BusyStatus>
264                     <Attendees>
265                       <Attendee>
266                         <Email>unittest@tine20.org</Email>
267                         <Name>Admin Account, Tine 2.0</Name>
268                         <AttendeeStatus>0</AttendeeStatus>
269                         <AttendeeType>1</AttendeeType>
270                       </Attendee>
271                       <Attendee>
272                         <Email>unittest@tine20.org</Email>
273                         <Name>Admin Account, Tine 2.0</Name>
274                         <AttendeeStatus>3</AttendeeStatus>
275                         <AttendeeType>1</AttendeeType>
276                       </Attendee>
277                       <Attendee>
278                         <Email>pwulf@tine20.org</Email>
279                         <Name>Wulf, Paul</Name>
280                         <AttendeeStatus>4</AttendeeStatus>
281                         <AttendeeType>1</AttendeeType>
282                       </Attendee>
283                     </Attendees>
284                   </Exception>
285                 </Exceptions>
286                 <Sensitivity xmlns="uri:Calendar">0</Sensitivity>
287                 <AllDayEvent xmlns="uri:Calendar">0</AllDayEvent>
288                 <MeetingStatus xmlns="uri:Calendar">1</MeetingStatus>
289                 <ResponseRequested xmlns="uri:Calendar">1</ResponseRequested>
290               </ApplicationData>
291             </Change>
292           </Commands>
293         </Collection>
294       </Collections>
295     </Sync>';
296     
297     protected $_testXMLMeetingResponse = '<?xml version="1.0" encoding="utf-8"?>
298     <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
299     <MeetingResponse xmlns="uri:MeetingResponse" xmlns:Search="uri:Search">
300         <Request>
301             <UserResponse>2</UserResponse>
302             <CollectionId>17</CollectionId>
303             <RequestId>f0c79775b6b44be446f91187e24566aa1c5d06ab</RequestId>
304             <InstanceId>20121125T130000Z</InstanceId>
305         </Request>
306     </MeetingResponse>';
307
308     protected $_testConcurrentUpdate = '<?xml version="1.0" encoding="utf-8"?>
309     <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
310     <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase" xmlns:Calendar="uri:Calendar">
311       <Collections>
312         <Collection>
313           <SyncKey>26</SyncKey>
314           <CollectionId>calendar-root</CollectionId>
315           <GetChanges/>
316           <WindowSize>25</WindowSize>
317           <Options>
318             <FilterType>4</FilterType>
319             <BodyPreference xmlns="uri:AirSyncBase">
320               <Type>1</Type>
321               <TruncationSize>32768</TruncationSize>
322             </BodyPreference>
323           </Options>
324           <Commands>
325             <Change>
326               <ServerId>b2cb312482f641185b205591f40f78a47ea9f667</ServerId>
327               <ApplicationData>
328                 <Timezone xmlns="uri:Calendar">xP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAFAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAMAAAAAAAAAxP///w==</Timezone>
329                 <AllDayEvent xmlns="uri:Calendar">0</AllDayEvent>
330                 <BusyStatus xmlns="uri:Calendar">2</BusyStatus>
331                 <DtStamp xmlns="uri:Calendar">20131106T072501Z</DtStamp>
332                 <EndTime xmlns="uri:Calendar">20140113T140000Z</EndTime>
333                 <Sensitivity xmlns="uri:Calendar">0</Sensitivity>
334                 <Subject xmlns="uri:Calendar">Mittagspause</Subject>
335                 <StartTime xmlns="uri:Calendar">20140113T120000Z</StartTime>
336                 <UID xmlns="uri:Calendar">1a5010a3d46316a2709cda40683911f95a856665</UID>
337                 <MeetingStatus xmlns="uri:Calendar">1</MeetingStatus>
338                 <Attendees xmlns="uri:Calendar">
339                   <Attendee>
340                     <Name>Unittest, Tine</Name>
341                     <Email>unittest@tine20.org</Email>
342                     <AttendeeType>1</AttendeeType>
343                   </Attendee>
344                 </Attendees>
345                 <Recurrence xmlns="uri:Calendar">
346                   <Type>1</Type>
347                   <Interval>1</Interval>
348                   <DayOfWeek>62</DayOfWeek>
349                   <FirstDayOfWeek>1</FirstDayOfWeek>
350                 </Recurrence>
351                 <Exceptions xmlns="uri:Calendar">
352                   <Exception>
353                     <Body xmlns="uri:AirSyncBase">
354                       <Type>1</Type>
355                       <Data/>
356                     </Body>
357                     <Deleted>0</Deleted>
358                     <ExceptionStartTime>20141203T120000Z</ExceptionStartTime>
359                     <StartTime>20141203T140000Z</StartTime>
360                     <EndTime>20141203T153000Z</EndTime>
361                     <Subject>Mittagspause</Subject>
362                     <BusyStatus>2</BusyStatus>
363                     <AllDayEvent>0</AllDayEvent>
364                     <Attendees>
365                       <Attendee>
366                         <Name>Unittest, Tine</Name>
367                         <Email>unittest@tine20.org</Email>
368                         <AttendeeType>1</AttendeeType>
369                       </Attendee>
370                     </Attendees>
371                   </Exception>
372                   <Exception>
373                     <Body xmlns="uri:AirSyncBase">
374                       <Type>1</Type>
375                       <Data/>
376                     </Body>
377                     <Deleted>0</Deleted>
378                     <ExceptionStartTime>20140625T110000Z</ExceptionStartTime>
379                     <StartTime>20140625T130000Z</StartTime>
380                     <EndTime>20140625T150000Z</EndTime>
381                     <Subject>Mittagspause</Subject>
382                     <BusyStatus>2</BusyStatus>
383                     <AllDayEvent>0</AllDayEvent>
384                     <Attendees>
385                       <Attendee>
386                         <Name>Unittest, Tine</Name>
387                         <Email>unittest@tine20.org</Email>
388                         <AttendeeType>1</AttendeeType>
389                       </Attendee>
390                     </Attendees>
391                   </Exception>
392                 </Exceptions>
393               </ApplicationData>
394             </Change>
395           </Commands>
396         </Collection>
397       </Collections>
398     </Sync>';
399
400     protected $_testConcurrentUpdateUpdate = '<?xml version="1.0" encoding="utf-8"?>
401     <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
402     <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase" xmlns:Calendar="uri:Calendar">
403       <Collections>
404         <Collection>
405           <SyncKey>26</SyncKey>
406           <CollectionId>calendar-root</CollectionId>
407           <GetChanges/>
408           <WindowSize>25</WindowSize>
409           <Options>
410             <FilterType>4</FilterType>
411             <BodyPreference xmlns="uri:AirSyncBase">
412               <Type>1</Type>
413               <TruncationSize>32768</TruncationSize>
414             </BodyPreference>
415           </Options>
416           <Commands>
417             <Change>
418               <ServerId>b2cb312482f641185b205591f40f78a47ea9f667</ServerId>
419               <ApplicationData>
420                 <Timezone xmlns="uri:Calendar">xP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAFAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAMAAAAAAAAAxP///w==</Timezone>
421                 <AllDayEvent xmlns="uri:Calendar">0</AllDayEvent>
422                 <BusyStatus xmlns="uri:Calendar">2</BusyStatus>
423                 <DtStamp xmlns="uri:Calendar">20131106T072501Z</DtStamp>
424                 <EndTime xmlns="uri:Calendar">20140113T140000Z</EndTime>
425                 <Sensitivity xmlns="uri:Calendar">0</Sensitivity>
426                 <Subject xmlns="uri:Calendar">Mittagspause</Subject>
427                 <StartTime xmlns="uri:Calendar">20140113T120000Z</StartTime>
428                 <UID xmlns="uri:Calendar">1a5010a3d46316a2709cda40683911f95a856665</UID>
429                 <MeetingStatus xmlns="uri:Calendar">1</MeetingStatus>
430                 <Attendees xmlns="uri:Calendar">
431                   <Attendee>
432                     <Name>Unittest, Tine</Name>
433                     <Email>unittest@tine20.org</Email>
434                     <AttendeeType>1</AttendeeType>
435                   </Attendee>
436                 </Attendees>
437                 <Recurrence xmlns="uri:Calendar">
438                   <Type>1</Type>
439                   <Interval>1</Interval>
440                   <DayOfWeek>62</DayOfWeek>
441                   <FirstDayOfWeek>1</FirstDayOfWeek>
442                 </Recurrence>
443                 <Exceptions xmlns="uri:Calendar">
444                   <Exception>
445                     <Body xmlns="uri:AirSyncBase">
446                       <Type>1</Type>
447                       <Data/>
448                     </Body>
449                     <Deleted>0</Deleted>
450                     <ExceptionStartTime>20141203T120000Z</ExceptionStartTime>
451                     <StartTime>20141203T140000Z</StartTime>
452                     <EndTime>20141203T153000Z</EndTime>
453                     <Subject>Mittagspause</Subject>
454                     <BusyStatus>2</BusyStatus>
455                     <AllDayEvent>0</AllDayEvent>
456                     <Attendees>
457                       <Attendee>
458                         <Name>Unittest, Tine</Name>
459                         <Email>unittest@tine20.org</Email>
460                         <AttendeeType>1</AttendeeType>
461                       </Attendee>
462                     </Attendees>
463                   </Exception>
464                   <Exception>
465                     <Body xmlns="uri:AirSyncBase">
466                       <Type>1</Type>
467                       <Data/>
468                     </Body>
469                     <Deleted>0</Deleted>
470                     <ExceptionStartTime>20140625T110000Z</ExceptionStartTime>
471                     <StartTime>20140625T130000Z</StartTime>
472                     <EndTime>20140625T150000Z</EndTime>
473                     <Subject>Abendessen</Subject>
474                     <BusyStatus>2</BusyStatus>
475                     <AllDayEvent>0</AllDayEvent>
476                     <Attendees>
477                       <Attendee>
478                         <Name>Unittest, Tine</Name>
479                         <Email>unittest@tine20.org</Email>
480                         <AttendeeType>1</AttendeeType>
481                       </Attendee>
482                     </Attendees>
483                   </Exception>
484                 </Exceptions>
485               </ApplicationData>
486             </Change>
487           </Commands>
488         </Collection>
489       </Collections>
490     </Sync>';
491
492     /**
493      * Runs the test methods of this class.
494      *
495      * @access public
496      * @static
497      */
498     public static function main()
499     {
500         $suite  = new PHPUnit_Framework_TestSuite('Tine 2.0 ActiveSync Controller Calendar Tests');
501         PHPUnit_TextUI_TestRunner::run($suite);
502     }
503     
504     /**
505      * (non-PHPdoc)
506      * @see ActiveSync/ActiveSync_TestCase::setUp()
507      */
508     protected function setUp()
509     {
510         parent::setUp();
511         
512         // replace email to make current user organizer and attendee
513         $testConfig = Zend_Registry::get('testConfig');
514         $email = ($testConfig->email) ? $testConfig->email : Tinebase_Core::getUser()->accountEmailAddress;
515         
516         $this->_testXMLInput = str_replace(array(
517             'lars@kneschke.de',
518             'unittest@tine20.org',
519         ), $email, $this->_testXMLInput);
520         
521         $this->_prefs = $prefs = new Calendar_Preference();
522         $this->_defaultCalendar = Tinebase_Core::getPreference('Calendar')->{Calendar_Preference::DEFAULTCALENDAR};
523     }
524     
525     protected  function tearDown()
526     {
527         parent::tearDown();
528         
529         if ($this->_defaultHasChanged) {
530             $this->_prefs->setValue(Calendar_Preference::DEFAULTCALENDAR, $this->_defaultCalendar);
531         }
532     }
533     
534     /**
535      * (non-PHPdoc)
536      * @see ActiveSync_TestCase::testCreateEntry()
537      */
538     public function testCreateEntry($syncrotonFolder = null)
539     {
540         if ($syncrotonFolder === null) {
541             $syncrotonFolder = $this->testCreateFolder();
542         }
543         
544         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_WEBOS), Tinebase_DateTime::now());
545         
546         $now = Tinebase_DateTime::now();
547         $thisYear = $now->format('Y');
548         $xmlString = preg_replace('/2012/', $thisYear, $this->_testXMLInput);
549         $xml = new SimpleXMLElement($xmlString);
550         $syncrotonEvent = new Syncroton_Model_Event($xml->Collections->Collection->Commands->Change[0]->ApplicationData);
551         
552         $serverId = $controller->createEntry($syncrotonFolder->serverId, $syncrotonEvent);
553         
554         $syncrotonEvent = $controller->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $syncrotonFolder->serverId)), $serverId);
555         
556         $this->assertEquals(0,         $syncrotonEvent->allDayEvent, 'alldayEvent');
557         $this->assertEquals(2,         $syncrotonEvent->busyStatus);
558         $this->assertEquals('Repeat',  $syncrotonEvent->subject);
559         $this->assertEquals(15,        $syncrotonEvent->reminder);
560         $this->assertTrue($syncrotonEvent->endTime instanceof DateTime);
561         $this->assertTrue($syncrotonEvent->startTime instanceof DateTime);
562         $this->assertEquals($thisYear . '1123T160000Z', $syncrotonEvent->endTime->format('Ymd\THis\Z'));
563         $this->assertEquals($thisYear . '1123T130000Z', $syncrotonEvent->startTime->format('Ymd\THis\Z'));
564         $this->assertEquals(1, count($syncrotonEvent->attendees), 'event: ' . var_export($syncrotonEvent->attendees, TRUE));
565         $this->assertEquals(Tinebase_Core::getUser()->accountEmailAddress, $syncrotonEvent->attendees[0]->email, 'event: ' . var_export($syncrotonEvent, TRUE));
566         
567         //Body
568         $this->assertTrue($syncrotonEvent->body instanceof Syncroton_Model_EmailBody);
569         $this->assertEquals('Hello', $syncrotonEvent->body->data);
570         
571         // Recurrence
572         $this->assertTrue($syncrotonEvent->recurrence instanceof Syncroton_Model_EventRecurrence);
573         $this->assertEquals(Syncroton_Model_EventRecurrence::TYPE_DAILY, $syncrotonEvent->recurrence->type);
574         $this->assertEquals(1, $syncrotonEvent->recurrence->interval);
575         $this->assertTrue($syncrotonEvent->recurrence->until instanceof DateTime);
576         $this->assertEquals($thisYear . '1128T225959Z', $syncrotonEvent->recurrence->until->format('Ymd\THis\Z'));
577         
578         // Exceptions
579         $this->assertEquals(2, count($syncrotonEvent->exceptions));
580         $this->assertTrue($syncrotonEvent->exceptions[0] instanceof Syncroton_Model_EventException);
581         $this->assertFalse(isset($syncrotonEvent->exceptions[0]->deleted), 'exception deleted with value of 0 should not be set');
582         $this->assertEquals('Repeat mal anders', $syncrotonEvent->exceptions[0]->subject);
583         $this->assertEquals($thisYear . '1125T130000Z', $syncrotonEvent->exceptions[0]->exceptionStartTime->format('Ymd\THis\Z'));
584         $this->assertEquals($thisYear . '1125T170000Z', $syncrotonEvent->exceptions[0]->endTime->format('Ymd\THis\Z'));
585         $this->assertEquals($thisYear . '1125T140000Z', $syncrotonEvent->exceptions[0]->startTime->format('Ymd\THis\Z'));
586         
587         $this->assertEquals(1, $syncrotonEvent->exceptions[1]->deleted);
588         $this->assertEquals($thisYear . '1124T130000Z', $syncrotonEvent->exceptions[1]->exceptionStartTime->format('Ymd\THis\Z'));
589         
590         return array($serverId, $syncrotonEvent);
591     }
592     
593     public function testCreateEntryPalmPreV12($syncrotonFolder = null)
594     {
595         if ($syncrotonFolder === null) {
596             $syncrotonFolder = $this->testCreateFolder();
597         }
598     
599         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), Tinebase_DateTime::now());
600     
601         $xml = new SimpleXMLElement($this->_testXMLInput_palmPreV12);
602         $syncrotonEvent = new Syncroton_Model_Event($xml->Collections->Collection->Commands->Change[0]->ApplicationData);
603     
604         $serverId = $controller->createEntry($syncrotonFolder->serverId, $syncrotonEvent);
605     
606         $syncrotonEvent = $controller->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $syncrotonFolder->serverId)), $serverId);
607     
608         #echo '----------------' . PHP_EOL; foreach ($syncrotonEvent as $key => $value) {echo "$key => "; var_dump($value);}
609         
610         $this->assertEquals(1,         $syncrotonEvent->allDayEvent);
611         $this->assertTrue($syncrotonEvent->endTime instanceof DateTime);
612         $this->assertTrue($syncrotonEvent->startTime instanceof DateTime);
613         $this->assertEquals('20101106T230000Z', $syncrotonEvent->endTime->format('Ymd\THis\Z'));
614         $this->assertEquals('20101103T230000Z', $syncrotonEvent->startTime->format('Ymd\THis\Z'));
615     
616         return array($serverId, $syncrotonEvent);
617     }
618     
619     public function testCreateEntryDailyRepeating($syncrotonFolder = null)
620     {
621         if ($syncrotonFolder === null) {
622             $syncrotonFolder = $this->testCreateFolder();
623         }
624     
625         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), Tinebase_DateTime::now());
626     
627         $xml = new SimpleXMLElement($this->_testXMLInput_DailyRepeatingEvent);
628         $syncrotonEvent = new Syncroton_Model_Event($xml->Collections->Collection->Commands->Add[0]->ApplicationData);
629     
630         $serverId = $controller->createEntry($syncrotonFolder->serverId, $syncrotonEvent);
631     
632         $syncrotonEvent = $controller->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $syncrotonFolder->serverId)), $serverId);
633     
634         #echo '----------------' . PHP_EOL; foreach ($syncrotonEvent as $key => $value) {echo "$key => "; var_dump($value);}
635         
636         $this->assertEquals(0, $syncrotonEvent->allDayEvent);
637         $this->assertTrue($syncrotonEvent->endTime instanceof DateTime);
638         $this->assertTrue($syncrotonEvent->startTime instanceof DateTime);
639         $this->assertEquals('20101220T100000Z', $syncrotonEvent->endTime->format('Ymd\THis\Z'));
640         $this->assertEquals('20101220T090000Z', $syncrotonEvent->startTime->format('Ymd\THis\Z'));
641     
642         // Recurrence
643         $this->assertTrue($syncrotonEvent->recurrence instanceof Syncroton_Model_EventRecurrence);
644         $this->assertEquals(Syncroton_Model_EventRecurrence::TYPE_DAILY, $syncrotonEvent->recurrence->type);
645         $this->assertEquals(1, $syncrotonEvent->recurrence->interval);
646         $this->assertTrue($syncrotonEvent->recurrence->until instanceof DateTime);
647         $this->assertEquals('20101222T225959Z', $syncrotonEvent->recurrence->until->format('Ymd\THis\Z'));
648         
649         return array($serverId, $syncrotonEvent);
650     }
651     
652     public function testCreateEntrySamsungGalaxyStatus($syncrotonFolder = null)
653     {
654         if ($syncrotonFolder === null) {
655             $syncrotonFolder = $this->testCreateFolder();
656         }
657     
658         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_SMASUNGGALAXYS2), Tinebase_DateTime::now());
659     
660         $xml = new SimpleXMLElement($this->_testXMLInput_SamsungGalaxyStatus);
661         $syncrotonEvent = new Syncroton_Model_Event($xml->Collections->Collection->Commands->Change[0]->ApplicationData);
662     
663         $serverId = $controller->createEntry($syncrotonFolder->serverId, $syncrotonEvent);
664     
665         $syncrotonEvent = $controller->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $syncrotonFolder->serverId)), $serverId);
666     
667         #echo '----------------' . PHP_EOL; foreach ($syncrotonEvent as $key => $value) {echo "$key => "; var_dump($value);}
668         
669         $this->assertEquals(Syncroton_Model_Event::BUSY_STATUS_BUSY, $syncrotonEvent->busyStatus);
670         
671         return array($serverId, $syncrotonEvent);
672     }
673     
674     public function testConcurrentUpdate($syncrotonFolder = null)
675     {
676         if ($syncrotonFolder === null) {
677             $syncrotonFolder = $this->testCreateFolder();
678         }
679         
680         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), Tinebase_DateTime::now());
681         
682         $xml = new SimpleXMLElement($this->_testConcurrentUpdate);
683         $syncrotonEvent = new Syncroton_Model_Event($xml->Collections->Collection->Commands->Change[0]->ApplicationData);
684         
685         $serverId = $controller->createEntry($syncrotonFolder->serverId, $syncrotonEvent);
686         
687         $event = Calendar_Controller_Event::getInstance()->get($serverId);
688         
689         // change exception #2
690         $exceptions = Calendar_Controller_Event::getInstance()->getRecurExceptions($event);
691         foreach ($exceptions as $exception) {
692             if ($exception->dtstart->toString() == '2014-06-25 13:00:00') {
693                 $exception->summary = 'Fruehstueck';
694                 Calendar_Controller_Event::getInstance()->update($exceptions[1]);
695             }
696         }
697         
698         $xmlUpdate = new SimpleXMLElement($this->_testConcurrentUpdateUpdate);
699         $syncrotonEvent = new Syncroton_Model_Event($xmlUpdate->Collections->Collection->Commands->Change[0]->ApplicationData);
700
701         $syncTimestamp = Calendar_Controller_Event::getInstance()->get($serverId)->creation_time;
702         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), $syncTimestamp);
703         
704         // update exception - should not throw concurrency exception
705         $serverId = $controller->updateEntry($syncrotonFolder->serverId, $serverId, $syncrotonEvent);
706         
707         $event = Calendar_Controller_Event::getInstance()->get($serverId);
708         $exceptions = Calendar_Controller_Event::getInstance()->getRecurExceptions($event);
709         
710         foreach ($exceptions as $exception) {
711             if ($exception->dtstart->toString() == '2014-06-25 13:00:00') {
712                 $this->assertEquals('Abendessen', $exception->summary, print_r($exceptions[1]->toArray(), true));
713             } else {
714                 $this->assertEquals('Mittagspause', $exception->summary, print_r($exceptions[1]->toArray(), true));
715             }
716         }
717     }
718
719     public function testCreateEntryOutlook13($syncrotonFolder = null)
720     {
721         if ($syncrotonFolder === null) {
722             $syncrotonFolder = $this->testCreateFolder();
723         }
724     
725         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_WEBOS), Tinebase_DateTime::now());
726     
727         $xml = new SimpleXMLElement($this->_testXMLInputOutlook13);
728         $syncrotonEvent = new Syncroton_Model_Event($xml->Collections->Collection->Commands->Change[0]->ApplicationData);
729     
730         $tine20Event = $controller->toTineModel($syncrotonEvent);
731 //         $serverId = $controller->createEntry($syncrotonFolder->serverId, $syncrotonEvent);
732     
733         $this->assertFalse(!!$tine20Event->exdate[0]->is_deleted);
734     }
735     
736     public function testUpdateEntry($syncrotonFolder = null)
737     {
738         if ($syncrotonFolder === null) {
739             $syncrotonFolder = $this->testCreateFolder();
740         }
741     
742         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), Tinebase_DateTime::now());
743     
744         list($serverId, $syncrotonEvent) = $this->testCreateEntry($syncrotonFolder);
745     
746         unset($syncrotonEvent->recurrence);
747         unset($syncrotonEvent->exceptions);
748         
749         // need to create new controller to set new sync timestamp for concurrency handling
750         $syncTimestamp = Calendar_Controller_Event::getInstance()->get($serverId)->last_modified_time;
751         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), $syncTimestamp);
752         $serverId = $controller->updateEntry($syncrotonFolder->serverId, $serverId, $syncrotonEvent);
753         
754         $syncrotonEvent = $controller->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $syncrotonFolder->serverId)), $serverId);
755     
756         $this->assertFalse($syncrotonEvent->recurrence instanceof Syncroton_Model_EventRecurrence);
757     
758         return array($serverId, $syncrotonEvent);
759     }
760
761     /**
762      * testUpdateExdate
763      * 
764      * @see 0010417: Exdate update does not update seq of base event
765      */
766     public function testUpdateExdate()
767     {
768         list($serverId, $syncrotonEvent) = $this->testCreateEntry();
769         
770         $event = Calendar_Controller_Event::getInstance()->get($serverId);
771         $container = Tinebase_Container::getInstance()->get($event->container_id);
772         
773         $exception = Calendar_Controller_Event::getInstance()->getRecurExceptions($event)->getFirstRecord();
774         $exception->dtstart->subHour(1);
775         Calendar_Controller_Event::getInstance()->update($exception);
776         
777         $baseEventAfterExdateUpdate = Calendar_Controller_Event::getInstance()->get($serverId);
778         $containerAfterExdateUpdate = Tinebase_Container::getInstance()->get($event->container_id);
779         $this->assertEquals($event->seq + 1, $baseEventAfterExdateUpdate->seq, 'base sequence should be updated');
780         $this->assertEquals($container->content_seq + 2, $containerAfterExdateUpdate->content_seq, 'container sequence should be updated');
781     }
782
783     /**
784      * testDeleteExdate
785      *
786      * @see : Exdate delete does not update seq of base event
787      */
788     public function testDeleteExdate()
789     {
790         list($serverId, $syncrotonEvent) = $this->testCreateEntry();
791
792         $event = Calendar_Controller_Event::getInstance()->get($serverId);
793         $container = Tinebase_Container::getInstance()->get($event->container_id);
794
795         $exception = Calendar_Controller_Event::getInstance()->getRecurExceptions($event)->getFirstRecord();
796         Calendar_Controller_Event::getInstance()->delete($exception);
797
798         $baseEventAfterExdateDelete = Calendar_Controller_Event::getInstance()->get($serverId);
799         $containerAfterExdateDelete = Tinebase_Container::getInstance()->get($event->container_id);
800         $this->assertGreaterThan($event->seq, $baseEventAfterExdateDelete->seq, 'base sequence should be updated');
801         $this->assertGreaterThan($container->content_seq, $containerAfterExdateDelete->content_seq, 'container sequence should be updated');
802     }
803
804     public function testRecurEventExceptionFilters($syncrotonFolder = null)
805     {
806         if ($syncrotonFolder === null) {
807             $syncrotonFolder = $this->testCreateFolder();
808         }
809         
810         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), Tinebase_DateTime::now());
811         
812         list($serverId, $syncrotonEvent) = $this->testCreateEntry($syncrotonFolder);
813         
814         // remove testuser as attendee
815         $eventBackend = new Calendar_Backend_Sql();
816         $exception = $eventBackend->getByProperty($syncrotonEvent->uID . '-' . $syncrotonEvent->exceptions[0]->exceptionStartTime->format(Tinebase_Record_Abstract::ISO8601LONG), 'recurid');
817         $ownAttendee = Calendar_Model_Attender::getOwnAttender($exception->attendee);
818         $eventBackend->deleteAttendee(array($ownAttendee->getId()));
819         
820         $syncrotonEvent = $controller->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $syncrotonFolder->serverId)), $serverId);
821         
822         // assert fallout by filter
823         $this->assertTrue((bool) $syncrotonEvent->exceptions[0]->deleted);
824         $this->assertTrue((bool) $syncrotonEvent->exceptions[1]->deleted);
825     }
826     
827     public function testStatusUpdate($syncrotonFolder = null)
828     {
829         if ($syncrotonFolder === null) {
830             $syncrotonFolder = $this->testCreateFolder();
831         }
832         
833         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), Tinebase_DateTime::now());
834         
835         list($serverId, $syncrotonEvent) = $this->testCreateEntry($syncrotonFolder);
836         
837         // transfer event to other user
838         $rwright = array_value('rwright', $this->_personas = Zend_Registry::get('personas'));
839         $eventBackend = new Calendar_Backend_Sql();
840         $eventBackend->updateMultiple($eventBackend->getMultipleByProperty($syncrotonEvent->uID, 'uid')->id, array(
841             'container_id'  => Tinebase_Core::getPreference('Calendar')->getValueForUser(Calendar_Preference::DEFAULTCALENDAR, $rwright->getId()),
842             'organizer'     => $rwright->contact_id
843         ));
844         
845         $syncrotonEvent->exceptions[0]->busyStatus = 1;
846         $syncrotonEvent->exceptions[0]->subject = 'do not update';
847         $serverId = $controller->updateEntry($syncrotonFolder->serverId, $serverId, $syncrotonEvent);
848         
849         $syncrotonEvent = $controller->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $syncrotonFolder->serverId)), $serverId);
850         
851         $event = Calendar_Controller_MSEventFacade::getInstance()->get($serverId);
852         $attendee = $event->exdate->getFirstRecord()->attendee->getFirstRecord();
853
854         $this->assertEquals(Calendar_Model_Attender::STATUS_TENTATIVE, $attendee->status);
855         $this->assertNotEquals('do not update', $event->summary);
856     }
857     
858     public function testMeetingResponse()
859     {
860         $syncrotonFolder = $this->testCreateFolder();
861         
862         list($serverId, $event) = $this->testCreateEntry($syncrotonFolder);
863         
864         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), Tinebase_DateTime::now());
865         
866         $XMLMeetingResponse = $this->_testXMLMeetingResponse;
867         
868         $XMLMeetingResponse = str_replace('<CollectionId>17</CollectionId>', '<CollectionId>' . $syncrotonFolder->serverId . '</CollectionId>', $XMLMeetingResponse);
869         $XMLMeetingResponse = str_replace('<RequestId>f0c79775b6b44be446f91187e24566aa1c5d06ab</RequestId>', '<RequestId>' . $serverId . '</RequestId>', $XMLMeetingResponse);
870         $XMLMeetingResponse = str_replace('<InstanceId>20121125T130000Z</InstanceId>', '', $XMLMeetingResponse);
871         
872         $xml = new SimpleXMLElement($XMLMeetingResponse);
873         
874         $meetingResponse = new Syncroton_Model_MeetingResponse($xml->Request[0]);
875         
876         $eventId = $controller->setAttendeeStatus($meetingResponse);
877         
878         $event = Calendar_Controller_Event::getInstance()->get($serverId);
879         $ownAttendee = Calendar_Model_Attender::getOwnAttender($event->attendee);
880         
881         $this->assertEquals(Calendar_Model_Attender::STATUS_TENTATIVE, $ownAttendee->status);
882     }
883     
884     public function testMeetingResponseWithExistingInstanceId()
885     {
886         $syncrotonFolder = $this->testCreateFolder();
887         
888         list($serverId, $event) = $this->testCreateEntry($syncrotonFolder);
889         
890         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), new Tinebase_DateTime(null, null, 'de_DE'));
891         
892         $XMLMeetingResponse = $this->_testXMLMeetingResponse;
893         
894         $thisYear = Tinebase_DateTime::now()->format('Y');
895         $XMLMeetingResponse = str_replace('2012', $thisYear, $XMLMeetingResponse);
896         $XMLMeetingResponse = str_replace('<CollectionId>17</CollectionId>', '<CollectionId>' . $syncrotonFolder->serverId . '</CollectionId>', $XMLMeetingResponse);
897         $XMLMeetingResponse = str_replace('<RequestId>f0c79775b6b44be446f91187e24566aa1c5d06ab</RequestId>', '<RequestId>' . $serverId . '</RequestId>', $XMLMeetingResponse);
898         
899         $xml = new SimpleXMLElement($XMLMeetingResponse);
900         
901         $meetingResponse = new Syncroton_Model_MeetingResponse($xml->Request[0]);
902         
903         $eventId = $controller->setAttendeeStatus($meetingResponse);
904         
905         $event = Calendar_Controller_MSEventFacade::getInstance()->get($serverId);
906         $instance = $event->exdate->filter('is_deleted', 0)->getFirstRecord();
907         $ownAttendee = Calendar_Model_Attender::getOwnAttender($instance->attendee);
908         
909         $this->assertEquals(Calendar_Model_Attender::STATUS_TENTATIVE, $ownAttendee->status);
910     }
911     
912     public function testMeetingResponseWithNewInstanceId()
913     {
914         $syncrotonFolder = $this->testCreateFolder();
915         
916         list($serverId, $event) = $this->testCreateEntry($syncrotonFolder);
917         
918         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), new Tinebase_DateTime(null, null, 'de_DE'));
919         
920         $XMLMeetingResponse = $this->_testXMLMeetingResponse;
921         
922         $thisYear = Tinebase_DateTime::now()->format('Y');
923         $XMLMeetingResponse = str_replace('2012', $thisYear, $XMLMeetingResponse);
924         $XMLMeetingResponse = str_replace('<CollectionId>17</CollectionId>', '<CollectionId>' . $syncrotonFolder->serverId . '</CollectionId>', $XMLMeetingResponse);
925         $XMLMeetingResponse = str_replace('<RequestId>f0c79775b6b44be446f91187e24566aa1c5d06ab</RequestId>', '<RequestId>' . $serverId . '</RequestId>', $XMLMeetingResponse);
926         $XMLMeetingResponse = str_replace('<InstanceId>20121125T130000Z</InstanceId>', '<InstanceId>20121126T130000Z</InstanceId>', $XMLMeetingResponse);
927         
928         $xml = new SimpleXMLElement($XMLMeetingResponse);
929         
930         $meetingResponse = new Syncroton_Model_MeetingResponse($xml->Request[0]);
931         
932         $eventId = $controller->setAttendeeStatus($meetingResponse);
933         
934         $event = Calendar_Controller_MSEventFacade::getInstance()->get($serverId);
935         $event->exdate->sort('dtstart', 'DESC');
936         $instance = $event->exdate->filter('is_deleted', 0)->getFirstRecord();
937         $ownAttendee = Calendar_Model_Attender::getOwnAttender($instance->attendee);
938         
939         $this->assertEquals(Calendar_Model_Attender::STATUS_TENTATIVE, $ownAttendee->status);
940     }
941     
942     /**
943      * test search events (unsyncable)
944      * 
945      * TODO finish this -> assertion fails atm because the event is found even if it is in an unsyncable folder and has no attendees (but 1 exdate)
946      */
947     public function _testUnsyncableSearch()
948     {
949         $this->markTestSkipped();
950         
951         $controller = $this->_getController($this->_getDevice(Syncroton_Model_Device::TYPE_WEBOS));
952
953         $xml = simplexml_import_dom($this->_getInputDOMDocument());
954         
955         $record = $controller->createEntry($this->_getContainerWithoutSyncGrant()->getId(), $xml->Collections->Collection->Commands->Change[0]->ApplicationData);
956         $record->exdate = new Tinebase_Record_RecordSet('Calendar_Model_Event');
957         $record->attendee = NULL;
958
959         $this->objects['events'][] = Calendar_Controller_MSEventFacade::getInstance()->update($record);
960         
961         $events = $controller->search($this->_specialFolderName, $xml->Collections->Collection->Commands->Change[0]->ApplicationData);
962         
963         $this->assertEquals(0, count($events));
964     }
965     
966     /**
967      * testEventWithTags
968      * 
969      * @see 0007346: events with tags are not synced
970      */
971     public function testEventWithTags()
972     {
973         $event = ActiveSync_TestCase::getTestEvent();
974         $event->tags = array(array(
975             'name' => 'test tag',
976             'type' => Tinebase_Model_Tag::TYPE_PERSONAL
977         ));
978         $event = Calendar_Controller_Event::getInstance()->create($event);
979         
980         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), Tinebase_DateTime::now());
981         $syncrotonEvent = $controller->toSyncrotonModel($event);
982         
983         $this->assertTrue(is_array($syncrotonEvent->categories));
984         $this->assertTrue(in_array('test tag', $syncrotonEvent->categories), 'tag not found in categories: ' . print_r($syncrotonEvent->categories, TRUE));
985     }
986     
987     /**
988      * testEventWithAlarmToSyncrotonModel
989      */
990     public function testEventWithAlarmToSyncrotonModel()
991     {
992         $event = ActiveSync_TestCase::getTestEvent();
993         $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(array(
994             'minutes_before' => 15,
995             'alarm_time'     => $event->dtstart->getClone()->subMinute(15),
996             'model'          => 'Calendar_Model_Event'
997         )));
998         
999         $event = Calendar_Controller_Event::getInstance()->create($event);
1000         
1001         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), Tinebase_DateTime::now());
1002         $syncrotonEvent = $controller->toSyncrotonModel($event);
1003         
1004         $this->assertEquals(15, $syncrotonEvent->reminder);
1005     }
1006     
1007     /**
1008      * @see 0007346 - organizer perspective for alarms
1009      */
1010     public function testEventWithAlarmToTine20Model()
1011     {
1012         $syncrotonFolder = $this->testCreateFolder();
1013         list($serverId, $event) = $this->testCreateEntry($syncrotonFolder);
1014         
1015         $alarm = Calendar_Controller_Event::getInstance()->get($serverId)->alarms->getFirstRecord();
1016         $this->assertFalse(!!$alarm->getOption('attendee'));
1017     }
1018     
1019     /**
1020      * calendar has different grant handling
1021      * @see 0007450: shared calendars of other users (iOS)
1022      */
1023     public function testGetAllFoldersWithContainerSyncFilter()
1024     {
1025         $device = $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE);
1026         $controller = Syncroton_Data_Factory::factory($this->_class, $device, new Tinebase_DateTime(null, null, 'de_DE'));
1027     
1028         $folderA = $this->testCreateFolder(); // personal of test user
1029         
1030         $sclever = array_value('sclever', Zend_Registry::get('personas'));
1031         $folderB = Tinebase_Core::getPreference('Calendar')->getValueForUser(Calendar_Preference::DEFAULTCALENDAR, $sclever->getId());
1032
1033         // have syncGerant for sclever
1034         Tinebase_Container::getInstance()->setGrants($folderB, new Tinebase_Record_RecordSet('Tinebase_Model_Grants', array(
1035             array(
1036                 'account_id'    => $sclever->getId(),
1037                 'account_type'  => 'user',
1038                 Tinebase_Model_Grants::GRANT_ADMIN    => true,
1039             ),
1040             array(
1041                 'account_id'    => Tinebase_Core::getUser()->getId(),
1042                 'account_type'  => 'user',
1043                 Tinebase_Model_Grants::GRANT_READ     => true,
1044                 Tinebase_Model_Grants::GRANT_SYNC     => true,
1045         ))), TRUE);
1046         
1047         $syncFilter = new Calendar_Model_EventFilter(array(
1048             array('field' => 'container_id', 'operator' => 'in', 'value' => array(
1049                 $folderA->serverId,
1050                 $folderB,
1051             ))
1052         ));
1053         
1054         $syncFavorite = Tinebase_PersistentFilter::getInstance()->create(new Tinebase_Model_PersistentFilter(array(
1055             'application_id'        => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(),
1056             'account_id'            => Tinebase_Core::getUser()->getId(),
1057             'model'                 => 'Calendar_Model_EventFilter',
1058             'filters'               => $syncFilter,
1059             'name'                  => 'testSyncFilter',
1060             'description'           => 'test two folders'
1061         )));
1062         
1063         $device->calendarfilterId = $syncFavorite->getId();
1064
1065         $allSyncrotonFolders = $controller->getAllFolders();
1066         
1067         $defaultFolderId = Tinebase_Core::getPreference('Calendar')->{Calendar_Preference::DEFAULTCALENDAR};
1068         
1069         foreach ($allSyncrotonFolders as $syncrotonFolder) {
1070             $this->assertTrue($syncrotonFolder->serverId == $defaultFolderId 
1071                 ? $syncrotonFolder->type === Syncroton_Command_FolderSync::FOLDERTYPE_CALENDAR
1072                 : $syncrotonFolder->type === Syncroton_Command_FolderSync::FOLDERTYPE_CALENDAR_USER_CREATED
1073             );
1074         }
1075         
1076         $this->assertEquals(2, count($allSyncrotonFolders));
1077         $this->assertArrayHasKey($folderA->serverId, $allSyncrotonFolders);
1078         $this->assertArrayHasKey($folderB, $allSyncrotonFolders);
1079     }
1080     
1081     /**
1082      * validate that current is not removed from the event after creating and updating event from phone 
1083      * 
1084      * @link https://forge.tine20.org/mantisbt/view.php?id=7606
1085      */
1086     public function testUpdateAfterCreate()
1087     {
1088         $syncrotonFolder = $this->testCreateFolder();
1089     
1090         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_SMASUNGGALAXYS2), Tinebase_DateTime::now());
1091     
1092         $xml = new SimpleXMLElement('
1093             <ApplicationData xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase" xmlns:Calendar="uri:Calendar">
1094               <Timezone xmlns="uri:Calendar">xP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAEAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAIAAAAAAAAAxP///w==</Timezone>
1095               <AllDayEvent xmlns="uri:Calendar">0</AllDayEvent>
1096               <StartTime xmlns="uri:Calendar">20130124T070000Z</StartTime>
1097               <EndTime xmlns="uri:Calendar">20130124T080000Z</EndTime>
1098               <DtStamp xmlns="uri:Calendar">20121228T001432Z</DtStamp>
1099               <Subject xmlns="uri:Calendar">Neues Ereignis</Subject>
1100               <Sensitivity xmlns="uri:Calendar">0</Sensitivity>
1101               <Body xmlns="uri:AirSyncBase">
1102                 <Type>1</Type>
1103                 <Data></Data>
1104               </Body>
1105               <OrganizerEmail xmlns="uri:Calendar">rep@tine.de</OrganizerEmail>
1106               <UID xmlns="uri:Calendar">24f59962-7243-4938-8b8c-a81fda46977e</UID>
1107               <BusyStatus xmlns="uri:Calendar">2</BusyStatus>
1108               <MeetingStatus xmlns="uri:Calendar">0</MeetingStatus>
1109             </ApplicationData>
1110         ');
1111         $syncrotonEvent = new Syncroton_Model_Event($xml);
1112     
1113         $serverId = $controller->createEntry($syncrotonFolder->serverId, $syncrotonEvent);
1114     
1115         #var_dump(Calendar_Controller_Event::getInstance()->get($serverId)->toArray());
1116         $this->assertEquals(1, count(Calendar_Controller_Event::getInstance()->get($serverId)->attendee), 'count after create');
1117         
1118         $xml = new SimpleXMLElement('
1119             <ApplicationData xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase" xmlns:Calendar="uri:Calendar">
1120                 <Timezone xmlns="uri:Calendar">xP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAEAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAIAAAAAAAAAxP///w==</Timezone>
1121                 <AllDayEvent xmlns="uri:Calendar">0</AllDayEvent>
1122                 <StartTime xmlns="uri:Calendar">20130124T070000Z</StartTime>
1123                 <EndTime xmlns="uri:Calendar">20130124T080000Z</EndTime>
1124                 <DtStamp xmlns="uri:Calendar">20121228T001526Z</DtStamp>
1125                 <Subject xmlns="uri:Calendar">Neues Ereignis geƤndert</Subject>
1126                 <Sensitivity xmlns="uri:Calendar">0</Sensitivity>
1127                 <Body xmlns="uri:AirSyncBase">
1128                   <Type>1</Type>
1129                   <Data></Data>
1130                 </Body>
1131                 <OrganizerEmail xmlns="uri:Calendar">rep@tine.de</OrganizerEmail>
1132                 <UID xmlns="uri:Calendar">24f59962-7243-4938-8b8c-a81fda46977e</UID>
1133                 <BusyStatus xmlns="uri:Calendar">2</BusyStatus>
1134                 <MeetingStatus xmlns="uri:Calendar">0</MeetingStatus>
1135             </ApplicationData>
1136         ');
1137         $syncrotonEvent = new Syncroton_Model_Event($xml);
1138         
1139         // need to creaate new controller to set new sync timestamp for concurrency handling
1140         $syncTimestamp = Calendar_Controller_Event::getInstance()->get($serverId)->creation_time;
1141         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_SMASUNGGALAXYS2), $syncTimestamp);
1142         
1143         $serverId = $controller->updateEntry($syncrotonFolder->serverId, $serverId, $syncrotonEvent);
1144         
1145         #var_dump(Calendar_Controller_Event::getInstance()->get($serverId)->toArray());
1146         $this->assertEquals(1, count(Calendar_Controller_Event::getInstance()->get($serverId)->attendee), 'count after update');
1147     }
1148
1149     /**
1150      * testAddAlarm
1151      * 
1152      * @see 0008230: added alarm to event on iOS 6.1 -> description removed
1153      */
1154     public function testAddAlarm()
1155     {
1156         $syncrotonFolder = $this->testCreateFolder();
1157         
1158         $event = ActiveSync_TestCase::getTestEvent();
1159         $event->summary = 'testtermin';
1160         $event->description = 'some text';
1161         $event->dtstart = new Tinebase_DateTime('2013-10-22 16:00:00');
1162         $event->dtend = new Tinebase_DateTime('2013-10-22 17:00:00');
1163         $event = Calendar_Controller_Event::getInstance()->create($event);
1164         
1165         $xml = new SimpleXMLElement('
1166           <ApplicationData>
1167             <Timezone xmlns="uri:Calendar">xP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAFAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
1168 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAMAAAAAAAAAxP///w==</Timezone>
1169             <AllDayEvent xmlns="uri:Calendar">0</AllDayEvent>
1170             <BusyStatus xmlns="uri:Calendar">2</BusyStatus>
1171             <DtStamp xmlns="uri:Calendar">20131021T142015Z</DtStamp>
1172             <EndTime xmlns="uri:Calendar">20131022T170000Z</EndTime>
1173             <Reminder xmlns="uri:Calendar">60</Reminder>
1174             <Sensitivity xmlns="uri:Calendar">0</Sensitivity>
1175             <Subject xmlns="uri:Calendar">testtermin</Subject>
1176             <StartTime xmlns="uri:Calendar">20131022T160000Z</StartTime>
1177             <UID xmlns="uri:Calendar">' . $event->uid . '</UID>
1178             <MeetingStatus xmlns="uri:Calendar">1</MeetingStatus>
1179             <Attendees xmlns="uri:Calendar">
1180               <Attendee>
1181                 <Name>' . Tinebase_Core::getUser()->accountDisplayName . '</Name>
1182                 <Email>' . Tinebase_Core::getUser()->accountEmailAddress . '</Email>
1183                 <AttendeeType>1</AttendeeType>
1184               </Attendee>
1185             </Attendees>
1186           </ApplicationData>
1187         ');
1188         $syncrotonEvent = new Syncroton_Model_Event($xml);
1189         
1190         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), $event->creation_time);
1191         $serverId = $controller->updateEntry($syncrotonFolder->serverId, $event->getId(), $syncrotonEvent);
1192         
1193         $updatedEvent = Calendar_Controller_Event::getInstance()->get($serverId);
1194         
1195         $this->assertEquals(1, count($updatedEvent->alarms));
1196         $this->assertEquals($event->description, $updatedEvent->description, 'description mismatch: ' . print_r($updatedEvent->toArray(), true));
1197     }
1198     
1199     public function testGetEntriesIPhone()
1200     {
1201         $syncrotonFolder = $this->testCreateFolder();
1202         
1203         $syncrotonFolder2 = $this->testCreateFolder();
1204         
1205         //make $syncrotonFolder2 the default
1206         $this->_prefs->setValue(Calendar_Preference::DEFAULTCALENDAR, $syncrotonFolder2->serverId);
1207         $this->_defaultHasChanged = true;
1208         
1209         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), Tinebase_DateTime::now());
1210         
1211         $now = Tinebase_DateTime::now();
1212         $thisYear = $now->format('Y');
1213         $xmlString = preg_replace('/2012/', $thisYear, $this->_testXMLInput);
1214         $xml = new SimpleXMLElement($xmlString);
1215         $syncrotonEvent = new Syncroton_Model_Event($xml->Collections->Collection->Commands->Change[0]->ApplicationData);
1216         
1217         $serverId = $controller->createEntry($syncrotonFolder->serverId, $syncrotonEvent);
1218         
1219         $syncrotonEvent = $controller->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $syncrotonFolder->serverId)), $serverId);
1220         
1221         $this->assertEmpty($syncrotonEvent->attendees, 'Events attendees found in folder which is not the default calendar');
1222         
1223         
1224         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), Tinebase_DateTime::now());
1225         
1226         $now = Tinebase_DateTime::now();
1227         $thisYear = $now->format('Y');
1228         $xmlString = preg_replace('/2012/', $thisYear, $this->_testXMLInput);
1229         $xml = new SimpleXMLElement($xmlString);
1230         $syncrotonEvent = new Syncroton_Model_Event($xml->Collections->Collection->Commands->Change[0]->ApplicationData);
1231         
1232         $serverId = $controller->createEntry($syncrotonFolder2->serverId, $syncrotonEvent);
1233         
1234         $syncrotonEvent = $controller->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $syncrotonFolder2->serverId)), $serverId);
1235         
1236         $this->assertNotEmpty($syncrotonEvent->attendees, 'Events attendees not found in default calendar');
1237     }
1238
1239     public function testUpdateEntriesIPhone()
1240     {
1241         // create event
1242         $syncrotonFolder = $this->testCreateFolder();
1243         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), Tinebase_DateTime::now());
1244         list($serverId, $syncrotonEvent) = $this->testCreateEntry($syncrotonFolder);
1245
1246         // update event
1247         $syncrotonEventtoUpdate = $controller->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $syncrotonFolder->serverId)), $serverId);
1248         $syncrotonEventtoUpdate->exceptions[0]->subject = 'update';
1249         $syncrotonEventtoUpdate->exceptions[0]->startTime->addHour(1);
1250         $syncrotonEventtoUpdate->exceptions[0]->endTime->addHour(1);
1251
1252         $syncTimestamp = Calendar_Controller_Event::getInstance()->get($serverId)->last_modified_time;
1253         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), $syncTimestamp);
1254         $serverId = $controller->updateEntry($syncrotonFolder->serverId, $serverId, $syncrotonEventtoUpdate);
1255
1256         // assert update
1257         $updatedSyncrotonEvent = $controller->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $syncrotonFolder->serverId)), $serverId);
1258         $this->assertEquals('update', $updatedSyncrotonEvent->exceptions[0]->subject);
1259         $this->assertNotEquals($syncrotonEvent->exceptions[0]->startTime->toString(), $updatedSyncrotonEvent->exceptions[0]->startTime->toString());
1260         $this->assertEquals($syncrotonEventtoUpdate->exceptions[0]->startTime->toString(), $updatedSyncrotonEvent->exceptions[0]->startTime->toString());
1261
1262         // assert attendee are preserved
1263         $updatedEvent = Calendar_Controller_MSEventFacade::getInstance()->get($serverId);
1264         $this->assertNotEmpty($updatedEvent->attendee, 'attendee must be preserved during update');
1265     }
1266
1267     /**
1268      * testUpdateEntriesIPhoneNonDefaultFolder
1269      *
1270      * for iOS devices we need to suppress attendee during sync for non default folder (see foldertype in foldersync)
1271      * iOS can only have one default folder
1272      *
1273      * NOTE: our implementation in not correct at the moment as we only append attendee if the event is in the default folder.
1274      *       correct implementation would be to append attendee when default folder is synced
1275      */
1276     public function testUpdateEntriesIPhoneNonDefaultFolder()
1277     {
1278         // create event in folder1
1279         $syncrotonFolder = $this->testCreateFolder();
1280         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), Tinebase_DateTime::now());
1281         list($serverId, $syncrotonEvent) = $this->testCreateEntry($syncrotonFolder);
1282
1283         // create new default folder2
1284         $syncrotonFolder2 = $this->testCreateFolder();
1285         Tinebase_Core::getPreference('Calendar')->setValue(Calendar_Preference::DEFAULTCALENDAR, $syncrotonFolder2->serverId);
1286
1287         // load event in non default folder2
1288         $syncrotonEventtoUpdate = $controller->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $syncrotonFolder->serverId)), $serverId);
1289         $this->assertEmpty($syncrotonEventtoUpdate->attendees, 'attendee in non default folders must be removed for ios');
1290
1291         // update event in non default folder
1292         $syncrotonEventtoUpdate->exceptions[0]->subject = 'update';
1293         $syncrotonEventtoUpdate->exceptions[0]->startTime->addHour(1);
1294         $syncrotonEventtoUpdate->exceptions[0]->endTime->addHour(1);
1295
1296         $syncTimestamp = Calendar_Controller_Event::getInstance()->get($serverId)->last_modified_time;
1297         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), $syncTimestamp);
1298         $serverId = $controller->updateEntry($syncrotonFolder->serverId, $serverId, $syncrotonEventtoUpdate);
1299
1300         // assert update from non default folder
1301         $updatedSyncrotonEvent = $controller->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $syncrotonFolder2->serverId)), $serverId);
1302         $this->assertEquals('update', $updatedSyncrotonEvent->exceptions[0]->subject);
1303         $this->assertNotEquals($syncrotonEvent->exceptions[0]->startTime->toString(), $updatedSyncrotonEvent->exceptions[0]->startTime->toString());
1304         $this->assertEquals($syncrotonEventtoUpdate->exceptions[0]->startTime->toString(), $updatedSyncrotonEvent->exceptions[0]->startTime->toString());
1305
1306         // assert attendee are preserved
1307         $updatedEvent = Calendar_Controller_MSEventFacade::getInstance()->get($serverId);
1308         $this->assertNotEmpty($updatedEvent->attendee, 'attendee must be preserved during update');
1309     }
1310 }