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