public static void SyncAppointment(AppointmentMatch match, Synchronizer sync) { if (match.GoogleAppointment == null && match.OutlookAppointment != null) { //no Google appointment string googleAppointmentId = AppointmentPropertiesUtils.GetOutlookGoogleAppointmentId(sync, match.OutlookAppointment); if (!string.IsNullOrEmpty(googleAppointmentId)) { //if (match.OutlookAppointment.IsRecurring && match.OutlookAppointment.RecurrenceState == Outlook.OlRecurrenceState.olApptMaster && // (Syncronizer.MonthsInPast == 0 || new DateTime(DateTime.Now.AddMonths(-Syncronizer.MonthsInPast).Year, match.OutlookAppointment.End.Month, match.OutlookAppointment.End.Day) >= DateTime.Now.AddMonths(-Syncronizer.MonthsInPast)) && // (Syncronizer.MonthsInFuture == 0 || new DateTime(DateTime.Now.AddMonths(-Syncronizer.MonthsInPast).Year, match.OutlookAppointment.Start.Month, match.OutlookAppointment.Start.Day) <= DateTime.Now.AddMonths(Syncronizer.MonthsInFuture)) // || // (Syncronizer.MonthsInPast == 0 || match.OutlookAppointment.End >= DateTime.Now.AddMonths(-Syncronizer.MonthsInPast)) && // (Syncronizer.MonthsInFuture == 0 || match.OutlookAppointment.Start <= DateTime.Now.AddMonths(Syncronizer.MonthsInFuture)) // ) //{ //Redundant check if exist, but in case an error occurred in MatchAppointments or not all appointments have been loaded (e.g. because months before/after constraint) Event matchingGoogleAppointment = null; if (sync.AllGoogleAppointments != null) { matchingGoogleAppointment = sync.GetGoogleAppointmentById(googleAppointmentId); } else { matchingGoogleAppointment = sync.LoadGoogleAppointments(googleAppointmentId, 0, 0, null, null); } if (matchingGoogleAppointment == null) { if (sync.SyncOption == SyncOption.OutlookToGoogleOnly || !sync.SyncDelete) { return; } else if (!sync.PromptDelete && match.OutlookAppointment.Recipients.Count == 0) { sync.DeleteOutlookResolution = DeleteResolution.DeleteOutlookAlways; } else if (sync.DeleteOutlookResolution != DeleteResolution.DeleteOutlookAlways && sync.DeleteOutlookResolution != DeleteResolution.KeepOutlookAlways) { var r = new ConflictResolver(); sync.DeleteOutlookResolution = r.ResolveDelete(match.OutlookAppointment); } switch (sync.DeleteOutlookResolution) { case DeleteResolution.KeepOutlook: case DeleteResolution.KeepOutlookAlways: AppointmentPropertiesUtils.ResetOutlookGoogleAppointmentId(sync, match.OutlookAppointment); break; case DeleteResolution.DeleteOutlook: case DeleteResolution.DeleteOutlookAlways: if (match.OutlookAppointment.Recipients.Count > 1) { //ToDo:Maybe find as better way, e.g. to ask the user, if he wants to overwrite the invalid appointment Logger.Log("Outlook Appointment not deleted, because multiple participants found, invitation maybe NOT sent by Google: " + match.OutlookAppointment.Subject + " - " + match.OutlookAppointment.Start, EventType.Information); AppointmentPropertiesUtils.ResetOutlookGoogleAppointmentId(sync, match.OutlookAppointment); break; } else { //Avoid recreating a GoogleAppointment already existing //==> Delete this OutlookAppointment instead if previous match existed but no match exists anymore return; } default: throw new ApplicationException("Cancelled"); } } else { sync.SkippedCount++; match.GoogleAppointment = matchingGoogleAppointment; Logger.Log("Outlook Appointment not deleted, because still existing on Google side, maybe because months restriction: " + match.OutlookAppointment.Subject + " - " + match.OutlookAppointment.Start, EventType.Information); return; } } //} if (sync.SyncOption == SyncOption.GoogleToOutlookOnly) { sync.SkippedCount++; Logger.Log(string.Format("Outlook appointment not added to Google, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.OutlookAppointment.Subject), EventType.Information); return; } //create a Google appointment from Outlook appointment match.GoogleAppointment = Factory.NewEvent(); sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); } else if (match.OutlookAppointment == null && match.GoogleAppointment != null) { //no Outlook appointment string outlookAppointmenttId = AppointmentPropertiesUtils.GetGoogleOutlookAppointmentId(sync.SyncProfile, match.GoogleAppointment); if (!string.IsNullOrEmpty(outlookAppointmenttId)) { if (sync.SyncOption == SyncOption.GoogleToOutlookOnly || !sync.SyncDelete) { return; } else if (!sync.PromptDelete) { sync.DeleteGoogleResolution = DeleteResolution.DeleteGoogleAlways; } else if (sync.DeleteGoogleResolution != DeleteResolution.DeleteGoogleAlways && sync.DeleteGoogleResolution != DeleteResolution.KeepGoogleAlways) { var r = new ConflictResolver(); sync.DeleteGoogleResolution = r.ResolveDelete(match.GoogleAppointment); } switch (sync.DeleteGoogleResolution) { case DeleteResolution.KeepGoogle: case DeleteResolution.KeepGoogleAlways: AppointmentPropertiesUtils.ResetGoogleOutlookAppointmentId(sync.SyncProfile, match.GoogleAppointment); break; case DeleteResolution.DeleteGoogle: case DeleteResolution.DeleteGoogleAlways: //Avoid recreating a OutlookAppointment already existing //==> Delete this googleAppointment instead if previous match existed but no match exists anymore return; default: throw new ApplicationException("Cancelled"); } } if (sync.SyncOption == SyncOption.OutlookToGoogleOnly) { sync.SkippedCount++; Logger.Log(string.Format("Google appointment not added to Outlook, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.GoogleAppointment.Summary), EventType.Information); return; } //create a Outlook appointment from Google appointment match.OutlookAppointment = Synchronizer.CreateOutlookAppointmentItem(Synchronizer.SyncAppointmentsFolder); sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); } else if (match.OutlookAppointment != null && match.GoogleAppointment != null) { //ToDo: Check how to overcome appointment recurrences, which need more than 60 seconds to update and therefore get updated again and again because of time tolerance 60 seconds violated again and again //merge appointment details //determine if this appointment pair were synchronized //DateTime? lastUpdated = GetOutlookPropertyValueDateTime(match.OutlookAppointment, sync.OutlookPropertyNameUpdated); DateTime?lastSynced = AppointmentPropertiesUtils.GetOutlookLastSync(sync, match.OutlookAppointment); if (lastSynced.HasValue) { //appointment pair was syncronysed before. //determine if Google appointment was updated since last sync //lastSynced is stored without seconds. take that into account. DateTime lastUpdatedOutlook = match.OutlookAppointment.LastModificationTime.AddSeconds(-match.OutlookAppointment.LastModificationTime.Second); DateTime lastUpdatedGoogle = match.GoogleAppointment.Updated.Value.AddSeconds(-match.GoogleAppointment.Updated.Value.Second); //consider GoogleAppointmentExceptions, because if they are updated, the master appointment doesn't have a new Saved TimeStamp foreach (Event googleAppointment in match.GoogleAppointmentExceptions) { if (googleAppointment.Updated != null)//happens for cancelled events { DateTime lastUpdatedGoogleException = googleAppointment.Updated.Value.AddSeconds(-googleAppointment.Updated.Value.Second); if (lastUpdatedGoogleException > lastUpdatedGoogle) { lastUpdatedGoogle = lastUpdatedGoogleException; } } else if (match.OutlookAppointment.IsRecurring && match.OutlookAppointment.RecurrenceState == Outlook.OlRecurrenceState.olApptMaster) { Outlook.AppointmentItem outlookRecurrenceException = null; try { var slaveRecurrence = match.OutlookAppointment.GetRecurrencePattern(); if (googleAppointment.OriginalStartTime != null && !string.IsNullOrEmpty(googleAppointment.OriginalStartTime.Date)) { outlookRecurrenceException = slaveRecurrence.GetOccurrence(DateTime.Parse(googleAppointment.OriginalStartTime.Date)); } else if (googleAppointment.OriginalStartTime != null && googleAppointment.OriginalStartTime.DateTime != null) { outlookRecurrenceException = slaveRecurrence.GetOccurrence(googleAppointment.OriginalStartTime.DateTime.Value); } } catch (Exception ignored) { Logger.Log("Google Appointment with OriginalEvent found, but Outlook occurrence not found: " + googleAppointment.Summary + " - " + googleAppointment.OriginalStartTime.DateTime + ": " + ignored, EventType.Debug); } if (outlookRecurrenceException != null && outlookRecurrenceException.MeetingStatus != Outlook.OlMeetingStatus.olMeetingCanceled) { lastUpdatedGoogle = DateTime.Now; break; //no need to search further, already newest date set } } } //check if both outlok and Google appointments where updated sync last sync if ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && (int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance) { //both appointments were updated. //options: 1) ignore 2) loose one based on SyncOption //throw new Exception("Both appointments were updated!"); switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite Google appointment Logger.Log("Outlook and Google appointment have been updated, Outlook appointment is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookAppointment.Subject + ".", EventType.Information); sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook appointment Logger.Log("Outlook and Google appointment have been updated, Google appointment is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.GoogleAppointment.Summary + ".", EventType.Information); sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); sync.ConflictResolution = r.Resolve(match.OutlookAppointment, match.GoogleAppointment, sync, false); } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: Logger.Log(string.Format("User skipped appointment ({0}).", match.ToString()), EventType.Information); sync.SkippedCount++; break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); break; default: throw new ApplicationException("Cancelled"); } break; } return; } //check if Outlook appointment was updated (with X second tolerance) if (sync.SyncOption != SyncOption.GoogleToOutlookOnly && ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || (int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly ) ) { //Outlook appointment was changed or changed Google appointment will be overwritten if ((int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly) { Logger.Log("Google appointment has been updated since last sync, but Outlook appointment is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookAppointment.Subject + ".", EventType.Information); } sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); //at the moment use Outlook as "master" source of appointments - in the event of a conflict Google appointment will be overwritten. //TODO: control conflict resolution by SyncOption return; } //check if Google appointment was updated (with X second tolerance) if (sync.SyncOption != SyncOption.OutlookToGoogleOnly && ((int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || (int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly ) ) { //google appointment was changed or changed Outlook appointment will be overwritten if ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly) { Logger.Log("Outlook appointment has been updated since last sync, but Google appointment is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookAppointment.Subject + ".", EventType.Information); } sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); } } else { //appointments were never synced. //merge appointments. switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite Google appointment sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook appointment sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); sync.ConflictResolution = r.Resolve(match.OutlookAppointment, match.GoogleAppointment, sync, true); } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: //Keep both, Google AND Outlook sync.Appointments.Add(new AppointmentMatch(match.OutlookAppointment, null)); sync.Appointments.Add(new AppointmentMatch(null, match.GoogleAppointment)); break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); break; default: throw new ApplicationException("Canceled"); } break; } } } else { throw new ArgumentNullException("AppointmenttMatch has all peers null."); } //} //finally //{ //if (outlookAppointment != null && // match.OutlookAppointment != null) //{ // match.OutlookAppointment.Update(outlookAppointment, sync); // Marshal.ReleaseComObject(outlookAppointment); // outlookAppointment = null; //} //} }
public static void SyncContact(ContactMatch match, Synchronizer sync) { Outlook.ContactItem outlookContactItem = match.OutlookContact != null ? match.OutlookContact.GetOriginalItemFromOutlook() : null; try { if (match.GoogleContact == null && match.OutlookContact != null) { //no google contact string googleContactId = match.OutlookContact.UserProperties.GoogleContactId; if (!string.IsNullOrEmpty(googleContactId)) { //Redundant check if exist, but in case an error occurred in MatchContacts Contact matchingGoogleContact = sync.GetGoogleContactById(googleContactId); if (matchingGoogleContact == null) { if (sync.SyncOption == SyncOption.OutlookToGoogleOnly || !sync.SyncDelete) return; else if (!sync.PromptDelete) sync.DeleteOutlookResolution = DeleteResolution.DeleteOutlookAlways; else if (sync.DeleteOutlookResolution != DeleteResolution.DeleteOutlookAlways && sync.DeleteOutlookResolution != DeleteResolution.KeepOutlookAlways) { var r = new ConflictResolver(); sync.DeleteOutlookResolution = r.ResolveDelete(match.OutlookContact); } switch (sync.DeleteOutlookResolution) { case DeleteResolution.KeepOutlook: case DeleteResolution.KeepOutlookAlways: ContactPropertiesUtils.ResetOutlookGoogleContactId(sync, outlookContactItem); break; case DeleteResolution.DeleteOutlook: case DeleteResolution.DeleteOutlookAlways: //Avoid recreating a GoogleContact already existing //==> Delete this outlookContact instead if previous match existed but no match exists anymore return; default: throw new ApplicationException("Cancelled"); } } } if (sync.SyncOption == SyncOption.GoogleToOutlookOnly) { sync.SkippedCount++; Logger.Log(string.Format("Outlook Contact not added to Google, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.OutlookContact.FileAs), EventType.Information); return; } //create a Google contact from Outlook contact match.GoogleContact = new Contact(); sync.UpdateContact(outlookContactItem, match.GoogleContact); } else if (match.OutlookContact == null && match.GoogleContact != null) { // no outlook contact string outlookId = ContactPropertiesUtils.GetGoogleOutlookContactId(sync.SyncProfile, match.GoogleContact); if (outlookId != null) { if (sync.SyncOption == SyncOption.GoogleToOutlookOnly || !sync.SyncDelete) return; else if (!sync.PromptDelete) sync.DeleteGoogleResolution = DeleteResolution.DeleteGoogleAlways; else if (sync.DeleteGoogleResolution != DeleteResolution.DeleteGoogleAlways && sync.DeleteGoogleResolution != DeleteResolution.KeepGoogleAlways) { var r = new ConflictResolver(); sync.DeleteGoogleResolution = r.ResolveDelete(match.GoogleContact); } switch (sync.DeleteGoogleResolution) { case DeleteResolution.KeepGoogle: case DeleteResolution.KeepGoogleAlways: ContactPropertiesUtils.ResetGoogleOutlookContactId(sync.SyncProfile, match.GoogleContact); break; case DeleteResolution.DeleteGoogle: case DeleteResolution.DeleteGoogleAlways: //Avoid recreating a OutlookContact already existing //==> Delete this googleContact instead if previous match existed but no match exists anymore return; default: throw new ApplicationException("Cancelled"); } } if (sync.SyncOption == SyncOption.OutlookToGoogleOnly) { sync.SkippedCount++; Logger.Log(string.Format("Google Contact not added to Outlook, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.GoogleContact.Title), EventType.Information); return; } //create a Outlook contact from Google contact outlookContactItem = Synchronizer.CreateOutlookContactItem(Synchronizer.SyncContactsFolder); sync.UpdateContact(match.GoogleContact, outlookContactItem); match.OutlookContact = new OutlookContactInfo(outlookContactItem, sync); } else if (match.OutlookContact != null && match.GoogleContact != null) { //merge contact details //determine if this contact pair were synchronized //DateTime? lastUpdated = GetOutlookPropertyValueDateTime(match.OutlookContact, sync.OutlookPropertyNameUpdated); DateTime? lastSynced = match.OutlookContact.UserProperties.LastSync; if (lastSynced.HasValue) { //contact pair was syncronysed before. //determine if google contact was updated since last sync //lastSynced is stored without seconds. take that into account. DateTime lastUpdatedOutlook = match.OutlookContact.LastModificationTime.AddSeconds(-match.OutlookContact.LastModificationTime.Second); DateTime lastUpdatedGoogle = match.GoogleContact.Updated.AddSeconds(-match.GoogleContact.Updated.Second); //check if both outlok and google contacts where updated sync last sync if (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance) { //both contacts were updated. //options: 1) ignore 2) loose one based on SyncOption //throw new Exception("Both contacts were updated!"); switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite google contact Logger.Log("Outlook and Google contact have been updated, Outlook contact is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookContact.FileAs + ".", EventType.Information); sync.UpdateContact(outlookContactItem, match.GoogleContact); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook contact Logger.Log("Outlook and Google contact have been updated, Google contact is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookContact.FileAs + ".", EventType.Information); sync.UpdateContact(match.GoogleContact, outlookContactItem); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); sync.ConflictResolution = r.Resolve(match, false); } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: Logger.Log(string.Format("User skipped contact ({0}).", match.ToString()), EventType.Information); sync.SkippedCount++; break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateContact(outlookContactItem, match.GoogleContact); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateContact(match.GoogleContact, outlookContactItem); break; default: throw new ApplicationException("Cancelled"); } break; } return; } //check if outlook contact was updated (with X second tolerance) if (sync.SyncOption != SyncOption.GoogleToOutlookOnly && (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly ) ) { //outlook contact was changed or changed Google contact will be overwritten if (lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly) Logger.Log("Google contact has been updated since last sync, but Outlook contact is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookContact.FileAs + ".", EventType.Information); sync.UpdateContact(outlookContactItem, match.GoogleContact); //at the moment use outlook as "master" source of contacts - in the event of a conflict google contact will be overwritten. //TODO: control conflict resolution by SyncOption return; } //check if google contact was updated (with X second tolerance) if (sync.SyncOption != SyncOption.OutlookToGoogleOnly && (lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly ) ) { //google contact was changed or changed Outlook contact will be overwritten if (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly) Logger.Log("Outlook contact has been updated since last sync, but Google contact is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookContact.FileAs + ".", EventType.Information); sync.UpdateContact(match.GoogleContact, outlookContactItem); } } else { //contacts were never synced. //merge contacts. switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite google contact sync.UpdateContact(outlookContactItem, match.GoogleContact); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook contact sync.UpdateContact(match.GoogleContact, outlookContactItem); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); sync.ConflictResolution = r.Resolve(match, true); } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: //Keep both, Google AND Outlook sync.Contacts.Add(new ContactMatch(match.OutlookContact, null)); sync.Contacts.Add(new ContactMatch(null, match.GoogleContact)); break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateContact(outlookContactItem, match.GoogleContact); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateContact(match.GoogleContact, outlookContactItem); break; default: throw new ApplicationException("Cancelled"); } break; } } } else throw new ArgumentNullException("ContactMatch has all peers null."); } catch (ArgumentNullException e) { throw e; } catch (Exception e) { throw new Exception("Error syncing contact " + (match.OutlookContact != null ? match.OutlookContact.FileAs : match.GoogleContact.Title) + ": " + e.Message, e); } finally { if (outlookContactItem != null && match.OutlookContact != null) { match.OutlookContact.Update(outlookContactItem, sync); Marshal.ReleaseComObject(outlookContactItem); outlookContactItem = null; } } }
private static void SyncAppointmentBothExists(AppointmentMatch m, AppointmentsSynchronizer sync) { //ToDo: Check how to overcome appointment recurrences, which need more than 60 seconds to update and therefore get updated again and again because of time tolerance 60 seconds violated again and again //merge appointment details //determine if this appointment pair were synchronized //DateTime? lastUpdated = GetOutlookPropertyValueDateTime(match.OutlookAppointment, sync.OutlookPropertyNameUpdated); DateTime?lastSynced = AppointmentPropertiesUtils.GetOutlookLastSync(sync, m.OutlookAppointment); if (lastSynced.HasValue) { //appointment pair was syncronysed before. //determine if Google appointment was updated since last sync //lastSynced is stored without seconds. take that into account. DateTime lastUpdatedOutlook = m.OutlookAppointment.LastModificationTime.AddSeconds(-m.OutlookAppointment.LastModificationTime.Second); DateTime lastUpdatedGoogle = m.GoogleAppointment.Updated.Value.AddSeconds(-m.GoogleAppointment.Updated.Value.Second); //consider GoogleAppointmentExceptions, because if they are updated, the master appointment doesn't have a new Saved TimeStamp foreach (var e in m.GoogleAppointmentExceptions) { if (e.Updated != null)//happens for cancelled events { DateTime lastUpdatedGoogleException = e.Updated.Value.AddSeconds(-e.Updated.Value.Second); if (lastUpdatedGoogleException > lastUpdatedGoogle) { lastUpdatedGoogle = lastUpdatedGoogleException; } } else if (m.OutlookAppointment.IsRecurring && m.OutlookAppointment.RecurrenceState == Outlook.OlRecurrenceState.olApptMaster) { Outlook.AppointmentItem outlookRecurrenceException = null; try { var slaveRecurrence = m.OutlookAppointment.GetRecurrencePattern(); if (e.OriginalStartTime != null && !string.IsNullOrEmpty(e.OriginalStartTime.Date)) { outlookRecurrenceException = slaveRecurrence.GetOccurrence(DateTime.Parse(e.OriginalStartTime.Date)); } else if (e.OriginalStartTime != null && e.OriginalStartTime.DateTime != null) { outlookRecurrenceException = slaveRecurrence.GetOccurrence(e.OriginalStartTime.DateTime.Value); } } catch (Exception ignored) { Logger.Log("Google Appointment with OriginalEvent found, but Outlook occurrence not found: " + e.Summary + " - " + e.OriginalStartTime.DateTime + ": " + ignored, EventType.Debug); } if (outlookRecurrenceException != null && outlookRecurrenceException.MeetingStatus != Outlook.OlMeetingStatus.olMeetingCanceled) { lastUpdatedGoogle = DateTime.Now; break; //no need to search further, already newest date set } } } //check if both outlok and Google appointments where updated sync last sync if ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && (int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance) { //both appointments were updated. //options: 1) ignore 2) loose one based on SyncOption //throw new Exception("Both appointments were updated!"); switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite Google appointment Logger.Log("Outlook and Google appointment have been updated, Outlook appointment is overwriting Google because of SyncOption " + sync.SyncOption + ": " + m.OutlookAppointment.Subject + ".", EventType.Information); sync.UpdateAppointment(m.OutlookAppointment, ref m.GoogleAppointment); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook appointment Logger.Log("Outlook and Google appointment have been updated, Google appointment is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + m.GoogleAppointment.Summary + ".", EventType.Information); sync.UpdateAppointment(ref m.GoogleAppointment, m.OutlookAppointment, m.GoogleAppointmentExceptions); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { using (var r = new ConflictResolver()) { sync.ConflictResolution = r.Resolve(m.OutlookAppointment, m.GoogleAppointment, sync, false); } } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: Logger.Log(string.Format("User skipped appointment ({0}).", m.ToString()), EventType.Information); sync.SkippedCount++; break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateAppointment(m.OutlookAppointment, ref m.GoogleAppointment); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateAppointment(ref m.GoogleAppointment, m.OutlookAppointment, m.GoogleAppointmentExceptions); break; default: throw new ApplicationException("Cancelled"); } break; } return; } //check if Outlook appointment was updated (with X second tolerance) if (sync.SyncOption != SyncOption.GoogleToOutlookOnly && ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || (int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly ) ) { //Outlook appointment was changed or changed Google appointment will be overwritten if ((int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly) { Logger.Log("Google appointment has been updated since last sync, but Outlook appointment is overwriting Google because of SyncOption " + sync.SyncOption + ": " + m.OutlookAppointment.Subject + ".", EventType.Information); } sync.UpdateAppointment(m.OutlookAppointment, ref m.GoogleAppointment); //at the moment use Outlook as "master" source of appointments - in the event of a conflict Google appointment will be overwritten. //TODO: control conflict resolution by SyncOption return; } //check if Google appointment was updated (with X second tolerance) if (sync.SyncOption != SyncOption.OutlookToGoogleOnly && ((int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || (int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly ) ) { //google appointment was changed or changed Outlook appointment will be overwritten if ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly) { Logger.Log("Outlook appointment has been updated since last sync, but Google appointment is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + m.OutlookAppointment.Subject + ".", EventType.Information); } sync.UpdateAppointment(ref m.GoogleAppointment, m.OutlookAppointment, m.GoogleAppointmentExceptions); } } else { //appointments were never synced. //merge appointments. switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite Google appointment sync.UpdateAppointment(m.OutlookAppointment, ref m.GoogleAppointment); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook appointment sync.UpdateAppointment(ref m.GoogleAppointment, m.OutlookAppointment, m.GoogleAppointmentExceptions); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { using (var r = new ConflictResolver()) { sync.ConflictResolution = r.Resolve(m.OutlookAppointment, m.GoogleAppointment, sync, true); } } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: //Keep both, Google AND Outlook sync.Appointments.Add(new AppointmentMatch(m.OutlookAppointment, null)); sync.Appointments.Add(new AppointmentMatch(null, m.GoogleAppointment)); break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateAppointment(m.OutlookAppointment, ref m.GoogleAppointment); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateAppointment(ref m.GoogleAppointment, m.OutlookAppointment, m.GoogleAppointmentExceptions); break; default: throw new ApplicationException("Canceled"); } break; } } }
public static void SyncAppointment(AppointmentMatch match, Synchronizer sync) { if (match.GoogleAppointment == null && match.OutlookAppointment != null) { //no Google appointment string googleAppointmentId = AppointmentPropertiesUtils.GetOutlookGoogleAppointmentId(sync, match.OutlookAppointment); if (!string.IsNullOrEmpty(googleAppointmentId)) { //if (match.OutlookAppointment.IsRecurring && match.OutlookAppointment.RecurrenceState == Outlook.OlRecurrenceState.olApptMaster && // (Syncronizer.MonthsInPast == 0 || new DateTime(DateTime.Now.AddMonths(-Syncronizer.MonthsInPast).Year, match.OutlookAppointment.End.Month, match.OutlookAppointment.End.Day) >= DateTime.Now.AddMonths(-Syncronizer.MonthsInPast)) && // (Syncronizer.MonthsInFuture == 0 || new DateTime(DateTime.Now.AddMonths(-Syncronizer.MonthsInPast).Year, match.OutlookAppointment.Start.Month, match.OutlookAppointment.Start.Day) <= DateTime.Now.AddMonths(Syncronizer.MonthsInFuture)) // || // (Syncronizer.MonthsInPast == 0 || match.OutlookAppointment.End >= DateTime.Now.AddMonths(-Syncronizer.MonthsInPast)) && // (Syncronizer.MonthsInFuture == 0 || match.OutlookAppointment.Start <= DateTime.Now.AddMonths(Syncronizer.MonthsInFuture)) // ) //{ //Redundant check if exist, but in case an error occurred in MatchAppointments or not all appointments have been loaded (e.g. because months before/after constraint) Event matchingGoogleAppointment = null; if (sync.AllGoogleAppointments != null) matchingGoogleAppointment = sync.GetGoogleAppointmentById(googleAppointmentId); else matchingGoogleAppointment = sync.LoadGoogleAppointments(googleAppointmentId, 0, 0, null, null); if (matchingGoogleAppointment == null) { if (sync.SyncOption == SyncOption.OutlookToGoogleOnly || !sync.SyncDelete) return; else if (!sync.PromptDelete && match.OutlookAppointment.Recipients.Count == 0) sync.DeleteOutlookResolution = DeleteResolution.DeleteOutlookAlways; else if (sync.DeleteOutlookResolution != DeleteResolution.DeleteOutlookAlways && sync.DeleteOutlookResolution != DeleteResolution.KeepOutlookAlways) { var r = new ConflictResolver(); sync.DeleteOutlookResolution = r.ResolveDelete(match.OutlookAppointment); } switch (sync.DeleteOutlookResolution) { case DeleteResolution.KeepOutlook: case DeleteResolution.KeepOutlookAlways: AppointmentPropertiesUtils.ResetOutlookGoogleAppointmentId(sync, match.OutlookAppointment); break; case DeleteResolution.DeleteOutlook: case DeleteResolution.DeleteOutlookAlways: if (match.OutlookAppointment.Recipients.Count > 1) { //ToDo:Maybe find as better way, e.g. to ask the user, if he wants to overwrite the invalid appointment Logger.Log("Outlook Appointment not deleted, because multiple participants found, invitation maybe NOT sent by Google: " + match.OutlookAppointment.Subject + " - " + match.OutlookAppointment.Start, EventType.Information); AppointmentPropertiesUtils.ResetOutlookGoogleAppointmentId(sync, match.OutlookAppointment); break; } else //Avoid recreating a GoogleAppointment already existing //==> Delete this OutlookAppointment instead if previous match existed but no match exists anymore return; default: throw new ApplicationException("Cancelled"); } } else { sync.SkippedCount++; match.GoogleAppointment = matchingGoogleAppointment; Logger.Log("Outlook Appointment not deleted, because still existing on Google side, maybe because months restriction: " + match.OutlookAppointment.Subject + " - " + match.OutlookAppointment.Start, EventType.Information); return; } } //} if (sync.SyncOption == SyncOption.GoogleToOutlookOnly) { sync.SkippedCount++; Logger.Log(string.Format("Outlook appointment not added to Google, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.OutlookAppointment.Subject), EventType.Information); return; } //create a Google appointment from Outlook appointment match.GoogleAppointment = Factory.NewEvent(); sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); } else if (match.OutlookAppointment == null && match.GoogleAppointment != null) { //no Outlook appointment string outlookAppointmenttId = AppointmentPropertiesUtils.GetGoogleOutlookAppointmentId(sync.SyncProfile,match.GoogleAppointment); if (!string.IsNullOrEmpty(outlookAppointmenttId)) { if (sync.SyncOption == SyncOption.GoogleToOutlookOnly || !sync.SyncDelete) return; else if (!sync.PromptDelete) sync.DeleteGoogleResolution = DeleteResolution.DeleteGoogleAlways; else if (sync.DeleteGoogleResolution != DeleteResolution.DeleteGoogleAlways && sync.DeleteGoogleResolution != DeleteResolution.KeepGoogleAlways) { var r = new ConflictResolver(); sync.DeleteGoogleResolution = r.ResolveDelete(match.GoogleAppointment); } switch (sync.DeleteGoogleResolution) { case DeleteResolution.KeepGoogle: case DeleteResolution.KeepGoogleAlways: AppointmentPropertiesUtils.ResetGoogleOutlookAppointmentId(sync.SyncProfile,match.GoogleAppointment); break; case DeleteResolution.DeleteGoogle: case DeleteResolution.DeleteGoogleAlways: //Avoid recreating a OutlookAppointment already existing //==> Delete this googleAppointment instead if previous match existed but no match exists anymore return; default: throw new ApplicationException("Cancelled"); } } if (sync.SyncOption == SyncOption.OutlookToGoogleOnly) { sync.SkippedCount++; Logger.Log(string.Format("Google appointment not added to Outlook, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.GoogleAppointment.Summary), EventType.Information); return; } //create a Outlook appointment from Google appointment match.OutlookAppointment = Synchronizer.CreateOutlookAppointmentItem(Synchronizer.SyncAppointmentsFolder); sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); } else if (match.OutlookAppointment != null && match.GoogleAppointment != null) { //ToDo: Check how to overcome appointment recurrences, which need more than 60 seconds to update and therefore get updated again and again because of time tolerance 60 seconds violated again and again //merge appointment details //determine if this appointment pair were synchronized //DateTime? lastUpdated = GetOutlookPropertyValueDateTime(match.OutlookAppointment, sync.OutlookPropertyNameUpdated); DateTime? lastSynced = AppointmentPropertiesUtils.GetOutlookLastSync(sync, match.OutlookAppointment); if (lastSynced.HasValue) { //appointment pair was syncronysed before. //determine if Google appointment was updated since last sync //lastSynced is stored without seconds. take that into account. DateTime lastUpdatedOutlook = match.OutlookAppointment.LastModificationTime.AddSeconds(-match.OutlookAppointment.LastModificationTime.Second); DateTime lastUpdatedGoogle = match.GoogleAppointment.Updated.Value.AddSeconds(-match.GoogleAppointment.Updated.Value.Second); //consider GoogleAppointmentExceptions, because if they are updated, the master appointment doesn't have a new Saved TimeStamp foreach (Event googleAppointment in match.GoogleAppointmentExceptions) { if (googleAppointment.Updated != null)//happens for cancelled events { DateTime lastUpdatedGoogleException = googleAppointment.Updated.Value.AddSeconds(-googleAppointment.Updated.Value.Second); if (lastUpdatedGoogleException > lastUpdatedGoogle) lastUpdatedGoogle = lastUpdatedGoogleException; } else if (match.OutlookAppointment.IsRecurring && match.OutlookAppointment.RecurrenceState == Outlook.OlRecurrenceState.olApptMaster) { Outlook.AppointmentItem outlookRecurrenceException = null; try { var slaveRecurrence = match.OutlookAppointment.GetRecurrencePattern(); if (googleAppointment.OriginalStartTime != null && !string.IsNullOrEmpty(googleAppointment.OriginalStartTime.Date)) outlookRecurrenceException = slaveRecurrence.GetOccurrence(DateTime.Parse(googleAppointment.OriginalStartTime.Date)); else if (googleAppointment.OriginalStartTime != null && googleAppointment.OriginalStartTime.DateTime != null) outlookRecurrenceException = slaveRecurrence.GetOccurrence(googleAppointment.OriginalStartTime.DateTime.Value); } catch (Exception ignored) { Logger.Log("Google Appointment with OriginalEvent found, but Outlook occurrence not found: " + googleAppointment.Summary + " - " + googleAppointment.OriginalStartTime.DateTime + ": " + ignored, EventType.Debug); } if (outlookRecurrenceException != null && outlookRecurrenceException.MeetingStatus != Outlook.OlMeetingStatus.olMeetingCanceled) { lastUpdatedGoogle = DateTime.Now; break; //no need to search further, already newest date set } } } //check if both outlok and Google appointments where updated sync last sync if ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && (int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance) { //both appointments were updated. //options: 1) ignore 2) loose one based on SyncOption //throw new Exception("Both appointments were updated!"); switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite Google appointment Logger.Log("Outlook and Google appointment have been updated, Outlook appointment is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookAppointment.Subject + ".", EventType.Information); sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook appointment Logger.Log("Outlook and Google appointment have been updated, Google appointment is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.GoogleAppointment.Summary + ".", EventType.Information); sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); sync.ConflictResolution = r.Resolve(match.OutlookAppointment, match.GoogleAppointment, sync, false); } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: Logger.Log(string.Format("User skipped appointment ({0}).", match.ToString()), EventType.Information); sync.SkippedCount++; break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); break; default: throw new ApplicationException("Cancelled"); } break; } return; } //check if Outlook appointment was updated (with X second tolerance) if (sync.SyncOption != SyncOption.GoogleToOutlookOnly && ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || (int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly ) ) { //Outlook appointment was changed or changed Google appointment will be overwritten if ((int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly) Logger.Log("Google appointment has been updated since last sync, but Outlook appointment is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookAppointment.Subject + ".", EventType.Information); sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); //at the moment use Outlook as "master" source of appointments - in the event of a conflict Google appointment will be overwritten. //TODO: control conflict resolution by SyncOption return; } //check if Google appointment was updated (with X second tolerance) if (sync.SyncOption != SyncOption.OutlookToGoogleOnly && ((int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || (int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly ) ) { //google appointment was changed or changed Outlook appointment will be overwritten if ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly) Logger.Log("Outlook appointment has been updated since last sync, but Google appointment is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookAppointment.Subject + ".", EventType.Information); sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); } } else { //appointments were never synced. //merge appointments. switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite Google appointment sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook appointment sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); sync.ConflictResolution = r.Resolve(match.OutlookAppointment, match.GoogleAppointment, sync, true); } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: //Keep both, Google AND Outlook sync.Appointments.Add(new AppointmentMatch(match.OutlookAppointment, null)); sync.Appointments.Add(new AppointmentMatch(null, match.GoogleAppointment)); break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions); break; default: throw new ApplicationException("Canceled"); } break; } } } else throw new ArgumentNullException("AppointmenttMatch has all peers null."); //} //finally //{ //if (outlookAppointment != null && // match.OutlookAppointment != null) //{ // match.OutlookAppointment.Update(outlookAppointment, sync); // Marshal.ReleaseComObject(outlookAppointment); // outlookAppointment = null; //} //} }
/// <summary> /// Updates Outlook appointment from master to slave (including groups/categories) /// </summary> public bool UpdateAppointment(ref Google.Apis.Calendar.v3.Data.Event master, Outlook.AppointmentItem slave, List<Google.Apis.Calendar.v3.Data.Event> googleAppointmentExceptions) { //if (master.Participants.Count > 1) //{ // bool organizerIsGoogle = AppointmentSync.IsOrganizer(AppointmentSync.GetOrganizer(master)); // if (organizerIsGoogle || AppointmentPropertiesUtils.GetOutlookGoogleAppointmentId(this, slave) == null) // {//Only update, if this appointment was organized on Google side or freshly created during tis sync // updated = true; // } // else // { // //ToDo:Maybe find as better way, e.g. to ask the user, if he wants to overwrite the invalid appointment // SkippedCount++; // Logger.Log("Skipped Updating appointment from Google to Outlook because multiple participants found and invitations NOT sent by Google: \"" + master.Summary + " - " + Syncronizer.GetTime(master) + "\".", EventType.Information); // } //} //else // updated = true; bool updated = false; if (slave.Recipients.Count > 1 && AppointmentPropertiesUtils.GetOutlookGoogleAppointmentId(this, slave) != null) { //ToDo:Maybe find as better way, e.g. to ask the user, if he wants to overwrite the invalid appointment switch (this.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite Google appointment Logger.Log("Multiple participants found, invitation maybe NOT sent by Google. Outlook appointment is overwriting Google because of SyncOption " + SyncOption + ": " + master.Summary + " - " + Synchronizer.GetTime(master) + ". ", EventType.Information); UpdateAppointment(slave, ref master); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook appointment Logger.Log("Multiple participants found, invitation maybe NOT sent by Google, but Google appointment is overwriting Outlook because of SyncOption " + SyncOption + ": " + master.Summary + " - " + Synchronizer.GetTime(master) + ".", EventType.Information); updated = true; break; case SyncOption.MergePrompt: //promp for sync option if ( //ConflictResolution != ConflictResolution.GoogleWinsAlways && //Shouldn't be used, because Outlook seems to be the master of the appointment ConflictResolution != ConflictResolution.OutlookWinsAlways && ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); ConflictResolution = r.Resolve("Cannot update appointment from Google to Outlook because multiple participants found, invitation maybe NOT sent by Google: \"" + master.Summary + " - " + Synchronizer.GetTime(master) + "\". Do you want to update it back from Outlook to Google?", slave, master, this); } switch (ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: //Skip SkippedCount++; Logger.Log("Skipped Updating appointment from Google to Outlook because multiple participants found, invitation maybe NOT sent by Google: \"" + master.Summary + " - " + Synchronizer.GetTime(master) + "\".", EventType.Information); break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: //Keep Outlook and overwrite Google UpdateAppointment(slave, ref master); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: //Keep Google and overwrite Outlook updated = true; break; default: throw new ApplicationException("Cancelled"); } break; } //if (MessageBox.Show("Cannot update appointment from Google to Outlook because multiple participants found, invitation maybe NOT sent by Google: \"" + master.Summary + " - " + Syncronizer.GetTime(master) + "\". Do you want to update it back from Outlook to Google?", "Outlook appointment cannot be overwritten from Google", MessageBoxButtons.YesNo) == DialogResult.Yes) // UpdateAppointment(slave, ref master); //else // SkippedCount++; // Logger.Log("Skipped Updating appointment from Google to Outlook because multiple participants found, invitation maybe NOT sent by Google: \"" + master.Summary + " - " + Syncronizer.GetTime(master) + "\".", EventType.Information); } else //Only update, if invitation was not sent on Outlook side or freshly created during this sync updated = true; if (updated) { AppointmentSync.UpdateAppointment(master, slave); AppointmentPropertiesUtils.SetOutlookGoogleAppointmentId(this, slave, master); try { //Try to save 2 times, because sometimes the first save fails with a COMException (Outlook aborted) slave.Save(); } catch (Exception) { try { slave.Save(); } catch (COMException ex) { Logger.Log("Error saving Outlook appointment: \"" + master.Summary + " - " + GetTime(master) + "\".\n"+ex.StackTrace, EventType.Warning); return false; } } if (master.Creator == null || AppointmentSync.IsOrganizer(master.Creator.Email)) { //only update Google, if I am the organizer, otherwise an error will be thrown AppointmentPropertiesUtils.SetGoogleOutlookAppointmentId(SyncProfile, master, slave); master = SaveGoogleAppointment(master); } SyncedCount++; Logger.Log("Updated appointment from Google to Outlook: \"" + master.Summary + " - " + GetTime(master) + "\".", EventType.Information); //After saving Outlook Appointment => also sync recurrence exceptions and increase SyncCount if (master.Recurrence != null && googleAppointmentExceptions != null && AppointmentSync.UpdateRecurrenceExceptions(googleAppointmentExceptions, slave, this)) SyncedCount++; } return true; }
/// <summary> /// Updates Outlook appointment from master to slave (including groups/categories) /// </summary> public void UpdateAppointment(Outlook.AppointmentItem master, ref Google.Apis.Calendar.v3.Data.Event slave) { bool updated = false; if (slave.Creator != null && !AppointmentSync.IsOrganizer(slave.Creator.Email)) // && AppointmentPropertiesUtils.GetGoogleOutlookAppointmentId(this.SyncProfile, slave) != null) { //ToDo:Maybe find as better way, e.g. to ask the user, if he wants to overwrite the invalid appointment switch (this.SyncOption) { case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite Outlook appointment Logger.Log("Different Organizer found on Google, invitation maybe NOT sent by Outlook. Google appointment is overwriting Outlook because of SyncOption " + SyncOption + ": " + master.Subject + " - " + master.Start + ". ", EventType.Information); UpdateAppointment(ref slave, master, null); break; case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite Google appointment Logger.Log("Different Organizer found on Google, invitation maybe NOT sent by Outlook, but Outlook appointment is overwriting Google because of SyncOption " + SyncOption + ": " + master.Subject + " - " + master.Start + ".", EventType.Information); updated = true; break; case SyncOption.MergePrompt: //promp for sync option if ( //ConflictResolution != ConflictResolution.OutlookWinsAlways && //Shouldn't be used, because Google seems to be the master of the appointment ConflictResolution != ConflictResolution.GoogleWinsAlways && ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); ConflictResolution = r.Resolve("Cannot update appointment from Outlook to Google because different Organizer found on Google, invitation maybe NOT sent by Outlook: \"" + master.Subject + " - " + master.Start + "\". Do you want to update it back from Google to Outlook?", slave, master, this); } switch (ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: //Skip SkippedCount++; Logger.Log("Skipped Updating appointment from Outlook to Google because different Organizer found on Google, invitation maybe NOT sent by Outlook: \"" + master.Subject + " - " + master.Start + "\".", EventType.Information); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: //Keep Google and overwrite Outlook UpdateAppointment(ref slave, master, null); break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: //Keep Outlook and overwrite Google updated = true; break; default: throw new ApplicationException("Cancelled"); } break; } } else //Only update, if invitation was not sent on Google side or freshly created during this sync updated = true; //if (master.Recipients.Count == 0 || // master.Organizer == null || // AppointmentSync.IsOrganizer(AppointmentSync.GetOrganizer(master), master)|| // slave.Id.Uri == null // ) //{//Only update, if this appointment was organized on Outlook side or freshly created during this sync if (updated) { AppointmentSync.UpdateAppointment(master, slave); if (slave.Creator == null || AppointmentSync.IsOrganizer(slave.Creator.Email)) { AppointmentPropertiesUtils.SetGoogleOutlookAppointmentId(SyncProfile, slave, master); slave = SaveGoogleAppointment(slave); } //ToDo: Doesn'T work for newly created recurrence appointments before save, because Event.Reminder is throwing NullPointerException and Reminders cannot be initialized, therefore moved to after saving //if (slave.Recurrence != null && slave.Reminders != null) //{ // if (slave.Reminders.Overrides != null) // { // slave.Reminders.Overrides.Clear(); // if (master.ReminderSet) // { // var reminder = new Google.Apis.Calendar.v3.Data.EventReminder(); // reminder.Minutes = master.ReminderMinutesBeforeStart; // if (reminder.Minutes > 40300) // { // //ToDo: Check real limit, currently 40300 // Logger.Log("Reminder Minutes to big (" + reminder.Minutes + "), set to maximum of 40300 minutes for appointment: " + master.Subject + " - " + master.Start, EventType.Warning); // reminder.Minutes = 40300; // } // reminder.Method = "popup"; // slave.Reminders.Overrides.Add(reminder); // } // } // slave = SaveGoogleAppointment(slave); //} AppointmentPropertiesUtils.SetOutlookGoogleAppointmentId(this, master, slave); master.Save(); //After saving Google Appointment => also sync recurrence exceptions and save again if ((slave.Creator == null || AppointmentSync.IsOrganizer(slave.Creator.Email)) && master.IsRecurring && master.RecurrenceState == Outlook.OlRecurrenceState.olApptMaster && AppointmentSync.UpdateRecurrenceExceptions(master, slave, this)) { slave = SaveGoogleAppointment(slave); } SyncedCount++; Logger.Log("Updated appointment from Outlook to Google: \"" + master.Subject + " - " + master.Start + "\".", EventType.Information); //} //else //{ // //ToDo:Maybe find as better way, e.g. to ask the user, if he wants to overwrite the invalid appointment // SkippedCount++; // //Logger.Log("Skipped Updating appointment from Outlook to Google because multiple recipients found and invitations NOT sent by Outlook: \"" + master.Subject + " - " + master.Start + "\".", EventType.Information); // Logger.Log("Skipped Updating appointment from Outlook to Google because meeting was received by Outlook: \"" + master.Subject + " - " + master.Start + "\".", EventType.Information); //} } }
public static void SyncNote(NoteMatch match, Syncronizer sync) { Outlook.NoteItem outlookNoteItem = match.OutlookNote; //try //{ if (match.GoogleNote == null && match.OutlookNote != null) { //no google note string googleNotetId = NotePropertiesUtils.GetOutlookGoogleNoteId(sync, outlookNoteItem); if (!string.IsNullOrEmpty(googleNotetId)) { //Redundant check if exist, but in case an error occurred in MatchNotes Document matchingGoogleNote = sync.GetGoogleNoteById(googleNotetId); if (matchingGoogleNote == null) { if (!sync.PromptDelete) { sync.DeleteOutlookResolution = DeleteResolution.DeleteOutlookAlways; } else if (sync.DeleteOutlookResolution != DeleteResolution.DeleteOutlookAlways && sync.DeleteOutlookResolution != DeleteResolution.KeepOutlookAlways) { var r = new ConflictResolver(); sync.DeleteOutlookResolution = r.ResolveDelete(match.OutlookNote); } } switch (sync.DeleteOutlookResolution) { case DeleteResolution.KeepOutlook: case DeleteResolution.KeepOutlookAlways: NotePropertiesUtils.ResetOutlookGoogleNoteId(sync, match.OutlookNote); break; case DeleteResolution.DeleteOutlook: case DeleteResolution.DeleteOutlookAlways: //Avoid recreating a GoogleNote already existing //==> Delete this outlookNote instead if previous match existed but no match exists anymore return; default: throw new ApplicationException("Cancelled"); } } if (sync.SyncOption == SyncOption.GoogleToOutlookOnly) { sync.SkippedCount++; Logger.Log(string.Format("Outlook Note not added to Google, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.OutlookNote.Subject), EventType.Information); return; } //create a Google note from Outlook note match.GoogleNote = new Document(); match.GoogleNote.Type = Document.DocumentType.Document; //match.GoogleNote.Categories.Add(new AtomCategory("http://schemas.google.com/docs/2007#document")); //match.GoogleNote.Categories.Add(new AtomCategory("document")); sync.UpdateNote(outlookNoteItem, match.GoogleNote); } else if (match.OutlookNote == null && match.GoogleNote != null) { // no outlook note if (NotePropertiesUtils.NoteFileExists(match.GoogleNote.Id, sync.SyncProfile)) { if (!sync.PromptDelete) { sync.DeleteGoogleResolution = DeleteResolution.DeleteGoogleAlways; } else if (sync.DeleteGoogleResolution != DeleteResolution.DeleteGoogleAlways && sync.DeleteGoogleResolution != DeleteResolution.KeepGoogleAlways) { var r = new ConflictResolver(); sync.DeleteGoogleResolution = r.ResolveDelete(match.GoogleNote, sync); } switch (sync.DeleteGoogleResolution) { case DeleteResolution.KeepGoogle: case DeleteResolution.KeepGoogleAlways: System.IO.File.Delete(NotePropertiesUtils.GetFileName(match.GoogleNote.Id, sync.SyncProfile)); break; case DeleteResolution.DeleteGoogle: case DeleteResolution.DeleteGoogleAlways: //Avoid recreating a OutlookNote already existing //==> Delete this googleNote instead if previous match existed but no match exists anymore return; default: throw new ApplicationException("Cancelled"); } } if (sync.SyncOption == SyncOption.OutlookToGoogleOnly) { sync.SkippedCount++; Logger.Log(string.Format("Google Note not added to Outlook, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.GoogleNote.Title), EventType.Information); return; } //create a Outlook note from Google note outlookNoteItem = Syncronizer.CreateOutlookNoteItem(Syncronizer.SyncNotesFolder); sync.UpdateNote(match.GoogleNote, outlookNoteItem); match.OutlookNote = outlookNoteItem; } else if (match.OutlookNote != null && match.GoogleNote != null) { //merge note details //determine if this note pair were syncronized //DateTime? lastUpdated = GetOutlookPropertyValueDateTime(match.OutlookNote, sync.OutlookPropertyNameUpdated); DateTime?lastSynced = NotePropertiesUtils.GetOutlookLastSync(sync, outlookNoteItem); if (lastSynced.HasValue) { //note pair was syncronysed before. //determine if google note was updated since last sync //lastSynced is stored without seconds. take that into account. DateTime lastUpdatedOutlook = match.OutlookNote.LastModificationTime.AddSeconds(-match.OutlookNote.LastModificationTime.Second); DateTime lastUpdatedGoogle = match.GoogleNote.Updated.AddSeconds(-match.GoogleNote.Updated.Second); //check if both outlok and google notes where updated sync last sync if (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance) { //both notes were updated. //options: 1) ignore 2) loose one based on SyncOption //throw new Exception("Both notes were updated!"); switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite google note Logger.Log("Outlook and Google note have been updated, Outlook note is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information); sync.UpdateNote(outlookNoteItem, match.GoogleNote); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook note Logger.Log("Outlook and Google note have been updated, Google note is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information); sync.UpdateNote(match.GoogleNote, outlookNoteItem); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); sync.ConflictResolution = r.Resolve(outlookNoteItem, match.GoogleNote, sync, false); } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: Logger.Log(string.Format("User skipped note ({0}).", match.ToString()), EventType.Information); sync.SkippedCount++; break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateNote(outlookNoteItem, match.GoogleNote); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateNote(match.GoogleNote, outlookNoteItem); break; default: throw new ApplicationException("Canceled"); } break; } return; } //check if outlook note was updated (with X second tolerance) if (sync.SyncOption != SyncOption.GoogleToOutlookOnly && (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly ) ) { //outlook note was changed or changed Google note will be overwritten if (lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly) { Logger.Log("Google note has been updated since last sync, but Outlook note is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information); } sync.UpdateNote(outlookNoteItem, match.GoogleNote); //at the moment use outlook as "master" source of notes - in the event of a conflict google note will be overwritten. //TODO: control conflict resolution by SyncOption return; } //check if google note was updated (with X second tolerance) if (sync.SyncOption != SyncOption.OutlookToGoogleOnly && (lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly ) ) { //google note was changed or changed Outlook note will be overwritten if (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly) { Logger.Log("Outlook note has been updated since last sync, but Google note is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information); } sync.UpdateNote(match.GoogleNote, outlookNoteItem); } } else { //notes were never synced. //merge notes. switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite google note sync.UpdateNote(outlookNoteItem, match.GoogleNote); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook note sync.UpdateNote(match.GoogleNote, outlookNoteItem); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); sync.ConflictResolution = r.Resolve(outlookNoteItem, match.GoogleNote, sync, true); } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: //Keep both, Google AND Outlook sync.Notes.Add(new NoteMatch(match.OutlookNote, null)); sync.Notes.Add(new NoteMatch(null, match.GoogleNote)); break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateNote(outlookNoteItem, match.GoogleNote); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateNote(match.GoogleNote, outlookNoteItem); break; default: throw new ApplicationException("Canceled"); } break; } } } else { throw new ArgumentNullException("NotetMatch has all peers null."); } //} //finally //{ //if (outlookNoteItem != null && // match.OutlookNote != null) //{ // match.OutlookNote.Update(outlookNoteItem, sync); // Marshal.ReleaseComObject(outlookNoteItem); // outlookNoteItem = null; //} //} }
public static void SyncNote(NoteMatch match, Synchronizer sync) { Outlook.NoteItem outlookNoteItem = match.OutlookNote; //try //{ if (match.GoogleNote == null && match.OutlookNote != null) { //no Google note string googleNotetId = NotePropertiesUtils.GetOutlookGoogleNoteId(sync, outlookNoteItem); if (!string.IsNullOrEmpty(googleNotetId)) { //Redundant check if exist, but in case an error occurred in MatchNotes Document matchingGoogleNote = sync.GetGoogleNoteById(googleNotetId); if (matchingGoogleNote == null) { if (sync.SyncOption == SyncOption.OutlookToGoogleOnly || !sync.SyncDelete) return; else if (!sync.PromptDelete) sync.DeleteOutlookResolution = DeleteResolution.DeleteOutlookAlways; else if (sync.DeleteOutlookResolution != DeleteResolution.DeleteOutlookAlways && sync.DeleteOutlookResolution != DeleteResolution.KeepOutlookAlways) { var r = new ConflictResolver(); sync.DeleteOutlookResolution = r.ResolveDelete(match.OutlookNote); } switch (sync.DeleteOutlookResolution) { case DeleteResolution.KeepOutlook: case DeleteResolution.KeepOutlookAlways: NotePropertiesUtils.ResetOutlookGoogleNoteId(sync, match.OutlookNote); break; case DeleteResolution.DeleteOutlook: case DeleteResolution.DeleteOutlookAlways: //Avoid recreating a GoogleNote already existing //==> Delete this outlookNote instead if previous match existed but no match exists anymore return; default: throw new ApplicationException("Cancelled"); } } } if (sync.SyncOption == SyncOption.GoogleToOutlookOnly) { sync.SkippedCount++; Logger.Log(string.Format("Outlook Note not added to Google, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.OutlookNote.Subject), EventType.Information); return; } //create a Google note from Outlook note match.GoogleNote = new Document(); match.GoogleNote.Type = Document.DocumentType.Document; //match.GoogleNote.Categories.Add(new AtomCategory("http://schemas.google.com/docs/2007#document")); //match.GoogleNote.Categories.Add(new AtomCategory("document")); sync.UpdateNote(outlookNoteItem, match.GoogleNote); } else if (match.OutlookNote == null && match.GoogleNote != null) { // no outlook note if (NotePropertiesUtils.NoteFileExists(match.GoogleNote.Id, sync.SyncProfile)) { if (sync.SyncOption == SyncOption.GoogleToOutlookOnly || !sync.SyncDelete) return; else if (!sync.PromptDelete) sync.DeleteGoogleResolution = DeleteResolution.DeleteGoogleAlways; else if (sync.DeleteGoogleResolution != DeleteResolution.DeleteGoogleAlways && sync.DeleteGoogleResolution != DeleteResolution.KeepGoogleAlways) { var r = new ConflictResolver(); sync.DeleteGoogleResolution = r.ResolveDelete(match.GoogleNote, sync); } switch (sync.DeleteGoogleResolution) { case DeleteResolution.KeepGoogle: case DeleteResolution.KeepGoogleAlways: System.IO.File.Delete(NotePropertiesUtils.GetFileName(match.GoogleNote.Id, sync.SyncProfile)); break; case DeleteResolution.DeleteGoogle: case DeleteResolution.DeleteGoogleAlways: //Avoid recreating a OutlookNote already existing //==> Delete this googleNote instead if previous match existed but no match exists anymore return; default: throw new ApplicationException("Cancelled"); } } if (sync.SyncOption == SyncOption.OutlookToGoogleOnly) { sync.SkippedCount++; Logger.Log(string.Format("Google Note not added to Outlook, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.GoogleNote.Title), EventType.Information); return; } //create a Outlook note from Google note outlookNoteItem = Synchronizer.CreateOutlookNoteItem(Synchronizer.SyncNotesFolder); sync.UpdateNote(match.GoogleNote, outlookNoteItem); match.OutlookNote = outlookNoteItem; } else if (match.OutlookNote != null && match.GoogleNote != null) { //merge note details //determine if this note pair were synchronized //DateTime? lastUpdated = GetOutlookPropertyValueDateTime(match.OutlookNote, sync.OutlookPropertyNameUpdated); DateTime? lastSynced = NotePropertiesUtils.GetOutlookLastSync(sync,outlookNoteItem); if (lastSynced.HasValue) { //note pair was syncronysed before. //determine if google note was updated since last sync //lastSynced is stored without seconds. take that into account. DateTime lastUpdatedOutlook = match.OutlookNote.LastModificationTime.AddSeconds(-match.OutlookNote.LastModificationTime.Second); DateTime lastUpdatedGoogle = match.GoogleNote.Updated.AddSeconds(-match.GoogleNote.Updated.Second); //check if both outlok and google notes where updated sync last sync if (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance) { //both notes were updated. //options: 1) ignore 2) loose one based on SyncOption //throw new Exception("Both notes were updated!"); switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite google note Logger.Log("Outlook and Google note have been updated, Outlook note is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information); sync.UpdateNote(outlookNoteItem, match.GoogleNote); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook note Logger.Log("Outlook and Google note have been updated, Google note is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information); sync.UpdateNote(match.GoogleNote, outlookNoteItem); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); sync.ConflictResolution = r.Resolve(outlookNoteItem, match.GoogleNote, sync, false); } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: Logger.Log(string.Format("User skipped note ({0}).", match.ToString()), EventType.Information); sync.SkippedCount++; break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateNote(outlookNoteItem, match.GoogleNote); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateNote(match.GoogleNote, outlookNoteItem); break; default: throw new ApplicationException("Canceled"); } break; } return; } //check if outlook note was updated (with X second tolerance) if (sync.SyncOption != SyncOption.GoogleToOutlookOnly && (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly ) ) { //outlook note was changed or changed Google note will be overwritten if (lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.OutlookToGoogleOnly) Logger.Log("Google note has been updated since last sync, but Outlook note is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information); sync.UpdateNote(outlookNoteItem, match.GoogleNote); //at the moment use outlook as "master" source of notes - in the event of a conflict google note will be overwritten. //TODO: control conflict resolution by SyncOption return; } //check if google note was updated (with X second tolerance) if (sync.SyncOption != SyncOption.OutlookToGoogleOnly && (lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance || lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly ) ) { //google note was changed or changed Outlook note will be overwritten if (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance && sync.SyncOption == SyncOption.GoogleToOutlookOnly) Logger.Log("Outlook note has been updated since last sync, but Google note is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information); sync.UpdateNote(match.GoogleNote, outlookNoteItem); } } else { //notes were never synced. //merge notes. switch (sync.SyncOption) { case SyncOption.MergeOutlookWins: case SyncOption.OutlookToGoogleOnly: //overwrite google note sync.UpdateNote(outlookNoteItem, match.GoogleNote); break; case SyncOption.MergeGoogleWins: case SyncOption.GoogleToOutlookOnly: //overwrite outlook note sync.UpdateNote(match.GoogleNote, outlookNoteItem); break; case SyncOption.MergePrompt: //promp for sync option if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways && sync.ConflictResolution != ConflictResolution.OutlookWinsAlways && sync.ConflictResolution != ConflictResolution.SkipAlways) { var r = new ConflictResolver(); sync.ConflictResolution = r.Resolve(outlookNoteItem, match.GoogleNote, sync, true); } switch (sync.ConflictResolution) { case ConflictResolution.Skip: case ConflictResolution.SkipAlways: //Keep both, Google AND Outlook sync.Notes.Add(new NoteMatch(match.OutlookNote, null)); sync.Notes.Add(new NoteMatch(null, match.GoogleNote)); break; case ConflictResolution.OutlookWins: case ConflictResolution.OutlookWinsAlways: sync.UpdateNote(outlookNoteItem, match.GoogleNote); break; case ConflictResolution.GoogleWins: case ConflictResolution.GoogleWinsAlways: sync.UpdateNote(match.GoogleNote, outlookNoteItem); break; default: throw new ApplicationException("Canceled"); } break; } } } else throw new ArgumentNullException("NotetMatch has all peers null."); //} //finally //{ //if (outlookNoteItem != null && // match.OutlookNote != null) //{ // match.OutlookNote.Update(outlookNoteItem, sync); // Marshal.ReleaseComObject(outlookNoteItem); // outlookNoteItem = null; //} //} }