|
1 /* |
|
2 * Copyright (c) 2002-2005 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: Implementation for meeting request processor |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 // INCLUDE FILES |
|
22 #include "CMRProcessor.h" |
|
23 #include "MRHelpers.h" |
|
24 #include "mrdatalog.h" |
|
25 #include "ICalUILogDef.h" |
|
26 #include <e32std.h> |
|
27 #include <calentry.h> //CCalEntry (Calendar entry API V2) |
|
28 #include <caluser.h> //caluser and attendee |
|
29 #include <MsgMailUIDs.h> //uid for mail application |
|
30 #include <cemailaccounts.h> |
|
31 #include <smtpset.h> |
|
32 #include <imapset.h> |
|
33 #include <pop3set.h> |
|
34 #include <MuiuMsvUiServiceUtilities.h> |
|
35 #include "MRViewersPanic.h" //panic codes |
|
36 #include "meetingrequestviewers.hrh" //common constants |
|
37 #include "ProcessingStructs.h" //scenario structures |
|
38 #include <SendUiConsts.h> //mailbox uids |
|
39 #include <stringloader.h> //loading of string resources |
|
40 #include <meetingrequestviewersuires.rsg> //resource definitions header |
|
41 #include <CalenInterimUtils2.h> |
|
42 #include <cmrmailboxutils.h> |
|
43 |
|
44 // CONSTANTS |
|
45 /// Unnamed namespace for local definitions |
|
46 namespace { |
|
47 |
|
48 _LIT( KPanicMsg, "CMRProcessor" ); |
|
49 |
|
50 void Panic( TPanicCode aReason ) |
|
51 { |
|
52 User::Panic( KPanicMsg, aReason ); |
|
53 } |
|
54 |
|
55 } // namespace |
|
56 |
|
57 // ============================ MEMBER FUNCTIONS =============================== |
|
58 |
|
59 // ----------------------------------------------------------------------------- |
|
60 // CMRProcessor::CMRProcessor |
|
61 // C++ default constructor can NOT contain any code, that |
|
62 // might leave. |
|
63 // ----------------------------------------------------------------------------- |
|
64 // |
|
65 CMRProcessor::CMRProcessor( |
|
66 CMRMailboxUtils& aMRMailboxUtils, |
|
67 CMRUtilsInternal& aMRUtils, |
|
68 const MAgnEntryUi::TAgnEntryUiInParams& aParams, |
|
69 RPointerArray<CCalEntry>& aEntries ) |
|
70 : iMRMailboxUtils( aMRMailboxUtils ), |
|
71 iMRUtils( aMRUtils ), |
|
72 iEntryUiInParams( aParams ), |
|
73 iArrayOfEntries( aEntries ), |
|
74 iEditMode( MMRModelInterface::EModeNotSet ), |
|
75 iOwnerRole( ENotFound ) |
|
76 { |
|
77 // set initial values to scenario data |
|
78 iCombinedScenData.iMethod = CCalEntry::EMethodNone; |
|
79 iCombinedScenData.iCallingApp = ENotSet; |
|
80 iCombinedScenData.iOpenedMailbox = ENotSet; |
|
81 iCombinedScenData.iUtilsDBResult = MMRUtilsTombsExt::EUndefined; |
|
82 iCombinedScenData.iMethodLevelValidity = ENotSet; |
|
83 iCombinedScenData.iEditorMode = ENotSet; |
|
84 iCombinedScenData.iMROperation = ENotSet; |
|
85 } |
|
86 |
|
87 // ----------------------------------------------------------------------------- |
|
88 // CMRProcessor::ConstructL |
|
89 // Symbian 2nd phase constructor can leave. |
|
90 // ----------------------------------------------------------------------------- |
|
91 // |
|
92 void CMRProcessor::ConstructL() |
|
93 { |
|
94 } |
|
95 |
|
96 // ----------------------------------------------------------------------------- |
|
97 // ?classname::NewL |
|
98 // Two-phased constructor. |
|
99 // ----------------------------------------------------------------------------- |
|
100 // |
|
101 CMRProcessor* CMRProcessor::NewL( |
|
102 CMRMailboxUtils& aMRMailboxUtils, |
|
103 CMRUtilsInternal& aMRUtils, |
|
104 const MAgnEntryUi::TAgnEntryUiInParams& aParams, |
|
105 RPointerArray<CCalEntry>& aEntries ) |
|
106 { |
|
107 CMRProcessor* self = new( ELeave ) CMRProcessor( aMRMailboxUtils, |
|
108 aMRUtils, |
|
109 aParams, |
|
110 aEntries ); |
|
111 CleanupStack::PushL( self ); |
|
112 self->ConstructL(); |
|
113 CleanupStack::Pop(); |
|
114 |
|
115 return self; |
|
116 } |
|
117 |
|
118 // Destructor |
|
119 CMRProcessor::~CMRProcessor() |
|
120 { |
|
121 delete iCombinedEntry; |
|
122 } |
|
123 |
|
124 CCalEntry* CMRProcessor::CombinedEntry() |
|
125 { |
|
126 // Should always be valid when this method is called |
|
127 __ASSERT_DEBUG( iCombinedEntry, Panic( ECombinedCalEntryNull ) ); |
|
128 return iCombinedEntry; |
|
129 } |
|
130 |
|
131 void CMRProcessor::SetPhoneOwnerL() |
|
132 { |
|
133 __ASSERT_DEBUG( iArrayOfEntries.Count() > 0, Panic( EEmptyEntryArray ) ); |
|
134 |
|
135 // set phone owner field of each entry: |
|
136 |
|
137 TInt count( iArrayOfEntries.Count() ); |
|
138 for ( TInt i( 0 ); i < count; ++i ) |
|
139 { |
|
140 CCalEntry& entry = *( iArrayOfEntries[i] ); |
|
141 iMRMailboxUtils.SetPhoneOwnerL( entry, iEntryUiInParams.iMailBoxId ); |
|
142 } |
|
143 |
|
144 // set iOwnerRole, use entry at index zero: |
|
145 |
|
146 CCalEntry& baseEntry = *( iArrayOfEntries[0] ); |
|
147 if ( iMRMailboxUtils.IsOrganizerL( baseEntry ) ) |
|
148 { |
|
149 iOwnerRole = EOrganiser; |
|
150 } |
|
151 else |
|
152 { |
|
153 CCalAttendee* thisAttendee = iMRMailboxUtils.ThisAttendeeL( baseEntry ); |
|
154 if ( thisAttendee ) |
|
155 { |
|
156 switch ( thisAttendee->RoleL() ) |
|
157 { |
|
158 case CCalAttendee::EOptParticipant: |
|
159 { |
|
160 iOwnerRole = EOptionalParticipant; |
|
161 break; |
|
162 } |
|
163 case CCalAttendee::ENonParticipant: |
|
164 { |
|
165 iOwnerRole = ENonRequiredParticipant; |
|
166 break; |
|
167 } |
|
168 case CCalAttendee::EReqParticipant: // fall through |
|
169 case CCalAttendee::EChair: // fall through |
|
170 default: |
|
171 { // Note: chair MUST NOT be interpreted as organizer! |
|
172 // Req.participant is not correct either but has less |
|
173 // side effects. |
|
174 iOwnerRole = ERequiredParticipant; |
|
175 break; |
|
176 } |
|
177 } |
|
178 } |
|
179 } |
|
180 } |
|
181 |
|
182 CMRProcessor::TOwnerRole CMRProcessor::OwnerRole() |
|
183 { |
|
184 return iOwnerRole; |
|
185 } |
|
186 |
|
187 MMRUtilsTombsExt::TMRUtilsDbResult CMRProcessor::ProcessingDbResult() |
|
188 { |
|
189 return iCombinedScenData.iUtilsDBResult; |
|
190 } |
|
191 |
|
192 TInt CMRProcessor::ProcessingResultOp() |
|
193 { |
|
194 return iCombinedScenData.iMROperation; |
|
195 } |
|
196 |
|
197 MMRModelInterface::TEditingMode CMRProcessor::EditingMode() const |
|
198 { |
|
199 return iEditMode; |
|
200 } |
|
201 |
|
202 void CMRProcessor::SetEditingModeL( MMRModelInterface::TEditingMode aEditMode ) |
|
203 { |
|
204 __ASSERT_DEBUG( iEditMode == MMRModelInterface::EModeNotSet, |
|
205 Panic( EEditModeResetAttempt ) ); |
|
206 iEditMode = aEditMode; |
|
207 if ( aEditMode == MMRModelInterface::EEditMeeting ) |
|
208 { // if we switch to edit meeting mode the combined entry must be re-set |
|
209 CCalEntry* entry = iArrayOfEntries[0]; |
|
210 ResetCombinedEntryL( *entry, TCalTime() ); |
|
211 iCombinedEntry->CopyFromL( *entry ); |
|
212 iCombinedEntry->SetMethodL( entry->MethodL() ); |
|
213 } |
|
214 // no need to do anything if aEditMode == EEditInstance since that is the |
|
215 // default when launching the MR Viewer with incoming entry array. |
|
216 } |
|
217 |
|
218 // TODO: CMRUtils::CheckEntryCondL doesn't, and shouldn't (because of e.g. |
|
219 // calendar sync), notice problems with out-of-date entry (meeting occurred in past), |
|
220 // but in processor we should identify that situation. Possibly add a new processing |
|
221 // struct data field, and possibly a new TMROperation code also? |
|
222 |
|
223 // TODO: somewhere we must set outparams: saved, meeting/instance deleted, |
|
224 // but is processor a good place for it. |
|
225 |
|
226 // TODO: when handling processing leave, we must take care that iCombinedEntry |
|
227 // is set to point to some entry, or to ensure that there doesn't become an access |
|
228 // violation because someone is trying to access it. |
|
229 void CMRProcessor::ProcessEntriesL() |
|
230 { |
|
231 if ( iArrayOfEntries.Count() < 1 ) |
|
232 { |
|
233 User::Leave( KErrArgument ); |
|
234 } |
|
235 CCalEntry* entry = iArrayOfEntries[0]; |
|
236 |
|
237 // Sets phone owner in entries and in iOwnerRole, this must be done at |
|
238 // early stage, before trying to read organizer or attendee fields |
|
239 SetPhoneOwnerL(); |
|
240 |
|
241 // set common values to scenario data |
|
242 iCombinedScenData.iMethod = entry->MethodL(); |
|
243 iCombinedScenData.iCallingApp = CallingApp(); |
|
244 iCombinedScenData.iOpenedMailbox = OpenedMailboxL( *entry ); |
|
245 iCombinedScenData.iEditorMode = EditorMode(); |
|
246 if ( iCombinedScenData.iEditorMode == MAgnEntryUi::EViewEntry ) |
|
247 { // in other cases editing mode will be decided later, but |
|
248 // in EViewEntry case we know it already now |
|
249 SetEditingModeL( MMRModelInterface::EViewOnly ); |
|
250 } |
|
251 |
|
252 TRAPD( err, |
|
253 { |
|
254 if ( entry->MethodL() == CCalEntry::EMethodReply ) |
|
255 { |
|
256 ProcessResponseArrayL(); |
|
257 } |
|
258 else |
|
259 { |
|
260 if ( iEntryUiInParams.iCallingApp.iUid == KUidCalendarApplication ) |
|
261 { |
|
262 ProcessRequestInCalendarL(); |
|
263 } |
|
264 else |
|
265 { |
|
266 ProcessRequestOrCancelArrayL(); |
|
267 } |
|
268 } |
|
269 } ); |
|
270 if ( err != KErrNone ) |
|
271 { |
|
272 ProcessErrorL(); |
|
273 } |
|
274 } |
|
275 |
|
276 /** |
|
277 * In case of calendar we show in start-up: |
|
278 * a) parent entry if meeting non-repeating |
|
279 * b) existing child entry if received as input |
|
280 * c) newly created child if repeating meeting but child doesn't exist |
|
281 * User may later on choose "edit series" mode if meeting is repeating. |
|
282 */ |
|
283 void CMRProcessor::ProcessRequestInCalendarL() |
|
284 { |
|
285 if ( iArrayOfEntries.Count() != 1 ) |
|
286 { |
|
287 // From calendar we support only receiving single entry at a time |
|
288 User::Leave( KErrArgument ); |
|
289 } |
|
290 |
|
291 CCalEntry* entry = iArrayOfEntries[0]; |
|
292 |
|
293 iCombinedScenData.iMROperation = |
|
294 MatchScenarioToDataL( iCombinedScenData, *entry ); |
|
295 if ( iCombinedScenData.iMROperation <= ENullOp ) |
|
296 { |
|
297 User::Leave( KErrArgument ); |
|
298 } |
|
299 |
|
300 if ( MREntryConsultant::IsRepeatingMeetingL( *entry ) ) |
|
301 { |
|
302 // reset combined entry, it will be a child representing the instance |
|
303 ResetCombinedEntryL( *entry, iEntryUiInParams.iInstanceDate ); |
|
304 |
|
305 if ( MREntryConsultant::IsModifyingEntryL( *entry ) ) |
|
306 { // entry exists for the instance, use that |
|
307 iCombinedEntry->CopyFromL( *entry ); |
|
308 iCombinedEntry->SetMethodL( entry->MethodL() ); |
|
309 } |
|
310 else |
|
311 { // create a fully populated child representing the instance |
|
312 SetInstanceStartAndEndL( *iCombinedEntry, |
|
313 *entry, |
|
314 iEntryUiInParams.iInstanceDate ); |
|
315 CCalenInterimUtils2::PopulateChildFromParentL( *iCombinedEntry, |
|
316 *entry ); |
|
317 } |
|
318 } |
|
319 else |
|
320 { // non-repeating meeting -> view/edit entire meeting |
|
321 // reset combined entry, it will be a parent |
|
322 ResetCombinedEntryL( *entry, TCalTime() ); |
|
323 iCombinedEntry->CopyFromL( *entry ); |
|
324 iCombinedEntry->SetMethodL( entry->MethodL() ); |
|
325 |
|
326 // in case of non-repeating meeting the editing mode is always |
|
327 // EEditMeeting and user doesn't have a possibility to change it |
|
328 SetEditingModeL( MMRModelInterface::EEditMeeting ); |
|
329 } |
|
330 } |
|
331 |
|
332 void CMRProcessor::ProcessResponseArrayL() |
|
333 { |
|
334 |
|
335 // TODO: we must check that entry isn't out of date or cancelled, |
|
336 // should we do that in HandleResponseStatusL or where? It would be |
|
337 // better to do it before calling TryCreateModForResponseL() to avoid |
|
338 // unnecessary mod entry in db. |
|
339 |
|
340 // 1. Handle entry at index 0 ,it may be response to a parent or a child |
|
341 |
|
342 CCalEntry& response = *( iArrayOfEntries[0] ); |
|
343 |
|
344 // when checking against db entry, respond should look like a valid update: |
|
345 iCombinedScenData.iUtilsDBResult = iMRUtils.CheckEntryCondL( response ); |
|
346 |
|
347 // ...but if attendee has responded to an instance of a meeting, then |
|
348 // corresponding modifying entry doesn't necessarily exist -> create it: |
|
349 if ( iCombinedScenData.iUtilsDBResult == MMRUtilsTombsExt::ECheckedValidNew ) |
|
350 { |
|
351 CreateModForResponseL( response, iCombinedScenData ); |
|
352 } |
|
353 |
|
354 // check response validity on method level and set status to request, if ok: |
|
355 CCalEntry* request = HandleResponseStatusL( response, iCombinedScenData ); |
|
356 if ( request ) |
|
357 { |
|
358 CleanupStack::PushL( request ); |
|
359 // match to scenario, entry to be updated is request, not response |
|
360 MatchScenarioAndSaveIfNeededL( *request, iCombinedScenData ); |
|
361 } |
|
362 else |
|
363 { |
|
364 // if request not found saving won't occur, we still perform matching |
|
365 // to get scenario completed, we give response as a parameter but it |
|
366 // won't be used in this case |
|
367 MatchScenarioAndSaveIfNeededL( response, iCombinedScenData ); |
|
368 } |
|
369 |
|
370 // 2. Go through additional entries, they don't affect combined entry |
|
371 // and related functionality |
|
372 |
|
373 TScenarioData tmpScenario( iCombinedScenData ); |
|
374 TInt count( iArrayOfEntries.Count() ); |
|
375 for ( TInt i( 1 ); i < count; ++i ) |
|
376 { |
|
377 CCalEntry& tmpResponse = *( iArrayOfEntries[i] ); |
|
378 tmpScenario.iUtilsDBResult = iMRUtils.CheckEntryCondL( tmpResponse ); |
|
379 if ( tmpScenario.iUtilsDBResult == MMRUtilsTombsExt::ECheckedValidNew ) |
|
380 { |
|
381 CreateModForResponseL( response, tmpScenario ); |
|
382 } |
|
383 CCalEntry* tmpRequest = HandleResponseStatusL( response, tmpScenario ); |
|
384 if ( tmpRequest ) |
|
385 { // for additional entries we only match scenario if request exists, |
|
386 // otherwise it is unnecessary |
|
387 CleanupStack::PushL( tmpRequest ); |
|
388 MatchScenarioAndSaveIfNeededL( *tmpRequest, tmpScenario ); |
|
389 CleanupStack::PopAndDestroy( tmpRequest ); |
|
390 } |
|
391 } |
|
392 |
|
393 // 3. Set combined entry == first response possibly mixed with request |
|
394 |
|
395 ResetCombinedEntryL( response, response.RecurrenceIdL() ); |
|
396 iCombinedEntry->CopyFromL( response ); |
|
397 iCombinedEntry->SetMethodL( CCalEntry::EMethodReply ); |
|
398 |
|
399 if ( iCombinedScenData.iMROperation == EUpdateStatusToCalendar ) |
|
400 { |
|
401 // TODO: set response summary with status information |
|
402 iCombinedEntry->SetLocationL( request->LocationL() ); |
|
403 iCombinedEntry->SetStartAndEndTimeL( request->StartTimeL(), |
|
404 request->EndTimeL() ); |
|
405 // Response won't necessarily contain RRule/RDate information, but |
|
406 // that isn't shown to the user either -> not needed in iCombinedEntry. |
|
407 } |
|
408 else |
|
409 { |
|
410 User::Leave( KErrArgument ); |
|
411 } |
|
412 |
|
413 if ( request ) |
|
414 { |
|
415 CleanupStack::PopAndDestroy( request ); |
|
416 } |
|
417 } |
|
418 |
|
419 /** |
|
420 * This method assumes that response has been verified to be valid. |
|
421 */ |
|
422 void CMRProcessor::CreateModForResponseL( |
|
423 const CCalEntry& aResponse, |
|
424 TScenarioData& aScenarioData ) const |
|
425 { |
|
426 // fetch parent: |
|
427 CCalEntry* request = iMRUtils.FetchEntryL( aResponse.UidL(), |
|
428 TCalTime() ); |
|
429 CleanupStack::PushL( request ); |
|
430 // create modifying entry: |
|
431 HBufC8* modUid = request->UidL().AllocLC(); |
|
432 CCalEntry* mod = CCalEntry::NewL( CCalEntry::EAppt, |
|
433 modUid, |
|
434 CCalEntry::EMethodRequest, |
|
435 request->SequenceNumberL(), |
|
436 aResponse.RecurrenceIdL(), |
|
437 CalCommon::EThisOnly ); |
|
438 CleanupStack::Pop( modUid ); // ownership transferred |
|
439 CleanupStack::PushL( mod ); |
|
440 |
|
441 // In this case response shouldn't really have any RRules or RDates, |
|
442 // and the recurrence id should specify the instance which attendee |
|
443 // responded to. Therefore create modifying entry representing that |
|
444 // instance. |
|
445 SetInstanceStartAndEndL( *mod, *request, aResponse.RecurrenceIdL() ); |
|
446 // Populate modifying entry fields, especially it is important that |
|
447 // also entire attendee list gets copied, since this modifying entry |
|
448 // will be exceptioned in the parent |
|
449 CCalenInterimUtils2::PopulateChildFromParentL( *mod, *request ); |
|
450 // Modifying entry must have the same DTSTAMP as originating entry, |
|
451 // since in this case they were sent as one request |
|
452 mod->SetDTStampL( request->DTStampL() ); |
|
453 // Set modifying request as sent! |
|
454 // TODO: this will be done differently when Symbian supports other fields |
|
455 // to indicate sending status |
|
456 mod->SetStatusL( CCalEntry::EConfirmed ); |
|
457 |
|
458 MMRUtilsTombsExt::TMRUtilsDbResult res = iMRUtils.StoreL( *mod, EFalse ); |
|
459 if ( res != MMRUtilsTombsExt::EStoredUpdate ) |
|
460 { // store should succeed if response was for a valid instance |
|
461 User::Leave( KErrGeneral ); |
|
462 } |
|
463 |
|
464 CleanupStack::PopAndDestroy( 2 ); // mod, request |
|
465 |
|
466 // now there exists a modifying entry and response is |
|
467 // an update to it: |
|
468 aScenarioData.iUtilsDBResult = MMRUtilsTombsExt::ECheckedValidUpdate; |
|
469 } |
|
470 |
|
471 CCalEntry* CMRProcessor::HandleResponseStatusL( |
|
472 const CCalEntry& aResponse, |
|
473 TScenarioData& aScenarioData ) const |
|
474 { |
|
475 aScenarioData.iMethodLevelValidity = EInvalid; |
|
476 if ( aResponse.AttendeesL().Count() != 1 ) |
|
477 { // response MUST have exactly one attendee, the respondent |
|
478 User::Leave( KErrArgument ); |
|
479 } |
|
480 CCalAttendee& respondent = *( aResponse.AttendeesL()[0] ); |
|
481 |
|
482 CCalEntry* request = iMRUtils.FetchEntryL( aResponse.UidL(), |
|
483 aResponse.RecurrenceIdL() ); |
|
484 if ( request ) |
|
485 { |
|
486 CleanupStack::PushL( request ); |
|
487 TBool changed( EFalse ); |
|
488 CCalAttendee* dbAttendee = |
|
489 RespondentInRequestL( aResponse, *request, changed ); |
|
490 if ( dbAttendee && changed ) |
|
491 { |
|
492 dbAttendee->SetStatusL( respondent.StatusL() ); |
|
493 aScenarioData.iMethodLevelValidity = EValidNeedsSave; |
|
494 } |
|
495 else if ( dbAttendee && !changed ) |
|
496 { |
|
497 aScenarioData.iMethodLevelValidity = EValidNoNeedSave; |
|
498 } |
|
499 CleanupStack::Pop( request ); |
|
500 } |
|
501 return request; // may be NULL |
|
502 } |
|
503 |
|
504 /** |
|
505 * We always show the first entry, if it is a child user will see that |
|
506 * child and responses affect that (and other children in the array) |
|
507 * If the first entry is a parent responses affect also children in the array. |
|
508 * |
|
509 * Also in case of cancellation entry is stored to db instead of just updating |
|
510 * the status, this means that e.g. description may be changed, but we |
|
511 * consider that is the desired behavior. |
|
512 */ |
|
513 void CMRProcessor::ProcessRequestOrCancelArrayL() |
|
514 { |
|
515 // 1. Handle entry at index 0 ,it may be a parent or a child |
|
516 |
|
517 CCalEntry& entry = *( iArrayOfEntries[0] ); |
|
518 iCombinedScenData.iUtilsDBResult = iMRUtils.CheckEntryCondL( entry ); |
|
519 MatchScenarioAndSaveIfNeededL( entry, iCombinedScenData ); |
|
520 |
|
521 // 2. Go through additional entries, they don't affect combined entry |
|
522 // and related functionality |
|
523 |
|
524 TScenarioData tmpScenario( iCombinedScenData ); |
|
525 TInt count( iArrayOfEntries.Count() ); |
|
526 for ( TInt i( 1 ); i < count; ++i ) |
|
527 { |
|
528 CCalEntry& tmpEntry = *( iArrayOfEntries[i] ); |
|
529 tmpScenario.iUtilsDBResult = iMRUtils.CheckEntryCondL( tmpEntry ); |
|
530 MatchScenarioAndSaveIfNeededL( tmpEntry, tmpScenario ); |
|
531 } |
|
532 |
|
533 // 3. Set combined entry == first entry in the input array |
|
534 |
|
535 ResetCombinedEntryL( entry, entry.RecurrenceIdL() ); |
|
536 |
|
537 if ( iCombinedScenData.iMROperation == EViewExistingEntry ) |
|
538 { |
|
539 iCombinedEntry->CopyFromL( entry ); |
|
540 iCombinedEntry->SetMethodL( entry.MethodL() ); |
|
541 } |
|
542 else if ( iCombinedScenData.iMROperation == ELoadIdenticalEntryFromDB || |
|
543 iCombinedScenData.iMROperation ==EStoreEntryToCalendar ) |
|
544 { |
|
545 // Identical entry exists in db -> fetch parent entry |
|
546 // from db and prepare for viewing |
|
547 ReadEntryFromDbL( entry, *iCombinedEntry ); |
|
548 } |
|
549 else |
|
550 { |
|
551 User::Leave( KErrArgument ); |
|
552 } |
|
553 } |
|
554 |
|
555 TBool CMRProcessor::MatchScenarioAndSaveIfNeededL( |
|
556 CCalEntry& aCalEntry, |
|
557 TScenarioData& aScenarioData ) const |
|
558 { |
|
559 TBool retVal( EFalse ); |
|
560 aScenarioData.iMROperation = |
|
561 MatchScenarioToDataL( aScenarioData, aCalEntry ); |
|
562 |
|
563 if ( aScenarioData.iMROperation == EStoreEntryToCalendar ) |
|
564 { |
|
565 MMRUtilsTombsExt::TMRUtilsDbResult dbResult = |
|
566 iMRUtils.StoreL( aCalEntry, EFalse ); |
|
567 if ( dbResult < MMRUtilsTombsExt::EUndefined ) |
|
568 { // store shouldn't fail if all checks were successful |
|
569 User::Leave( KErrGeneral ); |
|
570 } |
|
571 else |
|
572 { // we have saved something |
|
573 retVal = ETrue; |
|
574 } |
|
575 } |
|
576 else if ( aScenarioData.iMROperation == EUpdateStatusToCalendar ) |
|
577 { // update shouldn't fail if all checks were successful |
|
578 User::LeaveIfError( iMRUtils.UpdateEntryL( aCalEntry ) ); |
|
579 retVal = ETrue; |
|
580 } |
|
581 return retVal; |
|
582 } |
|
583 |
|
584 void CMRProcessor::ProcessErrorL() |
|
585 { |
|
586 switch ( iCombinedScenData.iMROperation ) |
|
587 { |
|
588 case EErrorSituation: |
|
589 { |
|
590 if ( iCombinedScenData.iUtilsDBResult == |
|
591 MMRUtilsTombsExt::EErrorHasBeenDeleted ) |
|
592 { // possibly valid entry but has been deleted from the phone |
|
593 |
|
594 // request has been deleted, we show the received response then |
|
595 // according to UI spec. if organizer cancels request it gets deleted |
|
596 // TODO: show info note Original meeting request is not in phone’s calendar. §qtn.mail.mtg.req.note.no.mtg§ with |
|
597 // TODO: this note should be shown always as an opening note, some opening notes |
|
598 // are shown only when launching for the first time -> handle that! |
|
599 } |
|
600 else |
|
601 {// TODO: handle |
|
602 } |
|
603 break; |
|
604 } |
|
605 case EErrorUnexpectedViewOnly: |
|
606 { // possibly valid entry but unexpected in this context |
|
607 // TODO: handle |
|
608 break; |
|
609 } |
|
610 case EErrorObsoleteViewOnly: |
|
611 { // possibly valid entry but obsolete in this context |
|
612 // TODO: handle |
|
613 break; |
|
614 } |
|
615 default: |
|
616 { |
|
617 User::Leave( KErrGeneral ); |
|
618 } |
|
619 } |
|
620 // show errorneus entry, but only for viewing |
|
621 CCalEntry& entry = *( iArrayOfEntries[0] ); |
|
622 ResetCombinedEntryL( entry, entry.RecurrenceIdL() ); |
|
623 iCombinedEntry->CopyFromL( entry ); |
|
624 iCombinedEntry->SetMethodL( entry.MethodL() ); |
|
625 } |
|
626 |
|
627 TInt CMRProcessor::MatchScenarioToDataL( |
|
628 TScenarioData currentScenario, |
|
629 const CCalEntry& aEntry ) const |
|
630 { |
|
631 MRDATA_LOG("# start scenario match #"); |
|
632 MRDATA_LOG1("entry name: %S", &(aEntry.SummaryL())); |
|
633 |
|
634 TInt retVal( EErrorSituation ); |
|
635 |
|
636 CArrayFixFlat<TInt>* scenarioValues = |
|
637 new( ELeave ) CArrayFixFlat<TInt>( dataFieldCount-1 ); |
|
638 |
|
639 CleanupStack::PushL( scenarioValues ); |
|
640 |
|
641 // Order of fields must match the order used in ProcessingStructs.h |
|
642 scenarioValues->AppendL( currentScenario.iMethod ); |
|
643 scenarioValues->AppendL( currentScenario.iCallingApp ); |
|
644 scenarioValues->AppendL( currentScenario.iOpenedMailbox ); |
|
645 scenarioValues->AppendL( currentScenario.iUtilsDBResult ); |
|
646 scenarioValues->AppendL( currentScenario.iMethodLevelValidity ); |
|
647 scenarioValues->AppendL( currentScenario.iEditorMode ); |
|
648 |
|
649 // Go through each predefined scenario and see if one of them matches |
|
650 for ( TInt scenarioCounter( 0 ); |
|
651 scenarioCounter < scenarioCount; |
|
652 scenarioCounter++ ) |
|
653 { |
|
654 TBool scenarioMismatch( EFalse ); |
|
655 const TInt* arrayPtr = scenarioArray[scenarioCounter]; |
|
656 for ( TInt i( 0 ); i < dataFieldCount-1; i++ ) |
|
657 { |
|
658 TInt arrayVal = *( arrayPtr+i ); |
|
659 TInt scenarioVal = scenarioValues->At( i ); |
|
660 |
|
661 if( arrayVal != ENotSet ) |
|
662 { |
|
663 if ( scenarioVal != arrayVal ) |
|
664 { |
|
665 scenarioMismatch = ETrue; |
|
666 } |
|
667 } |
|
668 } |
|
669 |
|
670 if ( !scenarioMismatch ) |
|
671 { // Match was found, retVal is operation corresponding to the |
|
672 // matching scenario, i.e. the last field in that scenario array |
|
673 retVal = *( arrayPtr + dataFieldCount - 1 ); |
|
674 MRDATA_LOG1("scenario id: %d", scenarioCounter ); |
|
675 MRDATA_LOG1("operation: %d", retVal ); |
|
676 break; |
|
677 } |
|
678 } |
|
679 |
|
680 CleanupStack::PopAndDestroy( scenarioValues ); |
|
681 |
|
682 MRDATA_LOG("# end scenario match #"); |
|
683 return retVal; |
|
684 } |
|
685 |
|
686 TInt CMRProcessor::CallingApp() const |
|
687 { |
|
688 TInt callingApp = iEntryUiInParams.iCallingApp.iUid; |
|
689 TInt retVal( ENotSet ); |
|
690 |
|
691 switch ( callingApp ) |
|
692 { |
|
693 case KUidCalendarApplication: |
|
694 { |
|
695 retVal = ECallerCalendarApp; |
|
696 break; |
|
697 } |
|
698 case KUidMailApplication: |
|
699 { |
|
700 retVal = ECallerMailApp; |
|
701 break; |
|
702 } |
|
703 case KUidBVAApplication: |
|
704 { |
|
705 retVal = ECallerBVApp; |
|
706 break; |
|
707 } |
|
708 default: |
|
709 { |
|
710 //error situation, should never come here |
|
711 retVal = ENotSet; |
|
712 break; |
|
713 } |
|
714 } |
|
715 return retVal; |
|
716 } |
|
717 |
|
718 TInt CMRProcessor::EditorMode() const |
|
719 { |
|
720 if ( iEntryUiInParams.iCallingApp.iUid == KUidCalendarApplication ) |
|
721 { // Editor mode is relevant only when calling app is calendar |
|
722 return iEntryUiInParams.iEditorMode; |
|
723 } |
|
724 else |
|
725 { |
|
726 return MAgnEntryUi::EViewEntry; |
|
727 } |
|
728 } |
|
729 |
|
730 // TODO: should we also support drafts mailbox type? |
|
731 // That should be done by checking IsSent( entry ) in the outbox case. |
|
732 TInt CMRProcessor::OpenedMailboxL( const CCalEntry& aEntry ) const |
|
733 { |
|
734 TInt retVal( ENotSet ); |
|
735 if ( iEntryUiInParams.iCallingApp.iUid == KUidMailApplication ) |
|
736 { |
|
737 // Organizer sends other types than responses, and attendees |
|
738 // on the other hand send only responses |
|
739 TBool isOrganizer( iMRMailboxUtils.IsOrganizerL( aEntry ) ); |
|
740 TBool isResponse( aEntry.MethodL() == CCalEntry::EMethodReply ); |
|
741 if ( ( isOrganizer && !isResponse ) || ( !isOrganizer && isResponse ) ) |
|
742 { |
|
743 retVal = EOpenedFromOutbox; |
|
744 } |
|
745 else |
|
746 { |
|
747 retVal = EOpenedFromInbox; |
|
748 } |
|
749 } |
|
750 return retVal; |
|
751 } |
|
752 |
|
753 CCalAttendee* CMRProcessor::RespondentInRequestL( |
|
754 const CCalEntry& aResponse, |
|
755 const CCalEntry& aRequest, |
|
756 TBool& aStatusChange ) const |
|
757 { |
|
758 __ASSERT_DEBUG( aResponse.MethodL() == CCalEntry::EMethodReply, |
|
759 Panic( EUnexpectedEntryMethodType ) ); |
|
760 |
|
761 aStatusChange = EFalse; |
|
762 CCalAttendee* respondent = aResponse.AttendeesL()[0]; |
|
763 CCalAttendee* dbAttendee = |
|
764 MREntryConsultant::EqualAttendeeL( *respondent, aRequest ); |
|
765 if ( dbAttendee ) |
|
766 { |
|
767 if ( dbAttendee->StatusL() != respondent->StatusL() ) |
|
768 { |
|
769 aStatusChange = ETrue; |
|
770 } |
|
771 } |
|
772 return dbAttendee; |
|
773 } |
|
774 |
|
775 void CMRProcessor::ReadEntryFromDbL( |
|
776 const CCalEntry& aSourceEntry, |
|
777 CCalEntry& aTargetEntry ) const |
|
778 { |
|
779 CCalEntry* dbEntry = iMRUtils.FetchEntryL( aSourceEntry.UidL(), |
|
780 aSourceEntry.RecurrenceIdL() ); |
|
781 CleanupStack::PushL( dbEntry ); |
|
782 aTargetEntry.CopyFromL( *dbEntry ); |
|
783 aTargetEntry.SetMethodL( dbEntry->MethodL() ); |
|
784 |
|
785 CleanupStack::PopAndDestroy( dbEntry ); |
|
786 } |
|
787 |
|
788 // method to be called externally |
|
789 void CMRProcessor::RefreshViewableEntryL() |
|
790 { |
|
791 __ASSERT_DEBUG( iCombinedEntry, Panic( ECombinedCalEntryNull ) ); |
|
792 if ( MREntryConsultant::ExistsInDbL( *iCombinedEntry, iMRUtils ) ) |
|
793 { // this method is only feasible if entry exists in the database, |
|
794 // otherwise we cannot refresh anything... |
|
795 ResetCombinedEntryL( *iCombinedEntry, iCombinedEntry->RecurrenceIdL() ); |
|
796 ReadEntryFromDbL( *iCombinedEntry, *iCombinedEntry ); |
|
797 } |
|
798 } |
|
799 |
|
800 void CMRProcessor::ResetCombinedEntryL( |
|
801 const CCalEntry& aBase, |
|
802 const TCalTime& aInstanceDate ) |
|
803 { |
|
804 HBufC8* calUid = aBase.UidL().AllocLC(); |
|
805 CCalEntry* newEntry; |
|
806 if ( aInstanceDate.TimeUtcL() != Time::NullTTime() ) |
|
807 { |
|
808 newEntry = CCalEntry::NewL( CCalEntry::EAppt, |
|
809 calUid, |
|
810 aBase.MethodL(), |
|
811 aBase.SequenceNumberL(), |
|
812 aInstanceDate, |
|
813 CalCommon::EThisOnly ); |
|
814 } |
|
815 else |
|
816 { |
|
817 newEntry = CCalEntry::NewL( CCalEntry::EAppt, |
|
818 calUid, |
|
819 aBase.MethodL(), |
|
820 aBase.SequenceNumberL() ); |
|
821 } |
|
822 delete iCombinedEntry; |
|
823 iCombinedEntry = NULL; |
|
824 iCombinedEntry = newEntry; |
|
825 CleanupStack::Pop( calUid ); // ownership transferred to iCombinedEntry |
|
826 } |
|
827 |
|
828 void CMRProcessor::SetInstanceStartAndEndL( |
|
829 CCalEntry& aChild, |
|
830 const CCalEntry& aParent, |
|
831 const TCalTime& aInstanceStart ) const |
|
832 { |
|
833 TTime dtstart( aParent.StartTimeL().TimeUtcL() ); // first instance start |
|
834 TTime dtend( aParent.EndTimeL().TimeUtcL() ); // first instance end |
|
835 TTime instStart( aInstanceStart.TimeUtcL() ); |
|
836 TTime nullTime( Time::NullTTime() ); |
|
837 if ( dtstart == nullTime || dtend == nullTime || instStart == nullTime ) |
|
838 { |
|
839 User::Leave( KErrArgument ); |
|
840 } |
|
841 |
|
842 TTimeIntervalMicroSeconds duration( 0 ); |
|
843 duration = dtend.MicroSecondsFrom( dtstart ); |
|
844 TCalTime instEnd; |
|
845 instEnd.SetTimeUtcL( instStart + duration ); |
|
846 |
|
847 aChild.SetStartAndEndTimeL( aInstanceStart, instEnd ); |
|
848 } |
|
849 |